diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index e89312ed28..c6a684d609 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -118,6 +118,10 @@ struct mbed_ssl_backend_data { #define TLS13_SUPPORT #endif +#if defined(TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#define HAS_SESSION_TICKETS +#endif + #if defined(THREADING_SUPPORT) static mbedtls_entropy_context ts_entropy; @@ -291,7 +295,8 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, break; #endif default: - failf(data, "mbedTLS: unsupported minimum TLS version value"); + failf(data, "mbedTLS: unsupported minimum TLS version value: %x", + conn_config->version); return CURLE_SSL_CONNECT_ERROR; } @@ -807,6 +812,12 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } +#ifdef MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED + /* New in mbedTLS 3.6.1, need to enable, default is now disabled */ + mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&backend->config, + MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED); +#endif + /* Always let mbedTLS verify certificates, if verifypeer or verifyhost are * disabled we clear the corresponding error flags in the verify callback * function. That is also where we log verification errors. */ @@ -1113,17 +1124,15 @@ static void mbedtls_session_free(void *sessionid, size_t idsize) } static CURLcode -mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) +mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data) { - CURLcode retcode = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_OK; - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); - if(ssl_config->primary.cache_session) { int ret; mbedtls_ssl_session *our_ssl_sessionid; @@ -1145,17 +1154,12 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) /* If there is already a matching session in the cache, delete it */ Curl_ssl_sessionid_lock(data); - retcode = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, - our_ssl_sessionid, 0, - mbedtls_session_free); + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, + our_ssl_sessionid, 0, + mbedtls_session_free); Curl_ssl_sessionid_unlock(data); - if(retcode) - return retcode; } - - connssl->connecting_state = ssl_connect_done; - - return CURLE_OK; + return result; } static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1314,7 +1318,6 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; int ret = -1; - ssize_t len = -1; (void)data; DEBUGASSERT(backend); @@ -1324,24 +1327,31 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(ret <= 0) { CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", buffersize, -ret); - if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) - return 0; - *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_READ) -#ifdef TLS13_SUPPORT - || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + switch(ret) { +#ifdef HAS_SESSION_TICKETS + case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: + mbed_new_session(cf, data); + FALLTHROUGH(); #endif - ) ? CURLE_AGAIN : CURLE_RECV_ERROR; - if(*curlcode != CURLE_AGAIN) { + case MBEDTLS_ERR_SSL_WANT_READ: + *curlcode = CURLE_AGAIN; + ret = -1; + break; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + *curlcode = CURLE_OK; + ret = 0; + break; + default: { char errorbuf[128]; mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "ssl_read returned: (-0x%04X) %s", -ret, errorbuf); + *curlcode = CURLE_RECV_ERROR; + ret = -1; + break; + } } - return -1; } - - len = ret; - - return len; + return (ssize_t)ret; } static size_t mbedtls_version(char *buffer, size_t size) @@ -1486,9 +1496,22 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, } /* repeat step2 until all transactions are done. */ if(ssl_connect_3 == connssl->connecting_state) { - retcode = mbed_connect_step3(cf, data); - if(retcode) - return retcode; + /* For tls1.3 we get notified about new sessions */ +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + struct ssl_connect_data *ctx = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)ctx->backend; + + if(mbedtls_ssl_get_version_number(&backend->ssl) <= + MBEDTLS_SSL_VERSION_TLS1_2) { +#else + { /* no TLSv1.3 supported here */ +#endif + retcode = mbed_new_session(cf, data); + if(retcode) + return retcode; + } + connssl->connecting_state = ssl_connect_done; } if(ssl_connect_done == connssl->connecting_state) { diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index 5d26d22f30..125b6387e7 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -71,10 +71,13 @@ class TestSSLUse: exp_resumed = 'Initial' # Rustls does not support sessions, TODO if env.curl_uses_lib('bearssl') and tls_max == '1.3': pytest.skip('BearSSL does not support TLSv1.3') - if env.curl_uses_lib('mbedtls') and tls_max == '1.3': + if env.curl_uses_lib('mbedtls') and tls_max == '1.3' and \ + not env.curl_lib_version_at_least('mbedtls', '3.6.0'): pytest.skip('mbedtls TLSv1.3 session resume not working in 3.6.0') - curl = CurlClient(env=env) + run_env = os.environ.copy() + run_env['CURL_DEBUG'] = 'ssl' + curl = CurlClient(env=env, run_env=run_env) # tell the server to close the connection after each request urln = f'https://{env.authority_for(env.domain1, proto)}/curltest/sslinfo?'\ f'id=[0-{count-1}]&close' @@ -91,9 +94,9 @@ class TestSSLUse: djson = json.load(f) assert djson['HTTPS'] == 'on', f'{i}: {djson}' if i == 0: - assert djson['SSL_SESSION_RESUMED'] == 'Initial', f'{i}: {djson}' + assert djson['SSL_SESSION_RESUMED'] == 'Initial', f'{i}: {djson}\n{r.dump_logs()}' else: - assert djson['SSL_SESSION_RESUMED'] == exp_resumed, f'{i}: {djson}' + assert djson['SSL_SESSION_RESUMED'] == exp_resumed, f'{i}: {djson}\n{r.dump_logs()}' # use host name with trailing dot, verify handshake @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) @@ -220,6 +223,9 @@ class TestSSLUse: if tls_proto == 'TLSv1.3': pytest.skip('BearSSL does not support TLSv1.3') tls_proto = 'TLSv1.2' + elif env.curl_uses_lib('mbedtls') and not env.curl_lib_version_at_least('mbedtls', '3.6.0'): + if tls_proto == 'TLSv1.3': + pytest.skip('mbedTLS < 3.6.0 does not support TLSv1.3') elif env.curl_uses_lib('sectransp'): # not in CI, so untested if tls_proto == 'TLSv1.3': pytest.skip('Secure Transport does not support TLSv1.3')