## CURL_SSLVERSION_DEFAULT
The default acceptable version range. The minimum acceptable version is by
-default TLS v1.0 since 7.39.0 (unless the TLS library has a stricter rule).
+default TLS v1.2 since 8.16.0 (unless the TLS library has a stricter rule).
## CURL_SSLVERSION_TLSv1
#endif /* ! CURL_DISABLE_HTTP */
#ifdef USE_SSL
-static CURLcode setopt_SSLVERSION(struct Curl_easy *data, CURLoption option,
- long arg)
+CURLcode Curl_setopt_SSLVERSION(struct Curl_easy *data, CURLoption option,
+ long arg)
{
/*
* Set explicit SSL version to try to connect with, as some SSL
version_max < CURL_SSLVERSION_MAX_NONE ||
version_max >= CURL_SSLVERSION_MAX_LAST)
return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(version == CURL_SSLVERSION_DEFAULT)
+ version = CURL_SSLVERSION_TLSv1_2;
primary->version = (unsigned char)version;
primary->version_max = (unsigned int)version_max;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSLVERSION:
#endif
-#ifdef USE_SSL
- return setopt_SSLVERSION(data, option, arg);
-#else
- return CURLE_NOT_BUILT_IN;
-#endif
+ return Curl_setopt_SSLVERSION(data, option, arg);
case CURLOPT_POSTFIELDSIZE:
/*
*
***************************************************************************/
+#ifdef USE_SSL
+CURLcode Curl_setopt_SSLVERSION(struct Curl_easy *data, CURLoption option,
+ long arg);
+#else
+#define Curl_setopt_SSLVERSION(a,b,c) CURLE_NOT_BUILT_IN
+#endif
+
CURLcode Curl_setstropt(char **charp, const char *s) WARN_UNUSED_RESULT;
CURLcode Curl_setblobopt(struct curl_blob **blobp,
const struct curl_blob *blob) WARN_UNUSED_RESULT;
#endif
}
+ /* set default minimum TLS version */
+#ifdef USE_SSL
+ Curl_setopt_SSLVERSION(data, CURLOPT_SSLVERSION, CURL_SSLVERSION_DEFAULT);
+#ifndef CURL_DISABLE_PROXY
+ Curl_setopt_SSLVERSION(data, CURLOPT_PROXY_SSLVERSION,
+ CURL_SSLVERSION_DEFAULT);
+#endif
+#endif
#ifndef CURL_DISABLE_FTP
set->wildcard_enabled = FALSE;
set->chunk_bgn = ZERO_NULL;
if((ssl_version == CURL_SSLVERSION_DEFAULT) ||
(ssl_version == CURL_SSLVERSION_TLSv1))
ssl_version = CURL_SSLVERSION_TLSv1_0;
- if(ssl_version_max == CURL_SSLVERSION_MAX_NONE)
- ssl_version_max = CURL_SSLVERSION_MAX_DEFAULT;
+ if((ssl_version_max == CURL_SSLVERSION_MAX_NONE) ||
+ (ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT))
+ ssl_version_max = tls13support ?
+ CURL_SSLVERSION_MAX_TLSv1_3 : CURL_SSLVERSION_MAX_TLSv1_2;
if(peer->transport == TRNSPRT_QUIC) {
- if((ssl_version_max != CURL_SSLVERSION_MAX_DEFAULT) &&
- (ssl_version_max < CURL_SSLVERSION_MAX_TLSv1_3)) {
+ if(ssl_version_max < CURL_SSLVERSION_MAX_TLSv1_3) {
failf(data, "QUIC needs at least TLS version 1.3");
return CURLE_SSL_CONNECT_ERROR;
}
return CURLE_OK;
}
- if(!tls13support) {
- /* If the running GnuTLS does not support TLS 1.3, we must not specify a
- prioritylist involving that since it will make GnuTLS return an en
- error back at us */
- if((ssl_version_max == CURL_SSLVERSION_MAX_TLSv1_3) ||
- (ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT)) {
- ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
- }
- }
- else if(ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT) {
- ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3;
- }
-
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:"
#if defined(GNUTLS_NO_TICKETS_TLS12)
init_flags |= GNUTLS_NO_TICKETS_TLS12;
-#elif defined(GNUTLS_NO_TICKETS)
- /* Disable TLS session tickets for non 1.3 connections */
- if((config->version != CURL_SSLVERSION_TLSv1_3) &&
- (config->version != CURL_SSLVERSION_DEFAULT))
- init_flags |= GNUTLS_NO_TICKETS;
#endif
#if defined(GNUTLS_NO_STATUS_REQUEST)
else {
infof(data, "SSL reusing session with ALPN '%s'",
scs->alpn ? scs->alpn : "-");
- if(ssl_config->earlydata && scs->alpn &&
- !cf->conn->connect_only &&
- (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3)) {
+ if(ssl_config->earlydata && scs->alpn && !cf->conn->connect_only) {
bool do_early_data = FALSE;
if(sess_reuse_cb) {
result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
#endif
#endif
+static long tlsversion(unsigned char mintls,
+ unsigned char maxtls)
+{
+ long tlsver = 0;
+ if(!mintls) { /* minimum is at default */
+ /* minimum is set to default, which we want to be 1.2 */
+ if(maxtls && (maxtls < 3))
+ /* max is set lower than 1.2 and minimum is default, change minimum to
+ the same as max */
+ mintls = maxtls;
+ }
+ switch(mintls) {
+ case 1:
+ tlsver = CURL_SSLVERSION_TLSv1_0;
+ break;
+ case 2:
+ tlsver = CURL_SSLVERSION_TLSv1_1;
+ break;
+ case 0: /* let default minimum be 1.2 */
+ case 3:
+ tlsver = CURL_SSLVERSION_TLSv1_2;
+ break;
+ case 4:
+ default: /* just in case */
+ tlsver = CURL_SSLVERSION_TLSv1_3;
+ break;
+ }
+ switch(maxtls) {
+ case 0: /* not set, leave it */
+ break;
+ case 1:
+ tlsver |= CURL_SSLVERSION_MAX_TLSv1_0;
+ break;
+ case 2:
+ tlsver |= CURL_SSLVERSION_MAX_TLSv1_1;
+ break;
+ case 3:
+ tlsver |= CURL_SSLVERSION_MAX_TLSv1_2;
+ break;
+ case 4:
+ default: /* just in case */
+ tlsver |= CURL_SSLVERSION_MAX_TLSv1_3;
+ break;
+ }
+ return tlsver;
+}
+
/* only called if libcurl supports TLS */
static CURLcode ssl_setopts(struct OperationConfig *config, CURL *curl)
{
my_setopt_long(curl, CURLOPT_DOH_SSL_VERIFYSTATUS, 1);
my_setopt_SSLVERSION(curl, CURLOPT_SSLVERSION,
- config->ssl_version | config->ssl_version_max);
+ tlsversion(config->ssl_version,
+ config->ssl_version_max));
if(config->proxy)
my_setopt_SSLVERSION(curl, CURLOPT_PROXY_SSLVERSION,
config->proxy_ssl_version);
curl_off_t sendpersecond; /* send to peer */
curl_off_t recvpersecond; /* receive from peer */
- long ssl_version;
- long ssl_version_max;
long proxy_ssl_version;
long ip_version;
long create_file_mode; /* CURLOPT_NEW_FILE_PERMS */
} file_clobber_mode;
unsigned char upload_flags; /* Bitmask for --upload-flags */
unsigned short porttouse;
+ unsigned char ssl_version; /* 0 - 4, 0 being default */
+ unsigned char ssl_version_max; /* 0 - 4, 0 being default */
BIT(remote_name_all); /* --remote-name-all */
BIT(remote_time);
BIT(cookiesession); /* new session? */
warnf(global, "--%s is deprecated and has no function anymore", a->lname);
}
+static ParameterError opt_sslver(struct OperationConfig *config,
+ unsigned char ver)
+{
+ if(config->ssl_version_max &&
+ (config->ssl_version_max < ver)) {
+ errorf(config->global, "Minimum TLS version set higher than max");
+ return PARAM_BAD_USE;
+ }
+ config->ssl_version = ver;
+ return PARAM_OK;
+}
+
/* opt_none is the function that handles ARG_NONE options */
static ParameterError opt_none(struct OperationConfig *config,
const struct LongShort *a)
{
+ ParameterError err = PARAM_OK;
switch(a->cmd) {
case C_ANYAUTH: /* --anyauth */
config->authtype = CURLAUTH_ANY;
sethttpver(config, CURL_HTTP_VERSION_3ONLY);
break;
case C_TLSV1: /* --tlsv1 */
- config->ssl_version = CURL_SSLVERSION_TLSv1;
+ err = opt_sslver(config, 1);
break;
case C_TLSV1_0: /* --tlsv1.0 */
- config->ssl_version = CURL_SSLVERSION_TLSv1_0;
+ err = opt_sslver(config, 1);
break;
case C_TLSV1_1: /* --tlsv1.1 */
- config->ssl_version = CURL_SSLVERSION_TLSv1_1;
+ err = opt_sslver(config, 2);
break;
case C_TLSV1_2: /* --tlsv1.2 */
- config->ssl_version = CURL_SSLVERSION_TLSv1_2;
+ err = opt_sslver(config, 3);
break;
case C_TLSV1_3: /* --tlsv1.3 */
- config->ssl_version = CURL_SSLVERSION_TLSv1_3;
+ err = opt_sslver(config, 4);
break;
case C_IPV4: /* --ipv4 */
config->ip_version = CURL_IPRESOLVE_V4;
config->proxy_ssl_version = CURL_SSLVERSION_TLSv1;
break;
}
- return PARAM_OK;
+ return err;
}
/* opt_bool is the function that handles boolean options */
break;
case C_TLS_MAX: /* --tls-max */
err = str2tls_max(&config->ssl_version_max, nextarg);
+ if(!err && (config->ssl_version_max < config->ssl_version)) {
+ errorf(global, "--tls-max set lower than minimum accepted version");
+ err = PARAM_BAD_USE;
+ }
break;
case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */
err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg);
* data.
*/
-ParameterError str2tls_max(long *val, const char *str)
+ParameterError str2tls_max(unsigned char *val, const char *str)
{
- static struct s_tls_max {
+ static struct s_tls_max {
const char *tls_max_str;
- long tls_max;
+ unsigned char 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 }
+ { "default", 0 }, /* lets the library decide */
+ { "1.0", 1 },
+ { "1.1", 2 },
+ { "1.2", 3 },
+ { "1.3", 4 }
};
size_t i = 0;
if(!str)
long delegation(struct OperationConfig *config, const char *str);
-ParameterError str2tls_max(long *val, const char *str);
+ParameterError str2tls_max(unsigned char *val, const char *str);
#endif /* HEADER_CURL_TOOL_PARAMHLP_H */
};
const struct NameValue setopt_nv_CURL_SSLVERSION_MAX[] = {
- NV(CURL_SSLVERSION_MAX_NONE),
+ {"", CURL_SSLVERSION_MAX_NONE},
NV(CURL_SSLVERSION_MAX_DEFAULT),
NV(CURL_SSLVERSION_MAX_TLSv1_0),
NV(CURL_SSLVERSION_MAX_TLSv1_1),
name, lval);
}
else {
- ret = easysrc_addf(&easysrc_code,
- "curl_easy_setopt(hnd, %s, (long)(%s | %s));",
- name, nv->name, nv2->name);
+ if(nv2->name && *nv2->name)
+ /* if max is set */
+ ret = easysrc_addf(&easysrc_code,
+ "curl_easy_setopt(hnd, %s, (long)(%s | %s));",
+ name, nv->name, nv2->name);
+ else
+ /* without a max */
+ ret = easysrc_addf(&easysrc_code,
+ "curl_easy_setopt(hnd, %s, (long)%s);",
+ name, nv->name);
}
}
$_ = '' if /CURLOPT_SSL_VERIFYPEER/
$_ = '' if /CURLOPT_SSH_KNOWNHOSTS/
$_ = '' if /CURLOPT_HTTP_VERSION/
+$_ = '' if /CURLOPT_SSLVERSION/
$_ = '' if /CURLOPT_HTTP09_ALLOWED/
$_ = '' if /CURLOPT_INTERLEAVEDATA/
</stripfile>
$_ = '' if /CURLOPT_SSH_KNOWNHOSTS/
$_ = '' if /CURLOPT_HTTP_VERSION/
$_ = '' if /CURLOPT_INTERLEAVEDATA/
+$_ = '' if /CURLOPT_SSLVERSION/
</stripfile>
<file name="%LOGDIR/test%TESTNUMBER.c" mode="text">
/********* Sample code generated by the curl command line tool **********
$_ = '' if /CURLOPT_SSH_KNOWNHOSTS/
$_ = '' if /CURLOPT_HTTP_VERSION/
$_ = '' if /CURLOPT_INTERLEAVEDATA/
+$_ = '' if /CURLOPT_SSLVERSION/
</stripfile>
<file name="%LOGDIR/test%TESTNUMBER.c" mode="text">
/********* Sample code generated by the curl command line tool **********
$_ = '' if /CURLOPT_SSH_KNOWNHOSTS/
$_ = '' if /CURLOPT_HTTP_VERSION/
$_ = '' if /CURLOPT_INTERLEAVEDATA/
+$_ = '' if /CURLOPT_SSLVERSION/
</stripfile>
<file name="%LOGDIR/test%TESTNUMBER.c" mode="text">
/********* Sample code generated by the curl command line tool **********
$_ = '' if /CURLOPT_SSH_KNOWNHOSTS/
$_ = '' if /CURLOPT_HTTP_VERSION/
$_ = '' if /CURLOPT_INTERLEAVEDATA/
+$_ = '' if /CURLOPT_SSLVERSION/
# CURL_DOES_CONVERSION generates an extra comment.
$_ = '' if /\/\* "value" \*\//
</stripfile>
$_ = '' if /CURLOPT_HTTP_VERSION/
$_ = '' if /CURLOPT_HTTP09_ALLOWED/
$_ = '' if /CURLOPT_INTERLEAVEDATA/
+$_ = '' if /CURLOPT_SSLVERSION/
</stripfile>
</verify>
</testcase>
$_ = '' if /CURLOPT_HTTP_VERSION/
$_ = '' if /CURLOPT_HTTP09_ALLOWED/
$_ = '' if /CURLOPT_INTERLEAVEDATA/
+$_ = '' if /CURLOPT_SSLVERSION/
</stripfile>
</verify>
</testcase>
$_ = '' if /CURLOPT_HTTP_VERSION/
$_ = '' if /CURLOPT_HTTP09_ALLOWED/
$_ = '' if /CURLOPT_INTERLEAVEDATA/
+$_ = '' if /CURLOPT_SSLVERSION/
</stripfile>
</verify>
</testcase>
$_ = '' if /CURLOPT_SSH_KNOWNHOSTS/
$_ = '' if /CURLOPT_HTTP_VERSION/
$_ = '' if /CURLOPT_INTERLEAVEDATA/
+$_ = '' if /CURLOPT_SSLVERSION/
</stripfile>
</verify>
</testcase>
$_ = '' if /CURLOPT_SSH_KNOWNHOSTS/
$_ = '' if /CURLOPT_HTTP_VERSION/
$_ = '' if /CURLOPT_INTERLEAVEDATA/
+$_ = '' if /CURLOPT_SSLVERSION/
</stripfile>
<file name="%LOGDIR/test%TESTNUMBER.c" mode="text">
/********* Sample code generated by the curl command line tool **********
curl_easy_setopt(hnd, CURLOPT_PROXY, "http://%HOSTIP:%HTTPPORT");
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "stripped");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
- curl_easy_setopt(hnd, CURLOPT_SSLVERSION, (long)(CURL_SSLVERSION_DEFAULT | CURL_SSLVERSION_MAX_TLSv1_3));
- curl_easy_setopt(hnd, CURLOPT_PROXY_SSLVERSION, (long)(CURL_SSLVERSION_TLSv1 | CURL_SSLVERSION_MAX_NONE));
+ curl_easy_setopt(hnd, CURLOPT_SSLVERSION, (long)(CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_3));
+ curl_easy_setopt(hnd, CURLOPT_PROXY_SSLVERSION, (long)CURL_SSLVERSION_TLSv1);
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
/* Here is a list of options the curl code used that cannot get generated
log = logging.getLogger(__name__)
+class TLSDefs:
+ TLS_VERSIONS = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']
+ TLS_VERSION_IDS = {
+ 'TLSv1': 0x301,
+ 'TLSv1.1': 0x302,
+ 'TLSv1.2': 0x303,
+ 'TLSv1.3': 0x304
+ }
+ CURL_ARG_MIN_VERSION_ID = {
+ 'none': 0x0,
+ 'tlsv1': 0x301,
+ 'tlsv1.0': 0x301,
+ 'tlsv1.1': 0x302,
+ 'tlsv1.2': 0x303,
+ 'tlsv1.3': 0x304,
+ }
+ CURL_ARG_MAX_VERSION_ID = {
+ 'none': 0x0,
+ '1.0': 0x301,
+ '1.1': 0x302,
+ '1.2': 0x303,
+ '1.3': 0x304,
+ }
+
+
class TestSSLUse:
@pytest.fixture(autouse=True, scope='class')
@staticmethod
def gen_test_17_09_list():
- return [[tls_proto, max_ver, min_ver]
- for tls_proto in ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']
- for max_ver in range(5)
- for min_ver in range(-2, 4)]
+ return [
+ [server_tls, min_arg, max_arg]
+ for server_tls in TLSDefs.TLS_VERSIONS
+ for min_arg in TLSDefs.CURL_ARG_MIN_VERSION_ID
+ for max_arg in TLSDefs.CURL_ARG_MAX_VERSION_ID
+ ]
- @pytest.mark.parametrize("tls_proto, max_ver, min_ver", gen_test_17_09_list())
- def test_17_09_ssl_min_max(self, env: Env, httpd, configures_httpd, tls_proto, max_ver, min_ver):
+ @pytest.mark.parametrize("server_tls, min_arg, max_arg", gen_test_17_09_list())
+ def test_17_09_ssl_min_max(self, env: Env, httpd, configures_httpd, server_tls, min_arg, max_arg):
+ # We test if curl using min/max versions arguments (and defaults) can connect
+ # to a server using 'server_tls' version only
httpd.set_extra_config('base', [
- f'SSLProtocol {tls_proto}',
+ f'SSLProtocol {server_tls}',
'SSLCipherSuite ALL:@SECLEVEL=0',
])
httpd.reload_if_config_changed()
+ # curl's TLS backend supported version
+ if env.curl_uses_lib('gnutls') or \
+ env.curl_uses_lib('quiche') or \
+ env.curl_uses_lib('aws-lc'):
+ curl_supported = [0x301, 0x302, 0x303, 0x304]
+ elif env.curl_uses_lib('openssl') and \
+ env.curl_lib_version_before('openssl', '3.0.0'):
+ curl_supported = [0x301, 0x302, 0x303, 0x304]
+ else: # most SSL backends dropped support for TLSv1.0, TLSv1.1
+ curl_supported = [0x303, 0x304]
+
+ extra_args = ['--trace-config', 'ssl']
+
+ # determine effective min/max version used by curl with these args
+ if max_arg != 'none':
+ extra_args.extend(['--tls-max', max_arg])
+ curl_max_ver = TLSDefs.CURL_ARG_MAX_VERSION_ID[max_arg]
+ else:
+ curl_max_ver = max(TLSDefs.TLS_VERSION_IDS.values())
+ if min_arg != 'none':
+ extra_args.append(f'--{min_arg}')
+ curl_min_ver = TLSDefs.CURL_ARG_MIN_VERSION_ID[min_arg]
+ else:
+ curl_min_ver = min(0x303, curl_max_ver) # TLSv1.2 is the default now
+
+ # collect all versions that curl is allowed with this command lines and supports
+ curl_allowed = [tid for tid in sorted(TLSDefs.TLS_VERSION_IDS.values())
+ if curl_min_ver <= tid <= curl_max_ver and
+ tid in curl_supported]
+ # we expect a successful transfer, when the server TLS version is allowed
+ server_ver = TLSDefs.TLS_VERSION_IDS[server_tls]
+ # do the transfer
proto = 'http/1.1'
run_env = os.environ.copy()
if env.curl_uses_lib('gnutls'):
run_env['GNUTLS_SYSTEM_PRIORITY_FILE'] = our_config
curl = CurlClient(env=env, run_env=run_env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/sslinfo'
- # SSL backend specifics
- if env.curl_uses_lib('gnutls'):
- supported = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']
- elif env.curl_uses_lib('quiche'):
- supported = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']
- elif env.curl_uses_lib('aws-lc'):
- supported = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']
- elif env.curl_uses_lib('openssl') and \
- env.curl_lib_version_before('openssl', '3.0.0'):
- supported = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']
- else: # most SSL backends dropped support for TLSv1.0, TLSv1.1
- supported = [None, None, 'TLSv1.2', 'TLSv1.3']
- # test
- extra_args = [[], ['--tlsv1'], ['--tlsv1.0'], ['--tlsv1.1'], ['--tlsv1.2'], ['--tlsv1.3']][min_ver+2] + \
- [['--tls-max', '1.0'], ['--tls-max', '1.1'], ['--tls-max', '1.2'], ['--tls-max', '1.3'], []][max_ver]
- extra_args.extend(['--trace-config', 'ssl'])
r = curl.http_get(url=url, alpn_proto=proto, extra_args=extra_args)
- if max_ver >= min_ver and tls_proto in supported[max(0, min_ver):min(max_ver, 3)+1]:
- assert r.exit_code == 0, f'extra_args={extra_args}\n{r.dump_logs()}'
+
+ if server_ver in curl_allowed:
+ assert r.exit_code == 0, f'should succeed, server={server_ver:04x}, curl=[{curl_min_ver:04x}, {curl_max_ver:04x}], allowed={curl_allowed}\n{r.dump_logs()}'
assert r.json['HTTPS'] == 'on', r.dump_logs()
- assert r.json['SSL_PROTOCOL'] == tls_proto, r.dump_logs()
+ assert r.json['SSL_PROTOCOL'] == server_tls, r.dump_logs()
else:
- assert r.exit_code != 0, f'extra_args={extra_args}\n{r.dump_logs()}'
+ assert r.exit_code != 0, f'should fail, server={server_ver:04x}, curl=[{curl_min_ver:04x}, {curl_max_ver:04x}]\n{r.dump_logs()}'
def test_17_10_h3_session_reuse(self, env: Env, httpd, nghttpx):
if not env.have_h3():