diff options
author | Maxim Yurchuk <maxim-yurchuk@ydb.tech> | 2024-10-04 17:24:16 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-04 17:24:16 +0300 |
commit | a1e4766748b5924d3879ab6a0259b28ec3c5d535 (patch) | |
tree | 4480150864228623d6c9101a4ba8c049bda9aa90 /contrib/libs/curl/lib/vtls/wolfssl.c | |
parent | 6536467764bed7822214815ce92ed4dcd5bf409b (diff) | |
parent | a46fe128b9c9c84438fc2aac337aeefdaecb99df (diff) | |
download | ydb-a1e4766748b5924d3879ab6a0259b28ec3c5d535.tar.gz |
Merge pull request #10090 from ydb-platform/mergelibs-241004-1110
Library import 241004-1110
Diffstat (limited to 'contrib/libs/curl/lib/vtls/wolfssl.c')
-rw-r--r-- | contrib/libs/curl/lib/vtls/wolfssl.c | 1189 |
1 files changed, 877 insertions, 312 deletions
diff --git a/contrib/libs/curl/lib/vtls/wolfssl.c b/contrib/libs/curl/lib/vtls/wolfssl.c index fa5f51eb26..d2a25455b8 100644 --- a/contrib/libs/curl/lib/vtls/wolfssl.c +++ b/contrib/libs/curl/lib/vtls/wolfssl.c @@ -36,6 +36,10 @@ #error #include <wolfssl/version.h> #error #include <wolfssl/options.h> +#if LIBWOLFSSL_VERSION_HEX < 0x03004006 /* wolfSSL 3.4.6 (2015) */ +#error "wolfSSL version should be at least 3.4.6" +#endif + /* To determine what functions are available we rely on one or both of: - the user's options.h generated by wolfSSL - the symbols detected by curl's configure @@ -61,7 +65,7 @@ #include "connect.h" /* for the connect timeout */ #include "select.h" #include "strcase.h" -#include "x509asn1.h" +#error #include "x509asn1.h" #include "curl_printf.h" #include "multiif.h" @@ -74,6 +78,14 @@ #include "curl_memory.h" #include "memdebug.h" +#ifdef USE_ECH +# include "curl_base64.h" +# define ECH_ENABLED(__data__) \ + (__data__->set.tls_ech && \ + !(__data__->set.tls_ech & CURLECH_DISABLE)\ + ) +#endif /* USE_ECH */ + /* KEEP_PEER_CERT is a product of the presence of build time symbol OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is in wolfSSL's settings.h, and the latter two are build time symbols in @@ -91,17 +103,11 @@ #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: * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in - * WolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that + * wolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that * option is not set, then TLS 1.3 will not be logged. * For TLS 1.2 and before, we use wolfSSL_get_keys(). * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA @@ -199,7 +205,7 @@ wolfssl_log_tls12_secret(SSL *ssl) } #endif /* OPENSSL_EXTRA */ -static int do_file_type(const char *type) +static int wolfssl_do_file_type(const char *type) { if(!type || !type[0]) return SSL_FILETYPE_PEM; @@ -210,7 +216,7 @@ static int do_file_type(const char *type) return -1; } -#ifdef HAVE_LIBOQS +#ifdef WOLFSSL_HAVE_KYBER struct group_name_map { const word16 group; const char *name; @@ -232,7 +238,6 @@ static const struct group_name_map gnm[] = { static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio) { wolfSSL_BIO_set_shutdown(bio, 1); - wolfSSL_BIO_set_init(bio, 1); wolfSSL_BIO_set_data(bio, NULL); return 1; } @@ -283,20 +288,35 @@ 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; + ssize_t nwritten, skiplen = 0; CURLcode result = CURLE_OK; DEBUGASSERT(data); - nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + if(backend->shutting_down && backend->io_send_blocked_len && + (backend->io_send_blocked_len < blen)) { + /* bug in wolfSSL: <https://github.com/wolfSSL/wolfssl/issues/7784> + * It adds the close notify message again every time we retry + * sending during shutdown. */ + CURL_TRC_CF(data, cf, "bio_write, shutdown restrict send of %d" + " to %d bytes", blen, backend->io_send_blocked_len); + skiplen = (ssize_t)(blen - backend->io_send_blocked_len); + blen = backend->io_send_blocked_len; + } + nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result); backend->io_result = result; CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d", blen, nwritten, result); wolfSSL_BIO_clear_retry_flags(bio); - if(nwritten < 0 && CURLE_AGAIN == result) + if(nwritten < 0 && CURLE_AGAIN == result) { BIO_set_retry_write(bio); + if(backend->shutting_down && !backend->io_send_blocked_len) + backend->io_send_blocked_len = blen; + } + else if(!result && skiplen) + nwritten += skiplen; return (int)nwritten; } @@ -304,8 +324,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; @@ -321,6 +341,8 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) wolfSSL_BIO_clear_retry_flags(bio); if(nread < 0 && CURLE_AGAIN == result) BIO_set_retry_read(bio); + else if(nread == 0) + connssl->peer_closed = TRUE; return (int)nread; } @@ -348,6 +370,335 @@ 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, + (long)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"); + } +#endif + (void)store; + wssl->x509_store_setup = TRUE; + 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 wolfSSL'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 = cache_criteria_met ? get_cached_x509_store(cf, data) : NULL; + if(cached_store && wolfSSL_CTX_get_cert_store(wssl->ctx) == cached_store) { + /* The cached store is already in use, do nothing. */ + } + else if(cached_store && wolfSSL_X509_STORE_up_ref(cached_store)) { + wolfSSL_CTX_set_cert_store(wssl->ctx, cached_store); + } + else if(cache_criteria_met) { + /* wolfSSL's initial store in CTX is not shareable by default. + * Make a new one, suitable for adding to the cache. See #14278 */ + X509_STORE *store = wolfSSL_X509_STORE_new(); + if(!store) { + failf(data, "SSL: could not create a X509 store"); + return CURLE_OUT_OF_MEMORY; + } + wolfSSL_CTX_set_cert_store(wssl->ctx, store); + + result = populate_x509_store(cf, data, store, wssl); + if(!result) { + set_cached_x509_store(cf, data, store); + } + } + else { + /* We never share the CTX's store, use it. */ + X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx); + result = populate_x509_store(cf, data, store, wssl); + } + + return result; +} + +#ifdef WOLFSSL_TLS13 +static size_t +wssl_get_default_ciphers(bool tls13, char *buf, size_t size) +{ + size_t len = 0; + char *term = buf; + int i; + char *str; + size_t n; + + for(i = 0; (str = wolfSSL_get_cipher_list(i)); i++) { + if((strncmp(str, "TLS13", 5) == 0) != tls13) + continue; + + n = strlen(str); + if(buf && len + n + 1 <= size) { + memcpy(buf + len, str, n); + term = buf + len + n; + *term = ':'; + } + len += n + 1; + } + + if(buf) + *term = '\0'; + + return len > 0 ? len - 1 : 0; +} +#endif + +#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ +static int +wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX* ctx, int version) +{ + int res; + switch(version) { + default: + case TLS1_VERSION: + res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1); + if(res == WOLFSSL_SUCCESS) + return res; + FALLTHROUGH(); + case TLS1_1_VERSION: + res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1_1); + if(res == WOLFSSL_SUCCESS) + return res; + FALLTHROUGH(); + case TLS1_2_VERSION: + res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1_2); +#ifdef WOLFSSL_TLS13 + if(res == WOLFSSL_SUCCESS) + return res; + FALLTHROUGH(); + case TLS1_3_VERSION: + res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1_3); +#endif + } + return res; +} +static int +wssl_legacy_CTX_set_max_proto_version(WOLFSSL_CTX* ctx, int version) +{ + (void) ctx, (void) version; + return WOLFSSL_NOT_IMPLEMENTED; +} +#define wolfSSL_CTX_set_min_proto_version wssl_legacy_CTX_set_min_proto_version +#define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version +#endif + /* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. @@ -355,135 +706,98 @@ static void wolfssl_bio_cf_free_methods(void) static CURLcode wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { + int res; 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; +#ifdef WOLFSSL_HAVE_KYBER + word16 pqkem = 0; size_t idx = 0; #endif -#ifdef HAVE_SNI - bool sni = FALSE; -#define use_sni(x) sni = (x) -#else -#define use_sni(x) Curl_nop_stmt -#endif - bool imported_native_ca = false; - bool imported_ca_info_blob = false; DEBUGASSERT(backend); if(connssl->state == ssl_connection_complete) return CURLE_OK; - if(conn_config->version_max != CURL_SSLVERSION_MAX_NONE) { - failf(data, "wolfSSL does not support to set maximum SSL/TLS version"); - return CURLE_SSL_CONNECT_ERROR; +#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ + req_method = wolfSSLv23_client_method(); +#else + req_method = wolfTLS_client_method(); +#endif + if(!req_method) { + failf(data, "wolfSSL: could not create a client method"); + return CURLE_OUT_OF_MEMORY; + } + + if(backend->ctx) + wolfSSL_CTX_free(backend->ctx); + + backend->ctx = wolfSSL_CTX_new(req_method); + if(!backend->ctx) { + failf(data, "wolfSSL: could not create a context"); + return CURLE_OUT_OF_MEMORY; } - /* check to see if we've been told to use an explicit SSL/TLS version */ switch(conn_config->version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: -#if LIBWOLFSSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ - /* minimum protocol version is set later after the CTX object is created */ - req_method = SSLv23_client_method(); -#else - infof(data, "wolfSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " - "TLS 1.0 is used exclusively"); - req_method = TLSv1_client_method(); -#endif - use_sni(TRUE); - break; case CURL_SSLVERSION_TLSv1_0: -#if defined(WOLFSSL_ALLOW_TLSV10) && !defined(NO_OLD_TLS) - req_method = TLSv1_client_method(); - use_sni(TRUE); -#else - failf(data, "wolfSSL does not support TLS 1.0"); - return CURLE_NOT_BUILT_IN; -#endif + res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_VERSION); break; case CURL_SSLVERSION_TLSv1_1: -#ifndef NO_OLD_TLS - req_method = TLSv1_1_client_method(); - use_sni(TRUE); -#else - failf(data, "wolfSSL does not support TLS 1.1"); - return CURLE_NOT_BUILT_IN; -#endif + res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_1_VERSION); break; case CURL_SSLVERSION_TLSv1_2: -#ifndef WOLFSSL_NO_TLS12 - req_method = TLSv1_2_client_method(); - use_sni(TRUE); -#else - failf(data, "wolfSSL does not support TLS 1.2"); - return CURLE_NOT_BUILT_IN; -#endif + res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_2_VERSION); break; - case CURL_SSLVERSION_TLSv1_3: #ifdef WOLFSSL_TLS13 - req_method = wolfTLSv1_3_client_method(); - use_sni(TRUE); + case CURL_SSLVERSION_TLSv1_3: + res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_3_VERSION); break; -#else - failf(data, "wolfSSL: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; #endif default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + failf(data, "wolfSSL: unsupported minimum TLS version value"); return CURLE_SSL_CONNECT_ERROR; } - - if(!req_method) { - failf(data, "SSL: couldn't create a method"); - return CURLE_OUT_OF_MEMORY; - } - - if(backend->ctx) - wolfSSL_CTX_free(backend->ctx); - backend->ctx = wolfSSL_CTX_new(req_method); - - if(!backend->ctx) { - failf(data, "SSL: couldn't create a context"); - return CURLE_OUT_OF_MEMORY; + if(res != WOLFSSL_SUCCESS) { + failf(data, "wolfSSL: failed set the minimum TLS version"); + return CURLE_SSL_CONNECT_ERROR; } - switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: -#if LIBWOLFSSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ - /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is - * whatever minimum version of TLS was built in and at least TLS 1.0. For - * later library versions that could change (eg TLS 1.0 built in but - * defaults to TLS 1.1) so we have this short circuit evaluation to find - * the minimum supported TLS version. - */ - if((wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1) != 1) && - (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_1) != 1) && - (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_2) != 1) + switch(conn_config->version_max) { #ifdef WOLFSSL_TLS13 - && (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_3) != 1) -#endif - ) { - failf(data, "SSL: couldn't set the minimum protocol version"); - return CURLE_SSL_CONNECT_ERROR; - } + case CURL_SSLVERSION_MAX_TLSv1_3: + res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); + break; #endif - default: + case CURL_SSLVERSION_MAX_TLSv1_2: + res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_2_VERSION); + break; + case CURL_SSLVERSION_MAX_TLSv1_1: + res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_1_VERSION); break; + case CURL_SSLVERSION_MAX_TLSv1_0: + res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_VERSION); + break; + case CURL_SSLVERSION_MAX_DEFAULT: + case CURL_SSLVERSION_MAX_NONE: + res = WOLFSSL_SUCCESS; + break; + default: + failf(data, "wolfSSL: unsupported maximum TLS version value"); + return CURLE_SSL_CONNECT_ERROR; + } + if(res != WOLFSSL_SUCCESS) { + failf(data, "wolfSSL: failed set the maximum TLS version"); + return CURLE_SSL_CONNECT_ERROR; } +#ifndef WOLFSSL_TLS13 ciphers = conn_config->cipher_list; if(ciphers) { if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { @@ -492,19 +806,57 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } infof(data, "Cipher selection: %s", ciphers); } +#else + if(conn_config->cipher_list || conn_config->cipher_list13) { + const char *ciphers12 = conn_config->cipher_list; + const char *ciphers13 = conn_config->cipher_list13; + + /* Set ciphers to a combination of ciphers_list and ciphers_list13. + * If cipher_list is not set use the default TLSv1.2 (1.1, 1.0) ciphers. + * If cipher_list13 is not set use the default TLSv1.3 ciphers. */ + size_t len13 = ciphers13 ? strlen(ciphers13) + : wssl_get_default_ciphers(true, NULL, 0); + size_t len12 = ciphers12 ? strlen(ciphers12) + : wssl_get_default_ciphers(false, NULL, 0); + + ciphers = malloc(len13 + 1 + len12 + 1); + if(!ciphers) + return CURLE_OUT_OF_MEMORY; + + if(ciphers13) + memcpy(ciphers, ciphers13, len13); + else + wssl_get_default_ciphers(true, ciphers, len13 + 1); + ciphers[len13] = ':'; + + if(ciphers12) + memcpy(ciphers + len13 + 1, ciphers12, len12); + else + wssl_get_default_ciphers(false, ciphers + len13 + 1, len12 + 1); + ciphers[len13 + 1 + len12] = '\0'; + + if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { + failf(data, "failed setting cipher list: %s", ciphers); + free(ciphers); + return CURLE_SSL_CIPHER; + } + infof(data, "Cipher selection: %s", ciphers); + free(ciphers); + } +#endif curves = conn_config->curves; if(curves) { -#ifdef HAVE_LIBOQS +#ifdef WOLFSSL_HAVE_KYBER for(idx = 0; gnm[idx].name != NULL; idx++) { if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) { - oqsAlg = gnm[idx].group; + pqkem = gnm[idx].group; break; } } - if(oqsAlg == 0) + if(pqkem == 0) #endif { if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { @@ -514,86 +866,89 @@ 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 the client certificate, and private key */ +#ifndef NO_FILESYSTEM + if(ssl_config->primary.cert_blob || ssl_config->primary.clientcert) { + const char *cert_file = ssl_config->primary.clientcert; + const char *key_file = ssl_config->key; + const struct curl_blob *cert_blob = ssl_config->primary.cert_blob; + const struct curl_blob *key_blob = ssl_config->key_blob; + int file_type = wolfssl_do_file_type(ssl_config->cert_type); + int rc; - /* 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; - } + switch(file_type) { + case WOLFSSL_FILETYPE_PEM: + rc = cert_blob ? + wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx, + cert_blob->data, + (long)cert_blob->len) : + wolfSSL_CTX_use_certificate_chain_file(backend->ctx, cert_file); + break; + case WOLFSSL_FILETYPE_ASN1: + rc = cert_blob ? + wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data, + (long)cert_blob->len, file_type) : + wolfSSL_CTX_use_certificate_file(backend->ctx, cert_file, file_type); + break; + default: + failf(data, "unknown cert type"); + return CURLE_BAD_FUNCTION_ARGUMENT; } - else { - imported_ca_info_blob = true; - infof(data, "successfully imported CA certificate blob"); + if(rc != 1) { + failf(data, "unable to use client certificate"); + return CURLE_SSL_CONNECT_ERROR; } - } -#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:"); - } + if(!key_blob && !key_file) { + key_blob = cert_blob; + key_file = cert_file; } - else { - /* Everything is fine. */ - infof(data, "successfully set certificate verify locations:"); + else + file_type = wolfssl_do_file_type(ssl_config->key_type); + + rc = key_blob ? + wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data, + (long)key_blob->len, file_type) : + wolfSSL_CTX_use_PrivateKey_file(backend->ctx, key_file, file_type); + if(rc != 1) { + failf(data, "unable to set private key"); + return CURLE_SSL_CONNECT_ERROR; } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); } +#else /* NO_FILESYSTEM */ + if(ssl_config->primary.cert_blob) { + const struct curl_blob *cert_blob = ssl_config->primary.cert_blob; + const struct curl_blob *key_blob = ssl_config->key_blob; + int file_type = wolfssl_do_file_type(ssl_config->cert_type); + int rc; - /* 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); - - if(wolfSSL_CTX_use_certificate_file(backend->ctx, - ssl_config->primary.clientcert, - file_type) != 1) { - failf(data, "unable to use client certificate (no key or wrong pass" - " phrase?)"); + switch(file_type) { + case WOLFSSL_FILETYPE_PEM: + rc = wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx, + cert_blob->data, + (long)cert_blob->len); + break; + case WOLFSSL_FILETYPE_ASN1: + rc = wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data, + (long)cert_blob->len, file_type); + break; + default: + failf(data, "unknown cert type"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if(rc != 1) { + failf(data, "unable to use client certificate"); return CURLE_SSL_CONNECT_ERROR; } - file_type = do_file_type(ssl_config->key_type); - if(wolfSSL_CTX_use_PrivateKey_file(backend->ctx, ssl_config->key, - file_type) != 1) { + if(!key_blob) + key_blob = cert_blob; + else + file_type = wolfssl_do_file_type(ssl_config->key_type); + + if(wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data, + (long)key_blob->len, + file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } @@ -609,7 +964,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) SSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI - if(sni && connssl->peer.sni) { + if(connssl->peer.sni) { size_t sni_len = strlen(connssl->peer.sni); if((sni_len < USHRT_MAX)) { if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, @@ -624,8 +979,14 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { - CURLcode result = (*data->set.ssl.fsslctx)(data, backend->ctx, - data->set.ssl.fsslctxp); + CURLcode result; + if(!backend->x509_store_setup) { + result = Curl_wssl_setup_x509_store(cf, data, backend); + if(result) + return result; + } + result = (*data->set.ssl.fsslctx)(data, backend->ctx, + data->set.ssl.fsslctxp); if(result) { failf(data, "error signaled by ssl ctx callback"); return result; @@ -633,7 +994,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #ifdef NO_FILESYSTEM else if(conn_config->verifypeer) { - failf(data, "SSL: Certificates can't be loaded because wolfSSL was built" + failf(data, "SSL: Certificates cannot be loaded because wolfSSL was built" " with \"no filesystem\". Either disable peer verification" " (insecure) or if you are building an application with libcurl you" " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); @@ -646,14 +1007,14 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) wolfSSL_free(backend->handle); backend->handle = wolfSSL_new(backend->ctx); if(!backend->handle) { - failf(data, "SSL: couldn't create a handle"); + failf(data, "SSL: could not create a handle"); return CURLE_OUT_OF_MEMORY; } -#ifdef HAVE_LIBOQS - if(oqsAlg) { - if(wolfSSL_UseKeyShare(backend->handle, oqsAlg) != WOLFSSL_SUCCESS) { - failf(data, "unable to use oqs KEM"); +#ifdef WOLFSSL_HAVE_KYBER + if(pqkem) { + if(wolfSSL_UseKeyShare(backend->handle, pqkem) != WOLFSSL_SUCCESS) { + failf(data, "unable to use PQ KEM"); } } #endif @@ -665,7 +1026,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) result = Curl_alpn_to_proto_str(&proto, connssl->alpn); if(result || - wolfSSL_UseALPN(backend->handle, (char *)proto.data, proto.len, + wolfSSL_UseALPN(backend->handle, + (char *)proto.data, (unsigned int)proto.len, WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { failf(data, "SSL: failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; @@ -692,16 +1054,17 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #endif /* HAVE_SECURE_RENEGOTIATION */ - /* Check if there's a cached ID we can/should use here! */ - if(ssl_config->primary.sessionid) { + /* Check if there is a cached ID we can/should use here! */ + if(ssl_config->primary.cache_session) { void *ssl_sessionid = NULL; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + &ssl_sessionid, NULL)) { /* we got a session id, use it! */ if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_delsessionid(data, ssl_sessionid); - infof(data, "Can't use session ID, going on without"); + infof(data, "cannot use session ID, going on without"); } else infof(data, "SSL reusing session ID"); @@ -709,6 +1072,82 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) Curl_ssl_sessionid_unlock(data); } +#ifdef USE_ECH + if(ECH_ENABLED(data)) { + int trying_ech_now = 0; + + if(data->set.str[STRING_ECH_PUBLIC]) { + infof(data, "ECH: outername not (yet) supported with wolfSSL"); + return CURLE_SSL_CONNECT_ERROR; + } + if(data->set.tls_ech == CURLECH_GREASE) { + infof(data, "ECH: GREASE'd ECH not yet supported for wolfSSL"); + return CURLE_SSL_CONNECT_ERROR; + } + if(data->set.tls_ech & CURLECH_CLA_CFG + && data->set.str[STRING_ECH_CONFIG]) { + char *b64val = data->set.str[STRING_ECH_CONFIG]; + word32 b64len = 0; + + b64len = (word32) strlen(b64val); + if(b64len + && wolfSSL_SetEchConfigsBase64(backend->handle, b64val, b64len) + != WOLFSSL_SUCCESS) { + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else { + trying_ech_now = 1; + infof(data, "ECH: ECHConfig from command line"); + } + } + else { + struct Curl_dns_entry *dns = NULL; + + dns = Curl_fetch_addr(data, connssl->peer.hostname, connssl->peer.port); + if(!dns) { + infof(data, "ECH: requested but no DNS info available"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else { + struct Curl_https_rrinfo *rinfo = NULL; + + rinfo = dns->hinfo; + if(rinfo && rinfo->echconfiglist) { + unsigned char *ecl = rinfo->echconfiglist; + size_t elen = rinfo->echconfiglist_len; + + infof(data, "ECH: ECHConfig from DoH HTTPS RR"); + if(wolfSSL_SetEchConfigs(backend->handle, ecl, (word32) elen) != + WOLFSSL_SUCCESS) { + infof(data, "ECH: wolfSSL_SetEchConfigs failed"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else { + trying_ech_now = 1; + infof(data, "ECH: imported ECHConfigList of length %ld", elen); + } + } + else { + infof(data, "ECH: requested but no ECHConfig available"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + Curl_resolv_unlink(data, &dns); + } + } + + if(trying_ech_now + && SSL_set_min_proto_version(backend->handle, TLS1_3_VERSION) != 1) { + infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); + return CURLE_SSL_CONNECT_ERROR; + } + + } +#endif /* USE_ECH */ + #ifdef USE_BIO_CHAIN { WOLFSSL_BIO *bio; @@ -734,17 +1173,39 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } +static char *wolfssl_strerror(unsigned long error, char *buf, + unsigned long size) +{ + DEBUGASSERT(size > 40); + *buf = '\0'; + + wolfSSL_ERR_error_string_n(error, buf, size); + + if(!*buf) { + const char *msg = error ? "Unknown error" : "No error"; + /* the string fits because the assert above assures this */ + strcpy(buf, msg); + } + + return buf; +} + + static CURLcode 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)? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif DEBUGASSERT(backend); @@ -758,6 +1219,16 @@ 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; + result = Curl_wssl_setup_x509_store(cf, data, backend); + if(result) + return result; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; ret = wolfSSL_connect(backend->handle); #ifdef OPENSSL_EXTRA @@ -785,15 +1256,14 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) #endif /* OPENSSL_EXTRA */ if(ret != 1) { - char error_buffer[WOLFSSL_MAX_ERROR_SZ]; - int detail = wolfSSL_get_error(backend->handle, ret); + int detail = wolfSSL_get_error(backend->handle, ret); if(SSL_ERROR_WANT_READ == detail) { - connssl->connecting_state = ssl_connect_2_reading; + connssl->io_need = CURL_SSL_IO_NEED_RECV; return CURLE_OK; } else if(SSL_ERROR_WANT_WRITE == detail) { - connssl->connecting_state = ssl_connect_2_writing; + connssl->io_need = CURL_SSL_IO_NEED_SEND; return CURLE_OK; } /* There is no easy way to override only the CN matching. @@ -825,7 +1295,6 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } #endif } -#if LIBWOLFSSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */ else if(ASN_NO_SIGNER_E == detail) { if(conn_config->verifypeer) { failf(data, " CA signer not available for verification"); @@ -838,13 +1307,47 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) "continuing anyway"); } } + else if(ASN_AFTER_DATE_E == detail) { + failf(data, "server verification failed: certificate has expired."); + return CURLE_PEER_FAILED_VERIFICATION; + } + else if(ASN_BEFORE_DATE_E == detail) { + failf(data, "server verification failed: certificate not valid yet."); + return CURLE_PEER_FAILED_VERIFICATION; + } +#ifdef USE_ECH + else if(-1 == detail) { + /* try access a retry_config ECHConfigList for tracing */ + byte echConfigs[1000]; + word32 echConfigsLen = 1000; + int rv = 0; + + /* this currently does not produce the retry_configs */ + rv = wolfSSL_GetEchConfigs(backend->handle, echConfigs, + &echConfigsLen); + if(rv != WOLFSSL_SUCCESS) { + infof(data, "Failed to get ECHConfigs"); + } + else { + char *b64str = NULL; + size_t blen = 0; + + rv = Curl_base64_encode((const char *)echConfigs, echConfigsLen, + &b64str, &blen); + if(!rv && b64str) + infof(data, "ECH: (not yet) retry_configs %s", b64str); + free(b64str); + } + } #endif else if(backend->io_result == CURLE_AGAIN) { return CURLE_OK; } else { + char error_buffer[256]; failf(data, "SSL_connect failed with error %d: %s", detail, - wolfSSL_ERR_error_string(detail, error_buffer)); + wolfssl_strerror((unsigned long)detail, error_buffer, + sizeof(error_buffer))); return CURLE_SSL_CONNECT_ERROR; } } @@ -884,6 +1387,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) pinnedpubkey, (const unsigned char *)pubkey->header, (size_t)(pubkey->end - pubkey->header)); + wolfSSL_FreeX509(x509); if(result) { failf(data, "SSL: public key does not match pinned public key"); return result; @@ -928,53 +1432,39 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } +static void wolfssl_session_free(void *sessionid, size_t idsize) +{ + (void)idsize; + wolfSSL_SESSION_free(sessionid); +} + + static CURLcode 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); DEBUGASSERT(backend); - if(ssl_config->primary.sessionid) { - bool incache; - bool added = FALSE; - void *old_ssl_sessionid = NULL; + if(ssl_config->primary.cache_session) { /* wolfSSL_get1_session allocates memory that has to be freed. */ WOLFSSL_SESSION *our_ssl_sessionid = wolfSSL_get1_session(backend->handle); if(our_ssl_sessionid) { Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)); - if(incache) { - if(old_ssl_sessionid != our_ssl_sessionid) { - infof(data, "old SSL session ID is stale, removing"); - Curl_ssl_delsessionid(data, old_ssl_sessionid); - incache = FALSE; - } - } - - if(!incache) { - result = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, 0, NULL); - if(result) { - Curl_ssl_sessionid_unlock(data); - wolfSSL_SESSION_free(our_ssl_sessionid); - failf(data, "failed to store ssl session"); - return result; - } - else { - added = TRUE; - } - } + /* call takes ownership of `our_ssl_sessionid` */ + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, + our_ssl_sessionid, 0, + wolfssl_session_free); Curl_ssl_sessionid_unlock(data); - - if(!added) { - /* If the session info wasn't added to the cache, free our copy. */ - wolfSSL_SESSION_free(our_ssl_sessionid); + if(result) { + failf(data, "failed to store ssl session"); + return result; } } } @@ -992,9 +1482,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; - char error_buffer[WOLFSSL_MAX_ERROR_SZ]; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; int rc; @@ -1009,7 +1498,7 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, switch(err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_write() */ + /* there is data pending, re-invoke SSL_write() */ CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len); *curlcode = CURLE_AGAIN; return -1; @@ -1020,9 +1509,13 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, return -1; } CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", len, rc, err); - failf(data, "SSL write: %s, errno %d", - wolfSSL_ERR_error_string(err, error_buffer), - SOCKERRNO); + { + char error_buffer[256]; + failf(data, "SSL write: %s, errno %d", + wolfssl_strerror((unsigned long)err, error_buffer, + sizeof(error_buffer)), + SOCKERRNO); + } *curlcode = CURLE_SEND_ERROR; return -1; } @@ -1031,22 +1524,122 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, return rc; } +static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *wctx = (struct wolfssl_ctx *)connssl->backend; + CURLcode result = CURLE_OK; + char buf[1024]; + char error_buffer[256]; + int nread = -1, err; + size_t i; + int detail; + + DEBUGASSERT(wctx); + if(!wctx->handle || cf->shutdown) { + *done = TRUE; + goto out; + } + + wctx->shutting_down = TRUE; + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + if(!(wolfSSL_get_shutdown(wctx->handle) & SSL_SENT_SHUTDOWN)) { + /* We have not started the shutdown from our side yet. Check + * if the server already sent us one. */ + ERR_clear_error(); + nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf)); + err = wolfSSL_get_error(wctx->handle, nread); + CURL_TRC_CF(data, cf, "wolfSSL_read, nread=%d, err=%d", nread, err); + if(!nread && err == SSL_ERROR_ZERO_RETURN) { + bool input_pending; + /* Yes, it did. */ + if(!send_shutdown) { + CURL_TRC_CF(data, cf, "SSL shutdown received, not sending"); + *done = TRUE; + goto out; + } + else if(!cf->next->cft->is_alive(cf->next, data, &input_pending)) { + /* Server closed the connection after its closy notify. It + * seems not interested to see our close notify, so do not + * send it. We are done. */ + CURL_TRC_CF(data, cf, "peer closed connection"); + connssl->peer_closed = TRUE; + *done = TRUE; + goto out; + } + } + } + + /* SSL should now have started the shutdown from our side. Since it + * was not complete, we are lacking the close notify from the server. */ + if(send_shutdown) { + ERR_clear_error(); + if(wolfSSL_shutdown(wctx->handle) == 1) { + CURL_TRC_CF(data, cf, "SSL shutdown finished"); + *done = TRUE; + goto out; + } + if(SSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->handle, nread)) { + CURL_TRC_CF(data, cf, "SSL shutdown still wants to send"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; + goto out; + } + /* Having sent the close notify, we use wolfSSL_read() to get the + * missing close notify from the server. */ + } + + for(i = 0; i < 10; ++i) { + ERR_clear_error(); + nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf)); + if(nread <= 0) + break; + } + err = wolfSSL_get_error(wctx->handle, nread); + switch(err) { + case SSL_ERROR_ZERO_RETURN: /* no more data */ + CURL_TRC_CF(data, cf, "SSL shutdown received"); + *done = TRUE; + break; + case SSL_ERROR_NONE: /* just did not get anything */ + case SSL_ERROR_WANT_READ: + /* SSL has send its notify and now wants to read the reply + * from the server. We are not really interested in that. */ + CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive"); + connssl->io_need = CURL_SSL_IO_NEED_RECV; + break; + case SSL_ERROR_WANT_WRITE: + CURL_TRC_CF(data, cf, "SSL shutdown send blocked"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; + break; + default: + detail = wolfSSL_get_error(wctx->handle, err); + CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)", + wolfssl_strerror((unsigned long)err, error_buffer, + sizeof(error_buffer)), + detail); + result = CURLE_RECV_ERROR; + break; + } + +out: + cf->shutdown = (result || *done); + return result; +} + 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; DEBUGASSERT(backend); if(backend->handle) { - char buf[32]; - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)wolfSSL_read(backend->handle, buf, (int)sizeof(buf)); - (void)wolfSSL_shutdown(backend->handle); wolfSSL_free(backend->handle); backend->handle = NULL; } @@ -1062,9 +1655,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; - char error_buffer[WOLFSSL_MAX_ERROR_SZ]; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen; int nread; @@ -1084,11 +1676,9 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, *curlcode = CURLE_OK; return 0; case SSL_ERROR_NONE: - /* FALLTHROUGH */ case SSL_ERROR_WANT_READ: - /* FALLTHROUGH */ case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke wolfSSL_read() */ + /* there is data pending, re-invoke wolfSSL_read() */ CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen); *curlcode = CURLE_AGAIN; return -1; @@ -1098,8 +1688,13 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, *curlcode = CURLE_AGAIN; return -1; } - failf(data, "SSL read: %s, errno %d", - wolfSSL_ERR_error_string(err, error_buffer), SOCKERRNO); + { + char error_buffer[256]; + failf(data, "SSL read: %s, errno %d", + wolfssl_strerror((unsigned long)err, error_buffer, + sizeof(error_buffer)), + SOCKERRNO); + } *curlcode = CURLE_RECV_ERROR; return -1; } @@ -1109,12 +1704,6 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, } -static void wolfssl_session_free(void *ptr) -{ - wolfSSL_SESSION_free(ptr); -} - - static size_t wolfssl_version(char *buffer, size_t size) { #if LIBWOLFSSL_VERSION_HEX >= 0x03006000 @@ -1152,43 +1741,18 @@ 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 return FALSE; } - -/* - * This function is called to shut down the SSL layer but keep the - * socket open (CCC - Clear Command Channel) - */ -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; - int retval = 0; - - (void)data; - DEBUGASSERT(ctx && ctx->backend); - - backend = (struct wolfssl_ssl_backend_data *)ctx->backend; - if(backend->handle) { - wolfSSL_ERR_clear_error(); - wolfSSL_free(backend->handle); - backend->handle = NULL; - } - return retval; -} - - static CURLcode wolfssl_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1207,7 +1771,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf, } if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ + /* Find out how much more time we are allowed */ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { @@ -1221,9 +1785,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf, return result; } - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { + while(ssl_connect_2 == connssl->connecting_state) { /* check allowed time left */ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -1234,14 +1796,13 @@ wolfssl_connect_common(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, nonblocking?0:timeout_ms); @@ -1272,10 +1833,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf, * have a valid fdset to wait on. */ result = wolfssl_connect_step2(cf, data); - if(result || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) + if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) return result; } /* repeat step2 until all transactions are done. */ @@ -1355,15 +1913,15 @@ 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; } const struct Curl_ssl Curl_ssl_wolfssl = { - { CURLSSLBACKEND_WOLFSSL, "WolfSSL" }, /* info */ + { CURLSSLBACKEND_WOLFSSL, "wolfssl" }, /* info */ #ifdef KEEP_PEER_CERT SSLSUPP_PINNEDPUBKEY | @@ -1373,9 +1931,17 @@ const struct Curl_ssl Curl_ssl_wolfssl = { #endif SSLSUPP_CA_PATH | SSLSUPP_CAINFO_BLOB | - SSLSUPP_SSL_CTX, +#ifdef USE_ECH + SSLSUPP_ECH | +#endif + SSLSUPP_SSL_CTX | +#ifdef WOLFSSL_TLS13 + SSLSUPP_TLS13_CIPHERSUITES | +#endif + SSLSUPP_CA_CACHE | + SSLSUPP_CIPHER_LIST, - sizeof(struct wolfssl_ssl_backend_data), + sizeof(struct wolfssl_ctx), wolfssl_init, /* init */ wolfssl_cleanup, /* cleanup */ @@ -1391,7 +1957,6 @@ const struct Curl_ssl Curl_ssl_wolfssl = { wolfssl_get_internals, /* get_internals */ wolfssl_close, /* close_one */ Curl_none_close_all, /* close_all */ - wolfssl_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */ @@ -1399,9 +1964,9 @@ const struct Curl_ssl Curl_ssl_wolfssl = { wolfssl_sha256sum, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ wolfssl_recv, /* recv decrypted data */ wolfssl_send, /* send data to encrypt */ + NULL, /* get_channel_binding */ }; #endif |