]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
openssl+sectrust: fix session reuse
authorStefan Eissing <stefan@eissing.org>
Wed, 1 Jul 2026 08:54:21 +0000 (10:54 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 1 Jul 2026 21:11:10 +0000 (23:11 +0200)
When TLS is verified via Apple SecTrust, openssl internal state is a
verification faiure (that is how it works, we use sectrust when openssl
fails to verify and natice ca store is enabled).

OpenSSL stores this verification status inside its TLS session objects.
On reuse, we see an unverified session and do not reuse it for a
verified connect attempt. While this is a performance penalty for most
connections, it *fails* on ftps:// transfers where servers expect
session reuse on DATA connections.

Fix this by remembering that TLS and a session was verified by sectrust
and allow reuse of such sessions for new connect attempts that also use
sectrust.

Closes #22235

lib/vquic/cf-ngtcp2-cmn.c
lib/vtls/openssl.c
lib/vtls/openssl.h
lib/vtls/vtls_scache.h
lib/vtls/vtls_spack.c

index 419c52009acdcce4c1e06e52981d6d34930796f5..a86960c91cbf91ca9ea95e8b5ff0839ff67a9555 100644 (file)
@@ -649,8 +649,8 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
       quic_tp_len = (size_t)tplen;
     }
 #endif
-    Curl_ossl_add_session(cf, data, ctx->ssl_peer.scache_key, ssl_sessionid,
-                          SSL_version(ssl), "h3", quic_tp, quic_tp_len);
+    Curl_ossl_add_session(cf, data, &ctx->tls.ossl, ctx->ssl_peer.scache_key,
+                          ssl_sessionid, "h3", quic_tp, quic_tp_len);
   }
   return 0;
 }
index ed401fe0d86351262accfe7b255281b2c6eb2045..cd8d43523a5c78c171c0ff71fb8f57587b80507c 100644 (file)
@@ -2632,9 +2632,9 @@ static CURLcode ossl_set_ssl_version_min_max(struct Curl_cfilter *cf,
 
 CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
                                struct Curl_easy *data,
+                               struct ossl_ctx *octx,
                                const char *ssl_peer_key,
                                SSL_SESSION *session,
-                               int ietf_tls_id,
                                const char *alpn,
                                unsigned char *quic_tp,
                                size_t quic_tp_len)
@@ -2651,6 +2651,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
     size_t der_session_size;
     unsigned char *der_session_ptr;
     size_t earlydata_max = 0;
+    int ietf_tls_id = SSL_version(octx->ssl);
 
     der_session_size = i2d_SSL_SESSION(session, NULL);
     if(der_session_size == 0) {
@@ -2688,6 +2689,9 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
                                       earlydata_max, qtp_clone, quic_tp_len,
                                       &sc_session);
     der_session_buf = NULL;  /* took ownership of sdata */
+#ifdef USE_APPLE_SECTRUST
+    sc_session->sectrust_verified = octx->sectrust_verified;
+#endif
     if(!result) {
       result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
       /* took ownership of `sc_session` */
@@ -2708,8 +2712,9 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
   if(cf) {
     struct Curl_easy *data = CF_DATA_CURRENT(cf);
     struct ssl_connect_data *connssl = cf->ctx;
-    Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid,
-                          SSL_version(ssl), connssl->negotiated.alpn, NULL, 0);
+    struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+    Curl_ossl_add_session(cf, data, octx, connssl->peer.scache_key,
+                          ssl_sessionid, connssl->negotiated.alpn, NULL, 0);
   }
   return 0;
 }
@@ -3353,7 +3358,12 @@ static CURLcode ossl_init_session_and_alpns(
         }
         else {
           if(conn_cfg->verifypeer &&
-             (SSL_get_verify_result(octx->ssl) != X509_V_OK)) {
+             (SSL_get_verify_result(octx->ssl) != X509_V_OK)
+#ifdef USE_APPLE_SECTRUST
+             /* if sectrust is used and verified the session before */
+             && (!ssl_config->native_ca_store || !scs->sectrust_verified)
+#endif
+            ) {
             /* Session was from unverified connection, cannot reuse here */
             SSL_set_session(octx->ssl, NULL);
             infof(data, "SSL session not peer verified, not reusing");
@@ -3362,6 +3372,9 @@ static CURLcode ossl_init_session_and_alpns(
             infof(data, "SSL reusing session with ALPN '%s'",
                   scs->alpn ? scs->alpn : "-");
             octx->reused_session = TRUE;
+#ifdef USE_APPLE_SECTRUST
+            octx->sectrust_session = scs->sectrust_verified;
+#endif
             infof(data, "SSL verify result: %lx",
                   (unsigned long)SSL_get_verify_result(octx->ssl));
 #ifdef HAVE_OPENSSL_EARLYDATA
@@ -4671,13 +4684,13 @@ static CURLcode ossl_chain_get_der(struct Curl_cfilter *cf,
 static CURLcode ossl_apple_verify(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
                                   struct ossl_ctx *octx,
-                                  struct ssl_peer *peer,
-                                  bool *pverified)
+                                  struct ssl_peer *peer)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ossl_certs_ctx chain;
   CURLcode result;
 
+  octx->sectrust_verified = FALSE;
   memset(&chain, 0, sizeof(chain));
   chain.sk = SSL_get_peer_cert_chain(octx->ssl);
   chain.num_certs = chain.sk ? sk_X509_num(chain.sk) : 0;
@@ -4689,8 +4702,11 @@ static CURLcode ossl_apple_verify(struct Curl_cfilter *cf,
       result = CURLE_PEER_FAILED_VERIFICATION;
     }
     else {
-      /* when session was reused, there is no peer cert chain */
-      *pverified = FALSE;
+      /* When session was reused, there is no peer cert chain.
+       * We trust it if it came from a SecTrust verified TLS. */
+      CURL_TRC_CF(data, cf, "session reused, sectrust_session=%d",
+                  octx->sectrust_session);
+      octx->sectrust_verified = (bool)octx->sectrust_session;
       return CURLE_OK;
     }
   }
@@ -4717,12 +4733,12 @@ static CURLcode ossl_apple_verify(struct Curl_cfilter *cf,
     if(!result && ocsp_missing && conn_config->verifystatus &&
        !octx->reused_session) {
       /* verified, but OCSP stapling is required and server sent none */
-      *pverified = TRUE;
+      octx->sectrust_verified = TRUE;
       failf(data, "No OCSP response received");
       return CURLE_SSL_INVALIDCERTSTATUS;
     }
   }
-  *pverified = !result;
+  octx->sectrust_verified = !result;
   return result;
 }
 #endif /* USE_APPLE_SECTRUST */
@@ -4739,9 +4755,6 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf,
   long ossl_verify;
   X509 *server_cert;
   bool verified = FALSE;
-#if !defined(OPENSSL_NO_OCSP) && defined(USE_APPLE_SECTRUST)
-  bool sectrust_verified = FALSE;
-#endif
 
   if(data->set.ssl.certinfo && !octx->reused_session) {
     /* asked to gather certificate info. Reused sessions do not have cert
@@ -4789,15 +4802,13 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf,
     /* we verify using Apple SecTrust *unless* OpenSSL already verified.
      * This may happen if the application intercepted the OpenSSL callback
      * and installed its own. */
-    result = ossl_apple_verify(cf, data, octx, peer, &verified);
+    result = ossl_apple_verify(cf, data, octx, peer);
     if(result && (result != CURLE_PEER_FAILED_VERIFICATION))
       goto out; /* unexpected error */
-    if(verified) {
+    if(octx->sectrust_verified) {
       infof(data, "SSL certificate verified via Apple SecTrust.");
       ssl_config->certverifyresult = X509_V_OK;
-#ifndef OPENSSL_NO_OCSP
-      sectrust_verified = TRUE;
-#endif
+      verified = TRUE;
     }
   }
 #endif
@@ -4816,9 +4827,9 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf,
 #ifndef OPENSSL_NO_OCSP
   if(conn_config->verifystatus &&
 #ifdef USE_APPLE_SECTRUST
-     !sectrust_verified && /* already verified via apple sectrust, cannot
-                            * verifystate via OpenSSL in that case as it
-                            * does not have the trust anchors */
+     !octx->sectrust_verified && /* already verified via sectrust, cannot
+                                  * verifystate via OpenSSL in that case as it
+                                  * does not have the trust anchors */
 #endif
      !octx->reused_session) {
     /* do not do this after Session ID reuse */
index 53ab60528f4b2fa428906004b3fdee74abef6202..1daeef50dfbe7a3f9685b1e2386de76485bde37b 100644 (file)
@@ -129,6 +129,10 @@ struct ossl_ctx {
   BIT(x509_store_setup);            /* x509 store has been set up */
   BIT(store_is_empty);              /* no certs/paths/blobs in x509 store */
   BIT(reused_session);              /* session-ID was reused for this */
+#ifdef USE_APPLE_SECTRUST
+  BIT(sectrust_verified);           /* peer was verified by sectrust */
+  BIT(sectrust_session);            /* session from sectrust verified peer */
+#endif
 };
 
 size_t Curl_ossl_version(char *buffer, size_t size);
@@ -182,9 +186,9 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf,
  */
 CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
                                struct Curl_easy *data,
+                               struct ossl_ctx *octx,
                                const char *ssl_peer_key,
                                SSL_SESSION *session,
-                               int ietf_tls_id,
                                const char *alpn,
                                unsigned char *quic_tp,
                                size_t quic_tp_len);
index effb1d8f96af5c2c6177c65012695383e69d903e..fc4eb9ef51eb1cc593ecb9f43603be9c8bbb9b3f 100644 (file)
@@ -131,6 +131,7 @@ struct Curl_ssl_session {
   const unsigned char *quic_tp; /* Optional QUIC transport param bytes */
   size_t quic_tp_len;          /* number of bytes in quic_tp */
   struct Curl_llist_node list; /*  internal storage handling */
+  BIT(sectrust_verified);      /* session comes from sectrust verified TLS */
 };
 
 /* Create a `session` instance. Does NOT need locking.
index 9cbdecc9018537435e5a94705ae8f087439e4dcf..e572e923f5424a03e19c22271f3e52bbb61913ae 100644 (file)
@@ -38,6 +38,7 @@
 #define CURL_SPACK_ALPN          0x05
 #define CURL_SPACK_EARLYDATA     0x06
 #define CURL_SPACK_QUICTP        0x07
+#define CURL_SPACK_SECTRUST      0x08
 
 static CURLcode spack_enc8(struct dynbuf *buf, uint8_t b)
 {
@@ -218,6 +219,9 @@ CURLcode Curl_ssl_session_pack(struct Curl_easy *data,
     if(!result)
       result = spack_enc32(buf, (uint32_t)s->earlydata_max);
   }
+  if(!result && s->sectrust_verified) {
+    result = spack_enc8(buf, CURL_SPACK_SECTRUST);
+  }
   if(!result && s->quic_tp && s->quic_tp_len) {
     result = spack_enc8(buf, CURL_SPACK_QUICTP);
     if(!result)
@@ -303,6 +307,9 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
         goto out;
       s->valid_until = (curl_off_t)val64;
       break;
+    case CURL_SPACK_SECTRUST:
+      s->sectrust_verified = TRUE;
+      break;
     default:  /* unknown tag */
       result = CURLE_READ_ERROR;
       goto out;