From 87ed6a2792cb102006ee187fd9b8bb7eaccced5c Mon Sep 17 00:00:00 2001 From: lwthiker Date: Wed, 2 Mar 2022 10:28:35 +0200 Subject: [PATCH 1/4] Make some of the TLS options configurable This commit makes some of the TLS options that are used for impersonation configurable via libcurl options and command line flags to curl-impersonate. The goal is to give more flexibility in configuring the TLS extensions instead of hardcoding everything into the binary. This will enable using the same binary for impersonating different browsers (e.g. Safari). The following options are now present: * CURLOPT_SSL_EC_CURVES and the '--curves' flag are now usable. These were present in the upstream curl but only for OpenSSL builds. This commit also enables them for BoringSSL. They configure TLS extension 'supported_groups' (no. 10). * CURLOPT_SSL_ENABLE_NPN and the '--no-npn' flags are usable. These were present in the upstream curl but were disabled in a previous commit by commenting out the relevant code (as Chrome disables NPN). They now work and the wrapper scripts use the '--no-npn' flag. * CURLOPT_SSL_ENABLE_ALPS and the '--alps' flag were added. These control the ALPS TLS extension that Chrome uses. * CURLOPT_SSL_SIG_HASH_ALGS and the '--signature-hashes' option were added. These control the clien't list of supported signature & hash algorithms, i.e. TLS extension 'signature_algorithms' (no. 13). --- chrome/curl_chrome98 | 3 +- chrome/curl_edge98 | 3 +- chrome/patches/curl-impersonate.patch | 494 +++++++++++++++++++++++--- 3 files changed, 451 insertions(+), 49 deletions(-) diff --git a/chrome/curl_chrome98 b/chrome/curl_chrome98 index b994886..b5ec6ee 100755 --- a/chrome/curl_chrome98 +++ b/chrome/curl_chrome98 @@ -20,5 +20,6 @@ dir=`echo "$0" | sed 's%/[^/]*$%%'` -H 'Sec-Fetch-Dest: document' \ -H 'Accept-Encoding: gzip, deflate, br' \ -H 'Accept-Language: en-US,en;q=0.9' \ - --http2 --false-start --tlsv1.2 --compressed \ + --http2 --false-start --compressed \ + --tlsv1.2 --no-npn --alps \ $@ diff --git a/chrome/curl_edge98 b/chrome/curl_edge98 index 8244502..b885d68 100755 --- a/chrome/curl_edge98 +++ b/chrome/curl_edge98 @@ -20,5 +20,6 @@ dir=`echo "$0" | sed 's%/[^/]*$%%'` -H 'Sec-Fetch-Dest: document' \ -H 'Accept-Encoding: gzip, deflate, br' \ -H 'Accept-Language: en-US,en;q=0.9' \ - --http2 --false-start --tlsv1.2 --compressed \ + --http2 --false-start --compressed \ + --tlsv1.2 --no-npn --alps \ $@ diff --git a/chrome/patches/curl-impersonate.patch b/chrome/patches/curl-impersonate.patch index 6d2e372..998130e 100644 --- a/chrome/patches/curl-impersonate.patch +++ b/chrome/patches/curl-impersonate.patch @@ -22,16 +22,26 @@ index 63e320236..deb054300 100644 LDFLAGS="$LDFLAGS $LD_H2" diff --git a/include/curl/curl.h b/include/curl/curl.h -index 7b69ce2d6..fe4bb36b9 100644 +index 7b69ce2d6..258461b59 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h -@@ -2135,6 +2135,10 @@ typedef enum { +@@ -2135,6 +2135,20 @@ typedef enum { /* Set MIME option flags. */ CURLOPT(CURLOPT_MIME_OPTIONS, CURLOPTTYPE_LONG, 315), + /* curl-impersonate: A list of headers used by the impersonated browser. + * If given, merged with CURLOPT_HTTPHEADER. */ + CURLOPT(CURLOPT_HTTPBASEHEADER, CURLOPTTYPE_SLISTPOINT, 316), ++ ++ /* curl-impersonate: A list of TLS signature hash algorithms. ++ * See https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.1.4.1 */ ++ CURLOPT(CURLOPT_SSL_SIG_HASH_ALGS, CURLOPTTYPE_STRINGPOINT, 317), ++ ++ /* curl-impersonate: Whether to enable ALPS in TLS or not. ++ * See https://datatracker.ietf.org/doc/html/draft-vvv-tls-alps. ++ * Support for ALPS is minimal and is intended only for the TLS client ++ * hello to match. */ ++ CURLOPT(CURLOPT_SSL_ENABLE_ALPS, CURLOPTTYPE_LONG, 318), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -57,7 +67,7 @@ index 2dbfb26b5..e0bf86169 100644 * NAME curl_easy_getinfo() * diff --git a/lib/easy.c b/lib/easy.c -index 20293a710..ec16aee23 100644 +index 20293a710..72327d75f 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -80,6 +80,7 @@ @@ -68,7 +78,7 @@ index 20293a710..ec16aee23 100644 /* The last 3 #include files should be in this order */ #include "curl_printf.h" -@@ -282,6 +283,161 @@ void curl_global_cleanup(void) +@@ -282,6 +283,185 @@ void curl_global_cleanup(void) init_flags = 0; } @@ -83,6 +93,12 @@ index 20293a710..ec16aee23 100644 + int httpversion; + int ssl_version; + const char *ciphers; ++ /* Enable TLS NPN extension. */ ++ bool npn; ++ /* Enable TLS ALPN extension. */ ++ bool alpn; ++ /* Enable TLS ALPS extension. */ ++ bool alps; + const char *http_headers[IMPERSONATE_MAX_HEADERS]; + /* Other TLS options will come here in the future once they are + * configurable through curl_easy_setopt() */ @@ -107,6 +123,9 @@ index 20293a710..ec16aee23 100644 + "AES256-GCM-SHA384," + "AES128-SHA," + "AES256-SHA", ++ .npn = false, ++ .alpn = true, ++ .alps = true, + .http_headers = { + "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Google Chrome\";v=\"98\"", + "sec-ch-ua-mobile: ?0", @@ -142,6 +161,9 @@ index 20293a710..ec16aee23 100644 + "AES256-GCM-SHA384," + "AES128-SHA," + "AES256-SHA", ++ .npn = false, ++ .alpn = true, ++ .alps = true, + .http_headers = { + "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Microsoft Edge\";v=\"98\"", + "sec-ch-ua-mobile: ?0", @@ -207,6 +229,18 @@ index 20293a710..ec16aee23 100644 + return ret; + } + ++ ret = curl_easy_setopt(data, CURLOPT_SSL_ENABLE_NPN, opts->npn ? 1 : 0); ++ if(ret) ++ return ret; ++ ++ ret = curl_easy_setopt(data, CURLOPT_SSL_ENABLE_ALPN, opts->alpn ? 1 : 0); ++ if(ret) ++ return ret; ++ ++ ret = curl_easy_setopt(data, CURLOPT_SSL_ENABLE_ALPS, opts->alps ? 1 : 0); ++ if(ret) ++ return ret; ++ + /* Build a linked list out of the static array of headers. */ + for(i = 0; i < IMPERSONATE_MAX_HEADERS; i++) { + if(opts->http_headers[i]) { @@ -230,7 +264,7 @@ index 20293a710..ec16aee23 100644 /* * curl_easy_init() is the external interface to alloc, setup and init an * easy handle that is returned. If anything goes wrong, NULL is returned. -@@ -290,6 +446,7 @@ struct Curl_easy *curl_easy_init(void) +@@ -290,6 +470,7 @@ struct Curl_easy *curl_easy_init(void) { CURLcode result; struct Curl_easy *data; @@ -238,7 +272,7 @@ index 20293a710..ec16aee23 100644 /* Make sure we inited the global SSL stuff */ if(!initialized) { -@@ -308,6 +465,22 @@ struct Curl_easy *curl_easy_init(void) +@@ -308,6 +489,22 @@ struct Curl_easy *curl_easy_init(void) return NULL; } @@ -261,7 +295,7 @@ index 20293a710..ec16aee23 100644 return data; } -@@ -878,6 +1051,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) +@@ -878,6 +1075,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) outcurl->state.referer_alloc = TRUE; } @@ -276,7 +310,7 @@ index 20293a710..ec16aee23 100644 * note: the engine name has already been copied by dupset */ if(outcurl->set.str[STRING_SSL_ENGINE]) { diff --git a/lib/easyoptions.c b/lib/easyoptions.c -index 04871ad1e..cd5998146 100644 +index 04871ad1e..f157f3b33 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -130,6 +130,7 @@ struct curl_easyoption Curl_easyopts[] = { @@ -287,6 +321,25 @@ index 04871ad1e..cd5998146 100644 {"HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0}, {"HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0}, {"HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0}, +@@ -297,8 +298,10 @@ struct curl_easyoption Curl_easyopts[] = { + {"SSL_CTX_DATA", CURLOPT_SSL_CTX_DATA, CURLOT_CBPTR, 0}, + {"SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, CURLOT_FUNCTION, 0}, + {"SSL_EC_CURVES", CURLOPT_SSL_EC_CURVES, CURLOT_STRING, 0}, ++ {"SSL_SIG_HASH_ALGS", CURLOPT_SSL_SIG_HASH_ALGS, CURLOT_STRING, 0}, + {"SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CURLOT_LONG, 0}, + {"SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CURLOT_LONG, 0}, ++ {"SSL_ENABLE_ALPS", CURLOPT_SSL_ENABLE_ALPS, CURLOT_LONG, 0}, + {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0}, + {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0}, + {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0}, +@@ -360,6 +363,6 @@ struct curl_easyoption Curl_easyopts[] = { + */ + int Curl_easyopts_check(void) + { +- return ((CURLOPT_LASTENTRY%10000) != (315 + 1)); ++ return ((CURLOPT_LASTENTRY%10000) != (318 + 1)); + } + #endif diff --git a/lib/http.c b/lib/http.c index f08a343e3..879151dd2 100644 --- a/lib/http.c @@ -588,7 +641,7 @@ index f8dcc63b4..e6b728592 100644 #ifdef USE_WINSOCK diff --git a/lib/setopt.c b/lib/setopt.c -index 599ed5d99..1baa48e70 100644 +index 599ed5d99..237e729e6 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -48,6 +48,7 @@ @@ -623,6 +676,32 @@ index 599ed5d99..1baa48e70 100644 case CURLOPT_HTTPHEADER: /* * Set a list with HTTP headers to use (or replace internals with) +@@ -2349,6 +2367,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) + result = Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES], + va_arg(param, char *)); + break; ++ ++ case CURLOPT_SSL_SIG_HASH_ALGS: ++ /* ++ * Set the list of hash algorithms we want to use in the SSL connection. ++ * Specify comma-delimited list of algorithms to use. ++ */ ++ result = Curl_setstropt(&data->set.str[STRING_SSL_SIG_HASH_ALGS], ++ va_arg(param, char *)); ++ break; + #endif + case CURLOPT_IPRESOLVE: + arg = va_arg(param, long); +@@ -2871,6 +2898,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) + case CURLOPT_SSL_ENABLE_ALPN: + data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; ++ case CURLOPT_SSL_ENABLE_ALPS: ++ data->set.ssl_enable_alps = (0 != va_arg(param, long)) ? TRUE : FALSE; ++ break; + #ifdef USE_UNIX_SOCKETS + case CURLOPT_UNIX_SOCKET_PATH: + data->set.abstract_unix_socket = FALSE; diff --git a/lib/transfer.c b/lib/transfer.c index 22704fa15..1e100140c 100644 --- a/lib/transfer.c @@ -645,7 +724,7 @@ index 22704fa15..1e100140c 100644 Curl_headersep(head->data[thislen]) ) return head->data; diff --git a/lib/url.c b/lib/url.c -index 9f1013554..f0f266797 100644 +index 9f1013554..975b567db 100644 --- a/lib/url.c +++ b/lib/url.c @@ -469,6 +469,11 @@ CURLcode Curl_close(struct Curl_easy **datap) @@ -660,11 +739,47 @@ index 9f1013554..f0f266797 100644 #ifndef CURL_DISABLE_DOH if(data->req.doh) { Curl_dyn_free(&data->req.doh->probe[0].serverdoh); +@@ -3808,6 +3813,7 @@ static CURLcode create_conn(struct Curl_easy *data, + data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; + data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; + data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; ++ data->set.ssl.primary.sig_hash_algs = data->set.str[STRING_SSL_SIG_HASH_ALGS]; + + #ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; +@@ -3925,6 +3931,11 @@ static CURLcode create_conn(struct Curl_easy *data, + conn->bits.tls_enable_alpn = TRUE; + if(data->set.ssl_enable_npn) + conn->bits.tls_enable_npn = TRUE; ++ ++ /* curl-impersonatE: Turn on ALPS if ALPN is enabled and the bit is ++ * enabled. */ ++ if(data->set.ssl_enable_alps) ++ conn->bits.tls_enable_alps = TRUE; + } + + if(waitpipe) diff --git a/lib/urldata.h b/lib/urldata.h -index cc9c88870..a35a20e10 100644 +index cc9c88870..db432abec 100644 --- a/lib/urldata.h +++ b/lib/urldata.h -@@ -1421,6 +1421,19 @@ struct UrlState { +@@ -257,6 +257,7 @@ struct ssl_primary_config { + struct curl_blob *ca_info_blob; + struct curl_blob *issuercert_blob; + char *curves; /* list of curves to use */ ++ char *sig_hash_algs; /* List of signature hash algorithms to use */ + BIT(verifypeer); /* set TRUE if this is desired */ + BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ + BIT(verifystatus); /* set TRUE if certificate status must be checked */ +@@ -517,6 +518,7 @@ struct ConnectBits { + BIT(tcp_fastopen); /* use TCP Fast Open */ + BIT(tls_enable_npn); /* TLS NPN extension? */ + BIT(tls_enable_alpn); /* TLS ALPN extension? */ ++ BIT(tls_enable_alps); /* TLS ALPS extension? */ + BIT(connect_only); + #ifndef CURL_DISABLE_DOH + BIT(doh); +@@ -1421,6 +1423,19 @@ struct UrlState { CURLcode hresult; /* used to pass return codes back from hyper callbacks */ #endif @@ -684,8 +799,24 @@ index cc9c88870..a35a20e10 100644 /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { +@@ -1579,6 +1594,7 @@ enum dupstring { + STRING_DNS_LOCAL_IP4, + STRING_DNS_LOCAL_IP6, + STRING_SSL_EC_CURVES, ++ STRING_SSL_SIG_HASH_ALGS, + + /* -- end of null-terminated strings -- */ + +@@ -1849,6 +1865,7 @@ struct UserDefined { + BIT(tcp_fastopen); /* use TCP Fast Open */ + BIT(ssl_enable_npn); /* TLS NPN extension? */ + BIT(ssl_enable_alpn);/* TLS ALPN extension? */ ++ BIT(ssl_enable_alps);/* TLS ALPS extension? */ + BIT(path_as_is); /* allow dotdots? */ + BIT(pipewait); /* wait for multiplex status before starting a new + connection */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c -index f836c63b0..5c562549f 100644 +index f836c63b0..a5c3a23ff 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -76,6 +76,8 @@ @@ -697,7 +828,132 @@ index f836c63b0..5c562549f 100644 #ifdef USE_AMISSL #include "amigaos.h" #endif -@@ -2629,6 +2631,31 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, +@@ -209,6 +211,10 @@ + !defined(OPENSSL_IS_BORINGSSL)) + #define HAVE_SSL_CTX_SET_CIPHERSUITES + #define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH ++#endif ++ ++#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L) && \ ++ !defined(LIBRESSL_VERSION_NUMBER)) + /* SET_EC_CURVES is available under the same preconditions: see + * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html + */ +@@ -253,6 +259,113 @@ + #define HAVE_OPENSSL_VERSION + #endif + ++#if defined(OPENSSL_IS_BORINGSSL) ++#define HAVE_SSL_CTX_SET_VERIFY_ALGORITHM_PREFS ++ ++/* ++ * kMaxSignatureAlgorithmNameLen and kSignatureAlgorithmNames ++ * Taken from BoringSSL, see ssl/ssl_privkey.cc ++ * */ ++static const size_t kMaxSignatureAlgorithmNameLen = 23; ++ ++static const struct { ++ uint16_t signature_algorithm; ++ const char *name; ++} kSignatureAlgorithmNames[] = { ++ {SSL_SIGN_RSA_PKCS1_MD5_SHA1, "rsa_pkcs1_md5_sha1"}, ++ {SSL_SIGN_RSA_PKCS1_SHA1, "rsa_pkcs1_sha1"}, ++ {SSL_SIGN_RSA_PKCS1_SHA256, "rsa_pkcs1_sha256"}, ++ {SSL_SIGN_RSA_PKCS1_SHA384, "rsa_pkcs1_sha384"}, ++ {SSL_SIGN_RSA_PKCS1_SHA512, "rsa_pkcs1_sha512"}, ++ {SSL_SIGN_ECDSA_SHA1, "ecdsa_sha1"}, ++ {SSL_SIGN_ECDSA_SECP256R1_SHA256, "ecdsa_secp256r1_sha256"}, ++ {SSL_SIGN_ECDSA_SECP384R1_SHA384, "ecdsa_secp384r1_sha384"}, ++ {SSL_SIGN_ECDSA_SECP521R1_SHA512, "ecdsa_secp521r1_sha512"}, ++ {SSL_SIGN_RSA_PSS_RSAE_SHA256, "rsa_pss_rsae_sha256"}, ++ {SSL_SIGN_RSA_PSS_RSAE_SHA384, "rsa_pss_rsae_sha384"}, ++ {SSL_SIGN_RSA_PSS_RSAE_SHA512, "rsa_pss_rsae_sha512"}, ++ {SSL_SIGN_ED25519, "ed25519"}, ++}; ++ ++#define MAX_SIG_ALGS \ ++ sizeof(kSignatureAlgorithmNames) / sizeof(kSignatureAlgorithmNames[0]) ++ ++/* Default signature hash algorithms taken from Chrome/Chromium. ++ * See kVerifyPeers @ net/socket/ssl_client_socket_impl.cc */ ++static const uint16_t default_sig_algs[] = { ++ SSL_SIGN_ECDSA_SECP256R1_SHA256, SSL_SIGN_RSA_PSS_RSAE_SHA256, ++ SSL_SIGN_RSA_PKCS1_SHA256, SSL_SIGN_ECDSA_SECP384R1_SHA384, ++ SSL_SIGN_RSA_PSS_RSAE_SHA384, SSL_SIGN_RSA_PKCS1_SHA384, ++ SSL_SIGN_RSA_PSS_RSAE_SHA512, SSL_SIGN_RSA_PKCS1_SHA512, ++}; ++ ++#define DEFAULT_SIG_ALGS_LENGTH \ ++ sizeof(default_sig_algs) / sizeof(default_sig_algs[0]) ++ ++static CURLcode parse_sig_algs(struct Curl_easy *data, ++ const char *sigalgs, ++ uint16_t *algs, ++ size_t *nalgs) ++{ ++ *nalgs = 0; ++ while (sigalgs && sigalgs[0]) { ++ int i; ++ bool found = FALSE; ++ const char *end; ++ size_t len; ++ char algname[kMaxSignatureAlgorithmNameLen + 1]; ++ ++ end = strpbrk(sigalgs, ":,"); ++ if (end) ++ len = end - sigalgs; ++ else ++ len = strlen(sigalgs); ++ ++ if (len > kMaxSignatureAlgorithmNameLen) { ++ failf(data, "Bad signature hash algorithm list"); ++ return CURLE_BAD_FUNCTION_ARGUMENT; ++ } ++ ++ if (!len) { ++ ++sigalgs; ++ continue; ++ } ++ ++ if (*nalgs == MAX_SIG_ALGS) { ++ /* Reached the maximum number of possible algorithms, but more data ++ * available in the list. */ ++ failf(data, "Bad signature hash algorithm list"); ++ return CURLE_BAD_FUNCTION_ARGUMENT; ++ } ++ ++ memcpy(algname, sigalgs, len); ++ algname[len] = 0; ++ ++ for (i = 0; i < MAX_SIG_ALGS; i++) { ++ if (strcasecompare(algname, kSignatureAlgorithmNames[i].name)) { ++ algs[*nalgs] = kSignatureAlgorithmNames[i].signature_algorithm; ++ (*nalgs)++; ++ found = TRUE; ++ break; ++ } ++ } ++ ++ if (!found) { ++ failf(data, "Unknown signature hash algorithm: '%s'", algname); ++ return CURLE_BAD_FUNCTION_ARGUMENT; ++ } ++ ++ if (end) ++ sigalgs = ++end; ++ else ++ break; ++ } ++ ++ return CURLE_OK; ++} ++ ++#endif ++ + struct ssl_backend_data { + struct Curl_easy *logger; /* transfer handle to pass trace logs to, only + using sockindex 0 */ +@@ -2629,6 +2742,31 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); } @@ -729,7 +985,7 @@ index f836c63b0..5c562549f 100644 static CURLcode ossl_connect_step1(struct Curl_easy *data, struct connectdata *conn, int sockindex) { -@@ -2767,7 +2794,10 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, +@@ -2767,7 +2905,10 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, ctx_options = SSL_OP_ALL; #ifdef SSL_OP_NO_TICKET @@ -741,19 +997,43 @@ index f836c63b0..5c562549f 100644 #endif #ifdef SSL_OP_NO_COMPRESSION -@@ -2821,8 +2851,11 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, - SSL_CTX_set_options(backend->ctx, ctx_options); - - #ifdef HAS_NPN -+ /* curl-impersonate: Do not enable the NPN extension. */ -+ /* - if(conn->bits.tls_enable_npn) - SSL_CTX_set_next_proto_select_cb(backend->ctx, select_next_proto_cb, data); -+ */ +@@ -2912,6 +3053,35 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, + } #endif - #ifdef HAS_ALPN -@@ -2937,6 +2970,19 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, ++#ifdef HAVE_SSL_CTX_SET_VERIFY_ALGORITHM_PREFS ++ { ++ uint16_t algs[MAX_SIG_ALGS]; ++ size_t nalgs; ++ /* curl-impersonate: Set the signature algorithms (TLS extension 13). ++ * See net/socket/ssl_client_socket_impl.cc in Chromium's source. */ ++ char *sig_hash_algs = SSL_CONN_CONFIG(sig_hash_algs); ++ if (sig_hash_algs) { ++ CURLcode result = parse_sig_algs(data, sig_hash_algs, algs, &nalgs); ++ if (result) ++ return result; ++ if (!SSL_CTX_set_verify_algorithm_prefs(backend->ctx, algs, nalgs)) { ++ failf(data, "failed setting signature hash algorithms list: '%s'", ++ sig_hash_algs); ++ return CURLE_SSL_CIPHER; ++ } ++ } else { ++ /* Use defaults from Chrome. */ ++ if (!SSL_CTX_set_verify_algorithm_prefs(backend->ctx, ++ default_sig_algs, ++ DEFAULT_SIG_ALGS_LENGTH)) { ++ failf(data, "failed setting signature hash algorithms list: '%s'", ++ sig_hash_algs); ++ return CURLE_SSL_CIPHER; ++ } ++ } ++ } ++#endif ++ + #ifdef USE_OPENSSL_SRP + if(ssl_authtype == CURL_TLSAUTH_SRP) { + char * const ssl_username = SSL_SET_OPTION(username); +@@ -2937,6 +3107,19 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, } #endif @@ -773,16 +1053,22 @@ index f836c63b0..5c562549f 100644 #if defined(USE_WIN32_CRYPTO) /* Import certificates from the Windows root certificate store if requested. -@@ -3236,6 +3282,41 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, +@@ -3236,6 +3419,33 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, SSL_set_connect_state(backend->handle); -+#ifdef USE_HTTP2 -+ /* curl-impersonate: This adds the ALPS extension (17513). -+ * Chromium calls this function as well in SSLClientSocketImpl::Init(). -+ * The 4th parameter is called "settings", and I don't know what it -+ * should contain. For now, use an empty string. */ -+ SSL_add_application_settings(backend->handle, "h2", 2, NULL, 0); ++#if defined(HAS_ALPN) && defined(USE_HTTP2) ++ if(conn->bits.tls_enable_alpn && ++ data->state.httpwant >= CURL_HTTP_VERSION_2 && ++ conn->bits.tls_enable_alps) { ++ /* curl-impersonate: This adds the ALPS extension (17513). ++ * Chromium calls this function as well in SSLClientSocketImpl::Init(). ++ * The 4th parameter is called "settings", and I don't know what it ++ * should contain. For now, use an empty string. */ ++ SSL_add_application_settings(backend->handle, ALPN_H2, ALPN_H2_LENGTH, ++ NULL, 0); ++ infof(data, "ALPS, offering %s", ALPN_H2); ++ } +#endif + + SSL_set_options(backend->handle, @@ -797,21 +1083,135 @@ index f836c63b0..5c562549f 100644 + + /* curl-impersonate: Some SSL settings copied over from Chrome. */ + SSL_set_shed_handshake_config(backend->handle, 1); -+ -+ /* curl-impersonate: Set the signature algorithms. -+ * (TLS extension 13). -+ * See net/socket/ssl_client_socket_impl.cc in Chromium's source. */ -+ static const uint16_t kVerifyPrefs[] = { -+ SSL_SIGN_ECDSA_SECP256R1_SHA256, SSL_SIGN_RSA_PSS_RSAE_SHA256, -+ SSL_SIGN_RSA_PKCS1_SHA256, SSL_SIGN_ECDSA_SECP384R1_SHA384, -+ SSL_SIGN_RSA_PSS_RSAE_SHA384, SSL_SIGN_RSA_PKCS1_SHA384, -+ SSL_SIGN_RSA_PSS_RSAE_SHA512, SSL_SIGN_RSA_PKCS1_SHA512, -+ }; -+ if (!SSL_set_verify_algorithm_prefs(backend->handle, kVerifyPrefs, -+ sizeof(kVerifyPrefs) / sizeof(kVerifyPrefs[0]))) { -+ return CURLE_SSL_CIPHER; -+ } + backend->server_cert = 0x0; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && +diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c +index 6007bbba0..24dd1e20b 100644 +--- a/lib/vtls/vtls.c ++++ b/lib/vtls/vtls.c +@@ -156,6 +156,7 @@ Curl_ssl_config_matches(struct ssl_primary_config *data, + Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) && + Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) && + Curl_safe_strcasecompare(data->curves, needle->curves) && ++ Curl_safe_strcasecompare(data->sig_hash_algs, needle->sig_hash_algs) && + Curl_safe_strcasecompare(data->pinned_key, needle->pinned_key)) + return TRUE; + +@@ -186,6 +187,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, + CLONE_STRING(cipher_list13); + CLONE_STRING(pinned_key); + CLONE_STRING(curves); ++ CLONE_STRING(sig_hash_algs); + + return TRUE; + } +@@ -205,6 +207,7 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) + Curl_safefree(sslc->ca_info_blob); + Curl_safefree(sslc->issuercert_blob); + Curl_safefree(sslc->curves); ++ Curl_safefree(sslc->sig_hash_algs); + } + + #ifdef USE_SSL +diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h +index 227b914e3..d2b0d5488 100644 +--- a/src/tool_cfgable.h ++++ b/src/tool_cfgable.h +@@ -165,6 +165,7 @@ struct OperationConfig { + bool crlf; + char *customrequest; + char *ssl_ec_curves; ++ char *ssl_sig_hash_algs; + char *krblevel; + char *request_target; + long httpversion; +@@ -274,6 +275,7 @@ struct OperationConfig { + char *oauth_bearer; /* OAuth 2.0 bearer token */ + bool nonpn; /* enable/disable TLS NPN extension */ + bool noalpn; /* enable/disable TLS ALPN extension */ ++ bool alps; /* enable/disable TLS ALPS extension */ + char *unix_socket_path; /* path to Unix domain socket */ + bool abstract_unix_socket; /* path to an abstract Unix domain socket */ + bool falsestart; +diff --git a/src/tool_getparam.c b/src/tool_getparam.c +index 7abbcc639..cf7ac3bf2 100644 +--- a/src/tool_getparam.c ++++ b/src/tool_getparam.c +@@ -279,6 +279,8 @@ static const struct LongShort aliases[]= { + {"EC", "etag-save", ARG_FILENAME}, + {"ED", "etag-compare", ARG_FILENAME}, + {"EE", "curves", ARG_STRING}, ++ {"EG", "signature-hashes", ARG_STRING}, ++ {"EH", "alps", ARG_BOOL}, + {"f", "fail", ARG_BOOL}, + {"fa", "fail-early", ARG_BOOL}, + {"fb", "styled-output", ARG_BOOL}, +@@ -1794,6 +1796,16 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ + GetStr(&config->ssl_ec_curves, nextarg); + break; + ++ case 'G': ++ /* --signature-hashes */ ++ GetStr(&config->ssl_sig_hash_algs, nextarg); ++ break; ++ ++ case 'H': ++ /* --alps */ ++ config->alps = toggle; ++ break; ++ + default: /* unknown flag */ + return PARAM_OPTION_UNKNOWN; + } +diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c +index 448fc7cb3..85bde0c80 100644 +--- a/src/tool_listhelp.c ++++ b/src/tool_listhelp.c +@@ -106,6 +106,9 @@ const struct helptxt helptext[] = { + {" --curves ", + "(EC) TLS key exchange algorithm(s) to request", + CURLHELP_TLS}, ++ {" --signature-hashes ", ++ "TLS signature hash algorithm(s) to use", ++ CURLHELP_TLS}, + {"-d, --data ", + "HTTP POST data", + CURLHELP_IMPORTANT | CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, +@@ -379,6 +382,9 @@ const struct helptxt helptext[] = { + {" --no-alpn", + "Disable the ALPN TLS extension", + CURLHELP_TLS | CURLHELP_HTTP}, ++ {" --alps", ++ "Enable the ALPS TLS extension", ++ CURLHELP_TLS | CURLHELP_HTTP}, + {"-N, --no-buffer", + "Disable buffering of the output stream", + CURLHELP_CURL}, +diff --git a/src/tool_operate.c b/src/tool_operate.c +index fe2c43b55..f24f57c25 100644 +--- a/src/tool_operate.c ++++ b/src/tool_operate.c +@@ -1520,6 +1520,10 @@ static CURLcode single_transfer(struct GlobalConfig *global, + if(config->ssl_ec_curves) + my_setopt_str(curl, CURLOPT_SSL_EC_CURVES, config->ssl_ec_curves); + ++ if(config->ssl_sig_hash_algs) ++ my_setopt_str(curl, CURLOPT_SSL_SIG_HASH_ALGS, ++ config->ssl_sig_hash_algs); ++ + if(curlinfo->features & CURL_VERSION_SSL) { + /* Check if config->cert is a PKCS#11 URI and set the + * config->cert_type if necessary */ +@@ -2061,6 +2065,10 @@ static CURLcode single_transfer(struct GlobalConfig *global, + my_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, 0L); + } + ++ if(config->alps) { ++ my_setopt(curl, CURLOPT_SSL_ENABLE_ALPS, 1L); ++ } ++ + /* new in 7.40.0, abstract support added in 7.53.0 */ + if(config->unix_socket_path) { + if(config->abstract_unix_socket) { From bafc1416e3070385f3fe74eb82bc3cffc12cafb6 Mon Sep 17 00:00:00 2001 From: lwthiker Date: Wed, 2 Mar 2022 15:23:24 +0200 Subject: [PATCH 2/4] Add full support for TLS certificate compression This commit adds: * Support for configuring the TLS certificate compression algorithms the client is willing to receive via the CURLOPT_SSL_CERT_COMPRESSION option or the '--cert-compression' command line flag. * Support for decompressing zlib-compressed certificates in addition to brotli. Previously brotli decompression only was available and it was hardcoded into the binary. --- chrome/curl_chrome98 | 1 + chrome/curl_edge98 | 1 + chrome/patches/curl-impersonate.patch | 284 +++++++++++++++++++++----- 3 files changed, 234 insertions(+), 52 deletions(-) diff --git a/chrome/curl_chrome98 b/chrome/curl_chrome98 index b5ec6ee..f29400b 100755 --- a/chrome/curl_chrome98 +++ b/chrome/curl_chrome98 @@ -22,4 +22,5 @@ dir=`echo "$0" | sed 's%/[^/]*$%%'` -H 'Accept-Language: en-US,en;q=0.9' \ --http2 --false-start --compressed \ --tlsv1.2 --no-npn --alps \ + --cert-compression brotli \ $@ diff --git a/chrome/curl_edge98 b/chrome/curl_edge98 index b885d68..2139af6 100755 --- a/chrome/curl_edge98 +++ b/chrome/curl_edge98 @@ -22,4 +22,5 @@ dir=`echo "$0" | sed 's%/[^/]*$%%'` -H 'Accept-Language: en-US,en;q=0.9' \ --http2 --false-start --compressed \ --tlsv1.2 --no-npn --alps \ + --cert-compression brotli \ $@ diff --git a/chrome/patches/curl-impersonate.patch b/chrome/patches/curl-impersonate.patch index 998130e..0646682 100644 --- a/chrome/patches/curl-impersonate.patch +++ b/chrome/patches/curl-impersonate.patch @@ -22,10 +22,10 @@ index 63e320236..deb054300 100644 LDFLAGS="$LDFLAGS $LD_H2" diff --git a/include/curl/curl.h b/include/curl/curl.h -index 7b69ce2d6..258461b59 100644 +index 7b69ce2d6..1405a228e 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h -@@ -2135,6 +2135,20 @@ typedef enum { +@@ -2135,6 +2135,26 @@ typedef enum { /* Set MIME option flags. */ CURLOPT(CURLOPT_MIME_OPTIONS, CURLOPTTYPE_LONG, 315), @@ -42,6 +42,12 @@ index 7b69ce2d6..258461b59 100644 + * Support for ALPS is minimal and is intended only for the TLS client + * hello to match. */ + CURLOPT(CURLOPT_SSL_ENABLE_ALPS, CURLOPTTYPE_LONG, 318), ++ ++ /* curl-impersonate: Comma-separated list of certificate compression ++ * algorithms to use. These are published in the client hello. ++ * Supported algorithms are "zlib" and "brotli". ++ * See https://datatracker.ietf.org/doc/html/rfc8879 */ ++ CURLOPT(CURLOPT_SSL_CERT_COMPRESSION, CURLOPTTYPE_STRINGPOINT, 319), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -67,7 +73,7 @@ index 2dbfb26b5..e0bf86169 100644 * NAME curl_easy_getinfo() * diff --git a/lib/easy.c b/lib/easy.c -index 20293a710..72327d75f 100644 +index 20293a710..5182c56b4 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -80,6 +80,7 @@ @@ -78,7 +84,7 @@ index 20293a710..72327d75f 100644 /* The last 3 #include files should be in this order */ #include "curl_printf.h" -@@ -282,6 +283,185 @@ void curl_global_cleanup(void) +@@ -282,6 +283,198 @@ void curl_global_cleanup(void) init_flags = 0; } @@ -99,6 +105,9 @@ index 20293a710..72327d75f 100644 + bool alpn; + /* Enable TLS ALPS extension. */ + bool alps; ++ /* TLS certificate compression algorithms. ++ * (TLS extension 27) */ ++ const char *cert_compression; + const char *http_headers[IMPERSONATE_MAX_HEADERS]; + /* Other TLS options will come here in the future once they are + * configurable through curl_easy_setopt() */ @@ -126,6 +135,7 @@ index 20293a710..72327d75f 100644 + .npn = false, + .alpn = true, + .alps = true, ++ .cert_compression = "brotli", + .http_headers = { + "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Google Chrome\";v=\"98\"", + "sec-ch-ua-mobile: ?0", @@ -164,6 +174,7 @@ index 20293a710..72327d75f 100644 + .npn = false, + .alpn = true, + .alps = true, ++ .cert_compression = "brotli", + .http_headers = { + "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Microsoft Edge\";v=\"98\"", + "sec-ch-ua-mobile: ?0", @@ -241,6 +252,14 @@ index 20293a710..72327d75f 100644 + if(ret) + return ret; + ++ if(opts->cert_compression) { ++ ret = curl_easy_setopt(data, ++ CURLOPT_SSL_CERT_COMPRESSION, ++ opts->cert_compression); ++ if(ret) ++ return ret; ++ } ++ + /* Build a linked list out of the static array of headers. */ + for(i = 0; i < IMPERSONATE_MAX_HEADERS; i++) { + if(opts->http_headers[i]) { @@ -264,7 +283,7 @@ index 20293a710..72327d75f 100644 /* * curl_easy_init() is the external interface to alloc, setup and init an * easy handle that is returned. If anything goes wrong, NULL is returned. -@@ -290,6 +470,7 @@ struct Curl_easy *curl_easy_init(void) +@@ -290,6 +483,7 @@ struct Curl_easy *curl_easy_init(void) { CURLcode result; struct Curl_easy *data; @@ -272,7 +291,7 @@ index 20293a710..72327d75f 100644 /* Make sure we inited the global SSL stuff */ if(!initialized) { -@@ -308,6 +489,22 @@ struct Curl_easy *curl_easy_init(void) +@@ -308,6 +502,22 @@ struct Curl_easy *curl_easy_init(void) return NULL; } @@ -295,7 +314,7 @@ index 20293a710..72327d75f 100644 return data; } -@@ -878,6 +1075,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) +@@ -878,6 +1088,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) outcurl->state.referer_alloc = TRUE; } @@ -310,7 +329,7 @@ index 20293a710..72327d75f 100644 * note: the engine name has already been copied by dupset */ if(outcurl->set.str[STRING_SSL_ENGINE]) { diff --git a/lib/easyoptions.c b/lib/easyoptions.c -index 04871ad1e..f157f3b33 100644 +index 04871ad1e..99c17e35a 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -130,6 +130,7 @@ struct curl_easyoption Curl_easyopts[] = { @@ -321,23 +340,24 @@ index 04871ad1e..f157f3b33 100644 {"HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0}, {"HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0}, {"HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0}, -@@ -297,8 +298,10 @@ struct curl_easyoption Curl_easyopts[] = { +@@ -297,8 +298,11 @@ struct curl_easyoption Curl_easyopts[] = { {"SSL_CTX_DATA", CURLOPT_SSL_CTX_DATA, CURLOT_CBPTR, 0}, {"SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, CURLOT_FUNCTION, 0}, {"SSL_EC_CURVES", CURLOPT_SSL_EC_CURVES, CURLOT_STRING, 0}, + {"SSL_SIG_HASH_ALGS", CURLOPT_SSL_SIG_HASH_ALGS, CURLOT_STRING, 0}, ++ {"SSL_CERT_COMPRESSION", CURLOPT_SSL_CERT_COMPRESSION, CURLOT_STRING, 0}, {"SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CURLOT_LONG, 0}, {"SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CURLOT_LONG, 0}, + {"SSL_ENABLE_ALPS", CURLOPT_SSL_ENABLE_ALPS, CURLOT_LONG, 0}, {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0}, {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0}, {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0}, -@@ -360,6 +363,6 @@ struct curl_easyoption Curl_easyopts[] = { +@@ -360,6 +364,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (315 + 1)); -+ return ((CURLOPT_LASTENTRY%10000) != (318 + 1)); ++ return ((CURLOPT_LASTENTRY%10000) != (319 + 1)); } #endif diff --git a/lib/http.c b/lib/http.c @@ -641,7 +661,7 @@ index f8dcc63b4..e6b728592 100644 #ifdef USE_WINSOCK diff --git a/lib/setopt.c b/lib/setopt.c -index 599ed5d99..237e729e6 100644 +index 599ed5d99..fc7ec2a7c 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -48,6 +48,7 @@ @@ -676,7 +696,7 @@ index 599ed5d99..237e729e6 100644 case CURLOPT_HTTPHEADER: /* * Set a list with HTTP headers to use (or replace internals with) -@@ -2349,6 +2367,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) +@@ -2349,6 +2367,27 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) result = Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES], va_arg(param, char *)); break; @@ -689,10 +709,22 @@ index 599ed5d99..237e729e6 100644 + result = Curl_setstropt(&data->set.str[STRING_SSL_SIG_HASH_ALGS], + va_arg(param, char *)); + break; ++ ++ case CURLOPT_SSL_CERT_COMPRESSION: ++ /* ++ * Set the list of ceritifcate compression algorithms we support in the TLS ++ * connection. ++ * Specify comma-delimited list of algorithms to use. Options are "zlib" ++ * and "brotli". ++ */ ++ result = Curl_setstropt(&data->set.str[STRING_SSL_CERT_COMPRESSION], ++ va_arg(param, char *)); ++ break; ++ #endif case CURLOPT_IPRESOLVE: arg = va_arg(param, long); -@@ -2871,6 +2898,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) +@@ -2871,6 +2910,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_SSL_ENABLE_ALPN: data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE; break; @@ -724,7 +756,7 @@ index 22704fa15..1e100140c 100644 Curl_headersep(head->data[thislen]) ) return head->data; diff --git a/lib/url.c b/lib/url.c -index 9f1013554..975b567db 100644 +index 9f1013554..7aa3ccf00 100644 --- a/lib/url.c +++ b/lib/url.c @@ -469,6 +469,11 @@ CURLcode Curl_close(struct Curl_easy **datap) @@ -739,15 +771,17 @@ index 9f1013554..975b567db 100644 #ifndef CURL_DISABLE_DOH if(data->req.doh) { Curl_dyn_free(&data->req.doh->probe[0].serverdoh); -@@ -3808,6 +3813,7 @@ static CURLcode create_conn(struct Curl_easy *data, +@@ -3808,6 +3813,9 @@ static CURLcode create_conn(struct Curl_easy *data, data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; + data->set.ssl.primary.sig_hash_algs = data->set.str[STRING_SSL_SIG_HASH_ALGS]; ++ data->set.ssl.primary.cert_compression = ++ data->set.str[STRING_SSL_CERT_COMPRESSION]; #ifndef CURL_DISABLE_PROXY data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; -@@ -3925,6 +3931,11 @@ static CURLcode create_conn(struct Curl_easy *data, +@@ -3925,6 +3933,11 @@ static CURLcode create_conn(struct Curl_easy *data, conn->bits.tls_enable_alpn = TRUE; if(data->set.ssl_enable_npn) conn->bits.tls_enable_npn = TRUE; @@ -760,18 +794,19 @@ index 9f1013554..975b567db 100644 if(waitpipe) diff --git a/lib/urldata.h b/lib/urldata.h -index cc9c88870..db432abec 100644 +index cc9c88870..0c6d56614 100644 --- a/lib/urldata.h +++ b/lib/urldata.h -@@ -257,6 +257,7 @@ struct ssl_primary_config { +@@ -257,6 +257,8 @@ struct ssl_primary_config { struct curl_blob *ca_info_blob; struct curl_blob *issuercert_blob; char *curves; /* list of curves to use */ + char *sig_hash_algs; /* List of signature hash algorithms to use */ ++ char *cert_compression; /* List of certificate compression algorithms. */ BIT(verifypeer); /* set TRUE if this is desired */ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ BIT(verifystatus); /* set TRUE if certificate status must be checked */ -@@ -517,6 +518,7 @@ struct ConnectBits { +@@ -517,6 +519,7 @@ struct ConnectBits { BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(tls_enable_npn); /* TLS NPN extension? */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ @@ -779,7 +814,7 @@ index cc9c88870..db432abec 100644 BIT(connect_only); #ifndef CURL_DISABLE_DOH BIT(doh); -@@ -1421,6 +1423,19 @@ struct UrlState { +@@ -1421,6 +1424,19 @@ struct UrlState { CURLcode hresult; /* used to pass return codes back from hyper callbacks */ #endif @@ -799,15 +834,16 @@ index cc9c88870..db432abec 100644 /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { -@@ -1579,6 +1594,7 @@ enum dupstring { +@@ -1579,6 +1595,8 @@ enum dupstring { STRING_DNS_LOCAL_IP4, STRING_DNS_LOCAL_IP6, STRING_SSL_EC_CURVES, + STRING_SSL_SIG_HASH_ALGS, ++ STRING_SSL_CERT_COMPRESSION, /* -- end of null-terminated strings -- */ -@@ -1849,6 +1865,7 @@ struct UserDefined { +@@ -1849,6 +1867,7 @@ struct UserDefined { BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(ssl_enable_npn); /* TLS NPN extension? */ BIT(ssl_enable_alpn);/* TLS ALPN extension? */ @@ -816,19 +852,24 @@ index cc9c88870..db432abec 100644 BIT(pipewait); /* wait for multiplex status before starting a new connection */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c -index f836c63b0..a5c3a23ff 100644 +index f836c63b0..6ef19b840 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c -@@ -76,6 +76,8 @@ +@@ -76,6 +76,13 @@ #include #include ++#ifdef HAVE_ZLIB_H ++#include ++#endif ++#ifdef HAVE_BROTLI +#include ++#endif + #ifdef USE_AMISSL #include "amigaos.h" #endif -@@ -209,6 +211,10 @@ +@@ -209,6 +216,10 @@ !defined(OPENSSL_IS_BORINGSSL)) #define HAVE_SSL_CTX_SET_CIPHERSUITES #define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH @@ -839,7 +880,7 @@ index f836c63b0..a5c3a23ff 100644 /* SET_EC_CURVES is available under the same preconditions: see * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html */ -@@ -253,6 +259,113 @@ +@@ -253,6 +264,113 @@ #define HAVE_OPENSSL_VERSION #endif @@ -953,10 +994,53 @@ index f836c63b0..a5c3a23ff 100644 struct ssl_backend_data { struct Curl_easy *logger; /* transfer handle to pass trace logs to, only using sockindex 0 */ -@@ -2629,6 +2742,31 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, +@@ -2629,6 +2747,151 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); } ++#ifdef HAVE_LIBZ ++int DecompressZlibCert(SSL *ssl, ++ CRYPTO_BUFFER** out, ++ size_t uncompressed_len, ++ const uint8_t* in, ++ size_t in_len) ++{ ++ z_stream strm; ++ uint8_t* data; ++ CRYPTO_BUFFER* decompressed = CRYPTO_BUFFER_alloc(&data, uncompressed_len); ++ if(!decompressed) { ++ return 0; ++ } ++ ++ strm.zalloc = NULL; ++ strm.zfree = NULL; ++ strm.opaque = NULL; ++ strm.next_in = (Bytef *)in; ++ strm.avail_in = in_len; ++ strm.next_out = (Bytef *)data; ++ strm.avail_out = uncompressed_len; ++ ++ if(inflateInit(&strm) != Z_OK) { ++ CRYPTO_BUFFER_free(decompressed); ++ return 0; ++ } ++ ++ if(inflate(&strm, Z_FINISH) != Z_STREAM_END || ++ strm.avail_in != 0 || ++ strm.avail_out != 0) { ++ inflateEnd(&strm); ++ CRYPTO_BUFFER_free(decompressed); ++ return 0; ++ } ++ ++ inflateEnd(&strm); ++ *out = decompressed; ++ return 1; ++} ++#endif ++ ++#ifdef HAVE_BROTLI ++ +/* Taken from Chromium and adapted to C, + * see net/ssl/cert_compression.cc + */ @@ -975,17 +1059,94 @@ index f836c63b0..a5c3a23ff 100644 + if (BrotliDecoderDecompress(in_len, in, &output_size, data) != + BROTLI_DECODER_RESULT_SUCCESS || + output_size != uncompressed_len) { ++ CRYPTO_BUFFER_free(decompressed); + return 0; + } + + *out = decompressed; + return 1; +} ++#endif ++ ++#if defined(HAVE_LIBZ) || defined(HAVE_BROTLI) ++static struct { ++ char *alg_name; ++ uint16_t alg_id; ++ ssl_cert_compression_func_t compress; ++ ssl_cert_decompression_func_t decompress; ++} cert_compress_algs[] = { ++#ifdef HAVE_LIBZ ++ {"zlib", TLSEXT_cert_compression_zlib, NULL, DecompressZlibCert}, ++#endif ++#ifdef HAVE_BROTLI ++ {"brotli", TLSEXT_cert_compression_brotli, NULL, DecompressBrotliCert}, ++#endif ++}; ++ ++#define NUM_CERT_COMPRESSION_ALGS \ ++ sizeof(cert_compress_algs) / sizeof(cert_compress_algs[0]) ++ ++/* ++ * curl-impersonate: ++ * Add support for TLS extension 27 - compress_certificate. ++ * This calls the BoringSSL-specific API SSL_CTX_add_cert_compression_alg ++ * for each algorithm specified in cert_compression, which is a comma separated list. ++ */ ++static CURLcode add_cert_compression(struct Curl_easy *data, ++ SSL_CTX *ctx, ++ const char *algorithms) ++{ ++ int i; ++ const char *s = algorithms; ++ char *alg_name; ++ size_t alg_name_len; ++ bool found; ++ ++ while (s && s[0]) { ++ found = FALSE; ++ ++ for(i = 0; i < NUM_CERT_COMPRESSION_ALGS; i++) { ++ alg_name = cert_compress_algs[i].alg_name; ++ alg_name_len = strlen(alg_name); ++ if(strlen(s) >= alg_name_len && ++ strncasecompare(s, alg_name, alg_name_len) && ++ (s[alg_name_len] == ',' || s[alg_name_len] == 0)) { ++ if(!SSL_CTX_add_cert_compression_alg(ctx, ++ cert_compress_algs[i].alg_id, ++ cert_compress_algs[i].compress, ++ cert_compress_algs[i].decompress)) { ++ failf(data, "Error adding certificate compression algorithm '%s'", ++ alg_name); ++ return CURLE_SSL_CIPHER; ++ } ++ s += alg_name_len; ++ if(*s == ',') ++ s += 1; ++ found = TRUE; ++ break; ++ } ++ } ++ ++ if(!found) { ++ failf(data, "Invalid compression algorithm list"); ++ return CURLE_BAD_FUNCTION_ARGUMENT; ++ } ++ } ++ ++ return CURLE_OK; ++} ++#else ++static CURLcode add_cert_compression(SSL_CTX *ctx, const char *algorithms) ++{ ++ /* No compression algorithms are available. */ ++ return CURLE_BAD_FUNCTION_ARGUMENT; ++} ++#endif + static CURLcode ossl_connect_step1(struct Curl_easy *data, struct connectdata *conn, int sockindex) { -@@ -2767,7 +2905,10 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, +@@ -2767,7 +3030,10 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, ctx_options = SSL_OP_ALL; #ifdef SSL_OP_NO_TICKET @@ -997,7 +1158,7 @@ index f836c63b0..a5c3a23ff 100644 #endif #ifdef SSL_OP_NO_COMPRESSION -@@ -2912,6 +3053,35 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, +@@ -2912,6 +3178,35 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, } #endif @@ -1033,7 +1194,7 @@ index f836c63b0..a5c3a23ff 100644 #ifdef USE_OPENSSL_SRP if(ssl_authtype == CURL_TLSAUTH_SRP) { char * const ssl_username = SSL_SET_OPTION(username); -@@ -2937,6 +3107,19 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, +@@ -2937,6 +3232,20 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, } #endif @@ -1046,14 +1207,15 @@ index f836c63b0..a5c3a23ff 100644 + /* Enable TLS GREASE. */ + SSL_CTX_set_grease_enabled(backend->ctx, 1); + -+ /* Add support for TLS extension 27 - compress_certificate. -+ * Add Brotli decompression. See Chromium net/ssl/cert_compression.cc */ -+ SSL_CTX_add_cert_compression_alg(backend->ctx, -+ TLSEXT_cert_compression_brotli, NULL, DecompressBrotliCert); ++ if(SSL_CONN_CONFIG(cert_compression) && ++ add_cert_compression(data, ++ backend->ctx, ++ SSL_CONN_CONFIG(cert_compression))) ++ return CURLE_SSL_CIPHER; #if defined(USE_WIN32_CRYPTO) /* Import certificates from the Windows root certificate store if requested. -@@ -3236,6 +3419,33 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, +@@ -3236,6 +3545,33 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, SSL_set_connect_state(backend->handle); @@ -1088,46 +1250,51 @@ index f836c63b0..a5c3a23ff 100644 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c -index 6007bbba0..24dd1e20b 100644 +index 6007bbba0..3c79e0d30 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c -@@ -156,6 +156,7 @@ Curl_ssl_config_matches(struct ssl_primary_config *data, +@@ -156,6 +156,9 @@ Curl_ssl_config_matches(struct ssl_primary_config *data, Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) && Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) && Curl_safe_strcasecompare(data->curves, needle->curves) && + Curl_safe_strcasecompare(data->sig_hash_algs, needle->sig_hash_algs) && ++ Curl_safe_strcasecompare(data->cert_compression, ++ needle->cert_compression) && Curl_safe_strcasecompare(data->pinned_key, needle->pinned_key)) return TRUE; -@@ -186,6 +187,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, +@@ -186,6 +189,8 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, CLONE_STRING(cipher_list13); CLONE_STRING(pinned_key); CLONE_STRING(curves); + CLONE_STRING(sig_hash_algs); ++ CLONE_STRING(cert_compression); return TRUE; } -@@ -205,6 +207,7 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) +@@ -205,6 +210,8 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) Curl_safefree(sslc->ca_info_blob); Curl_safefree(sslc->issuercert_blob); Curl_safefree(sslc->curves); + Curl_safefree(sslc->sig_hash_algs); ++ Curl_safefree(sslc->cert_compression); } #ifdef USE_SSL diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h -index 227b914e3..d2b0d5488 100644 +index 227b914e3..151b7b6dd 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h -@@ -165,6 +165,7 @@ struct OperationConfig { +@@ -165,6 +165,8 @@ struct OperationConfig { bool crlf; char *customrequest; char *ssl_ec_curves; + char *ssl_sig_hash_algs; ++ char *ssl_cert_compression; char *krblevel; char *request_target; long httpversion; -@@ -274,6 +275,7 @@ struct OperationConfig { +@@ -274,6 +276,7 @@ struct OperationConfig { char *oauth_bearer; /* OAuth 2.0 bearer token */ bool nonpn; /* enable/disable TLS NPN extension */ bool noalpn; /* enable/disable TLS ALPN extension */ @@ -1136,19 +1303,20 @@ index 227b914e3..d2b0d5488 100644 bool abstract_unix_socket; /* path to an abstract Unix domain socket */ bool falsestart; diff --git a/src/tool_getparam.c b/src/tool_getparam.c -index 7abbcc639..cf7ac3bf2 100644 +index 7abbcc639..09cd4dbc5 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c -@@ -279,6 +279,8 @@ static const struct LongShort aliases[]= { +@@ -279,6 +279,9 @@ static const struct LongShort aliases[]= { {"EC", "etag-save", ARG_FILENAME}, {"ED", "etag-compare", ARG_FILENAME}, {"EE", "curves", ARG_STRING}, + {"EG", "signature-hashes", ARG_STRING}, + {"EH", "alps", ARG_BOOL}, ++ {"EI", "cert-compression", ARG_STRING}, {"f", "fail", ARG_BOOL}, {"fa", "fail-early", ARG_BOOL}, {"fb", "styled-output", ARG_BOOL}, -@@ -1794,6 +1796,16 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ +@@ -1794,6 +1797,21 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ GetStr(&config->ssl_ec_curves, nextarg); break; @@ -1161,25 +1329,33 @@ index 7abbcc639..cf7ac3bf2 100644 + /* --alps */ + config->alps = toggle; + break; ++ ++ case 'I': ++ /* --cert-compression */ ++ GetStr(&config->ssl_cert_compression, nextarg); ++ break; + default: /* unknown flag */ return PARAM_OPTION_UNKNOWN; } diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c -index 448fc7cb3..85bde0c80 100644 +index 448fc7cb3..24e26b96e 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c -@@ -106,6 +106,9 @@ const struct helptxt helptext[] = { +@@ -106,6 +106,12 @@ const struct helptxt helptext[] = { {" --curves ", "(EC) TLS key exchange algorithm(s) to request", CURLHELP_TLS}, + {" --signature-hashes ", + "TLS signature hash algorithm(s) to use", ++ CURLHELP_TLS}, ++ {" --cert-compression ", ++ "TLS cert compressions algorithm(s) to use", + CURLHELP_TLS}, {"-d, --data ", "HTTP POST data", CURLHELP_IMPORTANT | CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, -@@ -379,6 +382,9 @@ const struct helptxt helptext[] = { +@@ -379,6 +385,9 @@ const struct helptxt helptext[] = { {" --no-alpn", "Disable the ALPN TLS extension", CURLHELP_TLS | CURLHELP_HTTP}, @@ -1190,21 +1366,25 @@ index 448fc7cb3..85bde0c80 100644 "Disable buffering of the output stream", CURLHELP_CURL}, diff --git a/src/tool_operate.c b/src/tool_operate.c -index fe2c43b55..f24f57c25 100644 +index fe2c43b55..843a94a76 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c -@@ -1520,6 +1520,10 @@ static CURLcode single_transfer(struct GlobalConfig *global, +@@ -1520,6 +1520,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(config->ssl_ec_curves) my_setopt_str(curl, CURLOPT_SSL_EC_CURVES, config->ssl_ec_curves); + if(config->ssl_sig_hash_algs) + my_setopt_str(curl, CURLOPT_SSL_SIG_HASH_ALGS, + config->ssl_sig_hash_algs); ++ ++ if(config->ssl_cert_compression) ++ my_setopt_str(curl, CURLOPT_SSL_CERT_COMPRESSION, ++ config->ssl_cert_compression); + if(curlinfo->features & CURL_VERSION_SSL) { /* Check if config->cert is a PKCS#11 URI and set the * config->cert_type if necessary */ -@@ -2061,6 +2065,10 @@ static CURLcode single_transfer(struct GlobalConfig *global, +@@ -2061,6 +2069,10 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, 0L); } From c1aa0bb50caa07350ecff60a0d72a97b10a674a0 Mon Sep 17 00:00:00 2001 From: lwthiker Date: Thu, 3 Mar 2022 12:16:30 +0200 Subject: [PATCH 3/4] Make TLS session ticket extension configurable A previous commit has enabled the TLS session ticket extension (by removing SSL_OP_NO_TICKET) because Chrome uses it. This commit makes it configurable via the CURLOPT_SSL_ENABLE_TICKET libcurl option or the '--tls-session-ticker' command line flag. The goal is to impersonate Safari which, as of version 15.3, does not use TLS session tickets. --- chrome/patches/curl-impersonate.patch | 142 ++++++++++++++++++-------- 1 file changed, 102 insertions(+), 40 deletions(-) diff --git a/chrome/patches/curl-impersonate.patch b/chrome/patches/curl-impersonate.patch index 0646682..91623a7 100644 --- a/chrome/patches/curl-impersonate.patch +++ b/chrome/patches/curl-impersonate.patch @@ -22,10 +22,10 @@ index 63e320236..deb054300 100644 LDFLAGS="$LDFLAGS $LD_H2" diff --git a/include/curl/curl.h b/include/curl/curl.h -index 7b69ce2d6..1405a228e 100644 +index 7b69ce2d6..42b7604d1 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h -@@ -2135,6 +2135,26 @@ typedef enum { +@@ -2135,6 +2135,29 @@ typedef enum { /* Set MIME option flags. */ CURLOPT(CURLOPT_MIME_OPTIONS, CURLOPTTYPE_LONG, 315), @@ -48,6 +48,9 @@ index 7b69ce2d6..1405a228e 100644 + * Supported algorithms are "zlib" and "brotli". + * See https://datatracker.ietf.org/doc/html/rfc8879 */ + CURLOPT(CURLOPT_SSL_CERT_COMPRESSION, CURLOPTTYPE_STRINGPOINT, 319), ++ ++ /* Enable/disable TLS session ticket extension (RFC5077) */ ++ CURLOPT(CURLOPT_SSL_ENABLE_TICKET, CURLOPTTYPE_LONG, 320), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -73,7 +76,7 @@ index 2dbfb26b5..e0bf86169 100644 * NAME curl_easy_getinfo() * diff --git a/lib/easy.c b/lib/easy.c -index 20293a710..5182c56b4 100644 +index 20293a710..b9c5a80b2 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -80,6 +80,7 @@ @@ -84,7 +87,7 @@ index 20293a710..5182c56b4 100644 /* The last 3 #include files should be in this order */ #include "curl_printf.h" -@@ -282,6 +283,198 @@ void curl_global_cleanup(void) +@@ -282,6 +283,207 @@ void curl_global_cleanup(void) init_flags = 0; } @@ -105,6 +108,8 @@ index 20293a710..5182c56b4 100644 + bool alpn; + /* Enable TLS ALPS extension. */ + bool alps; ++ /* Enable TLS session ticket extension. */ ++ bool tls_session_ticket; + /* TLS certificate compression algorithms. + * (TLS extension 27) */ + const char *cert_compression; @@ -135,6 +140,7 @@ index 20293a710..5182c56b4 100644 + .npn = false, + .alpn = true, + .alps = true, ++ .tls_session_ticket = true, + .cert_compression = "brotli", + .http_headers = { + "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Google Chrome\";v=\"98\"", @@ -174,6 +180,7 @@ index 20293a710..5182c56b4 100644 + .npn = false, + .alpn = true, + .alps = true, ++ .tls_session_ticket = true, + .cert_compression = "brotli", + .http_headers = { + "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Microsoft Edge\";v=\"98\"", @@ -252,6 +259,11 @@ index 20293a710..5182c56b4 100644 + if(ret) + return ret; + ++ ret = curl_easy_setopt(data, CURLOPT_SSL_ENABLE_TICKET, ++ opts->tls_session_ticket ? 1 : 0); ++ if(ret) ++ return ret; ++ + if(opts->cert_compression) { + ret = curl_easy_setopt(data, + CURLOPT_SSL_CERT_COMPRESSION, @@ -283,7 +295,7 @@ index 20293a710..5182c56b4 100644 /* * curl_easy_init() is the external interface to alloc, setup and init an * easy handle that is returned. If anything goes wrong, NULL is returned. -@@ -290,6 +483,7 @@ struct Curl_easy *curl_easy_init(void) +@@ -290,6 +492,7 @@ struct Curl_easy *curl_easy_init(void) { CURLcode result; struct Curl_easy *data; @@ -291,7 +303,7 @@ index 20293a710..5182c56b4 100644 /* Make sure we inited the global SSL stuff */ if(!initialized) { -@@ -308,6 +502,22 @@ struct Curl_easy *curl_easy_init(void) +@@ -308,6 +511,22 @@ struct Curl_easy *curl_easy_init(void) return NULL; } @@ -314,7 +326,7 @@ index 20293a710..5182c56b4 100644 return data; } -@@ -878,6 +1088,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) +@@ -878,6 +1097,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) outcurl->state.referer_alloc = TRUE; } @@ -329,7 +341,7 @@ index 20293a710..5182c56b4 100644 * note: the engine name has already been copied by dupset */ if(outcurl->set.str[STRING_SSL_ENGINE]) { diff --git a/lib/easyoptions.c b/lib/easyoptions.c -index 04871ad1e..99c17e35a 100644 +index 04871ad1e..385eab2e6 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -130,6 +130,7 @@ struct curl_easyoption Curl_easyopts[] = { @@ -340,7 +352,7 @@ index 04871ad1e..99c17e35a 100644 {"HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0}, {"HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0}, {"HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0}, -@@ -297,8 +298,11 @@ struct curl_easyoption Curl_easyopts[] = { +@@ -297,8 +298,12 @@ struct curl_easyoption Curl_easyopts[] = { {"SSL_CTX_DATA", CURLOPT_SSL_CTX_DATA, CURLOT_CBPTR, 0}, {"SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, CURLOT_FUNCTION, 0}, {"SSL_EC_CURVES", CURLOPT_SSL_EC_CURVES, CURLOT_STRING, 0}, @@ -349,15 +361,16 @@ index 04871ad1e..99c17e35a 100644 {"SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CURLOT_LONG, 0}, {"SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CURLOT_LONG, 0}, + {"SSL_ENABLE_ALPS", CURLOPT_SSL_ENABLE_ALPS, CURLOT_LONG, 0}, ++ {"SSL_ENABLE_TICKET", CURLOPT_SSL_ENABLE_TICKET, CURLOT_LONG, 0}, {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0}, {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0}, {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0}, -@@ -360,6 +364,6 @@ struct curl_easyoption Curl_easyopts[] = { +@@ -360,6 +365,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (315 + 1)); -+ return ((CURLOPT_LASTENTRY%10000) != (319 + 1)); ++ return ((CURLOPT_LASTENTRY%10000) != (320 + 1)); } #endif diff --git a/lib/http.c b/lib/http.c @@ -661,7 +674,7 @@ index f8dcc63b4..e6b728592 100644 #ifdef USE_WINSOCK diff --git a/lib/setopt.c b/lib/setopt.c -index 599ed5d99..fc7ec2a7c 100644 +index 599ed5d99..7a3880b0e 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -48,6 +48,7 @@ @@ -724,12 +737,15 @@ index 599ed5d99..fc7ec2a7c 100644 #endif case CURLOPT_IPRESOLVE: arg = va_arg(param, long); -@@ -2871,6 +2910,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) +@@ -2871,6 +2910,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_SSL_ENABLE_ALPN: data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE; break; + case CURLOPT_SSL_ENABLE_ALPS: + data->set.ssl_enable_alps = (0 != va_arg(param, long)) ? TRUE : FALSE; ++ break; ++ case CURLOPT_SSL_ENABLE_TICKET: ++ data->set.ssl_enable_ticket = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; #ifdef USE_UNIX_SOCKETS case CURLOPT_UNIX_SOCKET_PATH: @@ -756,7 +772,7 @@ index 22704fa15..1e100140c 100644 Curl_headersep(head->data[thislen]) ) return head->data; diff --git a/lib/url.c b/lib/url.c -index 9f1013554..7aa3ccf00 100644 +index 9f1013554..0eff9c354 100644 --- a/lib/url.c +++ b/lib/url.c @@ -469,6 +469,11 @@ CURLcode Curl_close(struct Curl_easy **datap) @@ -771,7 +787,15 @@ index 9f1013554..7aa3ccf00 100644 #ifndef CURL_DISABLE_DOH if(data->req.doh) { Curl_dyn_free(&data->req.doh->probe[0].serverdoh); -@@ -3808,6 +3813,9 @@ static CURLcode create_conn(struct Curl_easy *data, +@@ -622,6 +627,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) + set->tcp_nodelay = TRUE; + set->ssl_enable_npn = TRUE; + set->ssl_enable_alpn = TRUE; ++ set->ssl_enable_ticket = TRUE; + set->expect_100_timeout = 1000L; /* Wait for a second by default. */ + set->sep_headers = TRUE; /* separated header lists by default */ + set->buffer_size = READBUFFER_SIZE; +@@ -3808,6 +3814,9 @@ static CURLcode create_conn(struct Curl_easy *data, data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; @@ -781,20 +805,26 @@ index 9f1013554..7aa3ccf00 100644 #ifndef CURL_DISABLE_PROXY data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; -@@ -3925,6 +3933,11 @@ static CURLcode create_conn(struct Curl_easy *data, +@@ -3925,8 +3934,17 @@ static CURLcode create_conn(struct Curl_easy *data, conn->bits.tls_enable_alpn = TRUE; if(data->set.ssl_enable_npn) conn->bits.tls_enable_npn = TRUE; + -+ /* curl-impersonatE: Turn on ALPS if ALPN is enabled and the bit is ++ /* curl-impersonate: Turn on ALPS if ALPN is enabled and the bit is + * enabled. */ + if(data->set.ssl_enable_alps) + conn->bits.tls_enable_alps = TRUE; } ++ /* curl-impersonate: Add the TLS session ticket extension. */ ++ if(data->set.ssl_enable_ticket) ++ conn->bits.tls_enable_ticket = TRUE; ++ if(waitpipe) + /* There is a connection that *might* become usable for multiplexing + "soon", and we wait for that */ diff --git a/lib/urldata.h b/lib/urldata.h -index cc9c88870..0c6d56614 100644 +index cc9c88870..3f268bf14 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -257,6 +257,8 @@ struct ssl_primary_config { @@ -806,15 +836,16 @@ index cc9c88870..0c6d56614 100644 BIT(verifypeer); /* set TRUE if this is desired */ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ BIT(verifystatus); /* set TRUE if certificate status must be checked */ -@@ -517,6 +519,7 @@ struct ConnectBits { +@@ -517,6 +519,8 @@ struct ConnectBits { BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(tls_enable_npn); /* TLS NPN extension? */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ + BIT(tls_enable_alps); /* TLS ALPS extension? */ ++ BIT(tls_enable_ticket); /* TLS session ticket extension? */ BIT(connect_only); #ifndef CURL_DISABLE_DOH BIT(doh); -@@ -1421,6 +1424,19 @@ struct UrlState { +@@ -1421,6 +1425,19 @@ struct UrlState { CURLcode hresult; /* used to pass return codes back from hyper callbacks */ #endif @@ -834,7 +865,7 @@ index cc9c88870..0c6d56614 100644 /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { -@@ -1579,6 +1595,8 @@ enum dupstring { +@@ -1579,6 +1596,8 @@ enum dupstring { STRING_DNS_LOCAL_IP4, STRING_DNS_LOCAL_IP6, STRING_SSL_EC_CURVES, @@ -843,16 +874,17 @@ index cc9c88870..0c6d56614 100644 /* -- end of null-terminated strings -- */ -@@ -1849,6 +1867,7 @@ struct UserDefined { +@@ -1849,6 +1868,8 @@ struct UserDefined { BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(ssl_enable_npn); /* TLS NPN extension? */ BIT(ssl_enable_alpn);/* TLS ALPN extension? */ + BIT(ssl_enable_alps);/* TLS ALPS extension? */ ++ BIT(ssl_enable_ticket); /* TLS session ticket extension */ BIT(path_as_is); /* allow dotdots? */ BIT(pipewait); /* wait for multiplex status before starting a new connection */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c -index f836c63b0..6ef19b840 100644 +index f836c63b0..c310f65ba 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -76,6 +76,13 @@ @@ -1146,19 +1178,23 @@ index f836c63b0..6ef19b840 100644 static CURLcode ossl_connect_step1(struct Curl_easy *data, struct connectdata *conn, int sockindex) { -@@ -2767,7 +3030,10 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, +@@ -2767,7 +3030,14 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, ctx_options = SSL_OP_ALL; #ifdef SSL_OP_NO_TICKET - ctx_options |= SSL_OP_NO_TICKET; -+ /* curl-impersonate patch. ++ if(conn->bits.tls_enable_ticket) { ++ /* curl-impersonate: + * Turn off SSL_OP_NO_TICKET, we want TLS extension 35 (session_ticket) -+ * to be sent. */ -+ ctx_options &= ~SSL_OP_NO_TICKET; ++ * to be present in the client hello. */ ++ ctx_options &= ~SSL_OP_NO_TICKET; ++ } else { ++ ctx_options |= SSL_OP_NO_TICKET; ++ } #endif #ifdef SSL_OP_NO_COMPRESSION -@@ -2912,6 +3178,35 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, +@@ -2912,6 +3182,35 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, } #endif @@ -1194,7 +1230,7 @@ index f836c63b0..6ef19b840 100644 #ifdef USE_OPENSSL_SRP if(ssl_authtype == CURL_TLSAUTH_SRP) { char * const ssl_username = SSL_SET_OPTION(username); -@@ -2937,6 +3232,20 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, +@@ -2937,6 +3236,20 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, } #endif @@ -1215,7 +1251,7 @@ index f836c63b0..6ef19b840 100644 #if defined(USE_WIN32_CRYPTO) /* Import certificates from the Windows root certificate store if requested. -@@ -3236,6 +3545,33 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, +@@ -3236,6 +3549,33 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, SSL_set_connect_state(backend->handle); @@ -1282,7 +1318,7 @@ index 6007bbba0..3c79e0d30 100644 #ifdef USE_SSL diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h -index 227b914e3..151b7b6dd 100644 +index 227b914e3..91ffa283b 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -165,6 +165,8 @@ struct OperationConfig { @@ -1294,29 +1330,31 @@ index 227b914e3..151b7b6dd 100644 char *krblevel; char *request_target; long httpversion; -@@ -274,6 +276,7 @@ struct OperationConfig { +@@ -274,6 +276,8 @@ struct OperationConfig { char *oauth_bearer; /* OAuth 2.0 bearer token */ bool nonpn; /* enable/disable TLS NPN extension */ bool noalpn; /* enable/disable TLS ALPN extension */ + bool alps; /* enable/disable TLS ALPS extension */ ++ bool noticket; /* enable/disable TLS session ticket */ char *unix_socket_path; /* path to Unix domain socket */ bool abstract_unix_socket; /* path to an abstract Unix domain socket */ bool falsestart; diff --git a/src/tool_getparam.c b/src/tool_getparam.c -index 7abbcc639..09cd4dbc5 100644 +index 7abbcc639..e6165dc18 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c -@@ -279,6 +279,9 @@ static const struct LongShort aliases[]= { +@@ -279,6 +279,10 @@ static const struct LongShort aliases[]= { {"EC", "etag-save", ARG_FILENAME}, {"ED", "etag-compare", ARG_FILENAME}, {"EE", "curves", ARG_STRING}, + {"EG", "signature-hashes", ARG_STRING}, + {"EH", "alps", ARG_BOOL}, + {"EI", "cert-compression", ARG_STRING}, ++ {"EJ", "tls-session-ticket", ARG_BOOL}, {"f", "fail", ARG_BOOL}, {"fa", "fail-early", ARG_BOOL}, {"fb", "styled-output", ARG_BOOL}, -@@ -1794,6 +1797,21 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ +@@ -1794,6 +1798,26 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ GetStr(&config->ssl_ec_curves, nextarg); break; @@ -1334,15 +1372,20 @@ index 7abbcc639..09cd4dbc5 100644 + /* --cert-compression */ + GetStr(&config->ssl_cert_compression, nextarg); + break; ++ ++ case 'J': ++ /* --tls-session-ticket */ ++ config->noticket = (!toggle)?TRUE:FALSE; ++ break; + default: /* unknown flag */ return PARAM_OPTION_UNKNOWN; } diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c -index 448fc7cb3..24e26b96e 100644 +index 448fc7cb3..43201c639 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c -@@ -106,6 +106,12 @@ const struct helptxt helptext[] = { +@@ -106,6 +106,15 @@ const struct helptxt helptext[] = { {" --curves ", "(EC) TLS key exchange algorithm(s) to request", CURLHELP_TLS}, @@ -1351,11 +1394,14 @@ index 448fc7cb3..24e26b96e 100644 + CURLHELP_TLS}, + {" --cert-compression ", + "TLS cert compressions algorithm(s) to use", ++ CURLHELP_TLS}, ++ {" --no-tls-session-ticket", ++ "Disable the TLS session ticket extension", + CURLHELP_TLS}, {"-d, --data ", "HTTP POST data", CURLHELP_IMPORTANT | CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, -@@ -379,6 +385,9 @@ const struct helptxt helptext[] = { +@@ -379,6 +388,9 @@ const struct helptxt helptext[] = { {" --no-alpn", "Disable the ALPN TLS extension", CURLHELP_TLS | CURLHELP_HTTP}, @@ -1366,7 +1412,7 @@ index 448fc7cb3..24e26b96e 100644 "Disable buffering of the output stream", CURLHELP_CURL}, diff --git a/src/tool_operate.c b/src/tool_operate.c -index fe2c43b55..843a94a76 100644 +index fe2c43b55..c829515dd 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1520,6 +1520,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, @@ -1384,14 +1430,30 @@ index fe2c43b55..843a94a76 100644 if(curlinfo->features & CURL_VERSION_SSL) { /* Check if config->cert is a PKCS#11 URI and set the * config->cert_type if necessary */ -@@ -2061,6 +2069,10 @@ static CURLcode single_transfer(struct GlobalConfig *global, +@@ -2061,6 +2069,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, 0L); } + if(config->alps) { + my_setopt(curl, CURLOPT_SSL_ENABLE_ALPS, 1L); + } ++ ++ if (config->noticket) { ++ my_setopt(curl, CURLOPT_SSL_ENABLE_TICKET, 0L); ++ } + /* new in 7.40.0, abstract support added in 7.53.0 */ if(config->unix_socket_path) { if(config->abstract_unix_socket) { +diff --git a/src/tool_setopt.c b/src/tool_setopt.c +index 4c86eb321..eef8b187f 100644 +--- a/src/tool_setopt.c ++++ b/src/tool_setopt.c +@@ -179,6 +179,7 @@ static const struct NameValue setopt_nv_CURLNONZERODEFAULTS[] = { + NV1(CURLOPT_SSL_VERIFYHOST, 1), + NV1(CURLOPT_SSL_ENABLE_NPN, 1), + NV1(CURLOPT_SSL_ENABLE_ALPN, 1), ++ NV1(CURLOPT_SSL_ENABLE_TICKET, 1), + NV1(CURLOPT_TCP_NODELAY, 1), + NV1(CURLOPT_PROXY_SSL_VERIFYPEER, 1), + NV1(CURLOPT_PROXY_SSL_VERIFYHOST, 1), From e897975660369d019723076d8bbeb667d1c2e6cf Mon Sep 17 00:00:00 2001 From: lwthiker <99899249+lwthiker@users.noreply.github.com> Date: Thu, 3 Mar 2022 15:16:27 +0200 Subject: [PATCH 4/4] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cccc152..c82dbe5 100644 --- a/README.md +++ b/README.md @@ -71,9 +71,9 @@ CURLcode curl_easy_impersonate(struct Curl_easy *data, const char *target); ``` You can call it with the target names, e.g. `"chrome98"`, and it will internally set all the options and headers that are otherwise set by the wrapper scripts. Specifically it sets: * `CURLOPT_HTTP_VERSION` -* `CURLOPT_SSLVERSION` -* `CURLOPT_SSL_CIPHER_LIST` -* `CURLOPT_HTTPBASEHEADER` (non-standard option created for this project). +* `CURLOPT_SSLVERSION`, `CURLOPT_SSL_CIPHER_LIST`, `CURLOPT_SSL_EC_CURVES`, `CURLOPT_SSL_ENABLE_NPN`, `CURLOPT_SSL_ENABLE_ALPN` +* `CURLOPT_HTTPBASEHEADER` (non-standard HTTP option created for this project). +* `CURLOPT_SSL_ENABLE_ALPS`, `CURLOPT_SSL_SIG_HASH_ALGS`, `CURLOPT_SSL_CERT_COMPRESSION`, `CURLOPT_SSL_ENABLE_TICKET` (non-standard TLS options created for this project). Note that if you call `curl_easy_setopt()` later with one of the above it will override the options set by `curl_easy_impersonate()`.