Gitweb links:
...log
http://git.netsurf-browser.org/toolchains.git/shortlog/ab619018e422ed8b46d452793b3c72b062b84d0a
...commit
http://git.netsurf-browser.org/toolchains.git/commit/ab619018e422ed8b46d452793b3c72b062b84d0a
...tree
http://git.netsurf-browser.org/toolchains.git/tree/ab619018e422ed8b46d452793b3c72b062b84d0a
The branch, master has been updated
via ab619018e422ed8b46d452793b3c72b062b84d0a (commit)
from 2bbddd7aaabeae11c1862787ef6f99b3b816324a (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff
http://git.netsurf-browser.org/toolchains.git/commit/?id=ab619018e422ed8b46d452793b3c72b062b84d0a
commit ab619018e422ed8b46d452793b3c72b062b84d0a
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>
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
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
+
-----------------------------------------------------------------------
Summary of changes:
.../0002-openssl-split-out-x509-store-loading.p | 554 ++++++++++++++++++++
.../0003-openssl-pass-x509-store-to-be-populated.p | 174 ++++++
.../0004-urldata-fix-multi-handle-comment.p | 16 +
...tls-enable-ssl-backend-specific-storage-on-mu.p | 245 +++++++++
...0006-openssl-cache-x509-store-on-multi-handle.p | 202 +++++++
...enssl-use-new-x509-store-if-cached-is-24h-old.p | 50 ++
6 files changed, 1241 insertions(+)
create mode 100644
sdk/recipes/patches/libcurl/0002-openssl-split-out-x509-store-loading.p
create mode 100644
sdk/recipes/patches/libcurl/0003-openssl-pass-x509-store-to-be-populated.p
create mode 100644
sdk/recipes/patches/libcurl/0004-urldata-fix-multi-handle-comment.p
create mode 100644
sdk/recipes/patches/libcurl/0005-multi-vtls-enable-ssl-backend-specific-storage-on-mu.p
create mode 100644
sdk/recipes/patches/libcurl/0006-openssl-cache-x509-store-on-multi-handle.p
create mode 100644
sdk/recipes/patches/libcurl/0007-openssl-use-new-x509-store-if-cached-is-24h-old.p
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
+
--
Cross-compilation toolchains and environments
_______________________________________________
netsurf-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]