]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
gnutls: lazy init the trust settings
authorStefan Eissing <stefan@eissing.org>
Wed, 10 Apr 2024 11:30:16 +0000 (13:30 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 11 Apr 2024 06:59:25 +0000 (08:59 +0200)
- delay loading of trust anchors and CRLs after the ClientHello
  has been sent off
- add tracing to IO operations
- on IO errors, return the CURLcode of the underlying filter

Closes #13339

lib/vquic/vquic-tls.c
lib/vtls/gtls.c
lib/vtls/gtls.h

index 191ac22cec2fb152429d82fa3c723df1885d12ab..6b968200b369241977d0021515c2936ee4d728fb 100644 (file)
@@ -286,6 +286,12 @@ CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx,
       return result;
     ctx->ossl.x509_store_setup = TRUE;
   }
+#elif defined(USE_GNUTLS)
+  if(!ctx->gtls.trust_setup) {
+    CURLcode result = Curl_gtls_client_trust_setup(cf, data, &ctx->gtls);
+    if(result)
+      return result;
+  }
 #else
   (void)ctx; (void)cf; (void)data;
 #endif
index 743c774f7aa0cea95b7b461386fe1f4664149d65..bd9c6239d02b606a76ef93a926fc85d05ec1ecd2 100644 (file)
@@ -95,15 +95,18 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen)
 {
   struct Curl_cfilter *cf = s;
   struct ssl_connect_data *connssl = cf->ctx;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten;
   CURLcode result;
 
   DEBUGASSERT(data);
   nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+  CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %zd, err=%d",
+              blen, nwritten, result);
+  backend->gtls.io_result = result;
   if(nwritten < 0) {
-    struct gtls_ssl_backend_data *backend =
-      (struct gtls_ssl_backend_data *)connssl->backend;
     gnutls_transport_set_errno(backend->gtls.session,
                                (CURLE_AGAIN == result)? EAGAIN : EINVAL);
     nwritten = -1;
@@ -115,15 +118,27 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
 {
   struct Curl_cfilter *cf = s;
   struct ssl_connect_data *connssl = cf->ctx;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
   CURLcode result;
 
   DEBUGASSERT(data);
+  if(!backend->gtls.trust_setup) {
+    result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
+    if(result) {
+      gnutls_transport_set_errno(backend->gtls.session, EINVAL);
+      backend->gtls.io_result = result;
+      return -1;
+    }
+  }
+
   nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
+  CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %zd, err=%d",
+              blen, nread, result);
+  backend->gtls.io_result = result;
   if(nread < 0) {
-    struct gtls_ssl_backend_data *backend =
-      (struct gtls_ssl_backend_data *)connssl->backend;
     gnutls_transport_set_errno(backend->gtls.session,
                                (CURLE_AGAIN == result)? EAGAIN : EINVAL);
     nread = -1;
@@ -279,8 +294,17 @@ static CURLcode handshake(struct Curl_cfilter *cf,
       /* socket is readable or writable */
     }
 
+    backend->gtls.io_result = CURLE_OK;
     rc = gnutls_handshake(session);
 
+    if(!backend->gtls.trust_setup) {
+      /* After having send off the ClientHello, we prepare the trust
+       * store to verify the coming certificate from the server */
+      CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
+      if(result)
+        return result;
+    }
+
     if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
       connssl->connecting_state =
         gnutls_record_get_direction(session)?
@@ -301,6 +325,9 @@ static CURLcode handshake(struct Curl_cfilter *cf,
       infof(data, "gnutls_handshake() warning: %s", strerr);
       continue;
     }
+    else if((rc < 0) && backend->gtls.io_result) {
+      return backend->gtls.io_result;
+    }
     else if(rc < 0) {
       const char *strerr = NULL;
 
@@ -423,62 +450,15 @@ set_ssl_version_min_max(struct Curl_easy *data,
   return CURLE_SSL_CONNECT_ERROR;
 }
 
-static CURLcode gtls_client_init(struct Curl_easy *data,
-                                 struct ssl_primary_config *config,
-                                 struct ssl_config_data *ssl_config,
-                                 struct ssl_peer *peer,
-                                 struct gtls_ctx *gtls,
-                                 long *pverifyresult)
+CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data,
+                                      struct gtls_ctx *gtls)
 {
-  unsigned int init_flags;
+  struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf);
+  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   int rc;
-  bool sni = TRUE; /* default is SNI enabled */
-  const char *prioritylist;
-  const char *err = NULL;
-  const char *tls13support;
-  CURLcode result;
-
-  if(!gtls_inited)
-    gtls_init();
-
-  *pverifyresult = 0;
-
-  if(config->version == CURL_SSLVERSION_SSLv2) {
-    failf(data, "GnuTLS does not support SSLv2");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-  else if(config->version == CURL_SSLVERSION_SSLv3)
-    sni = FALSE; /* SSLv3 has no SNI */
-
-  /* allocate a cred struct */
-  rc = gnutls_certificate_allocate_credentials(&gtls->cred);
-  if(rc != GNUTLS_E_SUCCESS) {
-    failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-#ifdef USE_GNUTLS_SRP
-  if(config->username && Curl_auth_allowed_to_host(data)) {
-    infof(data, "Using TLS-SRP username: %s", config->username);
-
-    rc = gnutls_srp_allocate_client_credentials(&gtls->srp_client_cred);
-    if(rc != GNUTLS_E_SUCCESS) {
-      failf(data, "gnutls_srp_allocate_client_cred() failed: %s",
-            gnutls_strerror(rc));
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    rc = gnutls_srp_set_client_credentials(gtls->srp_client_cred,
-                                           config->username,
-                                           config->password);
-    if(rc != GNUTLS_E_SUCCESS) {
-      failf(data, "gnutls_srp_set_client_cred() failed: %s",
-            gnutls_strerror(rc));
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    }
-  }
-#endif
 
+  CURL_TRC_CF(data, cf, "setup trust anchors and CRLs");
   if(config->verifypeer) {
     bool imported_native_ca = false;
 
@@ -507,7 +487,7 @@ static CURLcode gtls_client_init(struct Curl_easy *data,
               config->CAfile, gnutls_strerror(rc),
               (imported_native_ca ? ", continuing anyway" : ""));
         if(!imported_native_ca) {
-          *pverifyresult = rc;
+          ssl_config->certverifyresult = rc;
           return CURLE_SSL_CACERT_BADFILE;
         }
       }
@@ -525,7 +505,7 @@ static CURLcode gtls_client_init(struct Curl_easy *data,
               config->CApath, gnutls_strerror(rc),
               (imported_native_ca ? ", continuing anyway" : ""));
         if(!imported_native_ca) {
-          *pverifyresult = rc;
+          ssl_config->certverifyresult = rc;
           return CURLE_SSL_CACERT_BADFILE;
         }
       }
@@ -548,6 +528,66 @@ static CURLcode gtls_client_init(struct Curl_easy *data,
       infof(data, "found %d CRL in %s", rc, config->CRLfile);
   }
 
+  gtls->trust_setup = TRUE;
+  return CURLE_OK;
+}
+
+static CURLcode gtls_client_init(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct ssl_peer *peer,
+                                 struct gtls_ctx *gtls)
+{
+  struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf);
+  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+  unsigned int init_flags;
+  int rc;
+  bool sni = TRUE; /* default is SNI enabled */
+  const char *prioritylist;
+  const char *err = NULL;
+  const char *tls13support;
+  CURLcode result;
+
+  if(!gtls_inited)
+    gtls_init();
+
+  if(config->version == CURL_SSLVERSION_SSLv2) {
+    failf(data, "GnuTLS does not support SSLv2");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+  else if(config->version == CURL_SSLVERSION_SSLv3)
+    sni = FALSE; /* SSLv3 has no SNI */
+
+  /* allocate a cred struct */
+  rc = gnutls_certificate_allocate_credentials(&gtls->cred);
+  if(rc != GNUTLS_E_SUCCESS) {
+    failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+#ifdef USE_GNUTLS_SRP
+  if(config->username && Curl_auth_allowed_to_host(data)) {
+    infof(data, "Using TLS-SRP username: %s", config->username);
+
+    rc = gnutls_srp_allocate_client_credentials(&gtls->srp_client_cred);
+    if(rc != GNUTLS_E_SUCCESS) {
+      failf(data, "gnutls_srp_allocate_client_cred() failed: %s",
+            gnutls_strerror(rc));
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    rc = gnutls_srp_set_client_credentials(gtls->srp_client_cred,
+                                           config->username,
+                                           config->password);
+    if(rc != GNUTLS_E_SUCCESS) {
+      failf(data, "gnutls_srp_set_client_cred() failed: %s",
+            gnutls_strerror(rc));
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
+  }
+#endif
+
+  ssl_config->certverifyresult = 0;
+
   /* Initialize TLS session as a client */
   init_flags = GNUTLS_CLIENT;
 
@@ -634,6 +674,11 @@ static CURLcode gtls_client_init(struct Curl_easy *data,
   }
 
   if(config->clientcert) {
+    if(!gtls->trust_setup) {
+      result = Curl_gtls_client_trust_setup(cf, data, gtls);
+      if(result)
+        return result;
+    }
     if(ssl_config->key_passwd) {
       const unsigned int supported_key_encryption_algorithms =
         GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR |
@@ -725,14 +770,11 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
                             void *ssl_user_data)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  long * const pverifyresult = &ssl_config->certverifyresult;
   CURLcode result;
 
   DEBUGASSERT(gctx);
 
-  result = gtls_client_init(data, conn_config, ssl_config, peer,
-                            gctx, pverifyresult);
+  result = gtls_client_init(cf, data, peer, gctx);
   if(result)
     return result;
 
@@ -1515,12 +1557,13 @@ static ssize_t gtls_send(struct Curl_cfilter *cf,
 
   (void)data;
   DEBUGASSERT(backend);
+  backend->gtls.io_result = CURLE_OK;
   rc = gnutls_record_send(backend->gtls.session, mem, len);
 
   if(rc < 0) {
-    *curlcode = (rc == GNUTLS_E_AGAIN)
-      ? CURLE_AGAIN
-      : CURLE_SEND_ERROR;
+    *curlcode = (rc == GNUTLS_E_AGAIN)?
+      CURLE_AGAIN :
+      (backend->gtls.io_result? backend->gtls.io_result : CURLE_SEND_ERROR);
 
     rc = -1;
   }
@@ -1656,6 +1699,7 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
   (void)data;
   DEBUGASSERT(backend);
 
+  backend->gtls.io_result = CURLE_OK;
   ret = gnutls_record_recv(backend->gtls.session, buf, buffersize);
   if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
     *curlcode = CURLE_AGAIN;
@@ -1680,7 +1724,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
     failf(data, "GnuTLS recv error (%d): %s",
 
           (int)ret, gnutls_strerror((int)ret));
-    *curlcode = CURLE_RECV_ERROR;
+    *curlcode = backend->gtls.io_result?
+                backend->gtls.io_result : CURLE_RECV_ERROR;
     ret = -1;
     goto out;
   }
index 766cd40ac884c6c73b796436d4cd240cb88c9447..f8388b37b8ef76130ea13409885cbcc4db2bb6d1 100644 (file)
@@ -51,6 +51,8 @@ struct gtls_ctx {
 #ifdef USE_GNUTLS_SRP
   gnutls_srp_client_credentials_t srp_client_cred;
 #endif
+  CURLcode io_result; /* result of last IO cfilter operation */
+  BIT(trust_setup); /* x509 anchors + CRLs have been set up */
 };
 
 typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
@@ -66,13 +68,16 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
                             void *cb_user_data,
                             void *ssl_user_data);
 
-CURLcode
-Curl_gtls_verifyserver(struct Curl_easy *data,
-                       gnutls_session_t session,
-                       struct ssl_primary_config *config,
-                       struct ssl_config_data *ssl_config,
-                       struct ssl_peer *peer,
-                       const char *pinned_key);
+CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data,
+                                      struct gtls_ctx *gtls);
+
+CURLcode Curl_gtls_verifyserver(struct Curl_easy *data,
+                                gnutls_session_t session,
+                                struct ssl_primary_config *config,
+                                struct ssl_config_data *ssl_config,
+                                struct ssl_peer *peer,
+                                const char *pinned_key);
 
 extern const struct Curl_ssl Curl_ssl_gnutls;