Orange-OpenSource / hurl

Hurl, run and test HTTP requests with plain text.
https://hurl.dev
Apache License 2.0
12.73k stars 477 forks source link

Getting SSL certificate info on reused connection #3031

Closed jcamiel closed 2 months ago

jcamiel commented 2 months ago

When HTTP connections are reused, we can't have certificate information on the reused transfer:

With this Hurl file:

GET https://google.fr
HTTP *

GET https://google.fr
HTTP *
[Asserts]
certificate "Expire-Date" daysAfterNow > 100

Executing:

error: Filter error
  --> /tmp/google.hurl:8:27
   |
   | GET https://google.fr
   | ...
 8 | certificate "Expire-Date" daysAfterNow > 100
   |                           ^^^^^^^^^^^^ missing value to apply filter
   |

When looking at libcurl debug logs:

* ------------------------------------------------------------------------------
* Executing entry 2
*
* Cookie store:
*
* Request:
* GET https://google.fr
*
* Request can be run with the following curl command:
* curl 'https://google.fr'
*
** WARNING: failed to open cookie file ""
** Found bundle for host: 0x600002724b70 [can multiplex]
** Re-using existing connection with host google.fr
** [HTTP/2] [3] OPENED stream for https://google.fr/
** [HTTP/2] [3] [:method: GET]
** [HTTP/2] [3] [:scheme: https]
** [HTTP/2] [3] [:authority: google.fr]
** [HTTP/2] [3] [:path: /]
** [HTTP/2] [3] [accept: */*]
** [HTTP/2] [3] [user-agent: hurl/4.3.0]

The pb seems that, when reusing connection, libcurl doesn't expose certificate through CURLOPT_CERTINFO.

We can reproduce it with this main.c file:

#include <stdio.h>
#include <curl/curl.h>

void print_cert(CURL* curl) {
    int i;
    struct curl_certinfo *ci;
    CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &ci);
    if (res) {
        fprintf(stderr, "Error calling CURLINFO_CERTINFO");
        return;
    }

    fprintf(stderr, "%d certs!\n", ci->num_of_certs);

    for (i = 0; i < ci->num_of_certs; i++) {
        struct curl_slist *slist;

        for (slist = ci->certinfo[i]; slist; slist = slist->next)
            fprintf(stderr, "%s\n", slist->data);
    }
}

int main(void) {
    CURL *curl = curl_easy_init();
    if (!curl) {
        return 1;
    }
    CURLcode res;
    curl_easy_setopt(curl, CURLOPT_URL, "https://google.com");

    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
    curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

    fprintf(stderr, "==================");

    res = curl_easy_perform(curl);
    if (!res) {
        print_cert(curl);
    }

    fprintf(stderr, "==================");

    res = curl_easy_perform(curl);
    if (!res) {
        print_cert(curl);
    }

    curl_easy_cleanup(curl);
}
$ gcc -L/opt/homebrew/opt/curl/lib -l curl main.c -o main
$ ./main 2>&1 >/dev/null | grep certs
3 certs!
0 certs!

The second call doesn't contains SSL certificates.

jcamiel commented 2 months ago

Sent mail in libcurl mailing list => https://curl.se/mail/lib-2024-07/0007.html