From 8cee4c92389a9cd024c438b65fa266a5d0e1456e Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 10 Apr 2024 13:30:16 +0200 Subject: [PATCH] gnutls: lazy init the trust settings - 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 | 6 ++ lib/vtls/gtls.c | 179 ++++++++++++++++++++++++++---------------- lib/vtls/gtls.h | 19 +++-- 3 files changed, 130 insertions(+), 74 deletions(-) diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index 191ac22cec..6b968200b3 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -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 diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 743c774f7a..bd9c6239d0 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -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(>ls->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(>ls->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(>ls->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(>ls->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; } diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h index 766cd40ac8..f8388b37b8 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -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; -- 2.47.3