summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <michael.drake@codethink.co.uk>2022-11-02 11:34:49 +0000
committerMichael Drake <michael.drake@codethink.co.uk>2022-11-02 11:34:49 +0000
commitab619018e422ed8b46d452793b3c72b062b84d0a (patch)
treef4d53da550ce1dfc1a0a0575dc0621fdba4f6608
parent2bbddd7aaabeae11c1862787ef6f99b3b816324a (diff)
downloadtoolchains-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
-rw-r--r--sdk/recipes/patches/libcurl/0002-openssl-split-out-x509-store-loading.p554
-rw-r--r--sdk/recipes/patches/libcurl/0003-openssl-pass-x509-store-to-be-populated.p174
-rw-r--r--sdk/recipes/patches/libcurl/0004-urldata-fix-multi-handle-comment.p16
-rw-r--r--sdk/recipes/patches/libcurl/0005-multi-vtls-enable-ssl-backend-specific-storage-on-mu.p245
-rw-r--r--sdk/recipes/patches/libcurl/0006-openssl-cache-x509-store-on-multi-handle.p202
-rw-r--r--sdk/recipes/patches/libcurl/0007-openssl-use-new-x509-store-if-cached-is-24h-old.p50
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
+