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
This commit is contained in:
Daniel Stenberg 2024-06-01 12:24:42 +02:00
parent 6da320357f
commit 80aa519545
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
11 changed files with 374 additions and 155 deletions

View file

@ -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

View file

@ -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 */

View file

@ -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);

View file

@ -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;

View file

@ -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
};

View file

@ -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),

View file

@ -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),

View file

@ -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;

View file

@ -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 "

View file

@ -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 */

View file

@ -26,8 +26,25 @@
#include "curl_setup.h"
#ifdef USE_WOLFSSL
#include <wolfssl/version.h>
#include <wolfssl/options.h>
#include <wolfssl/ssl.h>
#include <wolfssl/error-ssl.h>
#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 */