]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
openssl: reduce CA certificate bundle reparsing by caching
authorMichael Drake <michael.drake@codethink.co.uk>
Wed, 28 Sep 2022 14:51:44 +0000 (15:51 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 8 Nov 2022 09:06:12 +0000 (10:06 +0100)
Closes #9620

15 files changed:
lib/multi.c
lib/multihandle.h
lib/urldata.h
lib/vtls/bearssl.c
lib/vtls/gskit.c
lib/vtls/gtls.c
lib/vtls/mbedtls.c
lib/vtls/nss.c
lib/vtls/openssl.c
lib/vtls/rustls.c
lib/vtls/schannel.c
lib/vtls/sectransp.c
lib/vtls/vtls.c
lib/vtls/vtls.h
lib/vtls/wolfssl.c

index 51acba73ac67b3b3166bcec11ea3a4292bc184ae..09965de839d70313ec0206084282cfeb711f75e3 100644 (file)
@@ -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;
index a997784ea3772b70c2f9f9cdc5760e87e7ee8123..5a83656d5117cce0f248b1b771bcb322bbeb1864 100644 (file)
@@ -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) */
index fc79851765ba0f581b1187a8077c90cb585c4a90..bc8dfd623883204028e1dfe1059cc74d7aba286f 100644 (file)
@@ -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
index 1221ce8c84d4359c01fdfe8050d02810fc07cbd0..e66dd8469c903cc2c911f3f46e893154522d663c 100644 (file)
@@ -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 */
index 52d2eaec3d14b34af78a4edb9676ed5261b713fa..719314f68435396a861b2997b094b551e7726ff0 100644 (file)
@@ -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 */
index cf3dbc52381281c4a9ff24e5da48c5d1353e1f47..68e3fe299df059ea91598ff02f50359167045268 100644 (file)
@@ -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 */
index fbde8976eb431de9f62dc05d36a313e2f0f5db27..a5250289d8a11290b4051578ac872d969a5db1c5 100644 (file)
@@ -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 */
index b08808c433695d2f4dfb0fda9455c3bfd664d891..55d777d69b4bc43fc8ef0d2cb88873f7ff9c6031 100644 (file)
@@ -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 */
index 29ff13f1c60627eef057422692ad5ea06f87399a..4b2aa553400297e79207a7eeb7536874f5f92a35 100644 (file)
 #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 +281,14 @@ 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 */
+  struct curltime time; /* when the cached store was created */
+};
+#endif /* HAVE_SSL_X509_STORE_SHARE */
+
 #define push_certinfo(_label, _num)             \
 do {                              \
   long info_len = BIO_get_mem_data(mem, &ptr); \
@@ -2830,7 +2847,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 +2856,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 +2875,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 +2885,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 {
@@ -2891,161 +2903,530 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx,
   return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE);
 }
 
-static CURLcode ossl_connect_step1(struct Curl_easy *data,
-                                   struct connectdata *conn, int sockindex)
+static CURLcode populate_x509_store(struct Curl_easy *data,
+                                    struct connectdata *conn,
+                                    X509_STORE *store)
 {
   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;
-  void *ssl_sessionid = NULL;
-
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-  bool sni;
-  const char * const hostname = SSL_HOST_NAME();
-
-#ifdef ENABLE_IPV6
-  struct in6_addr addr;
-#else
-  struct in_addr addr;
-#endif
-#endif
-  const long int ssl_version = SSL_CONN_CONFIG(version);
-#ifdef USE_OPENSSL_SRP
-  const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(primary.authtype);
-#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;
+  const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
   bool imported_native_ca = false;
 
-  DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
-  DEBUGASSERT(backend);
-
-  /* Make funny stuff to get random input */
-  result = ossl_seed(data);
-  if(result)
-    return result;
+  if(!store)
+    return CURLE_OUT_OF_MEMORY;
 
-  SSL_SET_OPTION_LVALUE(certverifyresult) = !X509_V_OK;
+#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))) {
+    HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT"));
 
-  /* check to see if we've been told to use an explicit SSL/TLS version */
+    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;
 
-  switch(ssl_version) {
-  case CURL_SSLVERSION_DEFAULT:
-  case CURL_SSLVERSION_TLSv1:
-  case CURL_SSLVERSION_TLSv1_0:
-  case CURL_SSLVERSION_TLSv1_1:
-  case CURL_SSLVERSION_TLSv1_2:
-  case CURL_SSLVERSION_TLSv1_3:
-    /* it will be handled later with the context options */
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
-    req_method = TLS_client_method();
-#else
-    req_method = SSLv23_client_method();
+      /* 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
-    use_sni(TRUE);
-    break;
-  case CURL_SSLVERSION_SSLv2:
-    failf(data, "No SSLv2 support");
-    return CURLE_NOT_BUILT_IN;
-  case CURL_SSLVERSION_SSLv3:
-    failf(data, "No SSLv3 support");
-    return CURLE_NOT_BUILT_IN;
-  default:
-    failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  DEBUGASSERT(!backend->ctx);
-  backend->ctx = SSL_CTX_new(req_method);
 
-  if(!backend->ctx) {
-    failf(data, "SSL: couldn't create a context: %s",
-          ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer)));
-    return CURLE_OUT_OF_MEMORY;
-  }
+        pContext = CertEnumCertificatesInStore(hStore, pContext);
+        if(!pContext)
+          break;
 
-#ifdef SSL_MODE_RELEASE_BUFFERS
-  SSL_CTX_set_mode(backend->ctx, SSL_MODE_RELEASE_BUFFERS);
+#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
 
-#ifdef SSL_CTRL_SET_MSG_CALLBACK
-  if(data->set.fdebug && data->set.verbose) {
-    /* the SSL trace callback is only used for verbose logging */
-    SSL_CTX_set_msg_callback(backend->ctx, ossl_trace);
-    SSL_CTX_set_msg_callback_arg(backend->ctx, conn);
-    set_logger(conn, data);
-  }
-#endif
+        encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
+        if(!encoded_cert)
+          continue;
 
-  /* OpenSSL contains code to work around lots of bugs and flaws in various
-     SSL-implementations. SSL_CTX_set_options() is used to enabled those
-     work-arounds. The man page for this option states that SSL_OP_ALL enables
-     all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
-     enable the bug workaround options if compatibility with somewhat broken
-     implementations is desired."
+        GetSystemTimeAsFileTime(&now);
+        if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
+           CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
+          continue;
 
-     The "-no_ticket" option was introduced in OpenSSL 0.9.8j. It's a flag to
-     disable "rfc4507bis session ticket support". rfc4507bis was later turned
-     into the proper RFC5077: https://datatracker.ietf.org/doc/html/rfc5077
+        /* 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;
 
-     The enabled extension concerns the session management. I wonder how often
-     libcurl stops a connection and then resumes a TLS session. Also, sending
-     the session data is some overhead. I suggest that you just use your
-     proposed patch (which explicitly disables TICKET).
+        /* 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 someone writes an application with libcurl and OpenSSL who wants to
-     enable the feature, one can do this in the SSL callback.
+            if(!tmp) {
+              failf(data, "SSL: Out of memory allocating for OID list");
+              result = CURLE_OUT_OF_MEMORY;
+              break;
+            }
 
-     SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG option enabling allowed proper
-     interoperability with web server Netscape Enterprise Server 2.0.1 which
-     was released back in 1996.
+            enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
+            enhkey_usage_size = req_size;
+          }
 
-     Due to CVE-2010-4180, option SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG has
-     become ineffective as of OpenSSL 0.9.8q and 1.0.0c. In order to mitigate
-     CVE-2010-4180 when using previous OpenSSL versions we no longer enable
-     this option regardless of OpenSSL version and SSL_OP_ALL definition.
+          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;
 
-     OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability
-     (https://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to
-     SSL_OP_ALL that _disables_ that work-around despite the fact that
-     SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to
-     keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit
-     must not be set.
-  */
+              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;
+                }
+              }
 
-  ctx_options = SSL_OP_ALL;
+              if(!found)
+                continue;
+            }
+          }
+          else
+            continue;
+        }
+        else
+          continue;
 
-#ifdef SSL_OP_NO_TICKET
-  ctx_options |= SSL_OP_NO_TICKET;
-#endif
+        x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+        if(!x509)
+          continue;
 
-#ifdef SSL_OP_NO_COMPRESSION
-  ctx_options |= SSL_OP_NO_COMPRESSION;
+        /* 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);
+      }
 
-#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
-  /* mitigate CVE-2010-4180 */
-  ctx_options &= ~SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
+      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
 
-#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
-  /* unless the user explicitly asks to allow the protocol vulnerability we
-     use the work-around */
-  if(!SSL_SET_OPTION(enable_beast))
+  if(ca_info_blob) {
+    result = load_cacert_from_memory(store, 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 &&
+       !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 &&
+       !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;
+    }
+#else
+    /* tell OpenSSL where to find CA certificates that are used to verify the
+       server's certificate. */
+    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",
+            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 */
+    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(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);
+      return CURLE_SSL_CRL_BADFILE;
+    }
+    /* Everything is fine. */
+    infof(data, "successfully loaded CRL file:");
+    X509_STORE_set_flags(store,
+                         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(store, 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(store, X509_V_FLAG_PARTIAL_CHAIN);
+    }
+#endif
+  }
+
+  return result;
+}
+
+#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)
+{
+  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_expired(multi->ssl_backend_data) &&
+     !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->time = Curl_now();
+    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)
+{
+  CURLcode result = CURLE_OK;
+  char *ciphers;
+  SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
+  curl_socket_t sockfd = conn->sock[sockindex];
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  ctx_option_t ctx_options = 0;
+  void *ssl_sessionid = NULL;
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+  bool sni;
+  const char * const hostname = SSL_HOST_NAME();
+
+#ifdef ENABLE_IPV6
+  struct in6_addr addr;
+#else
+  struct in_addr addr;
+#endif
+#endif
+  const long int ssl_version = SSL_CONN_CONFIG(version);
+#ifdef USE_OPENSSL_SRP
+  const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(primary.authtype);
+#endif
+  char * const ssl_cert = SSL_SET_OPTION(primary.clientcert);
+  const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob);
+  const char * const ssl_cert_type = SSL_SET_OPTION(cert_type);
+  const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
+  char error_buffer[256];
+  struct ssl_backend_data *backend = connssl->backend;
+
+  DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
+  DEBUGASSERT(backend);
+
+  /* Make funny stuff to get random input */
+  result = ossl_seed(data);
+  if(result)
+    return result;
+
+  SSL_SET_OPTION_LVALUE(certverifyresult) = !X509_V_OK;
+
+  /* check to see if we've been told to use an explicit SSL/TLS version */
+
+  switch(ssl_version) {
+  case CURL_SSLVERSION_DEFAULT:
+  case CURL_SSLVERSION_TLSv1:
+  case CURL_SSLVERSION_TLSv1_0:
+  case CURL_SSLVERSION_TLSv1_1:
+  case CURL_SSLVERSION_TLSv1_2:
+  case CURL_SSLVERSION_TLSv1_3:
+    /* it will be handled later with the context options */
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+    req_method = TLS_client_method();
+#else
+    req_method = SSLv23_client_method();
+#endif
+    use_sni(TRUE);
+    break;
+  case CURL_SSLVERSION_SSLv2:
+    failf(data, "No SSLv2 support");
+    return CURLE_NOT_BUILT_IN;
+  case CURL_SSLVERSION_SSLv3:
+    failf(data, "No SSLv3 support");
+    return CURLE_NOT_BUILT_IN;
+  default:
+    failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  DEBUGASSERT(!backend->ctx);
+  backend->ctx = SSL_CTX_new(req_method);
+
+  if(!backend->ctx) {
+    failf(data, "SSL: couldn't create a context: %s",
+          ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer)));
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+#ifdef SSL_MODE_RELEASE_BUFFERS
+  SSL_CTX_set_mode(backend->ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+
+#ifdef SSL_CTRL_SET_MSG_CALLBACK
+  if(data->set.fdebug && data->set.verbose) {
+    /* the SSL trace callback is only used for verbose logging */
+    SSL_CTX_set_msg_callback(backend->ctx, ossl_trace);
+    SSL_CTX_set_msg_callback_arg(backend->ctx, conn);
+    set_logger(conn, data);
+  }
+#endif
+
+  /* OpenSSL contains code to work around lots of bugs and flaws in various
+     SSL-implementations. SSL_CTX_set_options() is used to enabled those
+     work-arounds. The man page for this option states that SSL_OP_ALL enables
+     all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
+     enable the bug workaround options if compatibility with somewhat broken
+     implementations is desired."
+
+     The "-no_ticket" option was introduced in OpenSSL 0.9.8j. It's a flag to
+     disable "rfc4507bis session ticket support". rfc4507bis was later turned
+     into the proper RFC5077: https://datatracker.ietf.org/doc/html/rfc5077
+
+     The enabled extension concerns the session management. I wonder how often
+     libcurl stops a connection and then resumes a TLS session. Also, sending
+     the session data is some overhead. I suggest that you just use your
+     proposed patch (which explicitly disables TICKET).
+
+     If someone writes an application with libcurl and OpenSSL who wants to
+     enable the feature, one can do this in the SSL callback.
+
+     SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG option enabling allowed proper
+     interoperability with web server Netscape Enterprise Server 2.0.1 which
+     was released back in 1996.
+
+     Due to CVE-2010-4180, option SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG has
+     become ineffective as of OpenSSL 0.9.8q and 1.0.0c. In order to mitigate
+     CVE-2010-4180 when using previous OpenSSL versions we no longer enable
+     this option regardless of OpenSSL version and SSL_OP_ALL definition.
+
+     OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability
+     (https://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to
+     SSL_OP_ALL that _disables_ that work-around despite the fact that
+     SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to
+     keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit
+     must not be set.
+  */
+
+  ctx_options = SSL_OP_ALL;
+
+#ifdef SSL_OP_NO_TICKET
+  ctx_options |= SSL_OP_NO_TICKET;
+#endif
+
+#ifdef SSL_OP_NO_COMPRESSION
+  ctx_options |= SSL_OP_NO_COMPRESSION;
+#endif
+
+#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
+  /* mitigate CVE-2010-4180 */
+  ctx_options &= ~SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
+#endif
+
+#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+  /* unless the user explicitly asks to allow the protocol vulnerability we
+     use the work-around */
+  if(!SSL_SET_OPTION(enable_beast))
     ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
 #endif
 
@@ -3196,249 +3577,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 = set_up_x509_store(data, conn, backend);
+  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
@@ -4571,6 +4712,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 */
 
@@ -4611,7 +4766,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 */
+  ossl_free_multi_ssl_backend_data /* free_multi_ssl_backend_data */
 };
 
 #endif /* USE_OPENSSL */
index 77a49f1ab4323455297f96eb055a5ac71ab62eab..45433bb85f7cb14f159a374b0cca0e32fb6bac9b 100644 (file)
@@ -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 */
index d5c22446e4c74b91a62c35e95b4b50971e7972f8..ed9746f531e5da2c19627b5e2a58ceab3c00d2f9 100644 (file)
@@ -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 */
index 26f04ceecd2c77ada320990420b35435708621ce..ffabf127698841cb80560a18906855b44bad06fe 100644 (file)
@@ -3529,7 +3529,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__
index cbe03f589846b7d7066f6808e1f3a8f5fe335dca..468cce202aaa1fe500beda5336f76a1b3670655a 100644 (file)
@@ -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 =
index 50c53b3fbd5e2fabbfc6668edb8b4ab5caa4c30c..1ab90c09ed64eedc98011e88bf3a507ae45e7da4 100644 (file)
@@ -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 */
index 594c39a3246d4724914ffde2c800358c8c5c06f5..bc2a3c03f7f724d9bcd2bc797da1b3e68e443948 100644 (file)
@@ -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