Imported Upstream version 7.59.0
[platform/upstream/curl.git] / lib / vtls / vtls.c
index f40a977..def1d30 100644 (file)
    to any specific SSL-layer.
 
    Curl_ssl_ - prefix for generic ones
-   Curl_ossl_ - prefix for OpenSSL ones
-   Curl_gtls_ - prefix for GnuTLS ones
-   Curl_nss_ - prefix for NSS ones
-   Curl_gskit_ - prefix for GSKit ones
-   Curl_polarssl_ - prefix for PolarSSL ones
-   Curl_cyassl_ - prefix for CyaSSL ones
-   Curl_schannel_ - prefix for Schannel SSPI ones
-   Curl_darwinssl_ - prefix for SecureTransport (Darwin) ones
-
-   Note that this source code uses curlssl_* functions, and they are all
-   defines/macros #defined by the lib-specific header files.
+
+   Note that this source code uses the functions of the configured SSL
+   backend via the global Curl_ssl instance.
 
    "SSL/TLS Strong Encryption: An Introduction"
    https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html
@@ -95,11 +87,15 @@ Curl_ssl_config_matches(struct ssl_primary_config* data,
                         struct ssl_primary_config* needle)
 {
   if((data->version == needle->version) &&
+     (data->version_max == needle->version_max) &&
      (data->verifypeer == needle->verifypeer) &&
      (data->verifyhost == needle->verifyhost) &&
+     (data->verifystatus == needle->verifystatus) &&
      Curl_safe_strcasecompare(data->CApath, needle->CApath) &&
      Curl_safe_strcasecompare(data->CAfile, needle->CAfile) &&
      Curl_safe_strcasecompare(data->clientcert, needle->clientcert) &&
+     Curl_safe_strcasecompare(data->random_file, needle->random_file) &&
+     Curl_safe_strcasecompare(data->egdsocket, needle->egdsocket) &&
      Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list))
     return TRUE;
 
@@ -110,38 +106,51 @@ bool
 Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
                               struct ssl_primary_config *dest)
 {
-  dest->verifyhost = source->verifyhost;
-  dest->verifypeer = source->verifypeer;
   dest->version = source->version;
+  dest->version_max = source->version_max;
+  dest->verifypeer = source->verifypeer;
+  dest->verifyhost = source->verifyhost;
+  dest->verifystatus = source->verifystatus;
+  dest->sessionid = source->sessionid;
 
-  CLONE_STRING(CAfile);
   CLONE_STRING(CApath);
-  CLONE_STRING(cipher_list);
-  CLONE_STRING(egdsocket);
-  CLONE_STRING(random_file);
+  CLONE_STRING(CAfile);
   CLONE_STRING(clientcert);
+  CLONE_STRING(random_file);
+  CLONE_STRING(egdsocket);
+  CLONE_STRING(cipher_list);
+
   return TRUE;
 }
 
 void Curl_free_primary_ssl_config(struct ssl_primary_config* sslc)
 {
-  Curl_safefree(sslc->CAfile);
   Curl_safefree(sslc->CApath);
-  Curl_safefree(sslc->cipher_list);
-  Curl_safefree(sslc->egdsocket);
-  Curl_safefree(sslc->random_file);
+  Curl_safefree(sslc->CAfile);
   Curl_safefree(sslc->clientcert);
+  Curl_safefree(sslc->random_file);
+  Curl_safefree(sslc->egdsocket);
+  Curl_safefree(sslc->cipher_list);
 }
 
+#ifdef USE_SSL
+static int multissl_init(const struct Curl_ssl *backend);
+#endif
+
 int Curl_ssl_backend(void)
 {
-  return (int)CURL_SSL_BACKEND;
+#ifdef USE_SSL
+  multissl_init(NULL);
+  return Curl_ssl->info.id;
+#else
+  return (int)CURLSSLBACKEND_NONE;
+#endif
 }
 
 #ifdef USE_SSL
 
 /* "global" init done? */
-static bool init_ssl=FALSE;
+static bool init_ssl = FALSE;
 
 /**
  * Global SSL init
@@ -156,7 +165,7 @@ int Curl_ssl_init(void)
     return 1;
   init_ssl = TRUE; /* never again */
 
-  return curlssl_init();
+  return Curl_ssl->init();
 }
 
 
@@ -165,7 +174,7 @@ void Curl_ssl_cleanup(void)
 {
   if(init_ssl) {
     /* only cleanup if we did a previous init */
-    curlssl_cleanup();
+    Curl_ssl->cleanup();
     init_ssl = FALSE;
   }
 }
@@ -173,11 +182,24 @@ void Curl_ssl_cleanup(void)
 static bool ssl_prefs_check(struct Curl_easy *data)
 {
   /* check for CURLOPT_SSLVERSION invalid parameter value */
-  if((data->set.ssl.primary.version < 0)
-     || (data->set.ssl.primary.version >= CURL_SSLVERSION_LAST)) {
+  const long sslver = data->set.ssl.primary.version;
+  if((sslver < 0) || (sslver >= CURL_SSLVERSION_LAST)) {
     failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION");
     return FALSE;
   }
+
+  switch(data->set.ssl.primary.version_max) {
+  case CURL_SSLVERSION_MAX_NONE:
+  case CURL_SSLVERSION_MAX_DEFAULT:
+    break;
+
+  default:
+    if((data->set.ssl.primary.version_max >> 16) < sslver) {
+      failf(data, "CURL_SSLVERSION_MAX incompatible with CURL_SSLVERSION");
+      return FALSE;
+    }
+  }
+
   return TRUE;
 }
 
@@ -187,12 +209,20 @@ ssl_connect_init_proxy(struct connectdata *conn, int sockindex)
   DEBUGASSERT(conn->bits.proxy_ssl_connected[sockindex]);
   if(ssl_connection_complete == conn->ssl[sockindex].state &&
      !conn->proxy_ssl[sockindex].use) {
-#if defined(HTTPS_PROXY_SUPPORT)
+    struct ssl_backend_data *pbdata;
+
+    if(!Curl_ssl->support_https_proxy)
+      return CURLE_NOT_BUILT_IN;
+
+    /* The pointers to the ssl backend data, which is opaque here, are swapped
+       rather than move the contents. */
+    pbdata = conn->proxy_ssl[sockindex].backend;
     conn->proxy_ssl[sockindex] = conn->ssl[sockindex];
+
     memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex]));
-#else
-    return CURLE_NOT_BUILT_IN;
-#endif
+    memset(pbdata, 0, Curl_ssl->sizeof_ssl_backend_data);
+
+    conn->ssl[sockindex].backend = pbdata;
   }
   return CURLE_OK;
 }
@@ -215,7 +245,7 @@ Curl_ssl_connect(struct connectdata *conn, int sockindex)
   conn->ssl[sockindex].use = TRUE;
   conn->ssl[sockindex].state = ssl_connection_negotiating;
 
-  result = curlssl_connect(conn, sockindex);
+  result = Curl_ssl->connect(conn, sockindex);
 
   if(!result)
     Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
@@ -239,12 +269,7 @@ Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
 
   /* mark this is being ssl requested from here on. */
   conn->ssl[sockindex].use = TRUE;
-#ifdef curlssl_connect_nonblocking
-  result = curlssl_connect_nonblocking(conn, sockindex, done);
-#else
-  *done = TRUE; /* fallback to BLOCKING */
-  result = curlssl_connect(conn, sockindex);
-#endif /* non-blocking connect support */
+  result = Curl_ssl->connect_nonblocking(conn, sockindex, done);
   if(!result && *done)
     Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
   return result;
@@ -293,9 +318,9 @@ bool Curl_ssl_getsessionid(struct connectdata *conn,
   int port = isProxy ? (int)conn->port : conn->remote_port;
   *ssl_sessionid = NULL;
 
-  DEBUGASSERT(data->set.general_ssl.sessionid);
+  DEBUGASSERT(SSL_SET_OPTION(primary.sessionid));
 
-  if(!data->set.general_ssl.sessionid)
+  if(!SSL_SET_OPTION(primary.sessionid))
     /* session ID re-use is disabled */
     return TRUE;
 
@@ -343,7 +368,7 @@ void Curl_ssl_kill_session(struct curl_ssl_session *session)
     /* defensive check */
 
     /* free the ID the SSL-layer specific way */
-    curlssl_session_free(session->sessionid);
+    Curl_ssl->session_free(session->sessionid);
 
     session->sessionid = NULL;
     session->age = 0; /* fresh */
@@ -361,7 +386,7 @@ void Curl_ssl_kill_session(struct curl_ssl_session *session)
 void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid)
 {
   size_t i;
-  struct Curl_easy *data=conn->data;
+  struct Curl_easy *data = conn->data;
 
   for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
     struct curl_ssl_session *check = &data->state.session[i];
@@ -385,9 +410,9 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
                                int sockindex)
 {
   size_t i;
-  struct Curl_easy *data=conn->data; /* the mother of all structs */
+  struct Curl_easy *data = conn->data; /* the mother of all structs */
   struct curl_ssl_session *store = &data->state.session[0];
-  long oldest_age=data->state.session[0].age; /* zero if unused */
+  long oldest_age = data->state.session[0].age; /* zero if unused */
   char *clone_host;
   char *clone_conn_to_host;
   int conn_to_port;
@@ -397,7 +422,7 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
     &conn->proxy_ssl_config :
     &conn->ssl_config;
 
-  DEBUGASSERT(data->set.general_ssl.sessionid);
+  DEBUGASSERT(SSL_SET_OPTION(primary.sessionid));
 
   clone_host = strdup(isProxy ? conn->http_proxy.host.name : conn->host.name);
   if(!clone_host)
@@ -481,7 +506,7 @@ void Curl_ssl_close_all(struct Curl_easy *data)
     Curl_safefree(data->state.session);
   }
 
-  curlssl_close_all(data);
+  Curl_ssl->close_all(data);
 }
 
 #if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
@@ -500,7 +525,7 @@ int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks,
     socks[0] = conn->sock[FIRSTSOCKET];
     return GETSOCK_WRITESOCK(0);
   }
-  else if(connssl->connecting_state == ssl_connect_2_reading) {
+  if(connssl->connecting_state == ssl_connect_2_reading) {
     /* read mode */
     socks[0] = conn->sock[FIRSTSOCKET];
     return GETSOCK_READSOCK(0);
@@ -524,12 +549,12 @@ int Curl_ssl_getsock(struct connectdata *conn,
 void Curl_ssl_close(struct connectdata *conn, int sockindex)
 {
   DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
-  curlssl_close(conn, sockindex);
+  Curl_ssl->close_one(conn, sockindex);
 }
 
 CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex)
 {
-  if(curlssl_shutdown(conn, sockindex))
+  if(Curl_ssl->shutdown(conn, sockindex))
     return CURLE_SSL_SHUTDOWN_FAILED;
 
   conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */
@@ -545,20 +570,20 @@ CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex)
  */
 CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine)
 {
-  return curlssl_set_engine(data, engine);
+  return Curl_ssl->set_engine(data, engine);
 }
 
 /* Selects the default SSL crypto engine
  */
 CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data)
 {
-  return curlssl_set_engine_default(data);
+  return Curl_ssl->set_engine_default(data);
 }
 
 /* Return list of OpenSSL crypto engine names. */
 struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data)
 {
-  return curlssl_engines_list(data);
+  return Curl_ssl->engines_list(data);
 }
 
 /*
@@ -584,9 +609,15 @@ CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount)
   return CURLE_OK;
 }
 
+static size_t Curl_multissl_version(char *buffer, size_t size);
+
 size_t Curl_ssl_version(char *buffer, size_t size)
 {
-  return curlssl_version(buffer, size);
+#ifdef CURL_WITH_MULTI_SSL
+  return Curl_multissl_version(buffer, size);
+#else
+  return Curl_ssl->version(buffer, size);
+#endif
 }
 
 /*
@@ -599,13 +630,13 @@ size_t Curl_ssl_version(char *buffer, size_t size)
  */
 int Curl_ssl_check_cxn(struct connectdata *conn)
 {
-  return curlssl_check_cxn(conn);
+  return Curl_ssl->check_cxn(conn);
 }
 
 bool Curl_ssl_data_pending(const struct connectdata *conn,
                            int connindex)
 {
-  return curlssl_data_pending(conn, connindex);
+  return Curl_ssl->data_pending(conn, connindex);
 }
 
 void Curl_ssl_free_certinfo(struct Curl_easy *data)
@@ -615,7 +646,7 @@ void Curl_ssl_free_certinfo(struct Curl_easy *data)
 
   if(ci->num_of_certs) {
     /* free all individual lists used */
-    for(i=0; i<ci->num_of_certs; i++) {
+    for(i = 0; i<ci->num_of_certs; i++) {
       curl_slist_free_all(ci->certinfo[i]);
       ci->certinfo[i] = NULL;
     }
@@ -669,7 +700,7 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
   snprintf(output, outlen, "%s:", label);
 
   /* memcpy the value (it might not be zero terminated) */
-  memcpy(&output[labellen+1], value, valuelen);
+  memcpy(&output[labellen + 1], value, valuelen);
 
   /* zero terminate the output */
   output[labellen + 1 + valuelen] = 0;
@@ -703,7 +734,7 @@ CURLcode Curl_ssl_random(struct Curl_easy *data,
                          unsigned char *entropy,
                          size_t length)
 {
-  return curlssl_random(data, entropy, length);
+  return Curl_ssl->random(data, entropy, length);
 }
 
 /*
@@ -778,12 +809,10 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
   size_t size, pem_len;
   CURLcode pem_read;
   CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
-#ifdef curlssl_sha256sum
   CURLcode encode;
   size_t encodedlen, pinkeylen;
   char *encoded, *pinkeycopy, *begin_pos, *end_pos;
   unsigned char *sha256sumdigest = NULL;
-#endif
 
   /* if a path wasn't specified, don't pin */
   if(!pinnedpubkey)
@@ -793,15 +822,20 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
 
   /* only do this if pinnedpubkey starts with "sha256//", length 8 */
   if(strncmp(pinnedpubkey, "sha256//", 8) == 0) {
-#ifdef curlssl_sha256sum
+    if(!Curl_ssl->sha256sum) {
+      /* without sha256 support, this cannot match */
+      return result;
+    }
+
     /* compute sha256sum of public key */
-    sha256sumdigest = malloc(SHA256_DIGEST_LENGTH);
+    sha256sumdigest = malloc(CURL_SHA256_DIGEST_LENGTH);
     if(!sha256sumdigest)
       return CURLE_OUT_OF_MEMORY;
-    curlssl_sha256sum(pubkey, pubkeylen,
-                      sha256sumdigest, SHA256_DIGEST_LENGTH);
+    Curl_ssl->sha256sum(pubkey, pubkeylen,
+                        sha256sumdigest, CURL_SHA256_DIGEST_LENGTH);
     encode = Curl_base64_encode(data, (char *)sha256sumdigest,
-                                SHA256_DIGEST_LENGTH, &encoded, &encodedlen);
+                                CURL_SHA256_DIGEST_LENGTH, &encoded,
+                                &encodedlen);
     Curl_safefree(sha256sumdigest);
 
     if(encode)
@@ -846,10 +880,6 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
     } while(end_pos && begin_pos);
     Curl_safefree(encoded);
     Curl_safefree(pinkeycopy);
-#else
-    /* without sha256 support, this cannot match */
-    (void)data;
-#endif
     return result;
   }
 
@@ -925,45 +955,375 @@ CURLcode Curl_ssl_md5sum(unsigned char *tmp, /* input */
                          unsigned char *md5sum, /* output */
                          size_t md5len)
 {
-#ifdef curlssl_md5sum
-  curlssl_md5sum(tmp, tmplen, md5sum, md5len);
-#else
+  return Curl_ssl->md5sum(tmp, tmplen, md5sum, md5len);
+}
+#endif
+
+/*
+ * Check whether the SSL backend supports the status_request extension.
+ */
+bool Curl_ssl_cert_status_request(void)
+{
+  return Curl_ssl->cert_status_request();
+}
+
+/*
+ * Check whether the SSL backend supports false start.
+ */
+bool Curl_ssl_false_start(void)
+{
+  return Curl_ssl->false_start();
+}
+
+/*
+ * Default implementations for unsupported functions.
+ */
+
+int Curl_none_init(void)
+{
+  return 1;
+}
+
+void Curl_none_cleanup(void)
+{ }
+
+int Curl_none_shutdown(struct connectdata *conn UNUSED_PARAM,
+                       int sockindex UNUSED_PARAM)
+{
+  (void)conn;
+  (void)sockindex;
+  return 0;
+}
+
+int Curl_none_check_cxn(struct connectdata *conn UNUSED_PARAM)
+{
+  (void)conn;
+  return -1;
+}
+
+CURLcode Curl_none_random(struct Curl_easy *data UNUSED_PARAM,
+                          unsigned char *entropy UNUSED_PARAM,
+                          size_t length UNUSED_PARAM)
+{
+  (void)data;
+  (void)entropy;
+  (void)length;
+  return CURLE_NOT_BUILT_IN;
+}
+
+void Curl_none_close_all(struct Curl_easy *data UNUSED_PARAM)
+{
+  (void)data;
+}
+
+void Curl_none_session_free(void *ptr UNUSED_PARAM)
+{
+  (void)ptr;
+}
+
+bool Curl_none_data_pending(const struct connectdata *conn UNUSED_PARAM,
+                            int connindex UNUSED_PARAM)
+{
+  (void)conn;
+  (void)connindex;
+  return 0;
+}
+
+bool Curl_none_cert_status_request(void)
+{
+  return FALSE;
+}
+
+CURLcode Curl_none_set_engine(struct Curl_easy *data UNUSED_PARAM,
+                              const char *engine UNUSED_PARAM)
+{
+  (void)data;
+  (void)engine;
+  return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode Curl_none_set_engine_default(struct Curl_easy *data UNUSED_PARAM)
+{
+  (void)data;
+  return CURLE_NOT_BUILT_IN;
+}
+
+struct curl_slist *Curl_none_engines_list(struct Curl_easy *data UNUSED_PARAM)
+{
+  (void)data;
+  return (struct curl_slist *)NULL;
+}
+
+bool Curl_none_false_start(void)
+{
+  return FALSE;
+}
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen,
+                          unsigned char *md5sum, size_t md5len UNUSED_PARAM)
+{
   MD5_context *MD5pw;
 
-  (void) md5len;
+  (void)md5len;
 
   MD5pw = Curl_MD5_init(Curl_DIGEST_MD5);
   if(!MD5pw)
     return CURLE_OUT_OF_MEMORY;
-  Curl_MD5_update(MD5pw, tmp, curlx_uztoui(tmplen));
+  Curl_MD5_update(MD5pw, input, curlx_uztoui(inputlen));
   Curl_MD5_final(MD5pw, md5sum);
-#endif
   return CURLE_OK;
 }
+#else
+CURLcode Curl_none_md5sum(unsigned char *input UNUSED_PARAM,
+                          size_t inputlen UNUSED_PARAM,
+                          unsigned char *md5sum UNUSED_PARAM,
+                          size_t md5len UNUSED_PARAM)
+{
+  (void)input;
+  (void)inputlen;
+  (void)md5sum;
+  (void)md5len;
+  return CURLE_NOT_BUILT_IN;
+}
 #endif
 
-/*
- * Check whether the SSL backend supports the status_request extension.
- */
-bool Curl_ssl_cert_status_request(void)
+static int Curl_multissl_init(void)
+{
+  if(multissl_init(NULL))
+    return 1;
+  return Curl_ssl->init();
+}
+
+static CURLcode Curl_multissl_connect(struct connectdata *conn, int sockindex)
+{
+  if(multissl_init(NULL))
+    return CURLE_FAILED_INIT;
+  return Curl_ssl->connect(conn, sockindex);
+}
+
+static CURLcode Curl_multissl_connect_nonblocking(struct connectdata *conn,
+                                                  int sockindex, bool *done)
 {
-#ifdef curlssl_cert_status_request
-  return curlssl_cert_status_request();
+  if(multissl_init(NULL))
+    return CURLE_FAILED_INIT;
+  return Curl_ssl->connect_nonblocking(conn, sockindex, done);
+}
+
+static void *Curl_multissl_get_internals(struct ssl_connect_data *connssl,
+                                         CURLINFO info)
+{
+  if(multissl_init(NULL))
+    return NULL;
+  return Curl_ssl->get_internals(connssl, info);
+}
+
+static void Curl_multissl_close(struct connectdata *conn, int sockindex)
+{
+  if(multissl_init(NULL))
+    return;
+  Curl_ssl->close_one(conn, sockindex);
+}
+
+static const struct Curl_ssl Curl_ssl_multi = {
+  { CURLSSLBACKEND_NONE, "multi" },  /* info */
+
+  0, /* have_ca_path */
+  0, /* have_certinfo */
+  0, /* have_pinnedpubkey */
+  0, /* have_ssl_ctx */
+  0, /* support_https_proxy */
+
+  (size_t)-1, /* something insanely large to be on the safe side */
+
+  Curl_multissl_init,                /* init */
+  Curl_none_cleanup,                 /* cleanup */
+  Curl_multissl_version,             /* version */
+  Curl_none_check_cxn,               /* check_cxn */
+  Curl_none_shutdown,                /* shutdown */
+  Curl_none_data_pending,            /* data_pending */
+  Curl_none_random,                  /* random */
+  Curl_none_cert_status_request,     /* cert_status_request */
+  Curl_multissl_connect,             /* connect */
+  Curl_multissl_connect_nonblocking, /* connect_nonblocking */
+  Curl_multissl_get_internals,       /* get_internals */
+  Curl_multissl_close,               /* close_one */
+  Curl_none_close_all,               /* close_all */
+  Curl_none_session_free,            /* session_free */
+  Curl_none_set_engine,              /* set_engine */
+  Curl_none_set_engine_default,      /* set_engine_default */
+  Curl_none_engines_list,            /* engines_list */
+  Curl_none_false_start,             /* false_start */
+  Curl_none_md5sum,                  /* md5sum */
+  NULL                               /* sha256sum */
+};
+
+const struct Curl_ssl *Curl_ssl =
+#if defined(CURL_WITH_MULTI_SSL)
+  &Curl_ssl_multi;
+#elif defined(USE_AXTLS)
+  &Curl_ssl_axtls;
+#elif defined(USE_CYASSL)
+  &Curl_ssl_cyassl;
+#elif defined(USE_DARWINSSL)
+  &Curl_ssl_darwinssl;
+#elif defined(USE_GNUTLS)
+  &Curl_ssl_gnutls;
+#elif defined(USE_GSKIT)
+  &Curl_ssl_gskit;
+#elif defined(USE_MBEDTLS)
+  &Curl_ssl_mbedtls;
+#elif defined(USE_NSS)
+  &Curl_ssl_nss;
+#elif defined(USE_OPENSSL)
+  &Curl_ssl_openssl;
+#elif defined(USE_POLARSSL)
+  &Curl_ssl_polarssl;
+#elif defined(USE_SCHANNEL)
+  &Curl_ssl_schannel;
 #else
-  return FALSE;
+#error "Missing struct Curl_ssl for selected SSL backend"
+#endif
+
+static const struct Curl_ssl *available_backends[] = {
+#if defined(USE_AXTLS)
+  &Curl_ssl_axtls,
 #endif
+#if defined(USE_CYASSL)
+  &Curl_ssl_cyassl,
+#endif
+#if defined(USE_DARWINSSL)
+  &Curl_ssl_darwinssl,
+#endif
+#if defined(USE_GNUTLS)
+  &Curl_ssl_gnutls,
+#endif
+#if defined(USE_GSKIT)
+  &Curl_ssl_gskit,
+#endif
+#if defined(USE_MBEDTLS)
+  &Curl_ssl_mbedtls,
+#endif
+#if defined(USE_NSS)
+  &Curl_ssl_nss,
+#endif
+#if defined(USE_OPENSSL)
+  &Curl_ssl_openssl,
+#endif
+#if defined(USE_POLARSSL)
+  &Curl_ssl_polarssl,
+#endif
+#if defined(USE_SCHANNEL)
+  &Curl_ssl_schannel,
+#endif
+  NULL
+};
+
+static size_t Curl_multissl_version(char *buffer, size_t size)
+{
+  static const struct Curl_ssl *selected;
+  static char backends[200];
+  static size_t total;
+  const struct Curl_ssl *current;
+
+  current = Curl_ssl == &Curl_ssl_multi ? available_backends[0] : Curl_ssl;
+
+  if(current != selected) {
+    char *p = backends;
+    int i;
+
+    selected = current;
+
+    for(i = 0; available_backends[i]; i++) {
+      if(i)
+        *(p++) = ' ';
+      if(selected != available_backends[i])
+        *(p++) = '(';
+      p += available_backends[i]->version(p, backends + sizeof(backends) - p);
+      if(selected != available_backends[i])
+        *(p++) = ')';
+    }
+    *p = '\0';
+    total = p - backends;
+  }
+
+  if(size < total)
+    memcpy(buffer, backends, total + 1);
+  else {
+    memcpy(buffer, backends, size - 1);
+    buffer[size - 1] = '\0';
+  }
+
+  return total;
 }
 
-/*
- * Check whether the SSL backend supports false start.
- */
-bool Curl_ssl_false_start(void)
+static int multissl_init(const struct Curl_ssl *backend)
 {
-#ifdef curlssl_false_start
-  return curlssl_false_start();
-#else
-  return FALSE;
+  const char *env;
+  char *env_tmp;
+  int i;
+
+  if(Curl_ssl != &Curl_ssl_multi)
+    return 1;
+
+  if(backend) {
+    Curl_ssl = backend;
+    return 0;
+  }
+
+  if(!available_backends[0])
+    return 1;
+
+  env = env_tmp = curl_getenv("CURL_SSL_BACKEND");
+#ifdef CURL_DEFAULT_SSL_BACKEND
+  if(!env)
+    env = CURL_DEFAULT_SSL_BACKEND;
 #endif
+  if(env) {
+    for(i = 0; available_backends[i]; i++) {
+      if(strcasecompare(env, available_backends[i]->info.name)) {
+        Curl_ssl = available_backends[i];
+        curl_free(env_tmp);
+        return 0;
+      }
+    }
+  }
+
+  /* Fall back to first available backend */
+  Curl_ssl = available_backends[0];
+  curl_free(env_tmp);
+  return 0;
+}
+
+CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
+                              const curl_ssl_backend ***avail)
+{
+  int i;
+
+  if(Curl_ssl != &Curl_ssl_multi)
+    return id == Curl_ssl->info.id ? CURLSSLSET_OK : CURLSSLSET_TOO_LATE;
+
+  for(i = 0; available_backends[i]; i++) {
+    if(available_backends[i]->info.id == id ||
+       (name && strcasecompare(available_backends[i]->info.name, name))) {
+      multissl_init(available_backends[i]);
+      return CURLSSLSET_OK;
+    }
+  }
+
+  if(avail)
+    *avail = (const curl_ssl_backend **)&available_backends;
+  return CURLSSLSET_UNKNOWN_BACKEND;
+}
+
+#else /* USE_SSL */
+CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
+                              const curl_ssl_backend ***avail)
+{
+  (void)id;
+  (void)name;
+  (void)avail;
+  return CURLSSLSET_NO_BACKENDS;
 }
 
-#endif /* USE_SSL */
+#endif /* !USE_SSL */