From 6448f98c1857de521fb2dd3f9d4e5659845b5474 Mon Sep 17 00:00:00 2001 From: Jozef Kralik Date: Tue, 13 Dec 2016 21:10:00 +0100 Subject: [PATCH] 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 | 150 +++++++++++-------- 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, 781 insertions(+), 227 deletions(-) create mode 100644 docs/cmdline-opts/tls-max.d 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: +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_sslver_from_curl(PRUint16 *nssver, long version) +{ + switch(version) { + case CURL_SSLVERSION_TLSv1: + /* TODO: set sslver->max to SSL_LIBRARY_VERSION_TLS_1_3 once stable */ +#ifdef SSL_LIBRARY_VERSION_TLS_1_2 + *nssver = SSL_LIBRARY_VERSION_TLS_1_2; +#elif defined SSL_LIBRARY_VERSION_TLS_1_1 + *nssver = SSL_LIBRARY_VERSION_TLS_1_1; +#else + *nssver = SSL_LIBRARY_VERSION_TLS_1_0; +#endif + return CURLE_OK; + + case CURL_SSLVERSION_SSLv2: + *nssver = SSL_LIBRARY_VERSION_2; + return CURLE_OK; + + case CURL_SSLVERSION_SSLv3: + *nssver = SSL_LIBRARY_VERSION_3_0; + return CURLE_OK; + + case CURL_SSLVERSION_TLSv1_0: + *nssver = SSL_LIBRARY_VERSION_TLS_1_0; + return CURLE_OK; + + case CURL_SSLVERSION_TLSv1_1: +#ifdef SSL_LIBRARY_VERSION_TLS_1_1 + *nssver = SSL_LIBRARY_VERSION_TLS_1_1; + return CURLE_OK; +#else + return CURLE_SSL_CONNECT_ERROR; +#endif + + case CURL_SSLVERSION_TLSv1_2: +#ifdef SSL_LIBRARY_VERSION_TLS_1_2 + *nssver = SSL_LIBRARY_VERSION_TLS_1_2; + return CURLE_OK; +#else + return CURLE_SSL_CONNECT_ERROR; +#endif + + case CURL_SSLVERSION_TLSv1_3: +#ifdef SSL_LIBRARY_VERSION_TLS_1_3 + *nssver = SSL_LIBRARY_VERSION_TLS_1_3; + return CURLE_OK; +#else + return CURLE_SSL_CONNECT_ERROR; +#endif + + default: + return CURLE_SSL_CONNECT_ERROR; + } +} + static CURLcode nss_init_sslver(SSLVersionRange *sslver, struct Curl_easy *data, struct connectdata *conn) { - switch(SSL_CONN_CONFIG(version)) { - case CURL_SSLVERSION_DEFAULT: + 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; - return CURLE_OK; - - 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; -#elif defined SSL_LIBRARY_VERSION_TLS_1_1 - sslver->max = SSL_LIBRARY_VERSION_TLS_1_1; -#else - sslver->max = 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; - return CURLE_OK; - - case CURL_SSLVERSION_SSLv3: - sslver->min = SSL_LIBRARY_VERSION_3_0; - sslver->max = 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; - 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; - return CURLE_OK; -#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; - return CURLE_OK; -#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; - return CURLE_OK; -#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; + 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 @@ -102,6 +102,41 @@ static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr, desc->cBuffers = NumArrElem; } +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) { @@ -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 */