diff options
author | Michael Drake <michael.drake@codethink.co.uk> | 2022-11-02 11:34:49 +0000 |
---|---|---|
committer | Michael Drake <michael.drake@codethink.co.uk> | 2022-11-02 11:34:49 +0000 |
commit | ab619018e422ed8b46d452793b3c72b062b84d0a (patch) | |
tree | f4d53da550ce1dfc1a0a0575dc0621fdba4f6608 | |
parent | 2bbddd7aaabeae11c1862787ef6f99b3b816324a (diff) | |
download | toolchains-ab619018e422ed8b46d452793b3c72b062b84d0a.tar.gz toolchains-ab619018e422ed8b46d452793b3c72b062b84d0a.tar.bz2 |
sdk: libcurl: Significantly faster https connections
Making HTTPS connections was slow because libcurl forces openssl
to rebuild the same X509_STORE from the same cabundle every time
a connection is made. This is a slow process and it can happen
many times per page.
These patches change libcurl to keep the built X509_STORE cached
and reuse it for subsequent connections.
These patches are being upstreamed here:
https://github.com/curl/curl/pull/9620
6 files changed, 1241 insertions, 0 deletions
diff --git a/sdk/recipes/patches/libcurl/0002-openssl-split-out-x509-store-loading.p b/sdk/recipes/patches/libcurl/0002-openssl-split-out-x509-store-loading.p new file mode 100644 index 0000000..658c801 --- /dev/null +++ b/sdk/recipes/patches/libcurl/0002-openssl-split-out-x509-store-loading.p @@ -0,0 +1,554 @@ +diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c +index ad2efa558..981075838 100644 +--- lib/vtls/openssl.c ++++ lib/vtls/openssl.c +@@ -2891,13 +2891,274 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, + return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); + } + ++static CURLcode populate_x509_store(struct Curl_easy *data, ++ struct connectdata *conn, int sockindex) ++{ ++ CURLcode result = CURLE_OK; ++ X509_LOOKUP *lookup = NULL; ++ const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); ++ const char * const ssl_cafile = ++ /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ ++ (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); ++ const char * const ssl_capath = SSL_CONN_CONFIG(CApath); ++ const char * const ssl_crlfile = SSL_SET_OPTION(primary.CRLfile); ++ const bool verifypeer = SSL_CONN_CONFIG(verifypeer); ++ struct ssl_connect_data *connssl = &conn->ssl[sockindex]; ++ struct ssl_backend_data *backend = connssl->backend; ++ bool imported_native_ca = false; ++ ++#if defined(USE_WIN32_CRYPTO) ++ /* Import certificates from the Windows root certificate store if requested. ++ https://stackoverflow.com/questions/9507184/ ++ https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 ++ https://datatracker.ietf.org/doc/html/rfc5280 */ ++ if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) && ++ (SSL_SET_OPTION(native_ca_store))) { ++ X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); ++ HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); ++ ++ if(hStore) { ++ PCCERT_CONTEXT pContext = NULL; ++ /* The array of enhanced key usage OIDs will vary per certificate and is ++ declared outside of the loop so that rather than malloc/free each ++ iteration we can grow it with realloc, when necessary. */ ++ CERT_ENHKEY_USAGE *enhkey_usage = NULL; ++ DWORD enhkey_usage_size = 0; ++ ++ /* This loop makes a best effort to import all valid certificates from ++ the MS root store. If a certificate cannot be imported it is skipped. ++ 'result' is used to store only hard-fail conditions (such as out of ++ memory) that cause an early break. */ ++ result = CURLE_OK; ++ for(;;) { ++ X509 *x509; ++ FILETIME now; ++ BYTE key_usage[2]; ++ DWORD req_size; ++ const unsigned char *encoded_cert; ++#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) ++ char cert_name[256]; ++#endif ++ ++ pContext = CertEnumCertificatesInStore(hStore, pContext); ++ if(!pContext) ++ break; ++ ++#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) ++ if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, ++ NULL, cert_name, sizeof(cert_name))) { ++ strcpy(cert_name, "Unknown"); ++ } ++ infof(data, "SSL: Checking cert \"%s\"", cert_name); ++#endif ++ ++ encoded_cert = (const unsigned char *)pContext->pbCertEncoded; ++ if(!encoded_cert) ++ continue; ++ ++ GetSystemTimeAsFileTime(&now); ++ if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || ++ CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) ++ continue; ++ ++ /* If key usage exists check for signing attribute */ ++ if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, ++ pContext->pCertInfo, ++ key_usage, sizeof(key_usage))) { ++ if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) ++ continue; ++ } ++ else if(GetLastError()) ++ continue; ++ ++ /* If enhanced key usage exists check for server auth attribute. ++ * ++ * Note "In a Microsoft environment, a certificate might also have EKU ++ * extended properties that specify valid uses for the certificate." ++ * The call below checks both, and behavior varies depending on what is ++ * found. For more details see CertGetEnhancedKeyUsage doc. ++ */ ++ if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { ++ if(req_size && req_size > enhkey_usage_size) { ++ void *tmp = realloc(enhkey_usage, req_size); ++ ++ if(!tmp) { ++ failf(data, "SSL: Out of memory allocating for OID list"); ++ result = CURLE_OUT_OF_MEMORY; ++ break; ++ } ++ ++ enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; ++ enhkey_usage_size = req_size; ++ } ++ ++ if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { ++ if(!enhkey_usage->cUsageIdentifier) { ++ /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is ++ good for all uses. If it returns zero, the certificate has no ++ valid uses." */ ++ if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) ++ continue; ++ } ++ else { ++ DWORD i; ++ bool found = false; ++ ++ for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { ++ if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, ++ enhkey_usage->rgpszUsageIdentifier[i])) { ++ found = true; ++ break; ++ } ++ } ++ ++ if(!found) ++ continue; ++ } ++ } ++ else ++ continue; ++ } ++ else ++ continue; ++ ++ x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); ++ if(!x509) ++ continue; ++ ++ /* Try to import the certificate. This may fail for legitimate reasons ++ such as duplicate certificate, which is allowed by MS but not ++ OpenSSL. */ ++ if(X509_STORE_add_cert(store, x509) == 1) { ++#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) ++ infof(data, "SSL: Imported cert \"%s\"", cert_name); ++#endif ++ imported_native_ca = true; ++ } ++ X509_free(x509); ++ } ++ ++ free(enhkey_usage); ++ CertFreeCertificateContext(pContext); ++ CertCloseStore(hStore, 0); ++ ++ if(result) ++ return result; ++ } ++ if(imported_native_ca) ++ infof(data, "successfully imported Windows CA store"); ++ else ++ infof(data, "error importing Windows CA store, continuing anyway"); ++ } ++#endif ++ ++ if(ca_info_blob) { ++ result = load_cacert_from_memory(backend->ctx, ca_info_blob); ++ if(result) { ++ if(result == CURLE_OUT_OF_MEMORY || ++ (verifypeer && !imported_native_ca)) { ++ failf(data, "error importing CA certificate blob"); ++ return result; ++ } ++ /* Only warn if no certificate verification is required. */ ++ infof(data, "error importing CA certificate blob, continuing anyway"); ++ } ++ } ++ ++ if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) { ++#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) ++ /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ ++ if(ssl_cafile && ++ !SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) { ++ /* Fail if we insist on successfully verifying the server. */ ++ failf(data, "error setting certificate file: %s", ssl_cafile); ++ return CURLE_SSL_CACERT_BADFILE; ++ } ++ if(ssl_capath && ++ !SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) { ++ /* Fail if we insist on successfully verifying the server. */ ++ failf(data, "error setting certificate path: %s", ssl_capath); ++ return CURLE_SSL_CACERT_BADFILE; ++ } ++#else ++ /* tell OpenSSL where to find CA certificates that are used to verify the ++ server's certificate. */ ++ if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) { ++ /* Fail if we insist on successfully verifying the server. */ ++ failf(data, "error setting certificate verify locations:" ++ " CAfile: %s CApath: %s", ++ ssl_cafile ? ssl_cafile : "none", ++ ssl_capath ? ssl_capath : "none"); ++ return CURLE_SSL_CACERT_BADFILE; ++ } ++#endif ++ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); ++ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); ++ } ++ ++#ifdef CURL_CA_FALLBACK ++ if(verifypeer && ++ !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) { ++ /* verifying the peer without any CA certificates won't ++ work so use openssl's built-in default as fallback */ ++ SSL_CTX_set_default_verify_paths(backend->ctx); ++ } ++#endif ++ ++ if(ssl_crlfile) { ++ /* tell OpenSSL where to find CRL file that is used to check certificate ++ * revocation */ ++ lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx), ++ X509_LOOKUP_file()); ++ if(!lookup || ++ (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { ++ failf(data, "error loading CRL file: %s", ssl_crlfile); ++ return CURLE_SSL_CRL_BADFILE; ++ } ++ /* Everything is fine. */ ++ infof(data, "successfully loaded CRL file:"); ++ X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), ++ X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); ++ ++ infof(data, " CRLfile: %s", ssl_crlfile); ++ } ++ ++ if(verifypeer) { ++ /* Try building a chain using issuers in the trusted store first to avoid ++ problems with server-sent legacy intermediates. Newer versions of ++ OpenSSL do alternate chain checking by default but we do not know how to ++ determine that in a reliable manner. ++ https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest ++ */ ++#if defined(X509_V_FLAG_TRUSTED_FIRST) ++ X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), ++ X509_V_FLAG_TRUSTED_FIRST); ++#endif ++#ifdef X509_V_FLAG_PARTIAL_CHAIN ++ if(!SSL_SET_OPTION(no_partialchain) && !ssl_crlfile) { ++ /* Have intermediate certificates in the trust store be treated as ++ trust-anchors, in the same way as self-signed root CA certificates ++ are. This allows users to verify servers using the intermediate cert ++ only, instead of needing the whole chain. ++ ++ Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we ++ cannot do partial chains with a CRL check. ++ */ ++ X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), ++ X509_V_FLAG_PARTIAL_CHAIN); ++ } ++#endif ++ } ++ ++ return result; ++} ++ + static CURLcode ossl_connect_step1(struct Curl_easy *data, + struct connectdata *conn, int sockindex) + { + CURLcode result = CURLE_OK; + char *ciphers; + SSL_METHOD_QUAL SSL_METHOD *req_method = NULL; +- X509_LOOKUP *lookup = NULL; + curl_socket_t sockfd = conn->sock[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + ctx_option_t ctx_options = 0; +@@ -2919,17 +3180,10 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, + #endif + char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); + const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); +- const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); + const char * const ssl_cert_type = SSL_SET_OPTION(cert_type); +- const char * const ssl_cafile = +- /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ +- (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); +- const char * const ssl_capath = SSL_CONN_CONFIG(CApath); + const bool verifypeer = SSL_CONN_CONFIG(verifypeer); +- const char * const ssl_crlfile = SSL_SET_OPTION(primary.CRLfile); + char error_buffer[256]; + struct ssl_backend_data *backend = connssl->backend; +- bool imported_native_ca = false; + + DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); + DEBUGASSERT(backend); +@@ -3196,249 +3450,9 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, + } + #endif + +- +-#if defined(USE_WIN32_CRYPTO) +- /* Import certificates from the Windows root certificate store if requested. +- https://stackoverflow.com/questions/9507184/ +- https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 +- https://datatracker.ietf.org/doc/html/rfc5280 */ +- if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) && +- (SSL_SET_OPTION(native_ca_store))) { +- X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); +- HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); +- +- if(hStore) { +- PCCERT_CONTEXT pContext = NULL; +- /* The array of enhanced key usage OIDs will vary per certificate and is +- declared outside of the loop so that rather than malloc/free each +- iteration we can grow it with realloc, when necessary. */ +- CERT_ENHKEY_USAGE *enhkey_usage = NULL; +- DWORD enhkey_usage_size = 0; +- +- /* This loop makes a best effort to import all valid certificates from +- the MS root store. If a certificate cannot be imported it is skipped. +- 'result' is used to store only hard-fail conditions (such as out of +- memory) that cause an early break. */ +- result = CURLE_OK; +- for(;;) { +- X509 *x509; +- FILETIME now; +- BYTE key_usage[2]; +- DWORD req_size; +- const unsigned char *encoded_cert; +-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +- char cert_name[256]; +-#endif +- +- pContext = CertEnumCertificatesInStore(hStore, pContext); +- if(!pContext) +- break; +- +-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +- if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, +- NULL, cert_name, sizeof(cert_name))) { +- strcpy(cert_name, "Unknown"); +- } +- infof(data, "SSL: Checking cert \"%s\"", cert_name); +-#endif +- +- encoded_cert = (const unsigned char *)pContext->pbCertEncoded; +- if(!encoded_cert) +- continue; +- +- GetSystemTimeAsFileTime(&now); +- if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || +- CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) +- continue; +- +- /* If key usage exists check for signing attribute */ +- if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, +- pContext->pCertInfo, +- key_usage, sizeof(key_usage))) { +- if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) +- continue; +- } +- else if(GetLastError()) +- continue; +- +- /* If enhanced key usage exists check for server auth attribute. +- * +- * Note "In a Microsoft environment, a certificate might also have EKU +- * extended properties that specify valid uses for the certificate." +- * The call below checks both, and behavior varies depending on what is +- * found. For more details see CertGetEnhancedKeyUsage doc. +- */ +- if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { +- if(req_size && req_size > enhkey_usage_size) { +- void *tmp = realloc(enhkey_usage, req_size); +- +- if(!tmp) { +- failf(data, "SSL: Out of memory allocating for OID list"); +- result = CURLE_OUT_OF_MEMORY; +- break; +- } +- +- enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; +- enhkey_usage_size = req_size; +- } +- +- if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { +- if(!enhkey_usage->cUsageIdentifier) { +- /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is +- good for all uses. If it returns zero, the certificate has no +- valid uses." */ +- if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) +- continue; +- } +- else { +- DWORD i; +- bool found = false; +- +- for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { +- if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, +- enhkey_usage->rgpszUsageIdentifier[i])) { +- found = true; +- break; +- } +- } +- +- if(!found) +- continue; +- } +- } +- else +- continue; +- } +- else +- continue; +- +- x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); +- if(!x509) +- continue; +- +- /* Try to import the certificate. This may fail for legitimate reasons +- such as duplicate certificate, which is allowed by MS but not +- OpenSSL. */ +- if(X509_STORE_add_cert(store, x509) == 1) { +-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +- infof(data, "SSL: Imported cert \"%s\"", cert_name); +-#endif +- imported_native_ca = true; +- } +- X509_free(x509); +- } +- +- free(enhkey_usage); +- CertFreeCertificateContext(pContext); +- CertCloseStore(hStore, 0); +- +- if(result) +- return result; +- } +- if(imported_native_ca) +- infof(data, "successfully imported Windows CA store"); +- else +- infof(data, "error importing Windows CA store, continuing anyway"); +- } +-#endif +- +- if(ca_info_blob) { +- result = load_cacert_from_memory(backend->ctx, ca_info_blob); +- if(result) { +- if(result == CURLE_OUT_OF_MEMORY || +- (verifypeer && !imported_native_ca)) { +- failf(data, "error importing CA certificate blob"); +- return result; +- } +- /* Only warn if no certificate verification is required. */ +- infof(data, "error importing CA certificate blob, continuing anyway"); +- } +- } +- +- if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) { +-#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) +- /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ +- if(ssl_cafile && +- !SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) { +- /* Fail if we insist on successfully verifying the server. */ +- failf(data, "error setting certificate file: %s", ssl_cafile); +- return CURLE_SSL_CACERT_BADFILE; +- } +- if(ssl_capath && +- !SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) { +- /* Fail if we insist on successfully verifying the server. */ +- failf(data, "error setting certificate path: %s", ssl_capath); +- return CURLE_SSL_CACERT_BADFILE; +- } +-#else +- /* tell OpenSSL where to find CA certificates that are used to verify the +- server's certificate. */ +- if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) { +- /* Fail if we insist on successfully verifying the server. */ +- failf(data, "error setting certificate verify locations:" +- " CAfile: %s CApath: %s", +- ssl_cafile ? ssl_cafile : "none", +- ssl_capath ? ssl_capath : "none"); +- return CURLE_SSL_CACERT_BADFILE; +- } +-#endif +- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); +- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); +- } +- +-#ifdef CURL_CA_FALLBACK +- if(verifypeer && +- !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) { +- /* verifying the peer without any CA certificates won't +- work so use openssl's built-in default as fallback */ +- SSL_CTX_set_default_verify_paths(backend->ctx); +- } +-#endif +- +- if(ssl_crlfile) { +- /* tell OpenSSL where to find CRL file that is used to check certificate +- * revocation */ +- lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx), +- X509_LOOKUP_file()); +- if(!lookup || +- (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { +- failf(data, "error loading CRL file: %s", ssl_crlfile); +- return CURLE_SSL_CRL_BADFILE; +- } +- /* Everything is fine. */ +- infof(data, "successfully loaded CRL file:"); +- X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), +- X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); +- +- infof(data, " CRLfile: %s", ssl_crlfile); +- } +- +- if(verifypeer) { +- /* Try building a chain using issuers in the trusted store first to avoid +- problems with server-sent legacy intermediates. Newer versions of +- OpenSSL do alternate chain checking by default but we do not know how to +- determine that in a reliable manner. +- https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest +- */ +-#if defined(X509_V_FLAG_TRUSTED_FIRST) +- X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), +- X509_V_FLAG_TRUSTED_FIRST); +-#endif +-#ifdef X509_V_FLAG_PARTIAL_CHAIN +- if(!SSL_SET_OPTION(no_partialchain) && !ssl_crlfile) { +- /* Have intermediate certificates in the trust store be treated as +- trust-anchors, in the same way as self-signed root CA certificates +- are. This allows users to verify servers using the intermediate cert +- only, instead of needing the whole chain. +- +- Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we +- cannot do partial chains with a CRL check. +- */ +- X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), +- X509_V_FLAG_PARTIAL_CHAIN); +- } +-#endif +- } ++ result = populate_x509_store(data, conn, sockindex); ++ if(result) ++ return result; + + /* OpenSSL always tries to verify the peer, this only says whether it should + * fail to connect if the verification fails, or if it should continue +-- +2.34.1 + diff --git a/sdk/recipes/patches/libcurl/0003-openssl-pass-x509-store-to-be-populated.p b/sdk/recipes/patches/libcurl/0003-openssl-pass-x509-store-to-be-populated.p new file mode 100644 index 0000000..f909bb1 --- /dev/null +++ b/sdk/recipes/patches/libcurl/0003-openssl-pass-x509-store-to-be-populated.p @@ -0,0 +1,174 @@ +diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c +index 981075838..d80f79921 100644 +--- lib/vtls/openssl.c ++++ lib/vtls/openssl.c +@@ -2830,7 +2830,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) + return res; + } + +-static CURLcode load_cacert_from_memory(SSL_CTX *ctx, ++static CURLcode load_cacert_from_memory(X509_STORE *store, + const struct curl_blob *ca_info_blob) + { + /* these need to be freed at the end */ +@@ -2839,16 +2839,11 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, + + /* everything else is just a reference */ + int i, count = 0; +- X509_STORE *cts = NULL; + X509_INFO *itmp = NULL; + + if(ca_info_blob->len > (size_t)INT_MAX) + return CURLE_SSL_CACERT_BADFILE; + +- cts = SSL_CTX_get_cert_store(ctx); +- if(!cts) +- return CURLE_OUT_OF_MEMORY; +- + cbio = BIO_new_mem_buf(ca_info_blob->data, (int)ca_info_blob->len); + if(!cbio) + return CURLE_OUT_OF_MEMORY; +@@ -2863,7 +2858,7 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, + for(i = 0; i < (int)sk_X509_INFO_num(inf); ++i) { + itmp = sk_X509_INFO_value(inf, i); + if(itmp->x509) { +- if(X509_STORE_add_cert(cts, itmp->x509)) { ++ if(X509_STORE_add_cert(store, itmp->x509)) { + ++count; + } + else { +@@ -2873,7 +2868,7 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, + } + } + if(itmp->crl) { +- if(X509_STORE_add_crl(cts, itmp->crl)) { ++ if(X509_STORE_add_crl(store, itmp->crl)) { + ++count; + } + else { +@@ -2892,7 +2887,8 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, + } + + static CURLcode populate_x509_store(struct Curl_easy *data, +- struct connectdata *conn, int sockindex) ++ struct connectdata *conn, ++ X509_STORE *store) + { + CURLcode result = CURLE_OK; + X509_LOOKUP *lookup = NULL; +@@ -2903,10 +2899,11 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + const char * const ssl_capath = SSL_CONN_CONFIG(CApath); + const char * const ssl_crlfile = SSL_SET_OPTION(primary.CRLfile); + const bool verifypeer = SSL_CONN_CONFIG(verifypeer); +- struct ssl_connect_data *connssl = &conn->ssl[sockindex]; +- struct ssl_backend_data *backend = connssl->backend; + bool imported_native_ca = false; + ++ if(!store) ++ return CURLE_OUT_OF_MEMORY; ++ + #if defined(USE_WIN32_CRYPTO) + /* Import certificates from the Windows root certificate store if requested. + https://stackoverflow.com/questions/9507184/ +@@ -2914,7 +2911,6 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + https://datatracker.ietf.org/doc/html/rfc5280 */ + if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) && + (SSL_SET_OPTION(native_ca_store))) { +- X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); + HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); + + if(hStore) { +@@ -3053,7 +3049,7 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + #endif + + if(ca_info_blob) { +- result = load_cacert_from_memory(backend->ctx, ca_info_blob); ++ result = load_cacert_from_memory(store, ca_info_blob); + if(result) { + if(result == CURLE_OUT_OF_MEMORY || + (verifypeer && !imported_native_ca)) { +@@ -3069,13 +3065,13 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ + if(ssl_cafile && +- !SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) { ++ !X509_STORE_load_file(store, ssl_cafile)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate file: %s", ssl_cafile); + return CURLE_SSL_CACERT_BADFILE; + } + if(ssl_capath && +- !SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) { ++ !X509_STORE_load_path(store, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate path: %s", ssl_capath); + return CURLE_SSL_CACERT_BADFILE; +@@ -3083,7 +3079,7 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + #else + /* tell OpenSSL where to find CA certificates that are used to verify the + server's certificate. */ +- if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) { ++ if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", +@@ -3101,15 +3097,14 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) { + /* verifying the peer without any CA certificates won't + work so use openssl's built-in default as fallback */ +- SSL_CTX_set_default_verify_paths(backend->ctx); ++ X509_STORE_set_default_paths(store); + } + #endif + + if(ssl_crlfile) { + /* tell OpenSSL where to find CRL file that is used to check certificate + * revocation */ +- lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx), +- X509_LOOKUP_file()); ++ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if(!lookup || + (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { + failf(data, "error loading CRL file: %s", ssl_crlfile); +@@ -3117,7 +3112,7 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + } + /* Everything is fine. */ + infof(data, "successfully loaded CRL file:"); +- X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), ++ X509_STORE_set_flags(store, + X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); + + infof(data, " CRLfile: %s", ssl_crlfile); +@@ -3131,8 +3126,7 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest + */ + #if defined(X509_V_FLAG_TRUSTED_FIRST) +- X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), +- X509_V_FLAG_TRUSTED_FIRST); ++ X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); + #endif + #ifdef X509_V_FLAG_PARTIAL_CHAIN + if(!SSL_SET_OPTION(no_partialchain) && !ssl_crlfile) { +@@ -3144,8 +3138,7 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we + cannot do partial chains with a CRL check. + */ +- X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), +- X509_V_FLAG_PARTIAL_CHAIN); ++ X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN); + } + #endif + } +@@ -3450,7 +3443,8 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, + } + #endif + +- result = populate_x509_store(data, conn, sockindex); ++ result = populate_x509_store(data, conn, ++ SSL_CTX_get_cert_store(backend->ctx)); + if(result) + return result; + +-- +2.34.1 + diff --git a/sdk/recipes/patches/libcurl/0004-urldata-fix-multi-handle-comment.p b/sdk/recipes/patches/libcurl/0004-urldata-fix-multi-handle-comment.p new file mode 100644 index 0000000..7bdf402 --- /dev/null +++ b/sdk/recipes/patches/libcurl/0004-urldata-fix-multi-handle-comment.p @@ -0,0 +1,16 @@ +diff --git a/lib/urldata.h b/lib/urldata.h +index f5bd5a3e9..aba07c275 100644 +--- lib/urldata.h ++++ lib/urldata.h +@@ -1531,7 +1531,7 @@ struct UrlState { + * Character pointer fields point to dynamic storage, unless otherwise stated. + */ + +-struct Curl_multi; /* declared and used only in multi.c */ ++struct Curl_multi; /* declared in multihandle.c */ + + /* + * This enumeration MUST not use conditional directives (#ifdefs), new +-- +2.34.1 + diff --git a/sdk/recipes/patches/libcurl/0005-multi-vtls-enable-ssl-backend-specific-storage-on-mu.p b/sdk/recipes/patches/libcurl/0005-multi-vtls-enable-ssl-backend-specific-storage-on-mu.p new file mode 100644 index 0000000..d961322 --- /dev/null +++ b/sdk/recipes/patches/libcurl/0005-multi-vtls-enable-ssl-backend-specific-storage-on-mu.p @@ -0,0 +1,245 @@ +diff --git a/lib/multi.c b/lib/multi.c +index 51acba73a..09965de83 100644 +--- lib/multi.c ++++ lib/multi.c +@@ -2770,6 +2770,11 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) + wakeup_close(multi->wakeup_pair[1]); + #endif + #endif ++ ++#ifdef USE_SSL ++ Curl_free_multi_ssl_backend_data(multi->ssl_backend_data); ++#endif ++ + free(multi); + + return CURLM_OK; +diff --git a/lib/multihandle.h b/lib/multihandle.h +index a997784ea..5a83656d5 100644 +--- lib/multihandle.h ++++ lib/multihandle.h +@@ -79,6 +79,10 @@ typedef enum { + /* value for MAXIMUM CONCURRENT STREAMS upper limit */ + #define INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) + ++/* Curl_multi SSL backend-specific data; declared differently by each SSL ++ backend */ ++struct multi_ssl_backend_data; ++ + /* This is the struct known as CURLM on the outside */ + struct Curl_multi { + /* First a simple identifier to easier detect if a user mix up +@@ -118,6 +122,10 @@ struct Curl_multi { + times of all currently set timers */ + struct Curl_tree *timetree; + ++#if defined(USE_SSL) ++ struct multi_ssl_backend_data *ssl_backend_data; ++#endif ++ + /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note + the pluralis form, there can be more than one easy handle waiting on the + same actual socket) */ +diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c +index 1221ce8c8..e66dd8469 100644 +--- lib/vtls/bearssl.c ++++ lib/vtls/bearssl.c +@@ -1208,7 +1208,8 @@ const struct Curl_ssl Curl_ssl_bearssl = { + Curl_none_false_start, /* false_start */ + bearssl_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ +- NULL /* disassociate_connection */ ++ NULL, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + #endif /* USE_BEARSSL */ +diff --git a/lib/vtls/gskit.c b/lib/vtls/gskit.c +index 4ee4edea6..18bd47919 100644 +--- lib/vtls/gskit.c ++++ lib/vtls/gskit.c +@@ -1323,7 +1323,8 @@ const struct Curl_ssl Curl_ssl_gskit = { + Curl_none_false_start, /* false_start */ + NULL, /* sha256sum */ + NULL, /* associate_connection */ +- NULL /* disassociate_connection */ ++ NULL, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + #endif /* USE_GSKIT */ +diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c +index cf3dbc523..68e3fe299 100644 +--- lib/vtls/gtls.c ++++ lib/vtls/gtls.c +@@ -1699,7 +1699,8 @@ const struct Curl_ssl Curl_ssl_gnutls = { + Curl_none_false_start, /* false_start */ + gtls_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ +- NULL /* disassociate_connection */ ++ NULL, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + #endif /* USE_GNUTLS */ +diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c +index fbde8976e..a5250289d 100644 +--- lib/vtls/mbedtls.c ++++ lib/vtls/mbedtls.c +@@ -1267,7 +1267,8 @@ const struct Curl_ssl Curl_ssl_mbedtls = { + Curl_none_false_start, /* false_start */ + mbedtls_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ +- NULL /* disassociate_connection */ ++ NULL, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + #endif /* USE_MBEDTLS */ +diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c +index b08808c43..55d777d69 100644 +--- lib/vtls/nss.c ++++ lib/vtls/nss.c +@@ -2530,7 +2530,8 @@ const struct Curl_ssl Curl_ssl_nss = { + nss_false_start, /* false_start */ + nss_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ +- NULL /* disassociate_connection */ ++ NULL, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + #endif /* USE_NSS */ +diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c +index d80f79921..b536b42da 100644 +--- lib/vtls/openssl.c ++++ lib/vtls/openssl.c +@@ -4619,7 +4619,8 @@ const struct Curl_ssl Curl_ssl_openssl = { + NULL, /* sha256sum */ + #endif + ossl_associate_connection, /* associate_connection */ +- ossl_disassociate_connection /* disassociate_connection */ ++ ossl_disassociate_connection, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + #endif /* USE_OPENSSL */ +diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c +index 77a49f1ab..45433bb85 100644 +--- lib/vtls/rustls.c ++++ lib/vtls/rustls.c +@@ -630,7 +630,8 @@ const struct Curl_ssl Curl_ssl_rustls = { + Curl_none_false_start, /* false_start */ + NULL, /* sha256sum */ + NULL, /* associate_connection */ +- NULL /* disassociate_connection */ ++ NULL, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + #endif /* USE_RUSTLS */ +diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c +index fcfb9c6df..1e6a93106 100644 +--- lib/vtls/schannel.c ++++ lib/vtls/schannel.c +@@ -2809,7 +2809,8 @@ const struct Curl_ssl Curl_ssl_schannel = { + Curl_none_false_start, /* false_start */ + schannel_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ +- NULL /* disassociate_connection */ ++ NULL, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + #endif /* USE_SCHANNEL */ +diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c +index d44f14ba6..d27adedca 100644 +--- lib/vtls/sectransp.c ++++ lib/vtls/sectransp.c +@@ -3531,7 +3531,8 @@ const struct Curl_ssl Curl_ssl_sectransp = { + sectransp_false_start, /* false_start */ + sectransp_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ +- NULL /* disassociate_connection */ ++ NULL, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + #ifdef __clang__ +diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c +index 9dee5aa3b..8a251b270 100644 +--- lib/vtls/vtls.c ++++ lib/vtls/vtls.c +@@ -655,6 +655,12 @@ void Curl_ssl_detach_conn(struct Curl_easy *data, + } + } + ++void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend) ++{ ++ if(Curl_ssl->free_multi_ssl_backend_data && mbackend) ++ Curl_ssl->free_multi_ssl_backend_data(mbackend); ++} ++ + void Curl_ssl_close_all(struct Curl_easy *data) + { + /* kill the session ID cache if not shared */ +@@ -1310,7 +1316,8 @@ static const struct Curl_ssl Curl_ssl_multi = { + Curl_none_false_start, /* false_start */ + NULL, /* sha256sum */ + NULL, /* associate_connection */ +- NULL /* disassociate_connection */ ++ NULL, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + const struct Curl_ssl *Curl_ssl = +diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h +index 50c53b3fb..1ab90c09e 100644 +--- lib/vtls/vtls.h ++++ lib/vtls/vtls.h +@@ -47,6 +47,10 @@ struct ssl_connect_data; + #define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \ + ALPN_ACCEPTED "%.*s" + ++/* Curl_multi SSL backend-specific data; declared differently by each SSL ++ backend */ ++struct multi_ssl_backend_data; ++ + struct Curl_ssl { + /* + * This *must* be the first entry to allow returning the list of available +@@ -102,6 +106,8 @@ struct Curl_ssl { + struct connectdata *conn, + int sockindex); + void (*disassociate_connection)(struct Curl_easy *data, int sockindex); ++ ++ void (*free_multi_ssl_backend_data)(struct multi_ssl_backend_data *mbackend); + }; + + #ifdef USE_SSL +@@ -311,6 +317,8 @@ void Curl_ssl_associate_conn(struct Curl_easy *data, + void Curl_ssl_detach_conn(struct Curl_easy *data, + struct connectdata *conn); + ++void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend); ++ + #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ + + #else /* if not USE_SSL */ +diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c +index 594c39a32..bc2a3c03f 100644 +--- lib/vtls/wolfssl.c ++++ lib/vtls/wolfssl.c +@@ -1241,7 +1241,8 @@ const struct Curl_ssl Curl_ssl_wolfssl = { + Curl_none_false_start, /* false_start */ + wolfssl_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ +- NULL /* disassociate_connection */ ++ NULL, /* disassociate_connection */ ++ NULL /* free_multi_ssl_backend_data */ + }; + + #endif +-- +2.34.1 + diff --git a/sdk/recipes/patches/libcurl/0006-openssl-cache-x509-store-on-multi-handle.p b/sdk/recipes/patches/libcurl/0006-openssl-cache-x509-store-on-multi-handle.p new file mode 100644 index 0000000..da158e6 --- /dev/null +++ b/sdk/recipes/patches/libcurl/0006-openssl-cache-x509-store-on-multi-handle.p @@ -0,0 +1,202 @@ +diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c +index b536b42da..6749b4be3 100644 +--- lib/vtls/openssl.c ++++ lib/vtls/openssl.c +@@ -110,6 +110,10 @@ + #include <openssl/ui.h> + #endif + ++#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) ++#define HAVE_THREADS ++#endif ++ + #if OPENSSL_VERSION_NUMBER >= 0x00909000L + #define SSL_METHOD_QUAL const + #else +@@ -259,6 +263,15 @@ + #define HAVE_OPENSSL_VERSION + #endif + ++/* ++ * Whether the OpenSSL version has the API needed to support sharing an ++ * X509_STORE between connections. The API is: ++ * * `X509_STORE_up_ref` -- Introduced: OpenSSL 1.1.0. ++ */ ++#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* OpenSSL >= 1.1.0 */ ++#define HAVE_SSL_X509_STORE_SHARE ++#endif ++ + struct ssl_backend_data { + struct Curl_easy *logger; /* transfer handle to pass trace logs to, only + using sockindex 0 */ +@@ -272,6 +285,13 @@ struct ssl_backend_data { + #endif + }; + ++#if defined(HAVE_SSL_X509_STORE_SHARE) ++struct multi_ssl_backend_data { ++ char *CAfile; /* CAfile path used to generate X509 store */ ++ X509_STORE *store; /* cached X509 store or NULL if none */ ++}; ++#endif /* HAVE_SSL_X509_STORE_SHARE */ ++ + #define push_certinfo(_label, _num) \ + do { \ + long info_len = BIO_get_mem_data(mem, &ptr); \ +@@ -3146,6 +3166,113 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + return result; + } + ++#if defined(HAVE_SSL_X509_STORE_SHARE) ++static bool cached_x509_store_different( ++ const struct multi_ssl_backend_data *mb, ++ const struct connectdata *conn) ++{ ++ if(!mb->CAfile || !SSL_CONN_CONFIG(CAfile)) ++ return mb->CAfile != SSL_CONN_CONFIG(CAfile); ++ ++ return strcmp(mb->CAfile, SSL_CONN_CONFIG(CAfile)); ++} ++ ++static X509_STORE *get_cached_x509_store(const struct Curl_easy *data, ++ const struct connectdata *conn) ++{ ++ struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; ++ X509_STORE *store = NULL; ++ ++ if(multi && ++ multi->ssl_backend_data && ++ multi->ssl_backend_data->store && ++ !cached_x509_store_different(multi->ssl_backend_data, conn)) { ++ store = multi->ssl_backend_data->store; ++ } ++ ++ return store; ++} ++ ++static void set_cached_x509_store(const struct Curl_easy *data, ++ const struct connectdata *conn, ++ X509_STORE *store) ++{ ++ struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; ++ struct multi_ssl_backend_data *mbackend; ++ ++ if(!multi) ++ return; ++ ++ if(!multi->ssl_backend_data) { ++ multi->ssl_backend_data = calloc(1, sizeof(struct multi_ssl_backend_data)); ++ if(!multi->ssl_backend_data) ++ return; ++ } ++ ++ mbackend = multi->ssl_backend_data; ++ ++ if(X509_STORE_up_ref(store)) { ++ char *CAfile = NULL; ++ ++ if(SSL_CONN_CONFIG(CAfile)) { ++ CAfile = strdup(SSL_CONN_CONFIG(CAfile)); ++ if(!CAfile) { ++ X509_STORE_free(store); ++ return; ++ } ++ } ++ ++ if(mbackend->store) { ++ X509_STORE_free(mbackend->store); ++ free(mbackend->CAfile); ++ } ++ ++ mbackend->store = store; ++ mbackend->CAfile = CAfile; ++ } ++} ++ ++static CURLcode set_up_x509_store(struct Curl_easy *data, ++ struct connectdata *conn, ++ struct ssl_backend_data *backend) ++{ ++ CURLcode result = CURLE_OK; ++ X509_STORE *cached_store = get_cached_x509_store(data, conn); ++ ++ /* Consider the X509 store cacheable if it comes exclusively from a CAfile, ++ or no source is provided and we are falling back to openssl's built-in ++ default. */ ++ bool cache_criteria_met = SSL_CONN_CONFIG(verifypeer) && ++ !SSL_CONN_CONFIG(CApath) && ++ !SSL_CONN_CONFIG(ca_info_blob) && ++ !SSL_SET_OPTION(primary.CRLfile) && ++ !SSL_SET_OPTION(native_ca_store); ++ ++ if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) { ++ SSL_CTX_set_cert_store(backend->ctx, cached_store); ++ } ++ else { ++ X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); ++ ++ result = populate_x509_store(data, conn, store); ++ if(result == CURLE_OK && cache_criteria_met) { ++ set_cached_x509_store(data, conn, store); ++ } ++ } ++ ++ return result; ++} ++#else /* HAVE_SSL_X509_STORE_SHARE */ ++static CURLcode set_up_x509_store(struct Curl_easy *data, ++ struct connectdata *conn, ++ struct ssl_backend_data *backend) ++{ ++ X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); ++ ++ return populate_x509_store(data, conn, store); ++} ++#endif /* HAVE_SSL_X509_STORE_SHARE */ ++ + static CURLcode ossl_connect_step1(struct Curl_easy *data, + struct connectdata *conn, int sockindex) + { +@@ -3443,8 +3570,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, + } + #endif + +- result = populate_x509_store(data, conn, +- SSL_CTX_get_cert_store(backend->ctx)); ++ result = set_up_x509_store(data, conn, backend); + if(result) + return result; + +@@ -4579,6 +4705,20 @@ static void ossl_disassociate_connection(struct Curl_easy *data, + } + } + ++static void ossl_free_multi_ssl_backend_data( ++ struct multi_ssl_backend_data *mbackend) ++{ ++#if defined(HAVE_SSL_X509_STORE_SHARE) ++ if(mbackend->store) { ++ X509_STORE_free(mbackend->store); ++ } ++ free(mbackend->CAfile); ++ free(mbackend); ++#else /* HAVE_SSL_X509_STORE_SHARE */ ++ (void)mbackend; ++#endif /* HAVE_SSL_X509_STORE_SHARE */ ++} ++ + const struct Curl_ssl Curl_ssl_openssl = { + { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */ + +@@ -4620,7 +4760,7 @@ const struct Curl_ssl Curl_ssl_openssl = { + #endif + ossl_associate_connection, /* associate_connection */ + ossl_disassociate_connection, /* disassociate_connection */ +- NULL /* free_multi_ssl_backend_data */ ++ ossl_free_multi_ssl_backend_data /* free_multi_ssl_backend_data */ + }; + + #endif /* USE_OPENSSL */ +-- +2.34.1 + diff --git a/sdk/recipes/patches/libcurl/0007-openssl-use-new-x509-store-if-cached-is-24h-old.p b/sdk/recipes/patches/libcurl/0007-openssl-use-new-x509-store-if-cached-is-24h-old.p new file mode 100644 index 0000000..d9c9c77 --- /dev/null +++ b/sdk/recipes/patches/libcurl/0007-openssl-use-new-x509-store-if-cached-is-24h-old.p @@ -0,0 +1,50 @@ +diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c +index 6749b4be3..9c752554e 100644 +--- lib/vtls/openssl.c ++++ lib/vtls/openssl.c +@@ -287,8 +287,9 @@ struct ssl_backend_data { + + #if defined(HAVE_SSL_X509_STORE_SHARE) + struct multi_ssl_backend_data { +- char *CAfile; /* CAfile path used to generate X509 store */ +- X509_STORE *store; /* cached X509 store or NULL if none */ ++ char *CAfile; /* CAfile path used to generate X509 store */ ++ X509_STORE *store; /* cached X509 store or NULL if none */ ++ struct curltime time; /* when the cached store was created */ + }; + #endif /* HAVE_SSL_X509_STORE_SHARE */ + +@@ -3167,6 +3168,14 @@ static CURLcode populate_x509_store(struct Curl_easy *data, + } + + #if defined(HAVE_SSL_X509_STORE_SHARE) ++#define X509_STORE_EXPIRY_MS (24 * 60 * 60 * 1000) /* 24 hours */ ++static bool cached_x509_store_expired(const struct multi_ssl_backend_data *mb) ++{ ++ struct curltime now = Curl_now(); ++ ++ return Curl_timediff(now, mb->time) >= X509_STORE_EXPIRY_MS; ++} ++ + static bool cached_x509_store_different( + const struct multi_ssl_backend_data *mb, + const struct connectdata *conn) +@@ -3186,6 +3195,7 @@ static X509_STORE *get_cached_x509_store(const struct Curl_easy *data, + if(multi && + multi->ssl_backend_data && + multi->ssl_backend_data->store && ++ !cached_x509_store_expired(multi->ssl_backend_data) && + !cached_x509_store_different(multi->ssl_backend_data, conn)) { + store = multi->ssl_backend_data->store; + } +@@ -3227,6 +3237,7 @@ static void set_cached_x509_store(const struct Curl_easy *data, + free(mbackend->CAfile); + } + ++ mbackend->time = Curl_now(); + mbackend->store = store; + mbackend->CAfile = CAfile; + } +-- +2.34.1 + |