gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [gnurl] 51/205: vtls: add options to specify range of enabl


From: gnunet
Subject: [GNUnet-SVN] [gnurl] 51/205: vtls: add options to specify range of enabled TLS versions
Date: Thu, 20 Apr 2017 16:19:51 +0200

This is an automated email from the git hooks/post-receive script.

ng0 pushed a commit to annotated tag gnurl-7.54.0
in repository gnurl.

commit 6448f98c1857de521fb2dd3f9d4e5659845b5474
Author: Jozef Kralik <address@hidden>
AuthorDate: Tue Dec 13 21:10:00 2016 +0100

    vtls: add options to specify range of enabled TLS versions
    
    This commit introduces the CURL_SSLVERSION_MAX_* constants as well as
    the --tls-max option of the curl tool.
    
    Closes https://github.com/curl/curl/pull/1166
---
 docs/cmdline-opts/tls-max.d                  |  24 +++++
 docs/libcurl/opts/CURLOPT_PROXY_SSLVERSION.3 |  20 +++-
 docs/libcurl/opts/CURLOPT_SSLVERSION.3       |  22 +++-
 docs/libcurl/symbols-in-versions             |   6 ++
 include/curl/curl.h                          |  12 +++
 lib/url.c                                    |  11 +-
 lib/urldata.h                                |   1 +
 lib/vtls/axtls.c                             |   6 ++
 lib/vtls/cyassl.c                            |   5 +
 lib/vtls/darwinssl.c                         | 145 ++++++++++++++++++++++-----
 lib/vtls/gskit.c                             |  48 +++++++--
 lib/vtls/gtls.c                              | 135 ++++++++++++++++++++-----
 lib/vtls/mbedtls.c                           |  91 +++++++++++++----
 lib/vtls/nss.c                               | 100 +++++++++++-------
 lib/vtls/openssl.c                           | 120 ++++++++++++----------
 lib/vtls/polarssl.c                          |  88 ++++++++++++----
 lib/vtls/schannel.c                          |  49 +++++++--
 lib/vtls/vtls.c                              |  19 +++-
 packages/OS400/curl.inc.in                   |  10 ++
 src/tool_cfgable.h                           |   1 +
 src/tool_getparam.c                          |   6 ++
 src/tool_help.c                              |   1 +
 src/tool_operate.c                           |   3 +-
 src/tool_paramhlp.c                          |  33 ++++++
 src/tool_paramhlp.h                          |   2 +
 25 files changed, 756 insertions(+), 202 deletions(-)

diff --git a/docs/cmdline-opts/tls-max.d b/docs/cmdline-opts/tls-max.d
new file mode 100644
index 000000000..7ae862252
--- /dev/null
+++ b/docs/cmdline-opts/tls-max.d
@@ -0,0 +1,24 @@
+Long: tls-max
+Arg: <VERSION>
+Tags: Versions
+Protocols: SSL
+Added: 7.54.0
+Requires: TLS
+See-also: tlsv1.0 tlsv1.1 tlsv1.2
+Help: Use TLSv1.0 or greater
+---
+VERSION defines maximum supported TLS version. A minimum is defined
+by arguments tlsv1.0 or tlsv1.1 or tlsv1.2.
+
+.RS
+.IP "default"
+Use up to recommended TLS version.
+.IP "1.0"
+Use up to TLSv1.0.
+.IP "1.1"
+Use up to TLSv1.1.
+.IP "1.2"
+Use up to TLSv1.2.
+.IP "1.3"
+Use up to TLSv1.3.
+.RE
diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSLVERSION.3 
b/docs/libcurl/opts/CURLOPT_PROXY_SSLVERSION.3
index f96a9e6c5..85ecdc41f 100644
--- a/docs/libcurl/opts/CURLOPT_PROXY_SSLVERSION.3
+++ b/docs/libcurl/opts/CURLOPT_PROXY_SSLVERSION.3
@@ -46,6 +46,23 @@ TLSv1.1
 TLSv1.2
 .IP CURL_SSLVERSION_TLSv1_3
 TLSv1.3
+.IP CURL_SSLVERSION_MAX_DEFAULT
+The flag defines maximum supported TLS version as TLSv1.2 or default
+value from SSL library. Only library NSS currently allows to get
+maximum supported TLS version.
+(Added in 7.54.0)
+.IP CURL_SSLVERSION_MAX_TLSv1_0
+The flag defines maximum supported TLS version as TLSv1.0.
+(Added in 7.54.0)
+.IP CURL_SSLVERSION_MAX_TLSv1_1
+The flag defines maximum supported TLS version as TLSv1.1.
+(Added in 7.54.0)
+.IP CURL_SSLVERSION_MAX_TLSv1_2
+The flag defines maximum supported TLS version as TLSv1.2.
+(Added in 7.54.0)
+.IP CURL_SSLVERSION_MAX_TLSv1_3
+The flag defines maximum supported TLS version as TLSv1.3.
+(Added in 7.54.0)
 .RE
 .SH DEFAULT
 CURL_SSLVERSION_DEFAULT
@@ -58,7 +75,8 @@ if(curl) {
   curl_easy_setopt(curl, CURLOPT_URL, "https://example.com";);
 
   /* ask libcurl to use TLS version 1.0 or later */
-  curl_easy_setopt(curl, CURLOPT_PROXY_SSLVERSION, CURL_SSLVERSION_TLSv1);
+  curl_easy_setopt(curl, CURLOPT_PROXY_SSLVERSION, CURL_SSLVERSION_TLSv1_1 |
+                   CURL_SSLVERSION_MAX_DEFAULT);
 
   /* Perform the request */
   curl_easy_perform(curl);
diff --git a/docs/libcurl/opts/CURLOPT_SSLVERSION.3 
b/docs/libcurl/opts/CURLOPT_SSLVERSION.3
index 77dfcd49d..d07ae8dde 100644
--- a/docs/libcurl/opts/CURLOPT_SSLVERSION.3
+++ b/docs/libcurl/opts/CURLOPT_SSLVERSION.3
@@ -50,6 +50,23 @@ TLSv1.1 (Added in 7.34.0)
 TLSv1.2 (Added in 7.34.0)
 .IP CURL_SSLVERSION_TLSv1_3
 TLSv1.3 (Added in 7.52.0)
+.IP CURL_SSLVERSION_MAX_DEFAULT
+The flag defines maximum supported TLS version as TLSv1.2 or default
+value from SSL library. Only library NSS currently allows to get
+maximum supported TLS version.
+(Added in 7.54.0)
+.IP CURL_SSLVERSION_MAX_TLSv1_0
+The flag defines maximum supported TLS version as TLSv1.0.
+(Added in 7.54.0)
+.IP CURL_SSLVERSION_MAX_TLSv1_1
+The flag defines maximum supported TLS version as TLSv1.1.
+(Added in 7.54.0)
+.IP CURL_SSLVERSION_MAX_TLSv1_2
+The flag defines maximum supported TLS version as TLSv1.2.
+(Added in 7.54.0)
+.IP CURL_SSLVERSION_MAX_TLSv1_3
+The flag defines maximum supported TLS version as TLSv1.3.
+(Added in 7.54.0)
 .RE
 .SH DEFAULT
 CURL_SSLVERSION_DEFAULT
@@ -61,8 +78,9 @@ CURL *curl = curl_easy_init();
 if(curl) {
   curl_easy_setopt(curl, CURLOPT_URL, "https://example.com";);
 
-  /* ask libcurl to use TLS version 1.0 or later */
-  curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
+  /* ask libcurl to use TLS version 1.1 or later */
+  curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1.1 |
+                   CURL_SSLVERSION_MAX_DEFAULT);
 
   /* Perform the request */
   curl_easy_perform(curl);
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index 8834ada54..f4cd9805f 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -798,6 +798,12 @@ CURL_SSLVERSION_TLSv1_0         7.34.0
 CURL_SSLVERSION_TLSv1_1         7.34.0
 CURL_SSLVERSION_TLSv1_2         7.34.0
 CURL_SSLVERSION_TLSv1_3         7.52.0
+CURL_SSLVERSION_MAX_NONE        7.54.0
+CURL_SSLVERSION_MAX_DEFAULT     7.54.0
+CURL_SSLVERSION_MAX_TLSv1_0     7.54.0
+CURL_SSLVERSION_MAX_TLSv1_1     7.54.0
+CURL_SSLVERSION_MAX_TLSv1_2     7.54.0
+CURL_SSLVERSION_MAX_TLSv1_3     7.54.0
 CURL_TIMECOND_IFMODSINCE        7.9.7
 CURL_TIMECOND_IFUNMODSINCE      7.9.7
 CURL_TIMECOND_LASTMOD           7.9.7
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 467bb0240..8b221ef8f 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -1884,6 +1884,18 @@ enum {
   CURL_SSLVERSION_LAST /* never use, keep last */
 };
 
+enum {
+  CURL_SSLVERSION_MAX_NONE =     0,
+  CURL_SSLVERSION_MAX_DEFAULT =  (CURL_SSLVERSION_TLSv1   << 16),
+  CURL_SSLVERSION_MAX_TLSv1_0 =  (CURL_SSLVERSION_TLSv1_0 << 16),
+  CURL_SSLVERSION_MAX_TLSv1_1 =  (CURL_SSLVERSION_TLSv1_1 << 16),
+  CURL_SSLVERSION_MAX_TLSv1_2 =  (CURL_SSLVERSION_TLSv1_2 << 16),
+  CURL_SSLVERSION_MAX_TLSv1_3 =  (CURL_SSLVERSION_TLSv1_3 << 16),
+
+  /* never use, keep last */
+  CURL_SSLVERSION_MAX_LAST =     (CURL_SSLVERSION_LAST    << 16)
+};
+
 enum CURL_TLSAUTH {
   CURL_TLSAUTH_NONE,
   CURL_TLSAUTH_SRP,
diff --git a/lib/url.c b/lib/url.c
index 300fc4d14..5943896eb 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -695,6 +695,9 @@ CURLcode Curl_open(struct Curl_easy **curl)
   return result;
 }
 
+#define C_SSLVERSION_VALUE(x) (x & 0xffff)
+#define C_SSLVERSION_MAX_VALUE(x) (x & 0xffff0000)
+
 CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option,
                      va_list param)
 {
@@ -927,7 +930,9 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption 
option,
      * implementations are lame.
      */
 #ifdef USE_SSL
-    data->set.ssl.primary.version = va_arg(param, long);
+    arg = va_arg(param, long);
+    data->set.ssl.primary.version = C_SSLVERSION_VALUE(arg);
+    data->set.ssl.primary.version_max = C_SSLVERSION_MAX_VALUE(arg);
 #else
     result = CURLE_UNKNOWN_OPTION;
 #endif
@@ -938,7 +943,9 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption 
option,
      * implementations are lame.
      */
 #ifdef USE_SSL
-    data->set.proxy_ssl.primary.version = va_arg(param, long);
+    arg = va_arg(param, long);
+    data->set.proxy_ssl.primary.version = C_SSLVERSION_VALUE(arg);
+    data->set.proxy_ssl.primary.version_max = C_SSLVERSION_MAX_VALUE(arg);
 #else
     result = CURLE_UNKNOWN_OPTION;
 #endif
diff --git a/lib/urldata.h b/lib/urldata.h
index 7f87913a9..ef6a78846 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -350,6 +350,7 @@ struct ssl_connect_data {
 
 struct ssl_primary_config {
   long version;          /* what version the client wants to use */
+  long version_max;      /* max supported version the client wants to use*/
   bool verifypeer;       /* set TRUE if this is desired */
   bool verifyhost;       /* set TRUE if CN/SAN must match hostname */
   bool verifystatus;     /* set TRUE if certificate status must be checked */
diff --git a/lib/vtls/axtls.c b/lib/vtls/axtls.c
index 1de758b0c..af01fe314 100644
--- a/lib/vtls/axtls.c
+++ b/lib/vtls/axtls.c
@@ -156,6 +156,12 @@ static CURLcode connect_prep(struct connectdata *conn, int 
sockindex)
        same connection */
     return CURLE_OK;
 
+  if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) {
+    failf(data, "axtls does not support CURL_SSLVERSION_MAX");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+
   /* axTLS only supports TLSv1 */
   /* check to see if we've been told to use an explicit SSL/TLS version */
   switch(SSL_CONN_CONFIG(version)) {
diff --git a/lib/vtls/cyassl.c b/lib/vtls/cyassl.c
index 1e3b346d6..2dfd79dce 100644
--- a/lib/vtls/cyassl.c
+++ b/lib/vtls/cyassl.c
@@ -149,6 +149,11 @@ cyassl_connect_step1(struct connectdata *conn,
   if(conssl->state == ssl_connection_complete)
     return CURLE_OK;
 
+  if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) {
+    failf(data, "CyaSSL does not support to set maximum SSL/TLS version");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
   /* check to see if we've been told to use an explicit SSL/TLS version */
   switch(SSL_CONN_CONFIG(version)) {
   case CURL_SSLVERSION_DEFAULT:
diff --git a/lib/vtls/darwinssl.c b/lib/vtls/darwinssl.c
index 25a8ab8b6..26ea04cbb 100644
--- a/lib/vtls/darwinssl.c
+++ b/lib/vtls/darwinssl.c
@@ -1044,6 +1044,109 @@ CF_INLINE bool is_file(const char *filename)
   return false;
 }
 
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+static CURLcode darwinssl_version_from_curl(long *darwinver, long version)
+{
+  switch(ssl_version) {
+    case CURL_SSLVERSION_TLSv1_0:
+      *darwinver = kTLSProtocol1;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_1:
+      *darwinver = kTLSProtocol11;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_2:
+      *darwinver = kTLSProtocol12;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_3:
+      break;
+  }
+  return CURLE_SSL_CONNECT_ERROR;
+}
+#endif
+
+static CURLcode
+set_ssl_version_min_max(struct connectdata *conn, int sockindex)
+{
+  struct Curl_easy *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  long ssl_version = SSL_CONN_CONFIG(version);
+  long ssl_version_max = SSL_CONN_CONFIG(version_max);
+
+  switch(ssl_version) {
+    case CURL_SSLVERSION_DEFAULT:
+    case CURL_SSLVERSION_TLSv1:
+      ssl_version = CURL_SSLVERSION_TLSv1_0;
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+      break;
+  }
+
+  switch(ssl_version_max) {
+    case CURL_SSLVERSION_MAX_NONE:
+      ssl_version_max = ssl_version << 16;
+      break;
+    case CURL_SSLVERSION_MAX_DEFAULT:
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+      break;
+  }
+
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+  if(SSLSetProtocolVersionMax != NULL) {
+    SSLProtocol darwin_ver_min = kTLSProtocol1;
+    SSLProtocol darwin_ver_max = kTLSProtocol1;
+    CURLcode result = darwinssl_version_from_curl(&darwin_ver_min,
+                                                  ssl_version);
+    if(result) {
+      failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
+      return result;
+    }
+    result = darwinssl_version_from_curl(&darwin_ver_max,
+                                         ssl_version_max >> 16);
+    if(result) {
+      failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
+      return result;
+    }
+
+    (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, darwin_ver_min);
+    (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, darwin_ver_max);
+    return result;
+  }
+  else {
+#if CURL_SUPPORT_MAC_10_8
+    long i = ssl_version;
+    (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+                                       kSSLProtocolAll,
+                                       false);
+    for(; i <= (ssl_version_max >> 16); i++) {
+      switch(i) {
+        case CURL_SSLVERSION_TLSv1_0:
+          (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+                                            kTLSProtocol1,
+                                            true);
+          break;
+        case CURL_SSLVERSION_TLSv1_1:
+          (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+                                            kTLSProtocol11,
+                                            true);
+          break;
+        case CURL_SSLVERSION_TLSv1_2:
+          (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+                                            kTLSProtocol12,
+                                            true);
+          break;
+        case CURL_SSLVERSION_TLSv1_3:
+          failf(data, "DarwinSSL: TLS 1.3 is not yet supported");
+          return CURLE_SSL_CONNECT_ERROR;
+      }
+    }
+    return CURLE_OK;
+#endif  /* CURL_SUPPORT_MAC_10_8 */
+  }
+#endif  /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+  failf(data, "DarwinSSL: cannot set SSL protocol");
+  return CURLE_SSL_CONNECT_ERROR;
+}
+
+
 static CURLcode darwinssl_connect_step1(struct connectdata *conn,
                                         int sockindex)
 {
@@ -1113,20 +1216,15 @@ static CURLcode darwinssl_connect_step1(struct 
connectdata *conn,
       (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol12);
       break;
     case CURL_SSLVERSION_TLSv1_0:
-      (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kTLSProtocol1);
-      (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol1);
-      break;
     case CURL_SSLVERSION_TLSv1_1:
-      (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kTLSProtocol11);
-      (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol11);
-      break;
     case CURL_SSLVERSION_TLSv1_2:
-      (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kTLSProtocol12);
-      (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol12);
-      break;
     case CURL_SSLVERSION_TLSv1_3:
-      failf(data, "DarwinSSL: TLS 1.3 is not yet supported");
-      return CURLE_SSL_CONNECT_ERROR;
+      {
+        CURLcode result = set_ssl_version_min_max(conn, sockindex);
+        if(result != CURLE_OK)
+          return result;
+        break;
+      }
     case CURL_SSLVERSION_SSLv3:
       err = SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol3);
       if(err != noErr) {
@@ -1167,23 +1265,15 @@ static CURLcode darwinssl_connect_step1(struct 
connectdata *conn,
                                          true);
       break;
     case CURL_SSLVERSION_TLSv1_0:
-      (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
-                                         kTLSProtocol1,
-                                         true);
-      break;
     case CURL_SSLVERSION_TLSv1_1:
-      (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
-                                         kTLSProtocol11,
-                                         true);
-      break;
     case CURL_SSLVERSION_TLSv1_2:
-      (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
-                                         kTLSProtocol12,
-                                         true);
-      break;
     case CURL_SSLVERSION_TLSv1_3:
-      failf(data, "DarwinSSL: TLS 1.3 is not yet supported");
-      return CURLE_SSL_CONNECT_ERROR;
+      {
+        CURLcode result = set_ssl_version_min_max(conn, sockindex);
+        if(result != CURLE_OK)
+          return result;
+        break;
+      }
     case CURL_SSLVERSION_SSLv3:
       err = SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
                                          kSSLProtocol3,
@@ -1209,6 +1299,11 @@ static CURLcode darwinssl_connect_step1(struct 
connectdata *conn,
 #endif  /* CURL_SUPPORT_MAC_10_8 */
   }
 #else
+  if(conn->ssl_config.version_max != CURL_SSLVERSION_MAX_NONE) {
+    failf(data, "Your version of the OS does not support to set maximum"
+                " SSL/TLS version");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
   (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocolAll, false);
   switch(conn->ssl_config.version) {
   case CURL_SSLVERSION_DEFAULT:
diff --git a/lib/vtls/gskit.c b/lib/vtls/gskit.c
index a0d462b70..d760b6a87 100644
--- a/lib/vtls/gskit.c
+++ b/lib/vtls/gskit.c
@@ -748,6 +748,40 @@ static ssize_t gskit_recv(struct connectdata *conn, int 
num, char *buf,
   return (ssize_t) nread;
 }
 
+static CURLcode
+set_ssl_version_min_max(unsigned int *protoflags, struct connectdata *conn)
+{
+  struct Curl_easy *data = conn->data;
+  long ssl_version = SSL_CONN_CONFIG(version);
+  long ssl_version_max = SSL_CONN_CONFIG(version_max);
+  long i = ssl_version;
+  switch(ssl_version_max) {
+    case CURL_SSLVERSION_MAX_NONE:
+      ssl_version_max = ssl_version;
+      break;
+    case CURL_SSLVERSION_MAX_DEFAULT:
+      ssl_version_max = CURL_SSLVERSION_TLSv1_2;
+      break;
+  }
+  for(; i <= (ssl_version_max >> 16); ++i) {
+    switch(i) {
+      case CURL_SSLVERSION_TLSv1_0:
+        *protoflags |= CURL_GSKPROTO_TLSV10_MASK;
+        break;
+      case CURL_SSLVERSION_TLSv1_1:
+        *protoflags |= CURL_GSKPROTO_TLSV11_MASK;
+        break;
+      case CURL_SSLVERSION_TLSv1_2:
+        *protoflags |= CURL_GSKPROTO_TLSV11_MASK;
+        break;
+      case CURL_SSLVERSION_TLSv1_3:
+        failf(data, "GSKit: TLS 1.3 is not yet supported");
+        return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
+  return CURLE_OK;
+}
 
 static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
 {
@@ -764,7 +798,7 @@ static CURLcode gskit_connect_step1(struct connectdata 
*conn, int sockindex)
   const char * const hostname = SSL_IS_PROXY()? conn->http_proxy.host.name:
     conn->host.name;
   const char *sni;
-  unsigned int protoflags;
+  unsigned int protoflags = 0;
   long timeout;
   Qso_OverlappedIO_t commarea;
   int sockpair[2];
@@ -849,17 +883,13 @@ static CURLcode gskit_connect_step1(struct connectdata 
*conn, int sockindex)
                  CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK;
     break;
   case CURL_SSLVERSION_TLSv1_0:
-    protoflags = CURL_GSKPROTO_TLSV10_MASK;
-    break;
   case CURL_SSLVERSION_TLSv1_1:
-    protoflags = CURL_GSKPROTO_TLSV11_MASK;
-    break;
   case CURL_SSLVERSION_TLSv1_2:
-    protoflags = CURL_GSKPROTO_TLSV12_MASK;
-    break;
   case CURL_SSLVERSION_TLSv1_3:
-    failf(data, "GSKit: TLS 1.3 is not yet supported");
-    return CURLE_SSL_CONNECT_ERROR;
+    result = set_ssl_version_min_max(&protoflags, conn);
+    if(result != CURLE_OK)
+      return result;
+    break;
   default:
     failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
     return CURLE_SSL_CONNECT_ERROR;
diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index faa70aca2..f41036049 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.c
@@ -375,6 +375,100 @@ static gnutls_x509_crt_fmt_t do_file_type(const char 
*type)
   return -1;
 }
 
+#ifndef USE_GNUTLS_PRIORITY_SET_DIRECT
+static CURLcode
+set_ssl_version_min_max(int *list, size_t list_size, struct connectdata *conn)
+{
+  struct Curl_easy *data = conn->data;
+  long ssl_version = SSL_CONN_CONFIG(version);
+  long ssl_version_max = SSL_CONN_CONFIG(version_max);
+  long i = ssl_version;
+  long protocol_priority_idx = 0;
+
+  switch(ssl_version_max) {
+    case CURL_SSLVERSION_MAX_NONE:
+      ssl_version_max = ssl_version << 16;
+      break;
+    case CURL_SSLVERSION_MAX_DEFAULT:
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+      break;
+  }
+
+  for(; i <= (ssl_version_max >> 16) &&
+        protocol_priority_idx < list_size; ++i) {
+    switch(i) {
+      case CURL_SSLVERSION_TLSv1_0:
+        protocol_priority[protocol_priority_idx++] = GNUTLS_TLS1_0;
+        break;
+      case CURL_SSLVERSION_TLSv1_1:
+        protocol_priority[protocol_priority_idx++] = GNUTLS_TLS1_1;
+        break;
+      case CURL_SSLVERSION_TLSv1_2:
+        protocol_priority[protocol_priority_idx++] = GNUTLS_TLS1_2;
+        break;
+      case CURL_SSLVERSION_TLSv1_3:
+        failf(data, "GnuTLS: TLS 1.3 is not yet supported");
+        return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+  return CURLE_OK;
+}
+#else
+#define GNUTLS_CIPHERS "NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509"
+/* If GnuTLS was compiled without support for SRP it will error out if SRP is
+   requested in the priority string, so treat it specially
+ */
+#define GNUTLS_SRP "+SRP"
+
+static CURLcode
+set_ssl_version_min_max(const char **prioritylist, struct connectdata *conn)
+{
+  struct Curl_easy *data = conn->data;
+  long ssl_version = SSL_CONN_CONFIG(version);
+  long ssl_version_max = SSL_CONN_CONFIG(version_max);
+  if(ssl_version == CURL_SSLVERSION_TLSv1_3 ||
+     ssl_version_max == CURL_SSLVERSION_MAX_TLSv1_3) {
+    failf(data, "GnuTLS: TLS 1.3 is not yet supported");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+  if(ssl_version_max == CURL_SSLVERSION_MAX_NONE) {
+    ssl_version_max = ssl_version << 16;
+  }
+  switch(ssl_version | ssl_version_max) {
+    case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_0:
+      *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+                      "+VERS-TLS1.0:" GNUTLS_SRP;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_1:
+      *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+                      "+VERS-TLS1.0:+VERS-TLS1.1:" GNUTLS_SRP;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2:
+    case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_DEFAULT:
+      *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+                      "+VERS-TLS1.0:+VERS-TLS1.1:+VERS-TLS1.2:" GNUTLS_SRP;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_1:
+      *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+                      "+VERS-TLS1.1:" GNUTLS_SRP;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_2:
+    case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_DEFAULT:
+      *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+                      "+VERS-TLS1.1:+VERS-TLS1.2:" GNUTLS_SRP;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2:
+    case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT:
+      *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+                      "+VERS-TLS1.2:" GNUTLS_SRP;
+      return CURLE_OK;
+  }
+
+  failf(data, "GnuTLS: cannot set ssl protocol");
+  return CURLE_SSL_CONNECT_ERROR;
+}
+#endif
+
 static CURLcode
 gtls_connect_step1(struct connectdata *conn,
                    int sockindex)
@@ -406,13 +500,8 @@ gtls_connect_step1(struct connectdata *conn,
     GNUTLS_CIPHER_3DES_CBC,
   };
   static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
-  static int protocol_priority[] = { 0, 0, 0, 0 };
+  int protocol_priority[] = { 0, 0, 0, 0 };
 #else
-#define GNUTLS_CIPHERS "NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509"
-/* If GnuTLS was compiled without support for SRP it will error out if SRP is
-   requested in the priority string, so treat it specially
- */
-#define GNUTLS_SRP "+SRP"
   const char *prioritylist;
   const char *err = NULL;
 #endif
@@ -576,7 +665,7 @@ gtls_connect_step1(struct connectdata *conn,
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  switch(SSL_CONN_CONFIG(version) {
+  switch(SSL_CONN_CONFIG(version)) {
     case CURL_SSLVERSION_SSLv3:
       protocol_priority[0] = GNUTLS_SSL3;
       break;
@@ -587,17 +676,16 @@ gtls_connect_step1(struct connectdata *conn,
       protocol_priority[2] = GNUTLS_TLS1_2;
       break;
     case CURL_SSLVERSION_TLSv1_0:
-      protocol_priority[0] = GNUTLS_TLS1_0;
-      break;
     case CURL_SSLVERSION_TLSv1_1:
-      protocol_priority[0] = GNUTLS_TLS1_1;
-      break;
     case CURL_SSLVERSION_TLSv1_2:
-      protocol_priority[0] = GNUTLS_TLS1_2;
-      break;
     case CURL_SSLVERSION_TLSv1_3:
-      failf(data, "GnuTLS: TLS 1.3 is not yet supported");
-      return CURLE_SSL_CONNECT_ERROR;
+      {
+        CURLcode result = set_ssl_version_min_max(protocol_priority,
+                sizeof(protocol_priority)/sizeof(protocol_priority[0]), conn);
+        if(result != CURLE_OK)
+          return result;
+        break;
+      }
     case CURL_SSLVERSION_SSLv2:
       failf(data, "GnuTLS does not support SSLv2");
       return CURLE_SSL_CONNECT_ERROR;
@@ -625,20 +713,15 @@ gtls_connect_step1(struct connectdata *conn,
       prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:" GNUTLS_SRP;
       break;
     case CURL_SSLVERSION_TLSv1_0:
-      prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-                     "+VERS-TLS1.0:" GNUTLS_SRP;
-      break;
     case CURL_SSLVERSION_TLSv1_1:
-      prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-                     "+VERS-TLS1.1:" GNUTLS_SRP;
-      break;
     case CURL_SSLVERSION_TLSv1_2:
-      prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-                     "+VERS-TLS1.2:" GNUTLS_SRP;
-      break;
     case CURL_SSLVERSION_TLSv1_3:
-      failf(data, "GnuTLS: TLS 1.3 is not yet supported");
-      return CURLE_SSL_CONNECT_ERROR;
+      {
+        CURLcode result = set_ssl_version_min_max(&prioritylist, conn);
+        if(result != CURLE_OK)
+          return result;
+        break;
+      }
     case CURL_SSLVERSION_SSLv2:
       failf(data, "GnuTLS does not support SSLv2");
       return CURLE_SSL_CONNECT_ERROR;
diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
index 07239bf26..dbcb1690c 100644
--- a/lib/vtls/mbedtls.c
+++ b/lib/vtls/mbedtls.c
@@ -157,6 +157,71 @@ const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr 
=
 static Curl_recv mbed_recv;
 static Curl_send mbed_send;
 
+static CURLcode mbedtls_version_from_curl(int *mbedver, long version)
+{
+  switch(ssl_version) {
+    case CURL_SSLVERSION_TLSv1_0:
+      *mbedver = MBEDTLS_SSL_MINOR_VERSION_1;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_1:
+      *mbedver = MBEDTLS_SSL_MINOR_VERSION_2;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_2:
+      *mbedver = MBEDTLS_SSL_MINOR_VERSION_3;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_3:
+      break;
+  }
+  return CURLE_SSL_CONNECT_ERROR;
+}
+
+static CURLcode
+set_ssl_version_min_max(struct connectdata *conn, int sockindex)
+{
+  struct Curl_easy *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_1;
+  int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_1;
+  long ssl_version = SSL_CONN_CONFIG(version);
+  long ssl_version_max = SSL_CONN_CONFIG(version_max);
+  CURLcode result = CURLE_OK;
+
+  switch(ssl_version) {
+    case CURL_SSLVERSION_DEFAULT:
+    case CURL_SSLVERSION_TLSv1:
+      ssl_version = CURL_SSLVERSION_TLSv1_0;
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+      break;
+  }
+
+  switch(ssl_version_max) {
+    case CURL_SSLVERSION_MAX_NONE:
+      ssl_version_max = ssl_version << 16;
+      break;
+    case CURL_SSLVERSION_MAX_DEFAULT:
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+      break;
+  }
+
+  result = mbedtls_version_from_curl(&mbedtls_ver_min, ssl_version);
+  if(result) {
+    failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
+    return result;
+  }
+  result = mbedtls_version_from_curl(&mbedtls_ver_max, ssl_version_max >> 16);
+  if(result) {
+    failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
+    return result;
+  }
+
+  mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
+                               mbedtls_ver_min);
+  mbedtls_ssl_conf_max_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
+                               mbedtls_ver_max);
+
+  return result;
+}
+
 static CURLcode
 mbed_connect_step1(struct connectdata *conn,
                    int sockindex)
@@ -333,29 +398,15 @@ mbed_connect_step1(struct connectdata *conn,
     infof(data, "mbedTLS: Set SSL version to SSLv3\n");
     break;
   case CURL_SSLVERSION_TLSv1_0:
-    mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
-                                 MBEDTLS_SSL_MINOR_VERSION_1);
-    mbedtls_ssl_conf_max_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
-                                 MBEDTLS_SSL_MINOR_VERSION_1);
-    infof(data, "mbedTLS: Set SSL version to TLS 1.0\n");
-    break;
   case CURL_SSLVERSION_TLSv1_1:
-    mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
-                                 MBEDTLS_SSL_MINOR_VERSION_2);
-    mbedtls_ssl_conf_max_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
-                                 MBEDTLS_SSL_MINOR_VERSION_2);
-    infof(data, "mbedTLS: Set SSL version to TLS 1.1\n");
-    break;
   case CURL_SSLVERSION_TLSv1_2:
-    mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
-                                 MBEDTLS_SSL_MINOR_VERSION_3);
-    mbedtls_ssl_conf_max_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
-                                 MBEDTLS_SSL_MINOR_VERSION_3);
-    infof(data, "mbedTLS: Set SSL version to TLS 1.2\n");
-    break;
   case CURL_SSLVERSION_TLSv1_3:
-    failf(data, "mbedTLS: TLS 1.3 is not yet supported");
-    return CURLE_SSL_CONNECT_ERROR;
+    {
+      CURLcode result = set_ssl_version_min_max(conn, sockindex);
+      if(result != CURLE_OK)
+        return result;
+      break;
+    }
   default:
     failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
     return CURLE_SSL_CONNECT_ERROR;
diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index d5158acef..a09f457f8 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.c
@@ -1512,78 +1512,108 @@ static CURLcode nss_load_ca_certificates(struct 
connectdata *conn,
   return CURLE_OK;
 }
 
-static CURLcode nss_init_sslver(SSLVersionRange *sslver,
-                                struct Curl_easy *data,
-                                struct connectdata *conn)
+static CURLcode nss_sslver_from_curl(PRUint16 *nssver, long version)
 {
-  switch(SSL_CONN_CONFIG(version)) {
-  case CURL_SSLVERSION_DEFAULT:
-    /* map CURL_SSLVERSION_DEFAULT to NSS default */
-    if(SSL_VersionRangeGetDefault(ssl_variant_stream, sslver) != SECSuccess)
-      return CURLE_SSL_CONNECT_ERROR;
-    /* ... but make sure we use at least TLSv1.0 according to libcurl API */
-    if(sslver->min < SSL_LIBRARY_VERSION_TLS_1_0)
-      sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
-    return CURLE_OK;
-
+  switch(version) {
   case CURL_SSLVERSION_TLSv1:
-    sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
     /* TODO: set sslver->max to SSL_LIBRARY_VERSION_TLS_1_3 once stable */
 #ifdef SSL_LIBRARY_VERSION_TLS_1_2
-    sslver->max = SSL_LIBRARY_VERSION_TLS_1_2;
+    *nssver = SSL_LIBRARY_VERSION_TLS_1_2;
 #elif defined SSL_LIBRARY_VERSION_TLS_1_1
-    sslver->max = SSL_LIBRARY_VERSION_TLS_1_1;
+    *nssver = SSL_LIBRARY_VERSION_TLS_1_1;
 #else
-    sslver->max = SSL_LIBRARY_VERSION_TLS_1_0;
+    *nssver = SSL_LIBRARY_VERSION_TLS_1_0;
 #endif
     return CURLE_OK;
 
   case CURL_SSLVERSION_SSLv2:
-    sslver->min = SSL_LIBRARY_VERSION_2;
-    sslver->max = SSL_LIBRARY_VERSION_2;
+    *nssver = SSL_LIBRARY_VERSION_2;
     return CURLE_OK;
 
   case CURL_SSLVERSION_SSLv3:
-    sslver->min = SSL_LIBRARY_VERSION_3_0;
-    sslver->max = SSL_LIBRARY_VERSION_3_0;
+    *nssver = SSL_LIBRARY_VERSION_3_0;
     return CURLE_OK;
 
   case CURL_SSLVERSION_TLSv1_0:
-    sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
-    sslver->max = SSL_LIBRARY_VERSION_TLS_1_0;
+    *nssver = SSL_LIBRARY_VERSION_TLS_1_0;
     return CURLE_OK;
 
   case CURL_SSLVERSION_TLSv1_1:
 #ifdef SSL_LIBRARY_VERSION_TLS_1_1
-    sslver->min = SSL_LIBRARY_VERSION_TLS_1_1;
-    sslver->max = SSL_LIBRARY_VERSION_TLS_1_1;
+    *nssver = SSL_LIBRARY_VERSION_TLS_1_1;
     return CURLE_OK;
+#else
+    return CURLE_SSL_CONNECT_ERROR;
 #endif
-    break;
 
   case CURL_SSLVERSION_TLSv1_2:
 #ifdef SSL_LIBRARY_VERSION_TLS_1_2
-    sslver->min = SSL_LIBRARY_VERSION_TLS_1_2;
-    sslver->max = SSL_LIBRARY_VERSION_TLS_1_2;
+    *nssver = SSL_LIBRARY_VERSION_TLS_1_2;
     return CURLE_OK;
+#else
+    return CURLE_SSL_CONNECT_ERROR;
 #endif
-    break;
 
   case CURL_SSLVERSION_TLSv1_3:
 #ifdef SSL_LIBRARY_VERSION_TLS_1_3
-    sslver->min = SSL_LIBRARY_VERSION_TLS_1_3;
-    sslver->max = SSL_LIBRARY_VERSION_TLS_1_3;
+    *nssver = SSL_LIBRARY_VERSION_TLS_1_3;
     return CURLE_OK;
+#else
+    return CURLE_SSL_CONNECT_ERROR;
 #endif
-    break;
 
   default:
-    failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
     return CURLE_SSL_CONNECT_ERROR;
   }
+}
 
-  failf(data, "TLS minor version cannot be set");
-  return CURLE_SSL_CONNECT_ERROR;
+static CURLcode nss_init_sslver(SSLVersionRange *sslver,
+                                struct Curl_easy *data,
+                                struct connectdata *conn)
+{
+  CURLcode result;
+  const long min = SSL_CONN_CONFIG(version);
+  const long max = SSL_CONN_CONFIG(version_max);
+
+  /* map CURL_SSLVERSION_DEFAULT to NSS default */
+  if(min == CURL_SSLVERSION_DEFAULT || max == CURL_SSLVERSION_MAX_DEFAULT) {
+    /* map CURL_SSLVERSION_DEFAULT to NSS default */
+    if(SSL_VersionRangeGetDefault(ssl_variant_stream, sslver) != SECSuccess)
+      return CURLE_SSL_CONNECT_ERROR;
+    /* ... but make sure we use at least TLSv1.0 according to libcurl API */
+    if(sslver->min < SSL_LIBRARY_VERSION_TLS_1_0)
+      sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
+  }
+
+  switch(min) {
+  case CURL_SSLVERSION_DEFAULT:
+    break;
+  case CURL_SSLVERSION_TLSv1:
+    sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
+    break;
+  default:
+    result = nss_sslver_from_curl(&sslver->min, min);
+    if(result) {
+      failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
+      return result;
+    }
+    if(max == CURL_SSLVERSION_MAX_NONE)
+      sslver->max = sslver->min;
+  }
+
+  switch(max) {
+  case CURL_SSLVERSION_MAX_NONE:
+  case CURL_SSLVERSION_MAX_DEFAULT:
+    break;
+  default:
+    result = nss_sslver_from_curl(&sslver->max, max >> 16);
+    if(result) {
+      failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
+      return result;
+    }
+  }
+
+  return CURLE_OK;
 }
 
 static CURLcode nss_fail_connect(struct ssl_connect_data *connssl,
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index 20626fec9..8881b57ac 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -1692,6 +1692,71 @@ get_ssl_version_txt(SSL *ssl)
   return "unknown";
 }
 
+static CURLcode
+set_ssl_version_min_max(long *ctx_options, struct connectdata *conn)
+{
+  struct Curl_easy *data = conn->data;
+  long ssl_version = SSL_CONN_CONFIG(version);
+  long ssl_version_max = SSL_CONN_CONFIG(version_max);
+  if(ssl_version_max == CURL_SSLVERSION_MAX_NONE) {
+    ssl_version_max = ssl_version << 16;
+  }
+
+  switch(ssl_version) {
+    case CURL_SSLVERSION_TLSv1_3:
+#ifdef TLS1_3_VERSION
+      SSL_CTX_set_max_proto_version(connssl->ctx, TLS1_3_VERSION);
+      *ctx_options |= SSL_OP_NO_TLSv1_2;
+#else
+      failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
+      return CURLE_NOT_BUILT_IN;
+#endif
+    case CURL_SSLVERSION_TLSv1_2:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+      *ctx_options |= SSL_OP_NO_TLSv1_1;
+#else
+      failf(data, OSSL_PACKAGE " was built without TLS 1.2 support");
+      return CURLE_NOT_BUILT_IN;
+#endif
+    case CURL_SSLVERSION_TLSv1_1:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+      *ctx_options |= SSL_OP_NO_TLSv1;
+#else
+      failf(data, OSSL_PACKAGE " was built without TLS 1.1 support");
+      return CURLE_NOT_BUILT_IN;
+#endif
+    case CURL_SSLVERSION_TLSv1_0:
+      *ctx_options |= SSL_OP_NO_SSLv2;
+      *ctx_options |= SSL_OP_NO_SSLv3;
+      break;
+  }
+
+  switch(ssl_version_max) {
+    case CURL_SSLVERSION_MAX_TLSv1_0:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+      *ctx_options |= SSL_OP_NO_TLSv1_1;
+#endif
+    case CURL_SSLVERSION_MAX_TLSv1_1:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+      *ctx_options |= SSL_OP_NO_TLSv1_2;
+#endif
+    case CURL_SSLVERSION_MAX_TLSv1_2:
+    case CURL_SSLVERSION_MAX_DEFAULT:
+#ifdef TLS1_3_VERSION
+      *ctx_options |= SSL_OP_NO_TLSv1_3;
+#endif
+      break;
+    case CURL_SSLVERSION_MAX_TLSv1_3:
+#ifdef TLS1_3_VERSION
+      break;
+#else
+      failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
+      return CURLE_NOT_BUILT_IN;
+#endif
+  }
+  return CURLE_OK;
+}
+
 static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
 {
   CURLcode result = CURLE_OK;
@@ -1701,7 +1766,7 @@ static CURLcode ossl_connect_step1(struct connectdata 
*conn, int sockindex)
   X509_LOOKUP *lookup = NULL;
   curl_socket_t sockfd = conn->sock[sockindex];
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  long ctx_options;
+  long ctx_options = 0;
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
   bool sni;
 #ifdef ENABLE_IPV6
@@ -1888,60 +1953,13 @@ static CURLcode ossl_connect_step1(struct connectdata 
*conn, int sockindex)
     break;
 
   case CURL_SSLVERSION_TLSv1_0:
-    ctx_options |= SSL_OP_NO_SSLv2;
-    ctx_options |= SSL_OP_NO_SSLv3;
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
-    ctx_options |= SSL_OP_NO_TLSv1_1;
-    ctx_options |= SSL_OP_NO_TLSv1_2;
-#ifdef TLS1_3_VERSION
-    ctx_options |= SSL_OP_NO_TLSv1_3;
-#endif
-#endif
-    break;
-
   case CURL_SSLVERSION_TLSv1_1:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
-    ctx_options |= SSL_OP_NO_SSLv2;
-    ctx_options |= SSL_OP_NO_SSLv3;
-    ctx_options |= SSL_OP_NO_TLSv1;
-    ctx_options |= SSL_OP_NO_TLSv1_2;
-#ifdef TLS1_3_VERSION
-    ctx_options |= SSL_OP_NO_TLSv1_3;
-#endif
-    break;
-#else
-    failf(data, OSSL_PACKAGE " was built without TLS 1.1 support");
-    return CURLE_NOT_BUILT_IN;
-#endif
-
   case CURL_SSLVERSION_TLSv1_2:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
-    ctx_options |= SSL_OP_NO_SSLv2;
-    ctx_options |= SSL_OP_NO_SSLv3;
-    ctx_options |= SSL_OP_NO_TLSv1;
-    ctx_options |= SSL_OP_NO_TLSv1_1;
-#ifdef TLS1_3_VERSION
-    ctx_options |= SSL_OP_NO_TLSv1_3;
-#endif
-    break;
-#else
-    failf(data, OSSL_PACKAGE " was built without TLS 1.2 support");
-    return CURLE_NOT_BUILT_IN;
-#endif
-
   case CURL_SSLVERSION_TLSv1_3:
-#ifdef TLS1_3_VERSION
-    SSL_CTX_set_max_proto_version(connssl->ctx, TLS1_3_VERSION);
-    ctx_options |= SSL_OP_NO_SSLv2;
-    ctx_options |= SSL_OP_NO_SSLv3;
-    ctx_options |= SSL_OP_NO_TLSv1;
-    ctx_options |= SSL_OP_NO_TLSv1_1;
-    ctx_options |= SSL_OP_NO_TLSv1_2;
+    result = set_ssl_version_min_max(&ctx_options, conn);
+    if(result != CURLE_OK)
+       return result;
     break;
-#else
-    failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
-    return CURLE_NOT_BUILT_IN;
-#endif
 
   case CURL_SSLVERSION_SSLv2:
 #ifndef OPENSSL_NO_SSL2
diff --git a/lib/vtls/polarssl.c b/lib/vtls/polarssl.c
index e5346ce3d..aa34bbc43 100644
--- a/lib/vtls/polarssl.c
+++ b/lib/vtls/polarssl.c
@@ -140,6 +140,68 @@ static void polarssl_debug(void *context, int level, const 
char *line)
 static Curl_recv polarssl_recv;
 static Curl_send polarssl_send;
 
+static CURLcode polarssl_version_from_curl(int *polarver, long version)
+{
+  switch(ssl_version) {
+    case CURL_SSLVERSION_TLSv1_0:
+      *polarver = SSL_MINOR_VERSION_1;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_1:
+      *polarver = SSL_MINOR_VERSION_2;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_2:
+      *polarver = SSL_MINOR_VERSION_3;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_3:
+      break;
+  }
+  return CURLE_SSL_CONNECT_ERROR;
+}
+
+static CURLcode
+set_ssl_version_min_max(struct connectdata *conn, int sockindex);
+{
+  struct Curl_easy *data = conn->data;
+  struct ssl_connect_data* connssl = &conn->ssl[sockindex];
+  long ssl_version = SSL_CONN_CONFIG(version);
+  long ssl_version_max = SSL_CONN_CONFIG(version_max);
+  int ssl_min_ver = SSL_MINOR_VERSION_1;
+  int ssl_max_ver = SSL_MINOR_VERSION_1;
+  CURLcode result = CURLE_OK;
+
+  switch(ssl_version) {
+    case CURL_SSLVERSION_DEFAULT:
+    case CURL_SSLVERSION_TLSv1:
+      ssl_version = CURL_SSLVERSION_TLSv1_0;
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+      break;
+  }
+
+  switch(ssl_version_max) {
+    case CURL_SSLVERSION_MAX_NONE:
+      ssl_version_max = ssl_version << 16;
+      break;
+    case CURL_SSLVERSION_MAX_DEFAULT:
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+      break;
+  }
+
+  result = polarssl_version_from_curl(&ssl_min_ver, ssl_version);
+  if(result) {
+    failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
+    return result;
+  }
+  result = polarssl_version_from_curl(&ssl_max_ver, ssl_version_max >> 16);
+  if(result) {
+    failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
+    return result;
+  }
+
+  ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3, ssl_min_ver);
+  ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3, ssl_max_ver);
+
+  return result;
+}
 
 static CURLcode
 polarssl_connect_step1(struct connectdata *conn,
@@ -287,29 +349,15 @@ polarssl_connect_step1(struct connectdata *conn,
     infof(data, "PolarSSL: Forced min. SSL Version to be SSLv3\n");
     break;
   case CURL_SSLVERSION_TLSv1_0:
-    ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
-                        SSL_MINOR_VERSION_1);
-    ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
-                        SSL_MINOR_VERSION_1);
-    infof(data, "PolarSSL: Forced min. SSL Version to be TLS 1.0\n");
-    break;
   case CURL_SSLVERSION_TLSv1_1:
-    ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
-                        SSL_MINOR_VERSION_2);
-    ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
-                        SSL_MINOR_VERSION_2);
-    infof(data, "PolarSSL: Forced min. SSL Version to be TLS 1.1\n");
-    break;
   case CURL_SSLVERSION_TLSv1_2:
-    ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
-                        SSL_MINOR_VERSION_3);
-    ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
-                        SSL_MINOR_VERSION_3);
-    infof(data, "PolarSSL: Forced min. SSL Version to be TLS 1.2\n");
-    break;
   case CURL_SSLVERSION_TLSv1_3:
-    failf(data, "PolarSSL: TLS 1.3 is not yet supported");
-    return CURLE_SSL_CONNECT_ERROR;
+    {
+      CURLcode result = set_ssl_version_min_max(conn, sockindex);
+      if(result != CURLE_OK)
+        return result;
+      break;
+    }
   default:
     failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
     return CURLE_SSL_CONNECT_ERROR;
diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
index bd9239956..47356a53d 100644
--- a/lib/vtls/schannel.c
+++ b/lib/vtls/schannel.c
@@ -103,6 +103,41 @@ static void InitSecBufferDesc(SecBufferDesc *desc, 
SecBuffer *BufArr,
 }
 
 static CURLcode
+set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn)
+{
+  struct Curl_easy *data = conn->data;
+  long ssl_version = SSL_CONN_CONFIG(version);
+  long ssl_version_max = SSL_CONN_CONFIG(version_max);
+  long i = ssl_version;
+
+  switch(ssl_version_max) {
+    case CURL_SSLVERSION_MAX_NONE:
+      ssl_version_max = ssl_version << 16;
+      break;
+    case CURL_SSLVERSION_MAX_DEFAULT:
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+      break;
+  }
+  for(; i <= (ssl_version_max >> 16); ++i) {
+    switch(i) {
+      case CURL_SSLVERSION_TLSv1_0:
+        schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT;
+        break;
+      case CURL_SSLVERSION_TLSv1_1:
+        schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT;
+        break;
+      case CURL_SSLVERSION_TLSv1_2:
+        schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT;
+        break;
+      case CURL_SSLVERSION_TLSv1_3:
+        failf(data, "Schannel: TLS 1.3 is not yet supported");
+        return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+  return CURLE_OK;
+}
+
+static CURLcode
 schannel_connect_step1(struct connectdata *conn, int sockindex)
 {
   ssize_t written = -1;
@@ -216,17 +251,15 @@ schannel_connect_step1(struct connectdata *conn, int 
sockindex)
         SP_PROT_TLS1_2_CLIENT;
       break;
     case CURL_SSLVERSION_TLSv1_0:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
-      break;
     case CURL_SSLVERSION_TLSv1_1:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
-      break;
     case CURL_SSLVERSION_TLSv1_2:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
-      break;
     case CURL_SSLVERSION_TLSv1_3:
-      failf(data, "Schannel: TLS 1.3 is not yet supported");
-      return CURLE_SSL_CONNECT_ERROR;
+      {
+        CURLcode result = set_ssl_version_min_max(&schannel_cred, conn);
+        if(result != CURLE_OK)
+          return result;
+        break;
+      }
     case CURL_SSLVERSION_SSLv3:
       schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
       break;
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index f40a97712..693380fb9 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -95,6 +95,7 @@ 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) &&
      Curl_safe_strcasecompare(data->CApath, needle->CApath) &&
@@ -113,6 +114,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config 
*source,
   dest->verifyhost = source->verifyhost;
   dest->verifypeer = source->verifypeer;
   dest->version = source->version;
+  dest->version_max = source->version_max;
 
   CLONE_STRING(CAfile);
   CLONE_STRING(CApath);
@@ -173,11 +175,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;
 }
 
diff --git a/packages/OS400/curl.inc.in b/packages/OS400/curl.inc.in
index c6224a781..b9b0808c7 100644
--- a/packages/OS400/curl.inc.in
+++ b/packages/OS400/curl.inc.in
@@ -262,6 +262,16 @@
      d                 c                   6
      d CURL_SSLVERSION_TLSv1_3...
      d                 c                   7
+     d CURL_SSLVERSION_MAX_DEFAULT...
+     d                 c                   X'00010000'
+     d CURL_SSLVERSION_MAX_TLSv1_0...
+     d                 c                   X'00040000'
+     d CURL_SSLVERSION_MAX_TLSv1_1...
+     d                 c                   X'00050000'
+     d CURL_SSLVERSION_MAX_TLSv1_2...
+     d                 c                   X'00060000'
+     d CURL_SSLVERSION_MAX_TLSv1_3...
+     d                 c                   X'00070000'
       *
      d CURL_TLSAUTH_NONE...
      d                 c                   0
diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h
index 0d2f765d2..b05c440e5 100644
--- a/src/tool_cfgable.h
+++ b/src/tool_cfgable.h
@@ -156,6 +156,7 @@ struct OperationConfig {
   struct curl_slist *postquote;
   struct curl_slist *prequote;
   long ssl_version;
+  long ssl_version_max;
   long proxy_ssl_version;
   long ip_version;
   curl_TimeCond timecond;
diff --git a/src/tool_getparam.c b/src/tool_getparam.c
index 686b01d7e..85d75ae6e 100644
--- a/src/tool_getparam.c
+++ b/src/tool_getparam.c
@@ -184,6 +184,7 @@ static const struct LongShort aliases[]= {
   {"$S", "tftp-no-options",          FALSE},
   {"$U", "connect-to",               TRUE},
   {"$W", "abstract-unix-socket",     TRUE},
+  {"$X", "tls-max",                  TRUE},
   {"0",   "http1.0",                 FALSE},
   {"01",  "http1.1",                 FALSE},
   {"02",  "http2",                   FALSE},
@@ -1060,6 +1061,11 @@ ParameterError getparameter(char *flag,    /* f or 
-long-flag */
         config->abstract_unix_socket = TRUE;
         GetStr(&config->unix_socket_path, nextarg);
         break;
+      case 'X': /* --tls-max */
+        err = str2tls_max(&config->ssl_version_max, nextarg);
+        if(err)
+          return err;
+        break;
       }
       break;
     case '#': /* --progress-bar */
diff --git a/src/tool_help.c b/src/tool_help.c
index 5085e542e..f6fe3527b 100644
--- a/src/tool_help.c
+++ b/src/tool_help.c
@@ -260,6 +260,7 @@ static const char *const helptext[] = {
   "     --tlsv1.1       Use TLSv1.1 (SSL)",
   "     --tlsv1.2       Use TLSv1.2 (SSL)",
   "     --tlsv1.3       Use TLSv1.3 (SSL)",
+  "     --tls-max VERSION  Use TLS up to VERSION (SSL)",
   "     --trace FILE    Write a debug trace to FILE",
   "     --trace-ascii FILE  Like --trace, but without hex output",
   "     --trace-time    Add time stamps to trace/verbose output",
diff --git a/src/tool_operate.c b/src/tool_operate.c
index c30b32046..a489b8dbd 100644
--- a/src/tool_operate.c
+++ b/src/tool_operate.c
@@ -1087,7 +1087,8 @@ static CURLcode operate_do(struct GlobalConfig *global,
           if(config->falsestart)
             my_setopt(curl, CURLOPT_SSL_FALSESTART, 1L);
 
-          my_setopt_enum(curl, CURLOPT_SSLVERSION, config->ssl_version);
+          my_setopt_enum(curl, CURLOPT_SSLVERSION,
+                         config->ssl_version | config->ssl_version_max);
           my_setopt_enum(curl, CURLOPT_PROXY_SSLVERSION,
                          config->proxy_ssl_version);
         }
diff --git a/src/tool_paramhlp.c b/src/tool_paramhlp.c
index 257e5c697..6b534ce5d 100644
--- a/src/tool_paramhlp.c
+++ b/src/tool_paramhlp.c
@@ -550,3 +550,36 @@ CURLcode get_args(struct OperationConfig *config, const 
size_t i)
 
   return result;
 }
+
+/*
+ * Parse the string and modify ssl_version in the val argument. Return PARAM_OK
+ * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
+ *
+ * Since this function gets called with the 'nextarg' pointer from within the
+ * getparameter a lot, we must check it for NULL before accessing the str
+ * data.
+ */
+
+ParameterError str2tls_max(long *val, const char *str)
+{
+   static struct s_tls_max {
+    const char *tls_max_str;
+    long tls_max;
+  } const tls_max_array[] = {
+    { "default", CURL_SSLVERSION_MAX_DEFAULT },
+    { "1.0",     CURL_SSLVERSION_MAX_TLSv1_0 },
+    { "1.1",     CURL_SSLVERSION_MAX_TLSv1_1 },
+    { "1.2",     CURL_SSLVERSION_MAX_TLSv1_2 },
+    { "1.3",     CURL_SSLVERSION_MAX_TLSv1_3 }
+  };
+  size_t i = 0;
+  if(!str)
+    return PARAM_REQUIRES_PARAMETER;
+  for(i = 0; i < sizeof(tls_max_array)/sizeof(tls_max_array[0]); i++) {
+    if(!strcmp(str, tls_max_array[i].tls_max_str)) {
+      *val = tls_max_array[i].tls_max;
+      return PARAM_OK;
+    }
+  }
+  return PARAM_BAD_USE;
+}
diff --git a/src/tool_paramhlp.h b/src/tool_paramhlp.h
index 89a99b2bb..cdfbacf3f 100644
--- a/src/tool_paramhlp.h
+++ b/src/tool_paramhlp.h
@@ -52,4 +52,6 @@ int ftpcccmethod(struct OperationConfig *config, const char 
*str);
 
 long delegation(struct OperationConfig *config, char *str);
 
+ParameterError str2tls_max(long *val, const char *str);
+
 #endif /* HEADER_CURL_TOOL_PARAMHLP_H */

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]