From: Daniel Stenberg Date: Sat, 1 Jun 2024 10:24:42 +0000 (+0200) Subject: wolfssl: support CA caching X-Git-Tag: curl-8_9_0~332 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=80aa5195458869aa42040bef0c7f38be6846b470;p=thirdparty%2Fcurl.git wolfssl: support CA caching As a bonus, add SSLSUPP_CA_CACHE to let TLS backends signal its support for this so that *setopt() return error if there is no support. Closes #13786 --- diff --git a/docs/libcurl/opts/CURLOPT_CA_CACHE_TIMEOUT.md b/docs/libcurl/opts/CURLOPT_CA_CACHE_TIMEOUT.md index aa479b6bec..e9ad726446 100644 --- a/docs/libcurl/opts/CURLOPT_CA_CACHE_TIMEOUT.md +++ b/docs/libcurl/opts/CURLOPT_CA_CACHE_TIMEOUT.md @@ -14,6 +14,8 @@ Protocol: - TLS TLS-backend: - OpenSSL + - Schannel + - wolfSSL --- # NAME @@ -31,16 +33,17 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_CA_CACHE_TIMEOUT, long age); # DESCRIPTION Pass a long, this sets the timeout in seconds. This tells libcurl the maximum -time any cached certificate store it has in memory may be kept and reused for -new connections. Once the timeout has expired, a subsequent fetch requiring a -certificate has to reload it. +time any cached CA certificate store it has in memory may be kept and reused +for new connections. Once the timeout has expired, a subsequent fetch +requiring a CA certificate has to reload it. -Building a certificate store from a CURLOPT_CAINFO(3) file is a slow -operation so curl may cache the generated certificate store internally to speed -up future connections. +Building a CA certificate store from a CURLOPT_CAINFO(3) file is a slow +operation so curl may cache the generated certificate store internally to +speed up future connections. -Set to zero to completely disable caching, or set to -1 to retain the cached -store remain forever. By default, libcurl caches this info for 24 hours. +Set the timeout to zero to completely disable caching, or set to -1 to retain +the cached store remain forever. By default, libcurl caches this info for 24 +hours. # DEFAULT @@ -74,8 +77,8 @@ int main(void) This option was added in curl 7.87.0. -This option is supported by OpenSSL and its forks (since 7.87.0) and Schannel -(since 8.5.0). +This option is supported by OpenSSL and its forks (since 7.87.0), Schannel +(since 8.5.0) and wolfSSL (since 8.9.0). # RETURN VALUE diff --git a/lib/setopt.c b/lib/setopt.c index e8b25454b2..4e4da969fd 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -203,13 +203,17 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.dns_cache_timeout = (int)arg; break; case CURLOPT_CA_CACHE_TIMEOUT: - arg = va_arg(param, long); - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - else if(arg > INT_MAX) - arg = INT_MAX; + if(Curl_ssl_supports(data, SSLSUPP_CA_CACHE)) { + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; - data->set.general_ssl.ca_cache_timeout = (int)arg; + data->set.general_ssl.ca_cache_timeout = (int)arg; + } + else + return CURLE_NOT_BUILT_IN; break; case CURLOPT_DNS_USE_GLOBAL_CACHE: /* deprecated */ diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 0d9d87f34a..bac69894b8 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -2120,7 +2120,7 @@ static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, return CURLE_FAILED_INIT; } #elif defined(USE_WOLFSSL) - if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->ssl_ctx) != 0) { + if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ctx) != 0) { failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); return CURLE_FAILED_INIT; } @@ -2211,7 +2211,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, #elif defined(USE_GNUTLS) ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); #else - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ssl); + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.handle); #endif ngtcp2_ccerr_default(&ctx->last_error); diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index aca18b4570..51fffbac8f 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -76,7 +76,7 @@ static void keylog_callback(const WOLFSSL *ssl, const char *line) } #endif -static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, +static CURLcode Curl_wssl_init_ctx(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, Curl_vquic_tls_ctx_setup *cb_setup, @@ -91,8 +91,8 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, goto out; } - ctx->ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); - if(!ctx->ssl_ctx) { + ctx->wssl.ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + if(!ctx->wssl.ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } @@ -103,9 +103,9 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, goto out; } - wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx); + wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx); - if(wolfSSL_CTX_set_cipher_list(ctx->ssl_ctx, conn_config->cipher_list13 ? + if(wolfSSL_CTX_set_cipher_list(ctx->wssl.ctx, conn_config->cipher_list13 ? conn_config->cipher_list13 : QUIC_CIPHERS) != 1) { char error_buffer[256]; @@ -115,7 +115,7 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, goto out; } - if(wolfSSL_CTX_set1_groups_list(ctx->ssl_ctx, conn_config->curves ? + if(wolfSSL_CTX_set1_groups_list(ctx->wssl.ctx, conn_config->curves ? conn_config->curves : (char *)QUIC_GROUPS) != 1) { failf(data, "wolfSSL failed to set curves"); @@ -127,7 +127,7 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, Curl_tls_keylog_open(); if(Curl_tls_keylog_enabled()) { #if defined(HAVE_SECRET_CALLBACK) - wolfSSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback); + wolfSSL_CTX_set_keylog_callback(ctx->wssl.ctx, keylog_callback); #else failf(data, "wolfSSL was built without keylog callback"); result = CURLE_NOT_BUILT_IN; @@ -139,12 +139,12 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, const char * const ssl_cafile = conn_config->CAfile; const char * const ssl_capath = conn_config->CApath; - wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); + wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_PEER, NULL); if(ssl_cafile || ssl_capath) { /* tell wolfSSL where to find CA certificates that are used to verify the server's certificate. */ int rc = - wolfSSL_CTX_load_verify_locations_ex(ctx->ssl_ctx, ssl_cafile, + wolfSSL_CTX_load_verify_locations_ex(ctx->wssl.ctx, ssl_cafile, ssl_capath, WOLFSSL_LOAD_FLAG_IGNORE_ERR); if(SSL_SUCCESS != rc) { @@ -163,18 +163,18 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, else { /* verifying the peer without any CA certificates won't work so use wolfssl's built-in default as fallback */ - wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx); + wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx); } #endif } else { - wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL); + wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_NONE, NULL); } /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { Curl_set_in_callback(data, true); - result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx, + result = (*data->set.ssl.fsslctx)(data, ctx->wssl.ctx, data->set.ssl.fsslctxp); Curl_set_in_callback(data, false); if(result) { @@ -185,36 +185,36 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, result = CURLE_OK; out: - if(result && ctx->ssl_ctx) { - SSL_CTX_free(ctx->ssl_ctx); - ctx->ssl_ctx = NULL; + if(result && ctx->wssl.ctx) { + SSL_CTX_free(ctx->wssl.ctx); + ctx->wssl.ctx = NULL; } return result; } /** SSL callbacks ***/ -static CURLcode curl_wssl_init_ssl(struct curl_tls_ctx *ctx, +static CURLcode Curl_wssl_init_ssl(struct curl_tls_ctx *ctx, struct Curl_easy *data, struct ssl_peer *peer, const char *alpn, size_t alpn_len, void *user_data) { (void)data; - DEBUGASSERT(!ctx->ssl); - DEBUGASSERT(ctx->ssl_ctx); - ctx->ssl = wolfSSL_new(ctx->ssl_ctx); + DEBUGASSERT(!ctx->wssl.handle); + DEBUGASSERT(ctx->wssl.ctx); + ctx->wssl.handle = wolfSSL_new(ctx->wssl.ctx); - wolfSSL_set_app_data(ctx->ssl, user_data); - wolfSSL_set_connect_state(ctx->ssl); - wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0); + wolfSSL_set_app_data(ctx->wssl.handle, user_data); + wolfSSL_set_connect_state(ctx->wssl.handle); + wolfSSL_set_quic_use_legacy_codepoint(ctx->wssl.handle, 0); if(alpn) - wolfSSL_set_alpn_protos(ctx->ssl, (const unsigned char *)alpn, + wolfSSL_set_alpn_protos(ctx->wssl.handle, (const unsigned char *)alpn, (int)alpn_len); if(peer->sni) { - wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME, + wolfSSL_UseSNI(ctx->wssl.handle, WOLFSSL_SNI_HOST_NAME, peer->sni, (unsigned short)strlen(peer->sni)); } @@ -243,11 +243,11 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, (const unsigned char *)alpn, alpn_len, cb_setup, cb_user_data, ssl_user_data); #elif defined(USE_WOLFSSL) - result = curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data); + result = Curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data); if(result) return result; - return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data); + return Curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data); #else #error "no TLS lib in used, should not happen" return CURLE_FAILED_INIT; @@ -267,10 +267,10 @@ void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx) if(ctx->gtls.session) gnutls_deinit(ctx->gtls.session); #elif defined(USE_WOLFSSL) - if(ctx->ssl) - wolfSSL_free(ctx->ssl); - if(ctx->ssl_ctx) - wolfSSL_CTX_free(ctx->ssl_ctx); + if(ctx->wssl.handle) + wolfSSL_free(ctx->wssl.handle); + if(ctx->wssl.ctx) + wolfSSL_CTX_free(ctx->wssl.ctx); #endif memset(ctx, 0, sizeof(*ctx)); } @@ -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_WOLFSSL) + if(!ctx->wssl.x509_store_setup) { + CURLcode result = Curl_wssl_setup_x509_store(cf, data, &ctx->wssl); + if(result) + return result; + } #elif defined(USE_GNUTLS) if(!ctx->gtls.trust_setup) { CURLcode result = Curl_gtls_client_trust_setup(cf, data, &ctx->gtls); @@ -325,7 +331,7 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, (void)data; if(conn_config->verifyhost) { if(peer->sni) { - WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->ssl); + WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.handle); if(wolfSSL_X509_check_host(cert, peer->sni, strlen(peer->sni), 0, NULL) == WOLFSSL_FAILURE) { result = CURLE_PEER_FAILED_VERIFICATION; diff --git a/lib/vquic/vquic-tls.h b/lib/vquic/vquic-tls.h index 1d35fd0e62..0ec74bfbae 100644 --- a/lib/vquic/vquic-tls.h +++ b/lib/vquic/vquic-tls.h @@ -31,14 +31,15 @@ #if defined(USE_HTTP3) && \ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) +#include "vtls/wolfssl.h" + struct curl_tls_ctx { #ifdef USE_OPENSSL struct ossl_ctx ossl; #elif defined(USE_GNUTLS) struct gtls_ctx gtls; #elif defined(USE_WOLFSSL) - WOLFSSL_CTX *ssl_ctx; - WOLFSSL *ssl; + struct wolfssl_ctx wssl; #endif }; diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index c9120cd37f..3618e7de15 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2849,8 +2849,8 @@ typedef long ctx_option_t; #if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */ static CURLcode ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, - struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); long ssl_version = conn_config->version; @@ -5273,6 +5273,7 @@ const struct Curl_ssl Curl_ssl_openssl = { #ifdef USE_ECH SSLSUPP_ECH | #endif + SSLSUPP_CA_CACHE | SSLSUPP_HTTPS_PROXY, sizeof(struct ossl_ctx), diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index b4fb3b7e5b..414907b16c 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -2910,6 +2910,7 @@ const struct Curl_ssl Curl_ssl_schannel = { #endif SSLSUPP_PINNEDPUBKEY | SSLSUPP_TLS13_CIPHERSUITES | + SSLSUPP_CA_CACHE | SSLSUPP_HTTPS_PROXY, sizeof(struct schannel_ssl_backend_data), diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index fcf17b8243..aa190b7809 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -453,7 +453,7 @@ static bool ssl_prefs_check(struct Curl_easy *data) } static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, - const struct alpn_spec *alpn) + const struct alpn_spec *alpn) { struct ssl_connect_data *ctx; diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index ab66779585..813bf2ada7 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -38,6 +38,7 @@ struct Curl_ssl_session; #define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */ #define SSLSUPP_CAINFO_BLOB (1<<6) #define SSLSUPP_ECH (1<<7) +#define SSLSUPP_CA_CACHE (1<<8) #define ALPN_ACCEPTED "ALPN: server accepted " diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 4acd354ffc..5c8dcd96fd 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -99,12 +99,6 @@ #undef USE_BIO_CHAIN #endif -struct wolfssl_ssl_backend_data { - WOLFSSL_CTX *ctx; - WOLFSSL *handle; - CURLcode io_result; /* result of last BIO cfilter operation */ -}; - #ifdef OPENSSL_EXTRA /* * Availability note: @@ -290,8 +284,8 @@ static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio, { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result = CURLE_OK; @@ -311,8 +305,8 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result = CURLE_OK; @@ -357,6 +351,252 @@ static void wolfssl_bio_cf_free_methods(void) #endif /* !USE_BIO_CHAIN */ +static CURLcode populate_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + X509_STORE *store, + struct wolfssl_ctx *wssl) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : conn_config->CAfile); + const char * const ssl_capath = conn_config->CApath; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + bool imported_native_ca = false; + +#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS) + /* load native CA certificates */ + if(ssl_config->native_ca_store) { + if(wolfSSL_CTX_load_system_CA_certs(wssl->ctx) != WOLFSSL_SUCCESS) { + infof(data, "error importing native CA store, continuing anyway"); + } + else { + imported_native_ca = true; + infof(data, "successfully imported native CA store"); + wssl->x509_store_setup = TRUE; + } + } +#endif /* !NO_FILESYSTEM */ + + /* load certificate blob */ + if(ca_info_blob) { + if(wolfSSL_CTX_load_verify_buffer(wssl->ctx, ca_info_blob->data, + ca_info_blob->len, + SSL_FILETYPE_PEM) != SSL_SUCCESS) { + if(imported_native_ca) { + infof(data, "error importing CA certificate blob, continuing anyway"); + } + else { + failf(data, "error importing CA certificate blob"); + return CURLE_SSL_CACERT_BADFILE; + } + } + else { + infof(data, "successfully imported CA certificate blob"); + wssl->x509_store_setup = TRUE; + } + } + +#ifndef NO_FILESYSTEM + /* load trusted cacert from file if not blob */ + + CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d", + ssl_cafile? ssl_cafile : "none", !!ca_info_blob); + if(!store) + return CURLE_OUT_OF_MEMORY; + + if((ssl_cafile || ssl_capath) && (!wssl->x509_store_setup)) { + int rc = + wolfSSL_CTX_load_verify_locations_ex(wssl->ctx, + ssl_cafile, + ssl_capath, + WOLFSSL_LOAD_FLAG_IGNORE_ERR); + if(SSL_SUCCESS != rc) { + if(conn_config->verifypeer) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + else { + /* Just continue with a warning if no strict certificate + verification is required. */ + infof(data, "error setting certificate verify locations," + " continuing anyway:"); + } + } + else { + /* Everything is fine. */ + infof(data, "successfully set certificate verify locations:"); + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + wssl->x509_store_setup = TRUE; + } +#endif + (void)store; + return CURLE_OK; +} + +/* key to use at `multi->proto_hash` */ +#define MPROTO_WSSL_X509_KEY "tls:wssl:x509:share" + +struct wssl_x509_share { + char *CAfile; /* CAfile path used to generate X509 store */ + WOLFSSL_X509_STORE *store; /* cached X509 store or NULL if none */ + struct curltime time; /* when the cached store was created */ +}; + +static void wssl_x509_share_free(void *key, size_t key_len, void *p) +{ + struct wssl_x509_share *share = p; + DEBUGASSERT(key_len == (sizeof(MPROTO_WSSL_X509_KEY)-1)); + DEBUGASSERT(!memcmp(MPROTO_WSSL_X509_KEY, key, key_len)); + (void)key; + (void)key_len; + if(share->store) { + wolfSSL_X509_STORE_free(share->store); + } + free(share->CAfile); + free(share); +} + +static bool +cached_x509_store_expired(const struct Curl_easy *data, + const struct wssl_x509_share *mb) +{ + const struct ssl_general_config *cfg = &data->set.general_ssl; + struct curltime now = Curl_now(); + timediff_t elapsed_ms = Curl_timediff(now, mb->time); + timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + + if(timeout_ms < 0) + return false; + + return elapsed_ms >= timeout_ms; +} + +static bool +cached_x509_store_different(struct Curl_cfilter *cf, + const struct wssl_x509_share *mb) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!mb->CAfile || !conn_config->CAfile) + return mb->CAfile != conn_config->CAfile; + + return strcmp(mb->CAfile, conn_config->CAfile); +} + +static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct Curl_multi *multi = data->multi; + struct wssl_x509_share *share; + WOLFSSL_X509_STORE *store = NULL; + + DEBUGASSERT(multi); + share = multi? Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_WSSL_X509_KEY, + sizeof(MPROTO_WSSL_X509_KEY)-1) : NULL; + if(share && share->store && + !cached_x509_store_expired(data, share) && + !cached_x509_store_different(cf, share)) { + store = share->store; + } + + return store; +} + +static void set_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + X509_STORE *store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi; + struct wssl_x509_share *share; + + DEBUGASSERT(multi); + if(!multi) + return; + share = Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_WSSL_X509_KEY, + sizeof(MPROTO_WSSL_X509_KEY)-1); + + if(!share) { + share = calloc(1, sizeof(*share)); + if(!share) + return; + if(!Curl_hash_add2(&multi->proto_hash, + (void *)MPROTO_WSSL_X509_KEY, + sizeof(MPROTO_WSSL_X509_KEY)-1, + share, wssl_x509_share_free)) { + free(share); + return; + } + } + + if(wolfSSL_X509_STORE_up_ref(store)) { + char *CAfile = NULL; + + if(conn_config->CAfile) { + CAfile = strdup(conn_config->CAfile); + if(!CAfile) { + X509_STORE_free(store); + return; + } + } + + if(share->store) { + X509_STORE_free(share->store); + free(share->CAfile); + } + + share->time = Curl_now(); + share->store = store; + share->CAfile = CAfile; + } +} + +CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wssl) +{ + 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); + CURLcode result = CURLE_OK; + WOLFSSL_X509_STORE *cached_store; + bool cache_criteria_met; + + /* Consider the X509 store cacheable if it comes exclusively from a CAfile, + or no source is provided and we are falling back to openssl's built-in + default. */ + cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) && + conn_config->verifypeer && + !conn_config->CApath && + !conn_config->ca_info_blob && + !ssl_config->primary.CRLfile && + !ssl_config->native_ca_store; + + cached_store = get_cached_x509_store(cf, data); + if(cached_store && cache_criteria_met + && wolfSSL_X509_STORE_up_ref(cached_store)) { + wolfSSL_CTX_set_cert_store(wssl->ctx, cached_store); + } + else { + X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx); + + result = populate_x509_store(cf, data, store, wssl); + if(result == CURLE_OK && cache_criteria_met) { + set_cached_x509_store(cf, data, store); + } + } + + return result; +} + /* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. @@ -366,15 +606,10 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { char *ciphers, *curves; struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : conn_config->CAfile); - const char * const ssl_capath = conn_config->CApath; WOLFSSL_METHOD* req_method = NULL; #ifdef HAVE_LIBOQS word16 oqsAlg = 0; @@ -386,8 +621,6 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #else #define use_sni(x) Curl_nop_stmt #endif - bool imported_native_ca = false; - bool imported_ca_info_blob = false; DEBUGASSERT(backend); @@ -524,71 +757,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } -#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS) - /* load native CA certificates */ - if(ssl_config->native_ca_store) { - if(wolfSSL_CTX_load_system_CA_certs(backend->ctx) != WOLFSSL_SUCCESS) { - infof(data, "error importing native CA store, continuing anyway"); - } - else { - imported_native_ca = true; - infof(data, "successfully imported native CA store"); - } - } -#endif /* !NO_FILESYSTEM */ - - /* load certificate blob */ - if(ca_info_blob) { - if(wolfSSL_CTX_load_verify_buffer(backend->ctx, ca_info_blob->data, - ca_info_blob->len, - SSL_FILETYPE_PEM) != SSL_SUCCESS) { - if(imported_native_ca) { - infof(data, "error importing CA certificate blob, continuing anyway"); - } - else { - failf(data, "error importing CA certificate blob"); - return CURLE_SSL_CACERT_BADFILE; - } - } - else { - imported_ca_info_blob = true; - infof(data, "successfully imported CA certificate blob"); - } - } - #ifndef NO_FILESYSTEM - /* load trusted cacert from file if not blob */ - if(ssl_cafile || ssl_capath) { - int rc = - wolfSSL_CTX_load_verify_locations_ex(backend->ctx, - ssl_cafile, - ssl_capath, - WOLFSSL_LOAD_FLAG_IGNORE_ERR); - if(SSL_SUCCESS != rc) { - if(conn_config->verifypeer && !imported_ca_info_blob && - !imported_native_ca) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return CURLE_SSL_CACERT_BADFILE; - } - else { - /* Just continue with a warning if no strict certificate - verification is required. */ - infof(data, "error setting certificate verify locations," - " continuing anyway:"); - } - } - else { - /* Everything is fine. */ - infof(data, "successfully set certificate verify locations:"); - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } - /* Load the client certificate, and private key */ if(ssl_config->primary.clientcert && ssl_config->key) { int file_type = do_file_type(ssl_config->cert_type); @@ -839,8 +1008,8 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { int ret = -1; struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); #ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? @@ -862,6 +1031,21 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } + if(!backend->x509_store_setup) { + /* After having send off the ClientHello, we prepare the x509 + * store to verify the coming certificate from the server */ + CURLcode result; + struct wolfssl_ctx wssl; + wssl.ctx = backend->ctx; + wssl.handle = backend->handle; + wssl.io_result = CURLE_OK; + wssl.x509_store_setup = FALSE; + result = Curl_wssl_setup_x509_store(cf, data, &wssl); + if(result) + return result; + backend->x509_store_setup = wssl.x509_store_setup; + } + ret = wolfSSL_connect(backend->handle); #ifdef OPENSSL_EXTRA @@ -1070,8 +1254,8 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { CURLcode result = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -1116,8 +1300,8 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; char error_buffer[WOLFSSL_MAX_ERROR_SZ]; int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; int rc; @@ -1158,8 +1342,8 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; (void) data; @@ -1187,8 +1371,8 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; char error_buffer[WOLFSSL_MAX_ERROR_SZ]; int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen; int nread; @@ -1269,12 +1453,12 @@ static bool wolfssl_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct ssl_connect_data *ctx = cf->ctx; - struct wolfssl_ssl_backend_data *backend; + struct wolfssl_ctx *backend; (void)data; DEBUGASSERT(ctx && ctx->backend); - backend = (struct wolfssl_ssl_backend_data *)ctx->backend; + backend = (struct wolfssl_ctx *)ctx->backend; if(backend->handle) /* SSL is in use */ return (0 != wolfSSL_pending(backend->handle)) ? TRUE : FALSE; else @@ -1290,13 +1474,13 @@ static int wolfssl_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *ctx = cf->ctx; - struct wolfssl_ssl_backend_data *backend; + struct wolfssl_ctx *backend; int retval = 0; (void)data; DEBUGASSERT(ctx && ctx->backend); - backend = (struct wolfssl_ssl_backend_data *)ctx->backend; + backend = (struct wolfssl_ctx *)ctx->backend; if(backend->handle) { wolfSSL_ERR_clear_error(); wolfSSL_free(backend->handle); @@ -1472,8 +1656,8 @@ static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */ static void *wolfssl_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; (void)info; DEBUGASSERT(backend); return backend->handle; @@ -1493,9 +1677,10 @@ const struct Curl_ssl Curl_ssl_wolfssl = { #ifdef USE_ECH SSLSUPP_ECH | #endif - SSLSUPP_SSL_CTX, + SSLSUPP_SSL_CTX | + SSLSUPP_CA_CACHE, - sizeof(struct wolfssl_ssl_backend_data), + sizeof(struct wolfssl_ctx), wolfssl_init, /* init */ wolfssl_cleanup, /* cleanup */ diff --git a/lib/vtls/wolfssl.h b/lib/vtls/wolfssl.h index a5ed848099..d75bdaa1e0 100644 --- a/lib/vtls/wolfssl.h +++ b/lib/vtls/wolfssl.h @@ -26,8 +26,25 @@ #include "curl_setup.h" #ifdef USE_WOLFSSL +#include +#include +#include +#include + +#include "urldata.h" extern const struct Curl_ssl Curl_ssl_wolfssl; +struct wolfssl_ctx { + WOLFSSL_CTX *ctx; + WOLFSSL *handle; + CURLcode io_result; /* result of last BIO cfilter operation */ + BIT(x509_store_setup); /* x509 store has been set up */ +}; + +CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wssl); + #endif /* USE_WOLFSSL */ #endif /* HEADER_CURL_WOLFSSL_H */