]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
mbedTLS: fix handling of TLSv1.3 sessions
authorStefan Eissing <stefan@eissing.org>
Thu, 10 Oct 2024 10:47:41 +0000 (12:47 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 13 Oct 2024 21:13:04 +0000 (23:13 +0200)
For TLSv1.3, if supported, observer special return code to retrieve
newly arrived session from mbedTLS.

Adjust test expectations now that TLSv1.3 session resumption works in
mbedTLS >= 3.6.0.

Based on #14135 by @ad-chaos
Closes #15245

lib/vtls/mbedtls.c
tests/http/test_17_ssl_use.py

index e89312ed28c03acbd14d73a513e42916100e4a9e..c6a684d609dc412ae6aa9d82836b2bbf238d2d55 100644 (file)
@@ -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) {
index 5d26d22f3034207b6502541e480da358f46fc172..125b6387e7f1d481e923f224b9056acf7debf779 100644 (file)
@@ -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')