From 5d2ecfef56e49a8e4bfad25a582ff1597987f717 Mon Sep 17 00:00:00 2001 From: dartraiden Date: Wed, 6 Nov 2024 20:55:13 +0300 Subject: libcurl: update to 8.11.0 --- libs/libcurl/src/vtls/bearssl.c | 55 ++-- libs/libcurl/src/vtls/cipher_suite.c | 4 +- libs/libcurl/src/vtls/gtls.c | 522 +++++++++++++++++++++++--------- libs/libcurl/src/vtls/gtls.h | 9 + libs/libcurl/src/vtls/keylog.c | 12 +- libs/libcurl/src/vtls/mbedtls.c | 222 ++++++++------ libs/libcurl/src/vtls/openssl.c | 247 ++++++++------- libs/libcurl/src/vtls/rustls.c | 30 +- libs/libcurl/src/vtls/schannel.c | 201 ++++++------ libs/libcurl/src/vtls/schannel_int.h | 13 +- libs/libcurl/src/vtls/schannel_verify.c | 314 +++++++++++-------- libs/libcurl/src/vtls/sectransp.c | 64 ++-- libs/libcurl/src/vtls/vtls.c | 242 ++++++++++----- libs/libcurl/src/vtls/vtls.h | 13 +- libs/libcurl/src/vtls/vtls_int.h | 47 ++- libs/libcurl/src/vtls/wolfssl.c | 469 ++++++++++++++++------------ libs/libcurl/src/vtls/wolfssl.h | 22 +- libs/libcurl/src/vtls/x509asn1.c | 6 +- 18 files changed, 1552 insertions(+), 940 deletions(-) (limited to 'libs/libcurl/src/vtls') diff --git a/libs/libcurl/src/vtls/bearssl.c b/libs/libcurl/src/vtls/bearssl.c index f262639540..f4d0cc43b3 100644 --- a/libs/libcurl/src/vtls/bearssl.c +++ b/libs/libcurl/src/vtls/bearssl.c @@ -609,11 +609,15 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable); if(ssl_config->primary.cache_session) { - void *session; + void *sdata; + size_t slen; + const br_ssl_session_parameters *session; CURL_TRC_CF(data, cf, "connect_step1, check session cache"); Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &session, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &sdata, &slen, NULL) && + slen == sizeof(*session)) { + session = sdata; br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); session_set = 1; infof(data, "BearSSL: reusing session ID"); @@ -653,10 +657,10 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { - Curl_set_in_callback(data, true); + Curl_set_in_callback(data, TRUE); ret = (*data->set.ssl.fsslctx)(data, &backend->ctx, data->set.ssl.fsslctxp); - Curl_set_in_callback(data, false); + Curl_set_in_callback(data, FALSE); if(ret) { failf(data, "BearSSL: error signaled by ssl ctx callback"); return ret; @@ -761,7 +765,6 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, (struct bearssl_ssl_backend_data *)connssl->backend; br_ssl_session_parameters session; char cipher_str[64]; - char ver_str[16]; CURLcode ret; DEBUGASSERT(backend); @@ -772,6 +775,7 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, return CURLE_OK; if(ret == CURLE_OK) { unsigned int tver; + int subver = 0; if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { failf(data, "SSL: connection closed during handshake"); @@ -780,19 +784,22 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, connssl->connecting_state = ssl_connect_3; /* Informational message */ tver = br_ssl_engine_get_version(&backend->ctx.eng); - if(tver == BR_TLS12) - strcpy(ver_str, "TLSv1.2"); - else if(tver == BR_TLS11) - strcpy(ver_str, "TLSv1.1"); - else if(tver == BR_TLS10) - strcpy(ver_str, "TLSv1.0"); - else { - msnprintf(ver_str, sizeof(ver_str), "TLS 0x%04x", tver); + switch(tver) { + case BR_TLS12: + subver = 2; /* 1.2 */ + break; + case BR_TLS11: + subver = 1; /* 1.1 */ + break; + case BR_TLS10: /* 1.0 */ + default: /* unknown, leave it at zero */ + break; } br_ssl_engine_get_session_parameters(&backend->ctx.eng, &session); Curl_cipher_suite_get_str(session.cipher_suite, cipher_str, - sizeof(cipher_str), true); - infof(data, "BearSSL: %s connection using %s", ver_str, cipher_str); + sizeof(cipher_str), TRUE); + infof(data, "BearSSL: TLS v1.%d connection using %s", subver, + cipher_str); } return ret; } @@ -820,8 +827,8 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, const char *proto; proto = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); - Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto, - proto? strlen(proto) : 0); + Curl_alpn_set_negotiated(cf, data, connssl, (const unsigned char *)proto, + proto ? strlen(proto) : 0); } if(ssl_config->primary.cache_session) { @@ -832,7 +839,8 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); Curl_ssl_sessionid_lock(data); - ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, session, 0, + ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, + session, sizeof(*session), bearssl_session_free); Curl_ssl_sessionid_unlock(data); if(ret) @@ -941,15 +949,14 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, /* if ssl is expecting something, check if it is available. */ if(connssl->io_need) { - - 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; + 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; CURL_TRC_CF(data, cf, "connect_common, check socket"); what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0:timeout_ms); + nonblocking ? 0 : timeout_ms); CURL_TRC_CF(data, cf, "connect_common, check socket -> %d", what); if(what < 0) { /* fatal error */ diff --git a/libs/libcurl/src/vtls/cipher_suite.c b/libs/libcurl/src/vtls/cipher_suite.c index be5dbf5cbe..ca7c5ba46d 100644 --- a/libs/libcurl/src/vtls/cipher_suite.c +++ b/libs/libcurl/src/vtls/cipher_suite.c @@ -844,10 +844,10 @@ static bool cs_is_separator(char c) case ':': case ',': case ';': - return true; + return TRUE; default:; } - return false; + return FALSE; } uint16_t Curl_cipher_suite_walk_str(const char **str, const char **end) diff --git a/libs/libcurl/src/vtls/gtls.c b/libs/libcurl/src/vtls/gtls.c index 2d03712801..398af77bf3 100644 --- a/libs/libcurl/src/vtls/gtls.c +++ b/libs/libcurl/src/vtls/gtls.c @@ -50,6 +50,7 @@ #include "vauth/vauth.h" #include "parsedate.h" #include "connect.h" /* for the connect timeout */ +#include "progress.h" #include "select.h" #include "strcase.h" #include "warnless.h" @@ -108,7 +109,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen) backend->gtls.io_result = result; if(nwritten < 0) { gnutls_transport_set_errno(backend->gtls.session, - (CURLE_AGAIN == result)? EAGAIN : EINVAL); + (CURLE_AGAIN == result) ? EAGAIN : EINVAL); nwritten = -1; } return nwritten; @@ -140,7 +141,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) backend->gtls.io_result = result; if(nread < 0) { gnutls_transport_set_errno(backend->gtls.session, - (CURLE_AGAIN == result)? EAGAIN : EINVAL); + (CURLE_AGAIN == result) ? EAGAIN : EINVAL); nread = -1; } else if(nread == 0) @@ -159,7 +160,7 @@ static int gtls_init(void) { int ret = 1; if(!gtls_inited) { - ret = gnutls_global_init()?0:1; + ret = gnutls_global_init() ? 0 : 1; #ifdef GTLSDEBUG gnutls_global_set_log_function(tls_log_func); gnutls_global_set_log_level(2); @@ -193,7 +194,7 @@ static void showtime(struct Curl_easy *data, sizeof(str), " %s: %s, %02d %s %4d %02d:%02d:%02d GMT", text, - Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], + Curl_wkday[tm->tm_wday ? tm->tm_wday-1 : 6], tm->tm_mday, Curl_month[tm->tm_mon], tm->tm_year + 1900, @@ -269,14 +270,14 @@ static CURLcode handshake(struct Curl_cfilter *cf, /* if ssl is expecting something, check if it is available. */ if(connssl->io_need) { int what; - 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; + 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?timeout_ms:1000); + nonblocking ? 0 : + timeout_ms ? timeout_ms : 1000); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -284,7 +285,7 @@ static CURLcode handshake(struct Curl_cfilter *cf, } else if(0 == what) { if(nonblocking) - return CURLE_OK; + return CURLE_AGAIN; else if(timeout_ms) { /* timeout */ failf(data, "SSL connection timeout at %ld", (long)timeout_ms); @@ -308,8 +309,8 @@ static CURLcode handshake(struct Curl_cfilter *cf, if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { connssl->io_need = - gnutls_record_get_direction(session)? - CURL_SSL_IO_NEED_SEND:CURL_SSL_IO_NEED_RECV; + gnutls_record_get_direction(session) ? + CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV; continue; } else if((rc < 0) && !gnutls_error_is_fatal(rc)) { @@ -509,7 +510,7 @@ static CURLcode gtls_populate_creds(struct Curl_cfilter *cf, int rc; if(config->verifypeer) { - bool imported_native_ca = false; + bool imported_native_ca = FALSE; if(ssl_config->native_ca_store) { rc = gnutls_certificate_set_x509_system_trust(creds); @@ -519,7 +520,7 @@ static CURLcode gtls_populate_creds(struct Curl_cfilter *cf, else { infof(data, "found %d certificates in native ca store", rc); if(rc > 0) - imported_native_ca = true; + imported_native_ca = TRUE; } } @@ -590,7 +591,7 @@ static bool gtls_shared_creds_expired(const struct Curl_easy *data, timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; if(timeout_ms < 0) - return false; + return FALSE; return elapsed_ms >= timeout_ms; } @@ -719,45 +720,57 @@ static void gtls_sessionid_free(void *sessionid, size_t idsize) free(sessionid); } -static CURLcode gtls_update_session_id(struct Curl_cfilter *cf, - struct Curl_easy *data, - gnutls_session_t session) +CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_session_t session, + struct ssl_peer *peer, + const char *alpn) { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct ssl_connect_data *connssl = cf->ctx; + void *connect_sessionid; + size_t connect_idsize = 0; CURLcode result = CURLE_OK; - if(ssl_config->primary.cache_session) { - /* we always unconditionally get the session id here, as even if we - already got it from the cache and asked to use it in the connection, it - might've been rejected and then a new one is in use now and we need to - detect that. */ - void *connect_sessionid; - size_t connect_idsize = 0; - - /* get the session ID data size */ - gnutls_session_get_data(session, NULL, &connect_idsize); - connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ - if(!connect_sessionid) { - return CURLE_OUT_OF_MEMORY; - } - else { - /* extract session ID to the allocated buffer */ - gnutls_session_get_data(session, connect_sessionid, &connect_idsize); - - CURL_TRC_CF(data, cf, "get session id (len=%zu) and store in cache", - connect_idsize); - Curl_ssl_sessionid_lock(data); - /* store this session id, takes ownership */ - result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, - connect_sessionid, connect_idsize, - gtls_sessionid_free); - Curl_ssl_sessionid_unlock(data); - } - } + if(!ssl_config->primary.cache_session) + return CURLE_OK; + + /* we always unconditionally get the session id here, as even if we + already got it from the cache and asked to use it in the connection, it + might've been rejected and then a new one is in use now and we need to + detect that. */ + + /* get the session ID data size */ + gnutls_session_get_data(session, NULL, &connect_idsize); + if(!connect_idsize) /* gnutls does this for some version combinations */ + return CURLE_OK; + + connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ + if(!connect_sessionid) + return CURLE_OUT_OF_MEMORY; + + /* extract session ID to the allocated buffer */ + gnutls_session_get_data(session, connect_sessionid, &connect_idsize); + + CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s) and store in cache", + connect_idsize, alpn ? alpn : "-"); + Curl_ssl_sessionid_lock(data); + /* store this session id, takes ownership */ + result = Curl_ssl_set_sessionid(cf, data, peer, alpn, + connect_sessionid, connect_idsize, + gtls_sessionid_free); + Curl_ssl_sessionid_unlock(data); return result; } +static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_session_t session) +{ + struct ssl_connect_data *connssl = cf->ctx; + return Curl_gtls_update_session_id(cf, data, session, &connssl->peer, + connssl->alpn_negotiated); +} + static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, unsigned when, unsigned int incoming, const gnutls_datum_t *msg) @@ -770,10 +783,10 @@ static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, struct Curl_easy *data = CF_DATA_CURRENT(cf); if(data) { CURL_TRC_CF(data, cf, "handshake: %s message type %d", - incoming? "incoming" : "outgoing", htype); + incoming ? "incoming" : "outgoing", htype); switch(htype) { case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { - gtls_update_session_id(cf, data, session); + cf_gtls_update_session_id(cf, data, session); break; } default: @@ -845,9 +858,13 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, init_flags |= GNUTLS_FORCE_CLIENT_CERT; #endif -#if defined(GNUTLS_NO_TICKETS) - /* Disable TLS session tickets */ - init_flags |= GNUTLS_NO_TICKETS; +#if defined(GNUTLS_NO_TICKETS_TLS12) + init_flags |= GNUTLS_NO_TICKETS_TLS12; +#elif defined(GNUTLS_NO_TICKETS) + /* Disable TLS session tickets for non 1.3 connections */ + if((config->version != CURL_SSLVERSION_TLSv1_3) && + (config->version != CURL_SSLVERSION_DEFAULT)) + init_flags |= GNUTLS_NO_TICKETS; #endif #if defined(GNUTLS_NO_STATUS_REQUEST) @@ -936,7 +953,19 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, if(result) return result; } - if(ssl_config->key_passwd) { + if(ssl_config->cert_type && strcasecompare(ssl_config->cert_type, "P12")) { + rc = gnutls_certificate_set_x509_simple_pkcs12_file( + gtls->shared_creds->creds, config->clientcert, GNUTLS_X509_FMT_DER, + ssl_config->key_passwd ? ssl_config->key_passwd : ""); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, + "error reading X.509 potentially-encrypted key or certificate " + "file: %s", + gnutls_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } + } + else if(ssl_config->key_passwd) { const unsigned int supported_key_encryption_algorithms = GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR | GNUTLS_PKCS_USE_PKCS12_RC2_40 | GNUTLS_PKCS_USE_PBES2_3DES | @@ -1022,11 +1051,15 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, struct Curl_easy *data, struct ssl_peer *peer, const unsigned char *alpn, size_t alpn_len, + struct ssl_connect_data *connssl, Curl_gtls_ctx_setup_cb *cb_setup, void *cb_user_data, void *ssl_user_data) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + gnutls_datum_t gtls_alpns[5]; + size_t gtls_alpns_count = 0; CURLcode result; DEBUGASSERT(gctx); @@ -1049,52 +1082,91 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, gnutls_session_set_keylog_function(gctx->session, keylog_callback); } - /* convert the ALPN string from our arguments to a list of strings - * that gnutls wants and will convert internally back to this very - * string for sending to the server. nice. */ - if(alpn && alpn_len) { - gnutls_datum_t alpns[5]; - size_t i, alen = alpn_len; - unsigned char *s = (unsigned char *)alpn; - unsigned char slen; - for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) { - slen = s[0]; - if(slen >= alen) - return CURLE_FAILED_INIT; - alpns[i].data = s + 1; - alpns[i].size = slen; - s += slen + 1; - alen -= (size_t)slen + 1; - } - if(alen) /* not all alpn chars used, wrong format or too many */ - return CURLE_FAILED_INIT; - if(i && gnutls_alpn_set_protocols(gctx->session, - alpns, (unsigned int)i, - GNUTLS_ALPN_MANDATORY)) { - failf(data, "failed setting ALPN"); - return CURLE_SSL_CONNECT_ERROR; - } - } - /* This might be a reconnect, so we check for a session ID in the cache to speed up things */ if(conn_config->cache_session) { void *ssl_sessionid; size_t ssl_idsize; - + char *session_alpn; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, &ssl_idsize)) { + if(!Curl_ssl_getsessionid(cf, data, peer, + &ssl_sessionid, &ssl_idsize, &session_alpn)) { /* we got a session id, use it! */ int rc; rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize); if(rc < 0) infof(data, "SSL failed to set session ID"); - else - infof(data, "SSL reusing session ID (size=%zu)", ssl_idsize); + else { + infof(data, "SSL reusing session ID (size=%zu, alpn=%s)", + ssl_idsize, session_alpn ? session_alpn : "-"); +#ifdef DEBUGBUILD + if((ssl_config->earlydata || !!getenv("CURL_USE_EARLYDATA")) && +#else + if(ssl_config->earlydata && +#endif + !cf->conn->connect_only && connssl && + (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3) && + Curl_alpn_contains_proto(connssl->alpn, session_alpn)) { + connssl->earlydata_max = + gnutls_record_get_max_early_data_size(gctx->session); + if((!connssl->earlydata_max || + connssl->earlydata_max == 0xFFFFFFFFUL)) { + /* Seems to be GnuTLS way to signal no EarlyData in session */ + CURL_TRC_CF(data, cf, "TLS session does not allow earlydata"); + } + else { + CURL_TRC_CF(data, cf, "TLS session allows %zu earlydata bytes, " + "reusing ALPN '%s'", + connssl->earlydata_max, session_alpn); + connssl->earlydata_state = ssl_earlydata_use; + connssl->state = ssl_connection_deferred; + result = Curl_alpn_set_negotiated(cf, data, connssl, + (const unsigned char *)session_alpn, + session_alpn ? strlen(session_alpn) : 0); + if(result) + return result; + /* We only try the ALPN protocol the session used before, + * otherwise we might send early data for the wrong protocol */ + gtls_alpns[0].data = (unsigned char *)session_alpn; + gtls_alpns[0].size = (unsigned)strlen(session_alpn); + gtls_alpns_count = 1; + } + } + } } Curl_ssl_sessionid_unlock(data); } + + /* convert the ALPN string from our arguments to a list of strings that + * gnutls wants and will convert internally back to this string for sending + * to the server. nice. */ + if(!gtls_alpns_count && alpn && alpn_len) { + size_t i, alen = alpn_len; + unsigned char *s = (unsigned char *)alpn; + unsigned char slen; + for(i = 0; (i < ARRAYSIZE(gtls_alpns)) && alen; ++i) { + slen = s[0]; + if(slen >= alen) + return CURLE_FAILED_INIT; + gtls_alpns[i].data = s + 1; + gtls_alpns[i].size = slen; + s += slen + 1; + alen -= (size_t)slen + 1; + } + if(alen) /* not all alpn chars used, wrong format or too many */ + return CURLE_FAILED_INIT; + gtls_alpns_count = i; + } + + if(gtls_alpns_count && + gnutls_alpn_set_protocols(gctx->session, + gtls_alpns, (unsigned int)gtls_alpns_count, + GNUTLS_ALPN_MANDATORY)) { + failf(data, "failed setting ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + return CURLE_OK; } @@ -1125,10 +1197,15 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer, - proto.data, proto.len, NULL, NULL, cf); + proto.data, proto.len, connssl, NULL, NULL, cf); if(result) return result; + if(connssl->alpn && (connssl->state != ssl_connection_deferred)) { + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } + gnutls_handshake_set_hook_function(backend->gtls.session, GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, gtls_handshake_cb); @@ -1313,7 +1390,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, cause = "attached OCSP status response is invalid"; failf(data, "server verification failed: %s. (CAfile: %s " "CRLfile: %s)", cause, - config->CAfile ? config->CAfile: "none", + config->CAfile ? config->CAfile : "none", ssl_config->primary.CRLfile ? ssl_config->primary.CRLfile : "none"); return CURLE_PEER_FAILED_VERIFICATION; @@ -1440,12 +1517,12 @@ Curl_gtls_verifyserver(struct Curl_easy *data, unload_file(issuerp); if(rc <= 0) { failf(data, "server certificate issuer check failed (IssuerCert: %s)", - config->issuercert?config->issuercert:"none"); + config->issuercert ? config->issuercert : "none"); gnutls_x509_crt_deinit(x509_cert); return CURLE_SSL_ISSUER_ERROR; } infof(data, " server certificate issuer check OK (Issuer Cert: %s)", - config->issuercert?config->issuercert:"none"); + config->issuercert ? config->issuercert : "none"); } size = sizeof(certname); @@ -1650,8 +1727,8 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, 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); #ifndef CURL_DISABLE_PROXY - const char *pinned_key = Curl_ssl_cf_is_proxy(cf)? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: + const char *pinned_key = Curl_ssl_cf_is_proxy(cf) ? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : data->set.str[STRING_SSL_PINNEDPUBLICKEY]; #else const char *pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; @@ -1663,22 +1740,75 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, if(result) goto out; - if(connssl->alpn) { - gnutls_datum_t proto; - int rc; - - rc = gnutls_alpn_get_selected_protocol(session, &proto); - if(rc == 0) - Curl_alpn_set_negotiated(cf, data, proto.data, proto.size); - else - Curl_alpn_set_negotiated(cf, data, NULL, 0); - } - /* Only on TLSv1.2 or lower do we have the session id now. For * TLSv1.3 we get it via a SESSION_TICKET message that arrives later. */ if(gnutls_protocol_get_version(session) < GNUTLS_TLS1_3) - result = gtls_update_session_id(cf, data, session); + result = cf_gtls_update_session_id(cf, data, session); + +out: + return result; +} + +static CURLcode gtls_set_earlydata(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t blen) +{ + struct ssl_connect_data *connssl = cf->ctx; + ssize_t nwritten = 0; + CURLcode result = CURLE_OK; + + DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_use); + DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata)); + if(blen) { + if(blen > connssl->earlydata_max) + blen = connssl->earlydata_max; + nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result); + CURL_TRC_CF(data, cf, "gtls_set_earlydata(len=%zu) -> %zd", + blen, nwritten); + if(nwritten < 0) + return result; + } + connssl->earlydata_state = ssl_earlydata_sending; + connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata); + return CURLE_OK; +} + +static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + CURLcode result = CURLE_OK; + const unsigned char *buf; + size_t blen; + ssize_t n; + DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sending); + backend->gtls.io_result = CURLE_OK; + while(Curl_bufq_peek(&connssl->earlydata, &buf, &blen)) { + n = gnutls_record_send_early_data(backend->gtls.session, buf, blen); + CURL_TRC_CF(data, cf, "gtls_send_earlydata(len=%zu) -> %zd", + blen, n); + if(n < 0) { + if(n == GNUTLS_E_AGAIN) + result = CURLE_AGAIN; + else + result = backend->gtls.io_result ? + backend->gtls.io_result : CURLE_SEND_ERROR; + goto out; + } + else if(!n) { + /* gnutls is buggy, it *SHOULD* return the amount of bytes it took in. + * Instead it returns 0 if everything was written. */ + n = (ssize_t)blen; + } + + Curl_bufq_skip(&connssl->earlydata, (size_t)n); + } + /* sent everything there was */ + infof(data, "SSL sending %" FMT_OFF_T " bytes of early data", + connssl->earlydata_skip); out: return result; } @@ -1696,46 +1826,89 @@ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, bool nonblocking, - bool *done) -{ + bool *done) { struct ssl_connect_data *connssl = cf->ctx; - CURLcode rc; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; CURLcode result = CURLE_OK; + DEBUGASSERT(backend); + /* Initiate the connection, if not already done */ - if(ssl_connect_1 == connssl->connecting_state) { - rc = gtls_connect_step1(cf, data); - if(rc) { - result = rc; + if(connssl->connecting_state == ssl_connect_1) { + result = gtls_connect_step1(cf, data); + if(result) goto out; - } + connssl->connecting_state = ssl_connect_2; } - rc = handshake(cf, data, TRUE, nonblocking); - if(rc) { - /* handshake() sets its own error message with failf() */ - result = rc; - goto out; + if(connssl->connecting_state == ssl_connect_2) { + if(connssl->earlydata_state == ssl_earlydata_use) { + goto out; + } + else if(connssl->earlydata_state == ssl_earlydata_sending) { + result = gtls_send_earlydata(cf, data); + if(result) + goto out; + connssl->earlydata_state = ssl_earlydata_sent; + if(!Curl_ssl_cf_is_proxy(cf)) + Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip); + } + DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) || + (connssl->earlydata_state == ssl_earlydata_sent)); + + result = handshake(cf, data, TRUE, nonblocking); + if(result) + goto out; + connssl->connecting_state = ssl_connect_3; } /* Finish connecting once the handshake is done */ - if(ssl_connect_1 == connssl->connecting_state) { - struct gtls_ssl_backend_data *backend = - (struct gtls_ssl_backend_data *)connssl->backend; - gnutls_session_t session; - DEBUGASSERT(backend); - session = backend->gtls.session; - rc = gtls_verifyserver(cf, data, session); - if(rc) { - result = rc; + if(connssl->connecting_state == ssl_connect_3) { + gnutls_datum_t proto; + int rc; + result = gtls_verifyserver(cf, data, backend->gtls.session); + if(result) goto out; - } + connssl->state = ssl_connection_complete; + connssl->connecting_state = ssl_connect_1; + + rc = gnutls_alpn_get_selected_protocol(backend->gtls.session, &proto); + if(rc) { /* No ALPN from server */ + proto.data = NULL; + proto.size = 0; + } + + result = Curl_alpn_set_negotiated(cf, data, connssl, + proto.data, proto.size); + if(result) + goto out; + + if(connssl->earlydata_state == ssl_earlydata_sent) { + if(gnutls_session_get_flags(backend->gtls.session) & + GNUTLS_SFLAGS_EARLY_DATA) { + connssl->earlydata_state = ssl_earlydata_accepted; + infof(data, "Server accepted %zu bytes of TLS early data.", + connssl->earlydata_skip); + } + else { + connssl->earlydata_state = ssl_earlydata_rejected; + if(!Curl_ssl_cf_is_proxy(cf)) + Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip); + infof(data, "Server rejected TLS early data."); + connssl->earlydata_skip = 0; + } + } } out: - *done = ssl_connect_1 == connssl->connecting_state; - + if(result == CURLE_AGAIN) { + *done = FALSE; + return CURLE_OK; + } + *done = ((connssl->connecting_state == ssl_connect_1) || + (connssl->state == ssl_connection_deferred)); return result; } @@ -1743,6 +1916,12 @@ static CURLcode gtls_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { + struct ssl_connect_data *connssl = cf->ctx; + if(connssl->state == ssl_connection_deferred) { + /* We refuse to be pushed, we are waiting for someone to send/recv. */ + *done = TRUE; + return CURLE_OK; + } return gtls_connect_common(cf, data, TRUE, done); } @@ -1761,6 +1940,26 @@ static CURLcode gtls_connect(struct Curl_cfilter *cf, return CURLE_OK; } +static CURLcode gtls_connect_deferred(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, + size_t blen, + bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + CURLcode result = CURLE_OK; + + DEBUGASSERT(connssl->state == ssl_connection_deferred); + *done = FALSE; + if(connssl->earlydata_state == ssl_earlydata_use) { + result = gtls_set_earlydata(cf, data, buf, blen); + if(result) + return result; + } + + return gtls_connect_common(cf, data, TRUE, done); +} + static bool gtls_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { @@ -1788,8 +1987,38 @@ static ssize_t gtls_send(struct Curl_cfilter *cf, ssize_t rc; size_t nwritten, total_written = 0; - (void)data; DEBUGASSERT(backend); + + if(connssl->state == ssl_connection_deferred) { + bool done = FALSE; + *curlcode = gtls_connect_deferred(cf, data, buf, blen, &done); + if(*curlcode) { + rc = -1; + goto out; + } + else if(!done) { + *curlcode = CURLE_AGAIN; + rc = -1; + goto out; + } + DEBUGASSERT(connssl->state == ssl_connection_complete); + } + + if(connssl->earlydata_skip) { + if(connssl->earlydata_skip >= blen) { + connssl->earlydata_skip -= blen; + *curlcode = CURLE_OK; + rc = (ssize_t)blen; + goto out; + } + else { + total_written += connssl->earlydata_skip; + buf = ((const char *)buf) + connssl->earlydata_skip; + blen -= connssl->earlydata_skip; + connssl->earlydata_skip = 0; + } + } + while(blen) { backend->gtls.io_result = CURLE_OK; rc = gnutls_record_send(backend->gtls.session, buf, blen); @@ -1800,9 +2029,9 @@ static ssize_t gtls_send(struct Curl_cfilter *cf, rc = (ssize_t)total_written; goto out; } - *curlcode = (rc == GNUTLS_E_AGAIN)? + *curlcode = (rc == GNUTLS_E_AGAIN) ? CURLE_AGAIN : - (backend->gtls.io_result? backend->gtls.io_result : CURLE_SEND_ERROR); + (backend->gtls.io_result ? backend->gtls.io_result : CURLE_SEND_ERROR); rc = -1; goto out; @@ -1836,7 +2065,9 @@ static CURLcode gtls_shutdown(struct Curl_cfilter *cf, size_t i; DEBUGASSERT(backend); - if(!backend->gtls.session || cf->shutdown) { + /* If we have no handshaked connection or already shut down */ + if(!backend->gtls.session || cf->shutdown || + connssl->state != ssl_connection_complete) { *done = TRUE; goto out; } @@ -1851,7 +2082,7 @@ static CURLcode gtls_shutdown(struct Curl_cfilter *cf, int ret = gnutls_bye(backend->gtls.session, GNUTLS_SHUT_RDWR); if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { CURL_TRC_CF(data, cf, "SSL shutdown, gnutls_bye EAGAIN"); - connssl->io_need = gnutls_record_get_direction(backend->gtls.session)? + connssl->io_need = gnutls_record_get_direction(backend->gtls.session) ? CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV; backend->gtls.sent_shutdown = FALSE; result = CURLE_OK; @@ -1881,7 +2112,7 @@ static CURLcode gtls_shutdown(struct Curl_cfilter *cf, *done = TRUE; } else if((nread == GNUTLS_E_AGAIN) || (nread == GNUTLS_E_INTERRUPTED)) { - connssl->io_need = gnutls_record_get_direction(backend->gtls.session)? + connssl->io_need = gnutls_record_get_direction(backend->gtls.session) ? CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV; } else { @@ -1934,7 +2165,21 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf, (void)data; DEBUGASSERT(backend); - backend->gtls.io_result = CURLE_OK; + if(connssl->state == ssl_connection_deferred) { + bool done = FALSE; + *curlcode = gtls_connect_deferred(cf, data, NULL, 0, &done); + if(*curlcode) { + ret = -1; + goto out; + } + else if(!done) { + *curlcode = CURLE_AGAIN; + ret = -1; + goto out; + } + DEBUGASSERT(connssl->state == ssl_connection_complete); + } + ret = gnutls_record_recv(backend->gtls.session, buf, buffersize); if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { *curlcode = CURLE_AGAIN; @@ -1957,10 +2202,9 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf, if(ret < 0) { failf(data, "GnuTLS recv error (%d): %s", - (int)ret, gnutls_strerror((int)ret)); - *curlcode = backend->gtls.io_result? - backend->gtls.io_result : CURLE_RECV_ERROR; + *curlcode = backend->gtls.io_result ? + backend->gtls.io_result : CURLE_RECV_ERROR; ret = -1; goto out; } @@ -1981,7 +2225,7 @@ static CURLcode gtls_random(struct Curl_easy *data, int rc; (void)data; rc = gnutls_rnd(GNUTLS_RND_RANDOM, entropy, length); - return rc?CURLE_FAILED_INIT:CURLE_OK; + return rc ? CURLE_FAILED_INIT : CURLE_OK; } static CURLcode gtls_sha256sum(const unsigned char *tmp, /* input */ diff --git a/libs/libcurl/src/vtls/gtls.h b/libs/libcurl/src/vtls/gtls.h index 2ba237a315..4f3c089bc6 100644 --- a/libs/libcurl/src/vtls/gtls.h +++ b/libs/libcurl/src/vtls/gtls.h @@ -45,6 +45,7 @@ struct Curl_cfilter; struct ssl_primary_config; struct ssl_config_data; struct ssl_peer; +struct ssl_connect_data; struct gtls_shared_creds { gnutls_certificate_credentials_t creds; @@ -78,6 +79,7 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, struct Curl_easy *data, struct ssl_peer *peer, const unsigned char *alpn, size_t alpn_len, + struct ssl_connect_data *connssl, Curl_gtls_ctx_setup_cb *cb_setup, void *cb_user_data, void *ssl_user_data); @@ -93,6 +95,13 @@ CURLcode Curl_gtls_verifyserver(struct Curl_easy *data, struct ssl_peer *peer, const char *pinned_key); +/* Extract TLS session and place in cache, if configured. */ +CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_session_t session, + struct ssl_peer *peer, + const char *alpn); + extern const struct Curl_ssl Curl_ssl_gnutls; #endif /* USE_GNUTLS */ diff --git a/libs/libcurl/src/vtls/keylog.c b/libs/libcurl/src/vtls/keylog.c index 9cdfc02213..e403114934 100644 --- a/libs/libcurl/src/vtls/keylog.c +++ b/libs/libcurl/src/vtls/keylog.c @@ -99,13 +99,13 @@ Curl_tls_keylog_write_line(const char *line) char buf[256]; if(!keylog_file_fp || !line) { - return false; + return FALSE; } linelen = strlen(line); if(linelen == 0 || linelen > sizeof(buf) - 2) { /* Empty line or too big to fit in a LF and NUL. */ - return false; + return FALSE; } memcpy(buf, line, linelen); @@ -117,7 +117,7 @@ Curl_tls_keylog_write_line(const char *line) /* Using fputs here instead of fprintf since libcurl's fprintf replacement may not be thread-safe. */ fputs(buf, keylog_file_fp); - return true; + return TRUE; } bool @@ -131,13 +131,13 @@ Curl_tls_keylog_write(const char *label, 2 * SECRET_MAXLEN + 1 + 1]; if(!keylog_file_fp) { - return false; + return FALSE; } pos = strlen(label); if(pos > KEYLOG_LABEL_MAXLEN || !secretlen || secretlen > SECRET_MAXLEN) { /* Should never happen - sanity check anyway. */ - return false; + return FALSE; } memcpy(line, label, pos); @@ -161,7 +161,7 @@ Curl_tls_keylog_write(const char *label, /* Using fputs here instead of fprintf since libcurl's fprintf replacement may not be thread-safe. */ fputs(line, keylog_file_fp); - return true; + return TRUE; } #endif /* TLS or QUIC backend */ diff --git a/libs/libcurl/src/vtls/mbedtls.c b/libs/libcurl/src/vtls/mbedtls.c index 14fedf8b23..20226b74bd 100644 --- a/libs/libcurl/src/vtls/mbedtls.c +++ b/libs/libcurl/src/vtls/mbedtls.c @@ -36,13 +36,6 @@ /* Define this to enable lots of debugging for mbedTLS */ /* #define MBEDTLS_DEBUG */ -#ifdef __GNUC__ -#pragma GCC diagnostic push -/* mbedTLS (as of v3.5.1) has a duplicate function declaration - in its public headers. Disable the warning that detects it. */ -#pragma GCC diagnostic ignored "-Wredundant-decls" -#endif - #include #if MBEDTLS_VERSION_NUMBER >= 0x02040000 #include @@ -63,10 +56,6 @@ # endif #endif -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - #include "cipher_suite.h" #include "strcase.h" #include "urldata.h" @@ -129,6 +118,10 @@ struct mbed_ssl_backend_data { #define TLS13_SUPPORT #endif +#if defined(TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#define HAS_SESSION_TICKETS +#endif + #if defined(THREADING_SUPPORT) static mbedtls_entropy_context ts_entropy; @@ -302,7 +295,8 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, break; #endif default: - failf(data, "mbedTLS: unsupported minimum TLS version value"); + failf(data, "mbedTLS: unsupported minimum TLS version value: %x", + conn_config->version); return CURLE_SSL_CONNECT_ERROR; } @@ -351,6 +345,7 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, cipher suite present in other SSL implementations. Provide provisional support for specifying the cipher suite here. */ #ifdef MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 static int mbed_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, bool prefer_rfc) @@ -361,6 +356,7 @@ mbed_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, return Curl_cipher_suite_get_str(id, buf, buf_size, prefer_rfc); return 0; } +#endif static uint16_t mbed_cipher_suite_walk_str(const char **str, const char **end) @@ -552,7 +548,7 @@ static int mbed_verify_cb(void *ptr, mbedtls_x509_crt *crt, mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", *flags); failf(data, "mbedTLS: %s", buf); #else - failf(data, "mbedTLS: cerificate verification error 0x%08x", *flags); + failf(data, "mbedTLS: certificate verification error 0x%08x", *flags); #endif } @@ -638,7 +634,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) ret = mbedtls_x509_crt_parse(&backend->cacert, newblob, ca_info_blob->len + 1); free(newblob); - if(ret<0) { + if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s", -ret, errorbuf); @@ -650,7 +646,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef MBEDTLS_FS_IO ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile); - if(ret<0) { + if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s", ssl_cafile, -ret, errorbuf); @@ -666,7 +662,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef MBEDTLS_FS_IO ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath); - if(ret<0) { + if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s", ssl_capath, -ret, errorbuf); @@ -816,6 +812,12 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } +#ifdef MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED + /* New in mbedTLS 3.6.1, need to enable, default is now disabled */ + mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&backend->config, + MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED); +#endif + /* Always let mbedTLS verify certificates, if verifypeer or verifyhost are * disabled we clear the corresponding error flags in the verify callback * function. That is also where we log verification errors. */ @@ -883,17 +885,27 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* Check if there is a cached ID we can/should use here! */ if(ssl_config->primary.cache_session) { - void *old_session = NULL; + void *sdata = NULL; + size_t slen = 0; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &old_session, NULL)) { - ret = mbedtls_ssl_set_session(&backend->ssl, old_session); + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + &sdata, &slen, NULL) && slen) { + mbedtls_ssl_session session; + + mbedtls_ssl_session_init(&session); + ret = mbedtls_ssl_session_load(&session, sdata, slen); if(ret) { - Curl_ssl_sessionid_unlock(data); - failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret); - return CURLE_SSL_CONNECT_ERROR; + failf(data, "error loading cached session: -0x%x", -ret); } - infof(data, "mbedTLS reusing session"); + else { + ret = mbedtls_ssl_set_session(&backend->ssl, &session); + if(ret) + failf(data, "error setting session: -0x%x", -ret); + else + infof(data, "SSL reusing session ID"); + } + mbedtls_ssl_session_free(&session); } Curl_ssl_sessionid_unlock(data); } @@ -911,7 +923,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) &backend->clicert, &backend->pk); } - if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni? + if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni ? connssl->peer.sni : connssl->peer.hostname)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and the name to set in the SNI extension. So even if curl connects to a @@ -975,8 +987,8 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; #ifndef CURL_DISABLE_PROXY - const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_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]; @@ -1016,7 +1028,7 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) uint16_t cipher_id; cipher_id = (uint16_t) mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl); - mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), true); + mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), TRUE); infof(data, "mbedTLS: %s Handshake complete, cipher is %s", mbedtls_ssl_get_version(&backend->ssl), cipher_str); } @@ -1059,7 +1071,7 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* Make a copy of our const peercert because mbedtls_pk_write_pubkey_der needs a non-const key, for now. - https://github.com/ARMmbed/mbedtls/issues/396 */ + https://github.com/Mbed-TLS/mbedtls/issues/396 */ #if MBEDTLS_VERSION_NUMBER == 0x03000000 if(mbedtls_x509_crt_parse_der(p, peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p), @@ -1102,8 +1114,8 @@ pinnedpubkey_error: if(connssl->alpn) { const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl); - Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto, - proto? strlen(proto) : 0); + Curl_alpn_set_negotiated(cf, data, connssl, (const unsigned char *)proto, + proto ? strlen(proto) : 0); } #endif @@ -1113,57 +1125,62 @@ pinnedpubkey_error: return CURLE_OK; } -static void mbedtls_session_free(void *sessionid, size_t idsize) +static void mbedtls_session_free(void *session, size_t slen) { - (void)idsize; - mbedtls_ssl_session_free(sessionid); - free(sessionid); + (void)slen; + free(session); } static CURLcode -mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) +mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data) { - CURLcode retcode = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_OK; - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); - if(ssl_config->primary.cache_session) { int ret; - mbedtls_ssl_session *our_ssl_sessionid; - - our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session)); - if(!our_ssl_sessionid) - return CURLE_OUT_OF_MEMORY; - - mbedtls_ssl_session_init(our_ssl_sessionid); + mbedtls_ssl_session session; + unsigned char *sdata = NULL; + size_t slen = 0; - ret = mbedtls_ssl_get_session(&backend->ssl, our_ssl_sessionid); + mbedtls_ssl_session_init(&session); + ret = mbedtls_ssl_get_session(&backend->ssl, &session); if(ret) { if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED) - mbedtls_ssl_session_free(our_ssl_sessionid); - free(our_ssl_sessionid); + mbedtls_ssl_session_free(&session); failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); return CURLE_SSL_CONNECT_ERROR; } - /* If there is already a matching session in the cache, delete it */ - Curl_ssl_sessionid_lock(data); - retcode = Curl_ssl_set_sessionid(cf, data, &connssl->peer, - our_ssl_sessionid, 0, - mbedtls_session_free); - Curl_ssl_sessionid_unlock(data); - if(retcode) - return retcode; + mbedtls_ssl_session_save(&session, NULL, 0, &slen); + if(!slen) { + failf(data, "failed to serialize session: length is 0"); + } + else { + sdata = malloc(slen); + if(sdata) { + ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen); + if(ret) { + failf(data, "failed to serialize session: -0x%x", -ret); + } + else { + Curl_ssl_sessionid_lock(data); + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, + sdata, slen, mbedtls_session_free); + Curl_ssl_sessionid_unlock(data); + if(!result) + sdata = NULL; + } + } + } + mbedtls_ssl_session_free(&session); + free(sdata); } - - connssl->connecting_state = ssl_connect_done; - - return CURLE_OK; + return result; } static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1186,7 +1203,7 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, #ifdef TLS13_SUPPORT || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) #endif - )? CURLE_AGAIN : CURLE_SEND_ERROR; + ) ? CURLE_AGAIN : CURLE_SEND_ERROR; ret = -1; } @@ -1322,7 +1339,6 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; int ret = -1; - ssize_t len = -1; (void)data; DEBUGASSERT(backend); @@ -1332,24 +1348,31 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(ret <= 0) { CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", buffersize, -ret); - if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) - return 0; - *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_READ) -#ifdef TLS13_SUPPORT - || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + switch(ret) { +#ifdef HAS_SESSION_TICKETS + case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: + mbed_new_session(cf, data); + FALLTHROUGH(); #endif - ) ? CURLE_AGAIN : CURLE_RECV_ERROR; - if(*curlcode != CURLE_AGAIN) { + case MBEDTLS_ERR_SSL_WANT_READ: + *curlcode = CURLE_AGAIN; + ret = -1; + break; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + *curlcode = CURLE_OK; + ret = 0; + break; + default: { char errorbuf[128]; mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "ssl_read returned: (-0x%04X) %s", -ret, errorbuf); + *curlcode = CURLE_RECV_ERROR; + ret = -1; + break; + } } - return -1; } - - len = ret; - - return len; + return (ssize_t)ret; } static size_t mbedtls_version(char *buffer, size_t size) @@ -1357,42 +1380,31 @@ static size_t mbedtls_version(char *buffer, size_t size) #ifdef MBEDTLS_VERSION_C /* if mbedtls_version_get_number() is available it is better */ unsigned int version = mbedtls_version_get_number(); - return msnprintf(buffer, size, "mbedTLS/%u.%u.%u", version>>24, - (version>>16)&0xff, (version>>8)&0xff); + return msnprintf(buffer, size, "mbedTLS/%u.%u.%u", version >> 24, + (version >> 16) & 0xff, (version >> 8) & 0xff); #else return msnprintf(buffer, size, "mbedTLS/%s", MBEDTLS_VERSION_STRING); #endif } +/* 'data' might be NULL */ static CURLcode mbedtls_random(struct Curl_easy *data, unsigned char *entropy, size_t length) { #if defined(MBEDTLS_CTR_DRBG_C) - int ret = -1; - char errorbuf[128]; + int ret; mbedtls_entropy_context ctr_entropy; mbedtls_ctr_drbg_context ctr_drbg; mbedtls_entropy_init(&ctr_entropy); mbedtls_ctr_drbg_init(&ctr_drbg); + (void)data; ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &ctr_entropy, NULL, 0); - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", - -ret, errorbuf); - } - else { + if(!ret) ret = mbedtls_ctr_drbg_random(&ctr_drbg, entropy, length); - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_random returned (-0x%04X) %s", - -ret, errorbuf); - } - } - mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&ctr_entropy); @@ -1452,11 +1464,10 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, /* if ssl is expecting something, check if it is available. */ if(connssl->io_need) { - - 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; + 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); @@ -1495,9 +1506,22 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, } /* repeat step2 until all transactions are done. */ if(ssl_connect_3 == connssl->connecting_state) { - retcode = mbed_connect_step3(cf, data); - if(retcode) - return retcode; + /* For tls1.3 we get notified about new sessions */ +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + struct ssl_connect_data *ctx = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)ctx->backend; + + if(mbedtls_ssl_get_version_number(&backend->ssl) <= + MBEDTLS_SSL_VERSION_TLS1_2) { +#else + { /* no TLSv1.3 supported here */ +#endif + retcode = mbed_new_session(cf, data); + if(retcode) + return retcode; + } + connssl->connecting_state = ssl_connect_done; } if(ssl_connect_done == connssl->connecting_state) { diff --git a/libs/libcurl/src/vtls/openssl.c b/libs/libcurl/src/vtls/openssl.c index 4aea58d87c..86931089b1 100644 --- a/libs/libcurl/src/vtls/openssl.c +++ b/libs/libcurl/src/vtls/openssl.c @@ -247,7 +247,7 @@ #elif defined(OPENSSL_IS_AWSLC) #define OSSL_PACKAGE "AWS-LC" #else -# if defined(USE_NGTCP2) && defined(USE_NGHTTP3) +# if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_MSH3) # define OSSL_PACKAGE "quictls" # else # define OSSL_PACKAGE "OpenSSL" @@ -916,7 +916,7 @@ ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) if(master_key_length <= 0) return; - *keylog_done = true; + *keylog_done = TRUE; Curl_tls_keylog_write("CLIENT_RANDOM", client_random, master_key, master_key_length); } @@ -1015,7 +1015,7 @@ static int passwd_callback(char *buf, int num, int encrypting, */ static bool rand_enough(void) { - return (0 != RAND_status()) ? TRUE : FALSE; + return (0 != RAND_status()); } static CURLcode ossl_seed(struct Curl_easy *data) @@ -1558,7 +1558,8 @@ fail: SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type); if(cert_use_result != 1) { failf(data, "unable to set private key file: '%s' type %s", - key_file?key_file:"(memory blob)", key_type?key_type:"PEM"); + key_file ? key_file : "(memory blob)", + key_type ? key_type : "PEM"); return 0; } break; @@ -1680,29 +1681,23 @@ fail: } /* returns non-zero on failure */ -static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) +static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d) { BIO *bio_out = BIO_new(BIO_s_mem()); BUF_MEM *biomem; int rc; - - if(!bio_out) - return 1; /* alloc failed! */ - - rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC); - BIO_get_mem_ptr(bio_out, &biomem); - - if((size_t)biomem->length < size) - size = biomem->length; - else - size--; /* do not overwrite the buffer end */ - - memcpy(buf, biomem->data, size); - buf[size] = 0; - - BIO_free(bio_out); - - return !rc; + CURLcode result = CURLE_OUT_OF_MEMORY; + + if(bio_out) { + Curl_dyn_reset(d); + rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC); + if(rc != -1) { + BIO_get_mem_ptr(bio_out, &biomem); + result = Curl_dyn_addn(d, biomem->data, biomem->length); + BIO_free(bio_out); + } + } + return result; } /** @@ -1940,8 +1935,9 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, /* 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) { + if(send_shutdown && !(SSL_get_shutdown(octx->ssl) & SSL_SENT_SHUTDOWN)) { ERR_clear_error(); + CURL_TRC_CF(data, cf, "send SSL close notify"); if(SSL_shutdown(octx->ssl) == 1) { CURL_TRC_CF(data, cf, "SSL shutdown finished"); *done = TRUE; @@ -1966,7 +1962,10 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, err = SSL_get_error(octx->ssl, nread); switch(err) { case SSL_ERROR_ZERO_RETURN: /* no more data */ - CURL_TRC_CF(data, cf, "SSL shutdown not received, but closed"); + if(SSL_shutdown(octx->ssl) == 1) + CURL_TRC_CF(data, cf, "SSL shutdown finished"); + else + CURL_TRC_CF(data, cf, "SSL shutdown not received, but closed"); *done = TRUE; break; case SSL_ERROR_NONE: /* just did not get anything */ @@ -2234,8 +2233,9 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, /* we have to look to the last occurrence of a commonName in the distinguished one to get the most significant one. */ int i = -1; - unsigned char *peer_CN = NULL; - int peerlen = 0; + unsigned char *cn = NULL; + int cnlen = 0; + bool free_cn = FALSE; /* The following is done because of a bug in 0.9.6b */ X509_NAME *name = X509_get_subject_name(server_cert); @@ -2259,21 +2259,17 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, conditional in the future when OpenSSL has been fixed. */ if(tmp) { if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { - peerlen = ASN1_STRING_length(tmp); - if(peerlen >= 0) { - peer_CN = OPENSSL_malloc(peerlen + 1); - if(peer_CN) { - memcpy(peer_CN, ASN1_STRING_get0_data(tmp), peerlen); - peer_CN[peerlen] = '\0'; - } - else - result = CURLE_OUT_OF_MEMORY; - } + cnlen = ASN1_STRING_length(tmp); + cn = (unsigned char *) ASN1_STRING_get0_data(tmp); + } + else { /* not a UTF8 name */ + cnlen = ASN1_STRING_to_UTF8(&cn, tmp); + free_cn = TRUE; } - else /* not a UTF8 name */ - peerlen = ASN1_STRING_to_UTF8(&peer_CN, tmp); - if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != peerlen)) { + if((cnlen <= 0) || !cn) + result = CURLE_OUT_OF_MEMORY; + else if((size_t)cnlen != strlen((char *)cn)) { /* there was a terminating zero before the end of string, this cannot match and we return failure! */ failf(data, "SSL: illegal cert name field"); @@ -2285,22 +2281,22 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if(result) /* error already detected, pass through */ ; - else if(!peer_CN) { + else if(!cn) { failf(data, "SSL: unable to obtain common name from peer certificate"); result = CURLE_PEER_FAILED_VERIFICATION; } - else if(!Curl_cert_hostcheck((const char *)peer_CN, - peerlen, peer->hostname, hostlen)) { + else if(!Curl_cert_hostcheck((const char *)cn, cnlen, + peer->hostname, hostlen)) { failf(data, "SSL: certificate subject name '%s' does not match " - "target hostname '%s'", peer_CN, peer->dispname); + "target hostname '%s'", cn, peer->dispname); result = CURLE_PEER_FAILED_VERIFICATION; } else { - infof(data, " common name: %s (matched)", peer_CN); + infof(data, " common name: %s (matched)", cn); } - if(peer_CN) - OPENSSL_free(peer_CN); + if(free_cn) + OPENSSL_free(cn); } return result; @@ -2685,11 +2681,9 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n", - verstr, direction?"OUT":"IN", + verstr, direction ? "OUT" : "IN", tls_rt_name, msg_name, msg_type); - if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) { - Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len); - } + Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len); } Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT : @@ -2922,8 +2916,8 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, } Curl_ssl_sessionid_lock(data); - result = Curl_ssl_set_sessionid(cf, data, peer, der_session_buf, - der_session_size, ossl_session_free); + result = Curl_ssl_set_sessionid(cf, data, peer, NULL, der_session_buf, + der_session_size, ossl_session_free); Curl_ssl_sessionid_unlock(data); } @@ -2941,8 +2935,8 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) struct ssl_connect_data *connssl; cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); - connssl = cf? cf->ctx : NULL; - data = connssl? CF_DATA_CURRENT(cf) : NULL; + connssl = cf ? cf->ctx : NULL; + data = connssl ? CF_DATA_CURRENT(cf) : NULL; Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid); return 0; } @@ -3012,7 +3006,7 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data, CURLcode result = CURLE_OK; HCERTSTORE hStore; - *imported = false; + *imported = FALSE; hStore = CertOpenSystemStoreA(0, name); if(hStore) { @@ -3034,20 +3028,19 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data, BYTE key_usage[2]; DWORD req_size; const unsigned char *encoded_cert; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - char cert_name[256]; -#endif - pContext = CertEnumCertificatesInStore(hStore, pContext); if(!pContext) break; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, - NULL, cert_name, sizeof(cert_name))) { - strcpy(cert_name, "Unknown"); + else { + char cert_name[256]; + if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, + NULL, cert_name, sizeof(cert_name))) + infof(data, "SSL: unknown cert name"); + else + infof(data, "SSL: Checking cert \"%s\"", cert_name); } - infof(data, "SSL: Checking cert \"%s\"", cert_name); #endif encoded_cert = (const unsigned char *)pContext->pbCertEncoded; if(!encoded_cert) @@ -3100,12 +3093,12 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data, } else { DWORD i; - bool found = false; + bool found = FALSE; for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, enhkey_usage->rgpszUsageIdentifier[i])) { - found = true; + found = TRUE; break; } } @@ -3129,9 +3122,9 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data, not OpenSSL. */ if(X509_STORE_add_cert(store, x509) == 1) { #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - infof(data, "SSL: Imported cert \"%s\"", cert_name); + infof(data, "SSL: Imported cert"); #endif - *imported = true; + *imported = TRUE; } X509_free(x509); } @@ -3163,11 +3156,11 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, const char * const ssl_capath = conn_config->CApath; const char * const ssl_crlfile = ssl_config->primary.CRLfile; const bool verifypeer = conn_config->verifypeer; - bool imported_native_ca = false; - bool imported_ca_info_blob = false; + bool imported_native_ca = FALSE; + bool imported_ca_info_blob = FALSE; CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d", - ssl_cafile? ssl_cafile : "none", !!ca_info_blob); + ssl_cafile ? ssl_cafile : "none", !!ca_info_blob); if(!store) return CURLE_OUT_OF_MEMORY; @@ -3185,14 +3178,14 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, }; size_t i; for(i = 0; i < ARRAYSIZE(storeNames); ++i) { - bool imported = false; + bool imported = FALSE; result = import_windows_cert_store(data, storeNames[i], store, &imported); if(result) return result; if(imported) { infof(data, "successfully imported Windows %s store", storeNames[i]); - imported_native_ca = true; + imported_native_ca = TRUE; } else infof(data, "error importing Windows %s store, continuing anyway", @@ -3207,7 +3200,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, return result; } else { - imported_ca_info_blob = true; + imported_ca_info_blob = TRUE; infof(data, "successfully imported CA certificate blob"); } } @@ -3371,9 +3364,9 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, X509_STORE *store = NULL; DEBUGASSERT(multi); - share = multi? Curl_hash_pick(&multi->proto_hash, - (void *)MPROTO_OSSL_X509_KEY, - sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL; + share = multi ? Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_OSSL_X509_KEY, + sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL; if(share && share->store && !cached_x509_store_expired(data, share) && !cached_x509_store_different(cf, share)) { @@ -3804,10 +3797,10 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return result; octx->x509_store_setup = TRUE; } - Curl_set_in_callback(data, true); + Curl_set_in_callback(data, TRUE); result = (*data->set.ssl.fsslctx)(data, octx->ssl_ctx, data->set.ssl.fsslctxp); - Curl_set_in_callback(data, false); + Curl_set_in_callback(data, FALSE); if(result) { failf(data, "error signaled by ssl ctx callback"); return result; @@ -3979,10 +3972,10 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, #endif octx->reused_session = FALSE; - if(ssl_config->primary.cache_session && transport == TRNSPRT_TCP) { + if(ssl_config->primary.cache_session) { Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid, - &der_sessionid_size)) { + &der_sessionid_size, NULL)) { /* we got a session id, use it! */ ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid, (long)der_sessionid_size); @@ -4001,8 +3994,8 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, octx->reused_session = TRUE; } else { - Curl_ssl_sessionid_unlock(data); - return CURLE_SSL_CONNECT_ERROR; + Curl_ssl_sessionid_unlock(data); + return CURLE_SSL_CONNECT_ERROR; } } Curl_ssl_sessionid_unlock(data); @@ -4231,10 +4224,10 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, "SSL certificate problem: %s", X509_verify_cert_error_string(lerr)); } - else - /* strcpy() is fine here as long as the string fits within - error_buffer */ - strcpy(error_buffer, "SSL certificate verification failed"); + else { + failf(data, "%s", "SSL certificate verification failed"); + return result; + } } #if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED) /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on @@ -4244,7 +4237,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, /* If client certificate is required, communicate the error to client */ result = CURLE_SSL_CLIENTCERT; - ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + failf(data, "TLS cert problem: %s", + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer))); } #endif #ifdef USE_ECH @@ -4259,12 +4253,14 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, ossl_trace_ech_retry_configs(data, octx->ssl, reason); result = CURLE_ECH_REQUIRED; - ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + failf(data, "ECH required: %s", + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer))); } #endif else { result = CURLE_SSL_CONNECT_ERROR; - ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + failf(data, "TLS connect error: %s", + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer))); } /* detail is already set to the SSL error above */ @@ -4285,9 +4281,6 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, return result; } - /* Could be a CERT problem */ - failf(data, "%s", error_buffer); - return result; } } @@ -4312,7 +4305,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, infof(data, "SSL connection using %s / %s / %s / %s", SSL_get_version(octx->ssl), SSL_get_cipher(octx->ssl), - negotiated_group_name? negotiated_group_name : "[blank]", + negotiated_group_name ? negotiated_group_name : "[blank]", OBJ_nid2sn(psigtype_nid)); #ifdef USE_ECH @@ -4356,9 +4349,9 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, infof(data, "ECH: unexpected status %d",rv); } infof(data, "ECH: result: status is %s, inner is %s, outer is %s", - (status?status:"NULL"), - (inner?inner:"NULL"), - (outer?outer:"NULL")); + (status ? status : "NULL"), + (inner ? inner : "NULL"), + (outer ? outer : "NULL")); OPENSSL_free(inner); OPENSSL_free(outer); if(rv == SSL_ECH_STATUS_GREASE_ECH) { @@ -4386,7 +4379,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, unsigned int len; SSL_get0_alpn_selected(octx->ssl, &neg_protocol, &len); - return Curl_alpn_set_negotiated(cf, data, neg_protocol, len); + return Curl_alpn_set_negotiated(cf, data, connssl, neg_protocol, len); } #endif @@ -4521,6 +4514,8 @@ static void infof_certstack(struct Curl_easy *data, const SSL *ssl) #define infof_certstack(data, ssl) #endif +#define MAX_CERT_NAME_LENGTH 2048 + CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, struct Curl_easy *data, struct ossl_ctx *octx, @@ -4530,18 +4525,19 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); CURLcode result = CURLE_OK; - int rc; long lerr; X509 *issuer; BIO *fp = NULL; char error_buffer[256]=""; - char buffer[2048]; const char *ptr; BIO *mem = BIO_new(BIO_s_mem()); bool strict = (conn_config->verifypeer || conn_config->verifyhost); + struct dynbuf dname; DEBUGASSERT(octx); + Curl_dyn_init(&dname, MAX_CERT_NAME_LENGTH); + if(!mem) { failf(data, "BIO_new return NULL, " OSSL_PACKAGE @@ -4566,11 +4562,11 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, } infof(data, "%s certificate:", - Curl_ssl_cf_is_proxy(cf)? "Proxy" : "Server"); + Curl_ssl_cf_is_proxy(cf) ? "Proxy" : "Server"); - rc = x509_name_oneline(X509_get_subject_name(octx->server_cert), - buffer, sizeof(buffer)); - infof(data, " subject: %s", rc?"[NONE]":buffer); + result = x509_name_oneline(X509_get_subject_name(octx->server_cert), + &dname); + infof(data, " subject: %s", result ? "[NONE]" : Curl_dyn_ptr(&dname)); #ifndef CURL_DISABLE_VERBOSE_STRINGS { @@ -4594,19 +4590,21 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, if(result) { X509_free(octx->server_cert); octx->server_cert = NULL; + Curl_dyn_free(&dname); return result; } } - rc = x509_name_oneline(X509_get_issuer_name(octx->server_cert), - buffer, sizeof(buffer)); - if(rc) { + result = x509_name_oneline(X509_get_issuer_name(octx->server_cert), + &dname); + if(result) { if(strict) failf(data, "SSL: could not get X509-issuer name"); result = CURLE_PEER_FAILED_VERIFICATION; } else { - infof(data, " issuer: %s", buffer); + infof(data, " issuer: %s", Curl_dyn_ptr(&dname)); + Curl_dyn_free(&dname); /* We could do all sorts of certificate verification stuff here before deallocating the certificate. */ @@ -4699,7 +4697,6 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, else infof(data, " SSL certificate verify ok."); } - infof_certstack(data, octx->ssl); #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ @@ -4715,7 +4712,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, bool incache; Curl_ssl_sessionid_lock(data); incache = !(Curl_ssl_getsessionid(cf, data, peer, - &old_ssl_sessionid, NULL)); + &old_ssl_sessionid, NULL, NULL)); if(incache) { infof(data, "Remove session ID again from cache"); Curl_ssl_delsessionid(data, old_ssl_sessionid); @@ -4735,8 +4732,8 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, result = CURLE_OK; #ifndef CURL_DISABLE_PROXY - ptr = Curl_ssl_cf_is_proxy(cf)? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: + ptr = Curl_ssl_cf_is_proxy(cf) ? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : data->set.str[STRING_SSL_PINNEDPUBLICKEY]; #else ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; @@ -4822,11 +4819,10 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, /* if ssl is expecting something, check if it is available. */ if(!nonblocking && connssl->io_need) { - - 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; + 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, timeout_ms); @@ -5136,9 +5132,8 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, } while(cf->next); if(!octx) { - failf(data, - "Failed to find SSL backend for endpoint"); - return CURLE_SSL_ENGINE_INITFAILED; + failf(data, "Failed to find the SSL filter"); + return CURLE_BAD_FUNCTION_ARGUMENT; } cert = SSL_get1_peer_certificate(octx->ssl); @@ -5209,9 +5204,9 @@ static size_t ossl_version(char *buffer, size_t size) #else return msnprintf(buffer, size, "%s/%lx.%lx.%lx", OSSL_PACKAGE, - (LIBRESSL_VERSION_NUMBER>>28)&0xf, - (LIBRESSL_VERSION_NUMBER>>20)&0xff, - (LIBRESSL_VERSION_NUMBER>>12)&0xff); + (LIBRESSL_VERSION_NUMBER >> 28) & 0xf, + (LIBRESSL_VERSION_NUMBER >> 20) & 0xff, + (LIBRESSL_VERSION_NUMBER >> 12) & 0xff); #endif #elif defined(OPENSSL_IS_BORINGSSL) #ifdef CURL_BORINGSSL_VERSION @@ -5262,9 +5257,9 @@ static size_t ossl_version(char *buffer, size_t size) #endif , OSSL_PACKAGE, - (ssleay_value>>28)&0xf, - (ssleay_value>>20)&0xff, - (ssleay_value>>12)&0xff, + (ssleay_value >> 28) & 0xf, + (ssleay_value >> 20) & 0xff, + (ssleay_value >> 12) & 0xff, sub); #endif /* OPENSSL_IS_BORINGSSL */ } diff --git a/libs/libcurl/src/vtls/rustls.c b/libs/libcurl/src/vtls/rustls.c index 18284eeffd..b10328d4aa 100644 --- a/libs/libcurl/src/vtls/rustls.c +++ b/libs/libcurl/src/vtls/rustls.c @@ -37,6 +37,7 @@ #include "sendf.h" #include "vtls.h" #include "vtls_int.h" +#include "rustls.h" #include "select.h" #include "strerror.h" #include "multiif.h" @@ -570,7 +571,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, break; default: failf(data, "rustls: unsupported minimum TLS version value"); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_BAD_FUNCTION_ARGUMENT; } switch(conn_config->version_max) { @@ -588,7 +589,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, case CURL_SSLVERSION_MAX_TLSv1_0: default: failf(data, "rustls: unsupported maximum TLS version value"); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_BAD_FUNCTION_ARGUMENT; } cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len)); @@ -610,7 +611,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to create crypto provider builder from default"); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_SSL_CIPHER; } result = @@ -622,7 +623,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, failf(data, "rustls: failed to set ciphersuites for crypto provider builder"); rustls_crypto_provider_builder_free(custom_provider_builder); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_SSL_CIPHER; } result = rustls_crypto_provider_builder_build( @@ -630,7 +631,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to build custom crypto provider"); rustls_crypto_provider_builder_free(custom_provider_builder); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_SSL_CIPHER; } result = rustls_client_config_builder_new_custom(custom_provider, @@ -640,7 +641,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, free(cipher_suites); if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to create client config"); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_SSL_CIPHER; } } @@ -747,7 +748,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to build client config"); rustls_client_config_free(backend->config); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_SSL_CONNECT_ERROR; } DEBUGASSERT(rconn == NULL); @@ -768,11 +769,12 @@ static void cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data, const struct rustls_connection *rconn) { + struct ssl_connect_data *const connssl = cf->ctx; const uint8_t *protocol = NULL; size_t len = 0; rustls_connection_get_alpn_protocol(rconn, &protocol, &len); - Curl_alpn_set_negotiated(cf, data, protocol, len); + Curl_alpn_set_negotiated(cf, data, connssl, protocol, len); } /* Given an established network connection, do a TLS handshake. @@ -852,7 +854,7 @@ cr_connect_common(struct Curl_cfilter *cf, ver = "TLSv1.3"; if(proto == RUSTLS_TLS_VERSION_TLSV1_2) ver = "TLSv1.2"; - Curl_cipher_suite_get_str(cipher, buf, sizeof(buf), true); + Curl_cipher_suite_get_str(cipher, buf, sizeof(buf), TRUE); infof(data, "rustls: handshake complete, %s, cipher: %s", ver, buf); } @@ -866,8 +868,8 @@ cr_connect_common(struct Curl_cfilter *cf, wants_write = rustls_connection_wants_write(rconn) || backend->plain_out_buffered; DEBUGASSERT(wants_read || wants_write); - writefd = wants_write?sockfd:CURL_SOCKET_BAD; - readfd = wants_read?sockfd:CURL_SOCKET_BAD; + writefd = wants_write ? sockfd : CURL_SOCKET_BAD; + readfd = wants_read ? sockfd : CURL_SOCKET_BAD; /* check allowed time left */ timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -878,7 +880,7 @@ cr_connect_common(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - socket_check_timeout = blocking?timeout_ms:0; + socket_check_timeout = blocking ? timeout_ms : 0; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, socket_check_timeout); @@ -894,7 +896,7 @@ cr_connect_common(struct Curl_cfilter *cf, } if(0 == what) { CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block", - wants_read&&wants_write ? "writing and reading" : + wants_read && wants_write ? "writing and reading" : wants_write ? "writing" : "reading"); if(wants_write) connssl->io_need |= CURL_SSL_IO_NEED_SEND; @@ -935,7 +937,7 @@ cr_connect_common(struct Curl_cfilter *cf, /* We should never fall through the loop. We should return either because the handshake is done or because we cannot read/write without blocking. */ - DEBUGASSERT(false); + DEBUGASSERT(FALSE); } static CURLcode diff --git a/libs/libcurl/src/vtls/schannel.c b/libs/libcurl/src/vtls/schannel.c index d785ea2c60..85d018d322 100644 --- a/libs/libcurl/src/vtls/schannel.c +++ b/libs/libcurl/src/vtls/schannel.c @@ -59,6 +59,17 @@ #include "curl_memory.h" #include "memdebug.h" +/* Some verbose debug messages are wrapped by SCH_DEV() instead of DEBUGF() + * and only shown if CURL_SCHANNEL_DEV_DEBUG was defined at build time. These + * messages are extra verbose and intended for curl developers debugging + * Schannel recv decryption. + */ +#ifdef CURL_SCHANNEL_DEV_DEBUG +#define SCH_DEV(x) x +#else +#define SCH_DEV(x) do { } while(0) +#endif + /* ALPN requires version 8.1 of the Windows SDK, which was shipped with Visual Studio 2013, aka _MSC_VER 1800: @@ -1090,14 +1101,14 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL); #else - backend->use_alpn = false; + backend->use_alpn = FALSE; #endif #ifdef _WIN32_WCE #ifdef HAS_MANUAL_VERIFY_API /* certificate validation on CE does not seem to work right; we will * do it following a more manual process. */ - backend->use_manual_cred_validation = true; + backend->use_manual_cred_validation = TRUE; #else #error "compiler too old to support Windows CE requisite manual cert verify" #endif @@ -1106,7 +1117,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(conn_config->CAfile || conn_config->ca_info_blob) { if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { - backend->use_manual_cred_validation = true; + backend->use_manual_cred_validation = TRUE; } else { failf(data, "schannel: this version of Windows is too old to support " @@ -1115,7 +1126,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } else - backend->use_manual_cred_validation = false; + backend->use_manual_cred_validation = FALSE; #else if(conn_config->CAfile || conn_config->ca_info_blob) { failf(data, "schannel: CA cert support not built in"); @@ -1130,7 +1141,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(ssl_config->primary.cache_session) { Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, - (void **)&old_cred, NULL)) { + (void **)&old_cred, NULL, NULL)) { backend->cred = old_cred; DEBUGF(infof(data, "schannel: reusing existing credential handle")); @@ -1153,7 +1164,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* A hostname associated with the credential is needed by InitializeSecurityContext for SNI and other reasons. */ - snihost = connssl->peer.sni? connssl->peer.sni : connssl->peer.hostname; + snihost = connssl->peer.sni ? connssl->peer.sni : connssl->peer.hostname; backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); if(!backend->cred->sni_hostname) return CURLE_OUT_OF_MEMORY; @@ -1300,10 +1311,10 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) "sent %zd bytes", written)); backend->recv_unrecoverable_err = CURLE_OK; - backend->recv_sspi_close_notify = false; - backend->recv_connection_closed = false; - backend->recv_renegotiating = false; - backend->encdata_is_incomplete = false; + backend->recv_sspi_close_notify = FALSE; + backend->recv_connection_closed = FALSE; + backend->recv_renegotiating = FALSE; + backend->encdata_is_incomplete = FALSE; /* continue to second handshake step */ connssl->connecting_state = ssl_connect_2; @@ -1332,7 +1343,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(backend); - doread = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? FALSE : TRUE; + doread = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ? FALSE : TRUE; connssl->io_need = CURL_SSL_IO_NEED_NONE; DEBUGF(infof(data, @@ -1355,7 +1366,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* buffer to store previously received and encrypted data */ if(!backend->encdata_buffer) { - backend->encdata_is_incomplete = false; + backend->encdata_is_incomplete = FALSE; backend->encdata_offset = 0; backend->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; backend->encdata_buffer = malloc(backend->encdata_length); @@ -1407,13 +1418,13 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* increase encrypted data buffer offset */ backend->encdata_offset += nread; - backend->encdata_is_incomplete = false; - DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); + backend->encdata_is_incomplete = FALSE; + SCH_DEV(infof(data, "schannel: encrypted data got %zd", nread)); } - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + SCH_DEV(infof(data, + "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); /* setup input buffers */ InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(backend->encdata_offset), @@ -1447,7 +1458,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* check if the handshake was incomplete */ if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { - backend->encdata_is_incomplete = true; + backend->encdata_is_incomplete = TRUE; connssl->io_need = CURL_SSL_IO_NEED_RECV; DEBUGF(infof(data, "schannel: received incomplete message, need more data")); @@ -1527,8 +1538,8 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* check if there was additional remaining encrypted data */ if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) { - DEBUGF(infof(data, "schannel: encrypted data length: %lu", - inbuf[1].cbBuffer)); + SCH_DEV(infof(data, "schannel: encrypted data length: %lu", + inbuf[1].cbBuffer)); /* There are two cases where we could be getting extra data here: 1) If we are renegotiating a connection and the handshake is already @@ -1571,8 +1582,8 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } #ifndef CURL_DISABLE_PROXY - pubkey_ptr = Curl_ssl_cf_is_proxy(cf)? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: + pubkey_ptr = Curl_ssl_cf_is_proxy(cf) ? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : data->set.str[STRING_SSL_PINNEDPUBLICKEY]; #else pubkey_ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; @@ -1617,9 +1628,9 @@ traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, void *arg) { const CERT_CONTEXT *current_context = NULL; - bool should_continue = true; - bool first = true; - bool reverse_order = false; + bool should_continue = TRUE; + bool first = TRUE; + bool reverse_order = FALSE; while(should_continue && (current_context = CertEnumCertificatesInStore( context->hCertStore, @@ -1630,9 +1641,9 @@ traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, by comparing SECPKG_ATTR_REMOTE_CERT_CONTEXT's pbCertContext with the first certificate's pbCertContext. */ if(first && context->pbCertEncoded != current_context->pbCertEncoded) - reverse_order = true; + reverse_order = TRUE; should_continue = func(current_context, reverse_order, arg); - first = false; + first = FALSE; } if(current_context) @@ -1646,7 +1657,7 @@ cert_counter_callback(const CERT_CONTEXT *ccert_context, bool reverse_order, (void)reverse_order; /* unused */ if(valid_cert_encoding(ccert_context)) (*(int *)certs_count)++; - return true; + return TRUE; } struct Adder_args @@ -1752,7 +1763,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) SecApplicationProtocolNegotiationStatus_Success) { unsigned char prev_alpn = cf->conn->alpn; - Curl_alpn_set_negotiated(cf, data, alpn_result.ProtocolId, + Curl_alpn_set_negotiated(cf, data, connssl, alpn_result.ProtocolId, alpn_result.ProtocolIdSize); if(backend->recv_renegotiating) { if(prev_alpn != cf->conn->alpn && @@ -1766,7 +1777,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) } else { if(!backend->recv_renegotiating) - Curl_alpn_set_negotiated(cf, data, NULL, 0); + Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0); } } #endif @@ -1776,7 +1787,8 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) Curl_ssl_sessionid_lock(data); /* Up ref count since call takes ownership */ backend->cred->refcount++; - result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, backend->cred, + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, + backend->cred, sizeof(struct Curl_schannel_cred), schannel_session_free); Curl_ssl_sessionid_unlock(data); @@ -1863,10 +1875,10 @@ schannel_connect_common(struct Curl_cfilter *cf, /* if ssl is expecting something, check if it is available. */ if(connssl->io_need) { - 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; + 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); @@ -2109,17 +2121,23 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, * cleanup. The pattern for return error is set *err, optional infof, goto * cleanup. * + * Some verbose debug messages are wrapped by SCH_DEV() instead of DEBUGF() + * and only shown if CURL_SCHANNEL_DEV_DEBUG was defined at build time. These + * messages are extra verbose and intended for curl developers debugging + * Schannel recv decryption. + * * Our priority is to always return as much decrypted data to the caller as * possible, even if an error occurs. The state of the decrypted buffer must * always be valid. Transfer of decrypted data to the caller's buffer is * handled in the cleanup. */ - DEBUGF(infof(data, "schannel: client wants to read %zu bytes", len)); + SCH_DEV(infof(data, "schannel: client wants to read %zu bytes", len)); *err = CURLE_OK; if(len && len <= backend->decdata_offset) { - infof(data, "schannel: enough decrypted data is already available"); + SCH_DEV(infof(data, + "schannel: enough decrypted data is already available")); goto cleanup; } else if(backend->recv_unrecoverable_err) { @@ -2157,13 +2175,13 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->encdata_buffer = reallocated_buffer; backend->encdata_length = reallocated_length; size = backend->encdata_length - backend->encdata_offset; - DEBUGF(infof(data, "schannel: encdata_buffer resized %zu", - backend->encdata_length)); + SCH_DEV(infof(data, "schannel: encdata_buffer resized %zu", + backend->encdata_length)); } - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + SCH_DEV(infof(data, + "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); /* read encrypted data from socket */ nread = Curl_conn_cf_recv(cf->next, data, @@ -2173,27 +2191,25 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(*err) { nread = -1; if(*err == CURLE_AGAIN) - DEBUGF(infof(data, - "schannel: recv returned CURLE_AGAIN")); + SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN")); else if(*err == CURLE_RECV_ERROR) infof(data, "schannel: recv returned CURLE_RECV_ERROR"); else infof(data, "schannel: recv returned error %d", *err); } else if(nread == 0) { - backend->recv_connection_closed = true; + backend->recv_connection_closed = TRUE; DEBUGF(infof(data, "schannel: server closed the connection")); } else if(nread > 0) { backend->encdata_offset += (size_t)nread; - backend->encdata_is_incomplete = false; - DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); + backend->encdata_is_incomplete = FALSE; + SCH_DEV(infof(data, "schannel: encrypted data got %zd", nread)); } } - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); /* decrypt loop */ while(backend->encdata_offset > 0 && sspi_status == SEC_E_OK && @@ -2221,8 +2237,8 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, /* check for successfully decrypted data, even before actual renegotiation or shutdown of the connection context */ if(inbuf[1].BufferType == SECBUFFER_DATA) { - DEBUGF(infof(data, "schannel: decrypted data length: %lu", - inbuf[1].cbBuffer)); + SCH_DEV(infof(data, "schannel: decrypted data length: %lu", + inbuf[1].cbBuffer)); /* increase buffer in order to fit the received amount of data */ size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? @@ -2254,16 +2270,16 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->decdata_offset += size; } - DEBUGF(infof(data, "schannel: decrypted data added: %zu", size)); - DEBUGF(infof(data, - "schannel: decrypted cached: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + SCH_DEV(infof(data, "schannel: decrypted data added: %zu", size)); + SCH_DEV(infof(data, + "schannel: decrypted cached: offset %zu length %zu", + backend->decdata_offset, backend->decdata_length)); } /* check for remaining encrypted data */ if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) { - DEBUGF(infof(data, "schannel: encrypted data length: %lu", - inbuf[3].cbBuffer)); + SCH_DEV(infof(data, "schannel: encrypted data length: %lu", + inbuf[3].cbBuffer)); /* check if the remaining data is less than the total amount * and therefore begins after the already processed data @@ -2277,9 +2293,9 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->encdata_offset = inbuf[3].cbBuffer; } - DEBUGF(infof(data, - "schannel: encrypted cached: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + SCH_DEV(infof(data, + "schannel: encrypted cached: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); } else { /* reset encrypted buffer offset, because there is no data remaining */ @@ -2299,9 +2315,9 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, connssl->state = ssl_connection_negotiating; connssl->connecting_state = ssl_connect_2; connssl->io_need = CURL_SSL_IO_NEED_SEND; - backend->recv_renegotiating = true; + backend->recv_renegotiating = TRUE; *err = schannel_connect_common(cf, data, FALSE, &done); - backend->recv_renegotiating = false; + backend->recv_renegotiating = FALSE; if(*err) { infof(data, "schannel: renegotiation failed"); goto cleanup; @@ -2315,25 +2331,30 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, else if(sspi_status == SEC_I_CONTEXT_EXPIRED) { /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not returned so we have to work around that in cleanup. */ - backend->recv_sspi_close_notify = true; + backend->recv_sspi_close_notify = TRUE; if(!backend->recv_connection_closed) - backend->recv_connection_closed = true; + backend->recv_connection_closed = TRUE; + /* We received the close notify just fine, any error we got + * from the lower filters afterwards (e.g. the socket), is not + * an error on the TLS data stream. That one ended here. */ + if(*err == CURLE_RECV_ERROR) + *err = CURLE_OK; infof(data, "schannel: server close notification received (close_notify)"); goto cleanup; } } else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { - backend->encdata_is_incomplete = true; + backend->encdata_is_incomplete = TRUE; if(!*err) *err = CURLE_AGAIN; - infof(data, "schannel: failed to decrypt data, need more data"); + SCH_DEV(infof(data, "schannel: failed to decrypt data, need more data")); goto cleanup; } else { #ifndef CURL_DISABLE_VERBOSE_STRINGS char buffer[STRERROR_LEN]; - infof(data, "schannel: failed to read data from server: %s", + failf(data, "schannel: failed to read data from server: %s", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); #endif *err = CURLE_RECV_ERROR; @@ -2341,17 +2362,15 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } } - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); - DEBUGF(infof(data, - "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + SCH_DEV(infof(data, "schannel: decrypted data buffer: offset %zu length %zu", + backend->decdata_offset, backend->decdata_length)); cleanup: /* Warning- there is no guarantee the encdata state is valid at this point */ - DEBUGF(infof(data, "schannel: schannel_recv cleanup")); + SCH_DEV(infof(data, "schannel: schannel_recv cleanup")); /* Error if the connection has closed without a close_notify. @@ -2370,10 +2389,10 @@ cleanup: VERSION_EQUAL); if(isWin2k && sspi_status == SEC_E_OK) - backend->recv_sspi_close_notify = true; + backend->recv_sspi_close_notify = TRUE; else { *err = CURLE_RECV_ERROR; - infof(data, "schannel: server closed abruptly (missing close_notify)"); + failf(data, "schannel: server closed abruptly (missing close_notify)"); } } @@ -2387,10 +2406,10 @@ cleanup: memmove(backend->decdata_buffer, backend->decdata_buffer + size, backend->decdata_offset - size); backend->decdata_offset -= size; - DEBUGF(infof(data, "schannel: decrypted data returned %zu", size)); - DEBUGF(infof(data, - "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + SCH_DEV(infof(data, "schannel: decrypted data returned %zu", size)); + SCH_DEV(infof(data, + "schannel: decrypted data buffer: offset %zu length %zu", + backend->decdata_offset, backend->decdata_length)); *err = CURLE_OK; return (ssize_t)size; } @@ -2536,7 +2555,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, if(!result) { if(written < (ssize_t)outbuf.cbBuffer) { /* TODO: handle partial sends */ - infof(data, "schannel: failed to send close msg: %s" + failf(data, "schannel: failed to send close msg: %s" " (bytes written: %zd)", curl_easy_strerror(result), written); result = CURLE_SEND_ERROR; goto out; @@ -2551,7 +2570,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, } else { if(!backend->recv_connection_closed) { - infof(data, "schannel: error sending close msg: %d", result); + failf(data, "schannel: error sending close msg: %d", result); result = CURLE_SEND_ERROR; goto out; } @@ -2622,7 +2641,7 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) Curl_safefree(backend->encdata_buffer); backend->encdata_length = 0; backend->encdata_offset = 0; - backend->encdata_is_incomplete = false; + backend->encdata_is_incomplete = FALSE; } /* free internal buffer for received decrypted data */ @@ -2732,7 +2751,7 @@ static void schannel_checksum(const unsigned char *input, DWORD provType, const unsigned int algId) { -#ifdef CURL_WINDOWS_APP +#ifdef CURL_WINDOWS_UWP (void)input; (void)inputlen; (void)provType; @@ -2896,7 +2915,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, DEBUGASSERT(multi); if(!multi) { - return false; + return FALSE; } share = Curl_hash_pick(&multi->proto_hash, @@ -2905,14 +2924,14 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, if(!share) { share = calloc(1, sizeof(*share)); if(!share) { - return false; + return FALSE; } if(!Curl_hash_add2(&multi->proto_hash, (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY, sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1, share, schannel_cert_share_free)) { free(share); - return false; + return FALSE; } } @@ -2927,7 +2946,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, if(conn_config->CAfile) { CAfile = strdup(conn_config->CAfile); if(!CAfile) { - return false; + return FALSE; } } } @@ -2942,7 +2961,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, share->cert_store = cert_store; share->CAinfo_blob_size = CAinfo_blob_size; share->CAfile = CAfile; - return true; + return TRUE; } const struct Curl_ssl Curl_ssl_schannel = { @@ -2952,7 +2971,7 @@ const struct Curl_ssl Curl_ssl_schannel = { #ifdef HAS_MANUAL_VERIFY_API SSLSUPP_CAINFO_BLOB | #endif -#ifndef CURL_WINDOWS_APP +#ifndef CURL_WINDOWS_UWP SSLSUPP_PINNEDPUBKEY | #endif SSLSUPP_TLS13_CIPHERSUITES | diff --git a/libs/libcurl/src/vtls/schannel_int.h b/libs/libcurl/src/vtls/schannel_int.h index c914ccfc6e..52d6fd5214 100644 --- a/libs/libcurl/src/vtls/schannel_int.h +++ b/libs/libcurl/src/vtls/schannel_int.h @@ -31,7 +31,7 @@ #include "vtls.h" #if (defined(__MINGW32__) || defined(CERT_CHAIN_REVOCATION_CHECK_CHAIN)) \ - && !defined(CURL_WINDOWS_APP) + && !defined(CURL_WINDOWS_UWP) #define HAS_MANUAL_VERIFY_API #endif @@ -176,6 +176,17 @@ struct schannel_cert_share { struct curltime time; /* when the cached store was created */ }; +/* +* size of the structure: 20 bytes. +*/ +struct num_ip_data { + DWORD size; /* 04 bytes */ + union { + struct in_addr ia; /* 04 bytes */ + struct in6_addr ia6; /* 16 bytes */ + } bData; +}; + HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, const struct Curl_easy *data); diff --git a/libs/libcurl/src/vtls/schannel_verify.c b/libs/libcurl/src/vtls/schannel_verify.c index afc19baba0..ee960ed2c5 100644 --- a/libs/libcurl/src/vtls/schannel_verify.c +++ b/libs/libcurl/src/vtls/schannel_verify.c @@ -39,6 +39,7 @@ #include "schannel.h" #include "schannel_int.h" +#include "inet_pton.h" #include "vtls.h" #include "vtls_int.h" #include "sendf.h" @@ -54,7 +55,6 @@ #define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend) - #ifdef HAS_MANUAL_VERIFY_API #define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ @@ -116,7 +116,7 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, const char *current_ca_file_ptr = ca_buffer; const char *ca_buffer_limit = ca_buffer + ca_buffer_size; - while(more_certs && (current_ca_file_ptrpCertInfo; - if(!cert_info) { - failf(data, "schannel: Null certificate info."); - return actual_length; - } - - extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, - cert_info->cExtension, - cert_info->rgExtension); - if(!extension) { - failf(data, "schannel: CertFindExtension() returned no extension."); - return actual_length; - } - - decode_para.cbSize = sizeof(CRYPT_DECODE_PARA); - - ret_val = - CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - szOID_SUBJECT_ALT_NAME2, - extension->Value.pbData, - extension->Value.cbData, - CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, - &decode_para, - &alt_name_info, - &alt_name_info_size); - if(!ret_val) { - failf(data, - "schannel: CryptDecodeObjectEx() returned no alternate name " - "information."); - return actual_length; - } - current_pos = host_names; /* Iterate over the alternate names and populate host_names. */ @@ -467,6 +430,88 @@ static DWORD cert_get_name_string(struct Curl_easy *data, return actual_length; } +/* +* Returns TRUE if the hostname is a numeric IPv4/IPv6 Address, +* and populates the buffer with IPv4/IPv6 info. +*/ + +static bool get_num_host_info(struct num_ip_data *ip_blob, + LPCSTR hostname) +{ + struct in_addr ia; + struct in6_addr ia6; + bool result = FALSE; + + int res = Curl_inet_pton(AF_INET, hostname, &ia); + if(res) { + ip_blob->size = sizeof(struct in_addr); + memcpy(&ip_blob->bData.ia, &ia, sizeof(struct in_addr)); + result = TRUE; + } + else { + res = Curl_inet_pton(AF_INET6, hostname, &ia6); + if(res) { + ip_blob->size = sizeof(struct in6_addr); + memcpy(&ip_blob->bData.ia6, &ia6, sizeof(struct in6_addr)); + result = TRUE; + } + } + return result; +} + +static bool get_alt_name_info(struct Curl_easy *data, + PCCERT_CONTEXT ctx, + PCERT_ALT_NAME_INFO *alt_name_info, + LPDWORD alt_name_info_size) +{ + bool result = FALSE; +#if defined(CURL_WINDOWS_UWP) + (void)data; + (void)ctx; + (void)alt_name_info; + (void)alt_name_info_size; +#else + PCERT_INFO cert_info = NULL; + PCERT_EXTENSION extension = NULL; + CRYPT_DECODE_PARA decode_para = { sizeof(CRYPT_DECODE_PARA), NULL, NULL }; + + if(!ctx) { + failf(data, "schannel: Null certificate context."); + return result; + } + + cert_info = ctx->pCertInfo; + if(!cert_info) { + failf(data, "schannel: Null certificate info."); + return result; + } + + extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, + cert_info->cExtension, + cert_info->rgExtension); + if(!extension) { + failf(data, "schannel: CertFindExtension() returned no extension."); + return result; + } + + if(!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + szOID_SUBJECT_ALT_NAME2, + extension->Value.pbData, + extension->Value.cbData, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, + &decode_para, + alt_name_info, + alt_name_info_size)) { + failf(data, + "schannel: CryptDecodeObjectEx() returned no alternate name " + "information."); + return result; + } + result = TRUE; +#endif + return result; +} + /* Verify the server's hostname */ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -481,6 +526,12 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; + PCERT_ALT_NAME_INFO alt_name_info = NULL; + DWORD alt_name_info_size = 0; + struct num_ip_data ip_blob = { 0 }; + bool Win8_compat; + struct num_ip_data *p = &ip_blob; + DWORD i; sspi_status = Curl_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, @@ -491,97 +542,123 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, char buffer[STRERROR_LEN]; failf(data, "schannel: Failed to read remote certificate context: %s", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - result = CURLE_PEER_FAILED_VERIFICATION; goto cleanup; } - /* Determine the size of the string needed for the cert hostname */ - len = cert_get_name_string(data, pCertContextServer, NULL, 0); - if(len == 0) { - failf(data, - "schannel: CertGetNameString() returned no " - "certificate name information"); - result = CURLE_PEER_FAILED_VERIFICATION; - goto cleanup; + Win8_compat = curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL); + if(get_num_host_info(p, conn_hostname) || !Win8_compat) { + if(!get_alt_name_info(data, pCertContextServer, + &alt_name_info, &alt_name_info_size)) { + goto cleanup; + } } - /* CertGetNameString guarantees that the returned name will not contain - * embedded null bytes. This appears to be undocumented behavior. - */ - cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR)); - if(!cert_hostname_buff) { - result = CURLE_OUT_OF_MEMORY; - goto cleanup; + if(p->size) { + for(i = 0; i < alt_name_info->cAltEntry; ++i) { + PCERT_ALT_NAME_ENTRY entry = &alt_name_info->rgAltEntry[i]; + if(entry->dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) { + if(entry->IPAddress.cbData == p->size) { + if(!memcmp(entry->IPAddress.pbData, &p->bData, + entry->IPAddress.cbData)) { + result = CURLE_OK; + infof(data, + "schannel: connection hostname (%s) matched cert's IP address!", + conn_hostname); + break; + } + } + } + } } - actual_len = cert_get_name_string( - data, pCertContextServer, (LPTSTR)cert_hostname_buff, len); - /* Sanity check */ - if(actual_len != len) { - failf(data, - "schannel: CertGetNameString() returned certificate " - "name information of unexpected size"); - result = CURLE_PEER_FAILED_VERIFICATION; - goto cleanup; - } + else { + /* Determine the size of the string needed for the cert hostname */ + len = cert_get_name_string(data, pCertContextServer, + NULL, 0, alt_name_info, Win8_compat); + if(len == 0) { + failf(data, + "schannel: CertGetNameString() returned no " + "certificate name information"); + goto cleanup; + } - /* cert_hostname_buff contains all DNS names, where each name is - * null-terminated and the last DNS name is double null-terminated. Due to - * this encoding, use the length of the buffer to iterate over all names. - */ - result = CURLE_PEER_FAILED_VERIFICATION; - while(cert_hostname_buff_index < len && - cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') && - result == CURLE_PEER_FAILED_VERIFICATION) { + /* CertGetNameString guarantees that the returned name will not contain + * embedded null bytes. This appears to be undocumented behavior. + */ + cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR)); + if(!cert_hostname_buff) { + result = CURLE_OUT_OF_MEMORY; + goto cleanup; + } + actual_len = cert_get_name_string(data, pCertContextServer, + (LPTSTR)cert_hostname_buff, len, alt_name_info, Win8_compat); - char *cert_hostname; + /* Sanity check */ + if(actual_len != len) { + failf(data, + "schannel: CertGetNameString() returned certificate " + "name information of unexpected size"); + goto cleanup; + } - /* Comparing the cert name and the connection hostname encoded as UTF-8 - * is acceptable since both values are assumed to use ASCII - * (or some equivalent) encoding + /* cert_hostname_buff contains all DNS names, where each name is + * null-terminated and the last DNS name is double null-terminated. Due to + * this encoding, use the length of the buffer to iterate over all names. */ - cert_hostname = curlx_convert_tchar_to_UTF8( + while(cert_hostname_buff_index < len && + cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') && + result == CURLE_PEER_FAILED_VERIFICATION) { + + char *cert_hostname; + + /* Comparing the cert name and the connection hostname encoded as UTF-8 + * is acceptable since both values are assumed to use ASCII + * (or some equivalent) encoding + */ + cert_hostname = curlx_convert_tchar_to_UTF8( &cert_hostname_buff[cert_hostname_buff_index]); - if(!cert_hostname) { - result = CURLE_OUT_OF_MEMORY; - } - else { - if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), - conn_hostname, hostlen)) { - infof(data, - "schannel: connection hostname (%s) validated " - "against certificate name (%s)", - conn_hostname, cert_hostname); - result = CURLE_OK; + if(!cert_hostname) { + result = CURLE_OUT_OF_MEMORY; } else { - size_t cert_hostname_len; + if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), + conn_hostname, hostlen)) { + infof(data, + "schannel: connection hostname (%s) validated " + "against certificate name (%s)", + conn_hostname, cert_hostname); + result = CURLE_OK; + } + else { + size_t cert_hostname_len; - infof(data, - "schannel: connection hostname (%s) did not match " - "against certificate name (%s)", - conn_hostname, cert_hostname); + infof(data, + "schannel: connection hostname (%s) did not match " + "against certificate name (%s)", + conn_hostname, cert_hostname); - cert_hostname_len = - _tcslen(&cert_hostname_buff[cert_hostname_buff_index]); + cert_hostname_len = + _tcslen(&cert_hostname_buff[cert_hostname_buff_index]); - /* Move on to next cert name */ - cert_hostname_buff_index += cert_hostname_len + 1; + /* Move on to next cert name */ + cert_hostname_buff_index += cert_hostname_len + 1; - result = CURLE_PEER_FAILED_VERIFICATION; + result = CURLE_PEER_FAILED_VERIFICATION; + } + curlx_unicodefree(cert_hostname); } - curlx_unicodefree(cert_hostname); } - } - if(result == CURLE_PEER_FAILED_VERIFICATION) { - failf(data, - "schannel: CertGetNameString() failed to match " - "connection hostname (%s) against server certificate names", - conn_hostname); + if(result == CURLE_PEER_FAILED_VERIFICATION) { + failf(data, + "schannel: CertGetNameString() failed to match " + "connection hostname (%s) against server certificate names", + conn_hostname); + } + else if(result != CURLE_OK) + failf(data, "schannel: server certificate name verification failed"); } - else if(result != CURLE_OK) - failf(data, "schannel: server certificate name verification failed"); cleanup: Curl_safefree(cert_hostname_buff); @@ -592,7 +669,6 @@ cleanup: return result; } - #ifdef HAS_MANUAL_VERIFY_API /* Verify the server's certificate and hostname */ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, diff --git a/libs/libcurl/src/vtls/sectransp.c b/libs/libcurl/src/vtls/sectransp.c index 8ca7d4e507..bf1d44dbdf 100644 --- a/libs/libcurl/src/vtls/sectransp.c +++ b/libs/libcurl/src/vtls/sectransp.c @@ -24,7 +24,7 @@ ***************************************************************************/ /* - * Source file for all iOS and macOS SecureTransport-specific code for the + * Source file for all iOS and macOS Secure Transport-specific code for the * TLS/SSL layer. No code but vtls.c should ever call or use these functions. */ @@ -197,7 +197,7 @@ static const uint16_t default_ciphers[] = { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ - /* TLSv1.3 is not supported by sectransp, but there is also other + /* TLSv1.3 is not supported by Secure Transport, but there is also other * code referencing TLSv1.3, like: kTLSProtocol13 ? */ TLS_AES_128_GCM_SHA256, /* 0x1301 */ TLS_AES_256_GCM_SHA384, /* 0x1302 */ @@ -278,7 +278,7 @@ static OSStatus sectransp_bio_cf_in_read(SSLConnectionRef connection, case CURLE_OK: case CURLE_AGAIN: rtn = errSSLWouldBlock; - backend->ssl_direction = false; + backend->ssl_direction = FALSE; break; default: rtn = ioErr; @@ -317,7 +317,7 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection, if(nwritten <= 0) { if(result == CURLE_AGAIN) { rtn = errSSLWouldBlock; - backend->ssl_direction = true; + backend->ssl_direction = TRUE; } else { rtn = ioErr; @@ -512,7 +512,7 @@ static OSStatus CopyIdentityWithLabel(char *label, * label matching below worked correctly */ keys[2] = kSecMatchLimit; /* identity searches need a SecPolicyRef in order to work */ - values[3] = SecPolicyCreateSSL(false, NULL); + values[3] = SecPolicyCreateSSL(FALSE, NULL); keys[3] = kSecMatchPolicy; /* match the name of the certificate (does not work in macOS 10.12.1) */ values[4] = label_cf; @@ -532,7 +532,7 @@ static OSStatus CopyIdentityWithLabel(char *label, keys_list_count = CFArrayGetCount(keys_list); *out_cert_and_key = NULL; status = 1; - for(i = 0; issl_ctx, kSSLProtocolAll, false); - SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol1, true); + SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, FALSE); + SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol1, TRUE); return CURLE_OK; #endif @@ -1069,7 +1069,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, #if CURL_SUPPORT_MAC_10_8 if(backend->ssl_ctx) (void)SSLDisposeContext(backend->ssl_ctx); - err = SSLNewContext(false, &(backend->ssl_ctx)); + err = SSLNewContext(FALSE, &(backend->ssl_ctx)); if(err != noErr) { failf(data, "SSL: could not create a context: OSStatus %d", err); return CURLE_OUT_OF_MEMORY; @@ -1079,7 +1079,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, #else if(backend->ssl_ctx) (void)SSLDisposeContext(backend->ssl_ctx); - err = SSLNewContext(false, &(backend->ssl_ctx)); + err = SSLNewContext(FALSE, &(backend->ssl_ctx)); if(err != noErr) { failf(data, "SSL: could not create a context: OSStatus %d", err); return CURLE_OUT_OF_MEMORY; @@ -1227,8 +1227,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, Mountain Lion. So we need to call SSLSetEnableCertVerify() on those older cats in order to disable certificate validation if the user turned that off. - (SecureTransport will always validate the certificate chain by - default.) + (Secure Transport always validates the certificate chain by default.) Note: Darwin 11.x.x is Lion (10.7) Darwin 12.x.x is Mountain Lion (10.8) @@ -1254,7 +1253,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, else { #if CURL_SUPPORT_MAC_10_8 err = SSLSetEnableCertVerify(backend->ssl_ctx, - conn_config->verifypeer?true:false); + conn_config->verifypeer ? true : FALSE); if(err != noErr) { failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; @@ -1263,7 +1262,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, } #else err = SSLSetEnableCertVerify(backend->ssl_ctx, - conn_config->verifypeer?true:false); + conn_config->verifypeer ? true : FALSE); if(err != noErr) { failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; @@ -1285,8 +1284,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, * Both hostname check and SNI require SSLSetPeerDomainName(). * Also: the verifyhost setting influences SNI usage */ if(conn_config->verifyhost) { - char *server = connssl->peer.sni? - connssl->peer.sni : connssl->peer.hostname; + char *server = connssl->peer.sni ? + connssl->peer.sni : connssl->peer.hostname; err = SSLSetPeerDomainName(backend->ssl_ctx, server, strlen(server)); if(err != noErr) { @@ -1335,7 +1334,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, - (void **)&ssl_sessionid, &ssl_sessionid_len)) { + (void **)&ssl_sessionid, &ssl_sessionid_len, + NULL)) { /* we got a session id, use it! */ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); Curl_ssl_sessionid_unlock(data); @@ -1363,8 +1363,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, ssl_sessionid, - ssl_sessionid_len, + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, + ssl_sessionid, ssl_sessionid_len, sectransp_session_free); Curl_ssl_sessionid_unlock(data); if(result) @@ -1605,7 +1605,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); goto out; } - ret = SecTrustSetAnchorCertificatesOnly(trust, true); + ret = SecTrustSetAnchorCertificatesOnly(trust, TRUE); if(ret != noErr) { failf(data, "SecTrustSetAnchorCertificatesOnly() returned error %d", ret); goto out; @@ -2054,7 +2054,7 @@ check_handshake: (void)SSLGetNegotiatedProtocolVersion(backend->ssl_ctx, &protocol); sectransp_cipher_suite_get_str((uint16_t) cipher, cipher_str, - sizeof(cipher_str), true); + sizeof(cipher_str), TRUE); switch(protocol) { case kSSLProtocol2: infof(data, "SSL 2.0 connection using %s", cipher_str); @@ -2169,7 +2169,7 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf, #ifndef CURL_DISABLE_VERBOSE_STRINGS const bool show_verbose_server_cert = data->set.verbose; #else - const bool show_verbose_server_cert = false; + const bool show_verbose_server_cert = FALSE; #endif struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); CURLcode result = ssl_config->certinfo ? @@ -2328,10 +2328,10 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, /* if ssl is expecting something, check if it is available. */ if(connssl->io_need) { - 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; + 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); @@ -2463,7 +2463,7 @@ static CURLcode sectransp_shutdown(struct Curl_cfilter *cf, } else { /* We would like to read the close notify from the server using - * secure transport, however SSLRead() no longer works after we + * Secure Transport, however SSLRead() no longer works after we * sent the notify from our side. So, we just read from the * underlying filter and hope it will end. */ nread = Curl_conn_cf_recv(cf->next, data, buf, sizeof(buf), &result); @@ -2544,10 +2544,10 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf, err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); if(err == noErr) return buffer > 0UL; - return false; + return FALSE; } else - return false; + return FALSE; } static CURLcode sectransp_random(struct Curl_easy *data UNUSED_PARAM, diff --git a/libs/libcurl/src/vtls/vtls.c b/libs/libcurl/src/vtls/vtls.c index 2a0d232024..0a90bf46fd 100644 --- a/libs/libcurl/src/vtls/vtls.c +++ b/libs/libcurl/src/vtls/vtls.c @@ -55,6 +55,16 @@ #include "vtls.h" /* generic SSL protos etc */ #include "vtls_int.h" + +#include "openssl.h" /* OpenSSL versions */ +#include "gtls.h" /* GnuTLS versions */ +#include "wolfssl.h" /* wolfSSL versions */ +#include "schannel.h" /* Schannel SSPI version */ +#include "sectransp.h" /* Secure Transport (Darwin) version */ +#include "mbedtls.h" /* mbedTLS versions */ +#include "bearssl.h" /* BearSSL versions */ +#include "rustls.h" /* Rustls versions */ + #include "slist.h" #include "sendf.h" #include "strcase.h" @@ -259,7 +269,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, return TRUE; } -static void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) +static void free_primary_ssl_config(struct ssl_primary_config *sslc) { Curl_safefree(sslc->CApath); Curl_safefree(sslc->CAfile); @@ -359,9 +369,9 @@ CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, void Curl_ssl_conn_config_cleanup(struct connectdata *conn) { - Curl_free_primary_ssl_config(&conn->ssl_config); + free_primary_ssl_config(&conn->ssl_config); #ifndef CURL_DISABLE_PROXY - Curl_free_primary_ssl_config(&conn->proxy_ssl_config); + free_primary_ssl_config(&conn->proxy_ssl_config); #endif } @@ -371,8 +381,8 @@ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy) if(data->conn) { struct ssl_primary_config *src, *dest; #ifndef CURL_DISABLE_PROXY - src = for_proxy? &data->set.proxy_ssl.primary : &data->set.ssl.primary; - dest = for_proxy? &data->conn->proxy_ssl_config : &data->conn->ssl_config; + src = for_proxy ? &data->set.proxy_ssl.primary : &data->set.ssl.primary; + dest = for_proxy ? &data->conn->proxy_ssl_config : &data->conn->ssl_config; #else (void)for_proxy; src = &data->set.ssl.primary; @@ -454,6 +464,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, return NULL; ctx->alpn = alpn; + Curl_bufq_init2(&ctx->earlydata, CURL_SSL_EARLY_MAX, 1, BUFQ_OPT_NO_SPARES); ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data); if(!ctx->backend) { free(ctx); @@ -465,6 +476,8 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, static void cf_ctx_free(struct ssl_connect_data *ctx) { if(ctx) { + Curl_safefree(ctx->alpn_negotiated); + Curl_bufq_free(&ctx->earlydata); free(ctx->backend); free(ctx); } @@ -527,7 +540,8 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, const struct ssl_peer *peer, void **ssl_sessionid, - size_t *idsize) /* set 0 if unknown */ + size_t *idsize, /* set 0 if unknown */ + char **palpn) { 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); @@ -537,6 +551,8 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, bool no_match = TRUE; *ssl_sessionid = NULL; + if(palpn) + *palpn = NULL; if(!ssl_config) return TRUE; @@ -575,13 +591,15 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, *ssl_sessionid = check->sessionid; if(idsize) *idsize = check->idsize; + if(palpn) + *palpn = check->alpn; no_match = FALSE; break; } } CURL_TRC_CF(data, cf, "%s cached session ID for %s://%s:%d", - no_match? "No": "Found", + no_match ? "No" : "Found", cf->conn->handler->scheme, peer->hostname, peer->port); return no_match; } @@ -601,10 +619,11 @@ void Curl_ssl_kill_session(struct Curl_ssl_session *session) session->sessionid_free = NULL; session->age = 0; /* fresh */ - Curl_free_primary_ssl_config(&session->ssl_config); + free_primary_ssl_config(&session->ssl_config); Curl_safefree(session->name); Curl_safefree(session->conn_to_host); + Curl_safefree(session->alpn); } } @@ -628,6 +647,7 @@ void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid) CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, struct Curl_easy *data, const struct ssl_peer *peer, + const char *alpn, void *ssl_sessionid, size_t idsize, Curl_ssl_sessionid_dtor *sessionid_free_cb) @@ -639,6 +659,7 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, long oldest_age; char *clone_host = NULL; char *clone_conn_to_host = NULL; + char *clone_alpn = NULL; int conn_to_port; long *general_age; void *old_sessionid; @@ -653,7 +674,7 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, return CURLE_OK; } - if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size)) { + if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size, NULL)) { if((old_size == idsize) && ((old_sessionid == ssl_sessionid) || (idsize && !memcmp(old_sessionid, ssl_sessionid, idsize)))) { @@ -679,6 +700,10 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, goto out; } + clone_alpn = alpn ? strdup(alpn) : NULL; + if(alpn && !clone_alpn) + goto out; + if(cf->conn->bits.conn_to_port) conn_to_port = cf->conn->conn_to_port; else @@ -711,7 +736,7 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, /* now init the session struct wisely */ if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) { - Curl_free_primary_ssl_config(&store->ssl_config); + free_primary_ssl_config(&store->ssl_config); store->sessionid = NULL; /* let caller free sessionid */ goto out; } @@ -727,6 +752,8 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, store->conn_to_host = clone_conn_to_host; /* clone connect to hostname */ clone_conn_to_host = NULL; store->conn_to_port = conn_to_port; /* connect to port number */ + store->alpn = clone_alpn; + clone_alpn = NULL; /* port number */ store->remote_port = peer->port; store->scheme = cf->conn->handler->scheme; @@ -737,6 +764,7 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, out: free(clone_host); free(clone_conn_to_host); + free(clone_alpn); if(result) { failf(data, "Failed to add Session ID to cache for %s://%s:%d [%s]", store->scheme, store->name, store->remote_port, @@ -857,7 +885,7 @@ void Curl_ssl_free_certinfo(struct Curl_easy *data) if(ci->num_of_certs) { /* free all individual lists used */ int i; - for(i = 0; inum_of_certs; i++) { + for(i = 0; i < ci->num_of_certs; i++) { curl_slist_free_all(ci->certinfo[i]); ci->certinfo[i] = NULL; } @@ -941,14 +969,17 @@ CURLcode Curl_ssl_random(struct Curl_easy *data, static CURLcode pubkey_pem_to_der(const char *pem, unsigned char **der, size_t *der_len) { - char *stripped_pem, *begin_pos, *end_pos; - size_t pem_count, stripped_pem_count = 0, pem_len; + char *begin_pos, *end_pos; + size_t pem_count, pem_len; CURLcode result; + struct dynbuf pbuf; /* if no pem, exit. */ if(!pem) return CURLE_BAD_CONTENT_ENCODING; + Curl_dyn_init(&pbuf, MAX_PINNED_PUBKEY_SIZE); + begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----"); if(!begin_pos) return CURLE_BAD_CONTENT_ENCODING; @@ -968,26 +999,23 @@ static CURLcode pubkey_pem_to_der(const char *pem, pem_len = end_pos - pem; - stripped_pem = malloc(pem_len - pem_count + 1); - if(!stripped_pem) - return CURLE_OUT_OF_MEMORY; - /* * Here we loop through the pem array one character at a time between the * correct indices, and place each character that is not '\n' or '\r' * into the stripped_pem array, which should represent the raw base64 string */ while(pem_count < pem_len) { - if('\n' != pem[pem_count] && '\r' != pem[pem_count]) - stripped_pem[stripped_pem_count++] = pem[pem_count]; + if('\n' != pem[pem_count] && '\r' != pem[pem_count]) { + result = Curl_dyn_addn(&pbuf, &pem[pem_count], 1); + if(result) + return result; + } ++pem_count; } - /* Place the null terminator in the correct place */ - stripped_pem[stripped_pem_count] = '\0'; - result = Curl_base64_decode(stripped_pem, der, der_len); + result = Curl_base64_decode(Curl_dyn_ptr(&pbuf), der, der_len); - Curl_safefree(stripped_pem); + Curl_dyn_free(&pbuf); return result; } @@ -1000,8 +1028,6 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, const char *pinnedpubkey, const unsigned char *pubkey, size_t pubkeylen) { - FILE *fp; - unsigned char *buf = NULL, *pem_ptr = NULL; CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; #ifdef CURL_DISABLE_VERBOSE_STRINGS (void)data; @@ -1014,7 +1040,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, return result; /* only do this if pinnedpubkey starts with "sha256//", length 8 */ - if(strncmp(pinnedpubkey, "sha256//", 8) == 0) { + if(!strncmp(pinnedpubkey, "sha256//", 8)) { CURLcode encode; size_t encodedlen = 0; char *encoded = NULL, *pinkeycopy, *begin_pos, *end_pos; @@ -1078,26 +1104,28 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, } while(end_pos && begin_pos); Curl_safefree(encoded); Curl_safefree(pinkeycopy); - return result; } - - fp = fopen(pinnedpubkey, "rb"); - if(!fp) - return result; - - do { + else { long filesize; size_t size, pem_len; CURLcode pem_read; + struct dynbuf buf; + char unsigned *pem_ptr = NULL; + size_t left; + FILE *fp = fopen(pinnedpubkey, "rb"); + if(!fp) + return result; + + Curl_dyn_init(&buf, MAX_PINNED_PUBKEY_SIZE); /* Determine the file's size */ if(fseek(fp, 0, SEEK_END)) - break; + goto end; filesize = ftell(fp); if(fseek(fp, 0, SEEK_SET)) - break; + goto end; if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE) - break; + goto end; /* * if the size of our certificate is bigger than the file @@ -1105,36 +1133,37 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, */ size = curlx_sotouz((curl_off_t) filesize); if(pubkeylen > size) - break; + goto end; /* - * Allocate buffer for the pinned key - * With 1 additional byte for null terminator in case of PEM key + * Read the file into the dynbuf */ - buf = malloc(size + 1); - if(!buf) - break; - - /* Returns number of elements read, which should be 1 */ - if((int) fread(buf, size, 1, fp) != 1) - break; + left = size; + do { + char buffer[1024]; + size_t want = left > sizeof(buffer) ? sizeof(buffer) : left; + if(want != fread(buffer, 1, want, fp)) + goto end; + if(Curl_dyn_addn(&buf, buffer, want)) + goto end; + left -= want; + } while(left); /* If the sizes are the same, it cannot be base64 encoded, must be der */ if(pubkeylen == size) { - if(!memcmp(pubkey, buf, pubkeylen)) + if(!memcmp(pubkey, Curl_dyn_ptr(&buf), pubkeylen)) result = CURLE_OK; - break; + goto end; } /* * Otherwise we will assume it is PEM and try to decode it * after placing null terminator */ - buf[size] = '\0'; - pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len); + pem_read = pubkey_pem_to_der(Curl_dyn_ptr(&buf), &pem_ptr, &pem_len); /* if it was not read successfully, exit */ if(pem_read) - break; + goto end; /* * if the size of our certificate does not match the size of @@ -1142,11 +1171,11 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, */ if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen)) result = CURLE_OK; - } while(0); - - Curl_safefree(buf); - Curl_safefree(pem_ptr); - fclose(fp); +end: + Curl_dyn_free(&buf); + Curl_safefree(pem_ptr); + fclose(fp); + } return result; } @@ -1715,7 +1744,9 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, if(!result && *done) { cf->connected = TRUE; connssl->handshake_done = Curl_now(); - DEBUGASSERT(connssl->state == ssl_connection_complete); + /* Connection can be deferred when sending early data */ + DEBUGASSERT(connssl->state == ssl_connection_complete || + connssl->state == ssl_connection_deferred); } out: CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done); @@ -1743,13 +1774,16 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf, bool eos, CURLcode *err) { struct cf_call_data save; - ssize_t nwritten; + ssize_t nwritten = 0; - (void)eos; /* unused */ - CF_DATA_SAVE(save, cf, data); + (void)eos; + /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */ *err = CURLE_OK; - nwritten = Curl_ssl->send_plain(cf, data, buf, len, err); - CF_DATA_RESTORE(cf, save); + if(len > 0) { + CF_DATA_SAVE(save, cf, data); + nwritten = Curl_ssl->send_plain(cf, data, buf, len, err); + CF_DATA_RESTORE(cf, save); + } return nwritten; } @@ -1851,7 +1885,7 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf, default: break; } - return cf->next? + return cf->next ? cf->next->cft->query(cf->next, data, query, pres1, pres2) : CURLE_UNKNOWN_OPTION; } @@ -1881,7 +1915,7 @@ static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, return FALSE; } /* ssl backend does not know */ - return cf->next? + return cf->next ? cf->next->cft->is_alive(cf->next, data, input_pending) : FALSE; /* pessimistic in absence of data */ } @@ -1950,7 +1984,7 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, out: if(result) cf_ctx_free(ctx); - *pcf = result? NULL : cf; + *pcf = result ? NULL : cf; return result; } @@ -2008,7 +2042,7 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, out: if(result) cf_ctx_free(ctx); - *pcf = result? NULL : cf; + *pcf = result ? NULL : cf; return result; } @@ -2029,7 +2063,7 @@ CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, bool Curl_ssl_supports(struct Curl_easy *data, unsigned int ssl_option) { (void)data; - return (Curl_ssl->supports & ssl_option)? TRUE : FALSE; + return (Curl_ssl->supports & ssl_option); } static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf) @@ -2124,7 +2158,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, struct Curl_cfilter *cf, *head; CURLcode result = CURLE_OK; - head = data->conn? data->conn->cfilter[sockindex] : NULL; + head = data->conn ? data->conn->cfilter[sockindex] : NULL; for(cf = head; cf; cf = cf->next) { if(cf->cft == &Curl_cft_ssl) { bool done; @@ -2154,7 +2188,7 @@ Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; return &data->set.ssl; #else - return Curl_ssl_cf_is_proxy(cf)? &data->set.proxy_ssl : &data->set.ssl; + return Curl_ssl_cf_is_proxy(cf) ? &data->set.proxy_ssl : &data->set.ssl; #endif } @@ -2164,7 +2198,7 @@ Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf) #ifdef CURL_DISABLE_PROXY return &cf->conn->ssl_config; #else - return Curl_ssl_cf_is_proxy(cf)? + return Curl_ssl_cf_is_proxy(cf) ? &cf->conn->proxy_ssl_config : &cf->conn->ssl_config; #endif } @@ -2215,20 +2249,73 @@ CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf, return CURLE_OK; } +bool Curl_alpn_contains_proto(const struct alpn_spec *spec, + const char *proto) +{ + size_t i, plen = proto ? strlen(proto) : 0; + for(i = 0; spec && plen && i < spec->count; ++i) { + size_t slen = strlen(spec->entries[i]); + if((slen == plen) && !memcmp(proto, spec->entries[i], plen)) + return TRUE; + } + return FALSE; +} + CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, struct Curl_easy *data, + struct ssl_connect_data *connssl, const unsigned char *proto, size_t proto_len) { + CURLcode result = CURLE_OK; unsigned char *palpn = #ifndef CURL_DISABLE_PROXY - (cf->conn->bits.tunnel_proxy && Curl_ssl_cf_is_proxy(cf))? + (cf->conn->bits.tunnel_proxy && Curl_ssl_cf_is_proxy(cf)) ? &cf->conn->proxy_alpn : &cf->conn->alpn #else &cf->conn->alpn #endif ; + if(connssl->alpn_negotiated) { + /* When we ask for a specific ALPN protocol, we need the confirmation + * of it by the server, as we have installed protocol handler and + * connection filter chain for exactly this protocol. */ + if(!proto_len) { + failf(data, "ALPN: asked for '%s' from previous session, " + "but server did not confirm it. Refusing to continue.", + connssl->alpn_negotiated); + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } + else if((strlen(connssl->alpn_negotiated) != proto_len) || + memcmp(connssl->alpn_negotiated, proto, proto_len)) { + failf(data, "ALPN: asked for '%s' from previous session, but server " + "selected '%.*s'. Refusing to continue.", + connssl->alpn_negotiated, (int)proto_len, proto); + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } + /* ALPN is exactly what we asked for, done. */ + infof(data, "ALPN: server confirmed to use '%s'", + connssl->alpn_negotiated); + goto out; + } + + if(proto && proto_len) { + if(memchr(proto, '\0', proto_len)) { + failf(data, "ALPN: server selected protocol contains NUL. " + "Refusing to continue."); + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } + connssl->alpn_negotiated = malloc(proto_len + 1); + if(!connssl->alpn_negotiated) + return CURLE_OUT_OF_MEMORY; + memcpy(connssl->alpn_negotiated, proto, proto_len); + connssl->alpn_negotiated[proto_len] = 0; + } + if(proto && proto_len) { if(proto_len == ALPN_HTTP_1_1_LENGTH && !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) { @@ -2254,15 +2341,22 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, /* return CURLE_NOT_BUILT_IN; */ goto out; } - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, (int)proto_len, proto); + + if(connssl->state == ssl_connection_deferred) + infof(data, VTLS_INFOF_ALPN_DEFERRED, (int)proto_len, proto); + else + infof(data, VTLS_INFOF_ALPN_ACCEPTED, (int)proto_len, proto); } else { *palpn = CURL_HTTP_VERSION_NONE; - infof(data, VTLS_INFOF_NO_ALPN); + if(connssl->state == ssl_connection_deferred) + infof(data, VTLS_INFOF_NO_ALPN_DEFERRED); + else + infof(data, VTLS_INFOF_NO_ALPN); } out: - return CURLE_OK; + return result; } #endif /* USE_SSL */ diff --git a/libs/libcurl/src/vtls/vtls.h b/libs/libcurl/src/vtls/vtls.h index c716b2c6f8..171a5d8e55 100644 --- a/libs/libcurl/src/vtls/vtls.h +++ b/libs/libcurl/src/vtls/vtls.h @@ -43,15 +43,18 @@ struct Curl_ssl_session; #define ALPN_ACCEPTED "ALPN: server accepted " -#define VTLS_INFOF_NO_ALPN \ +#define VTLS_INFOF_NO_ALPN \ "ALPN: server did not agree on a protocol. Uses default." -#define VTLS_INFOF_ALPN_OFFER_1STR \ +#define VTLS_INFOF_ALPN_OFFER_1STR \ "ALPN: curl offers %s" -#define VTLS_INFOF_ALPN_ACCEPTED_1STR \ - ALPN_ACCEPTED "%s" -#define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \ +#define VTLS_INFOF_ALPN_ACCEPTED \ ALPN_ACCEPTED "%.*s" +#define VTLS_INFOF_NO_ALPN_DEFERRED \ + "ALPN: deferred handshake for early data without specific protocol." +#define VTLS_INFOF_ALPN_DEFERRED \ + "ALPN: deferred handshake for early data using '%.*s'." + /* Curl_multi SSL backend-specific data; declared differently by each SSL backend */ struct Curl_cfilter; diff --git a/libs/libcurl/src/vtls/vtls_int.h b/libs/libcurl/src/vtls/vtls_int.h index 6cd2867dee..250273ef9d 100644 --- a/libs/libcurl/src/vtls/vtls_int.h +++ b/libs/libcurl/src/vtls/vtls_int.h @@ -29,6 +29,8 @@ #ifdef USE_SSL +struct ssl_connect_data; + /* see https://www.iana.org/assignments/tls-extensiontype-values/ */ #define ALPN_HTTP_1_1_LENGTH 8 #define ALPN_HTTP_1_1 "http/1.1" @@ -61,9 +63,13 @@ CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf, CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, struct Curl_easy *data, + struct ssl_connect_data *connssl, const unsigned char *proto, size_t proto_len); +bool Curl_alpn_contains_proto(const struct alpn_spec *spec, + const char *proto); + /* enum for the nonblocking SSL connection state machine */ typedef enum { ssl_connect_1, @@ -74,14 +80,27 @@ typedef enum { typedef enum { ssl_connection_none, + ssl_connection_deferred, ssl_connection_negotiating, ssl_connection_complete } ssl_connection_state; +typedef enum { + ssl_earlydata_none, + ssl_earlydata_use, + ssl_earlydata_sending, + ssl_earlydata_sent, + ssl_earlydata_accepted, + ssl_earlydata_rejected +} ssl_earlydata_state; + #define CURL_SSL_IO_NEED_NONE (0) #define CURL_SSL_IO_NEED_RECV (1<<0) #define CURL_SSL_IO_NEED_SEND (1<<1) +/* Max earlydata payload we want to send */ +#define CURL_SSL_EARLY_MAX (64*1024) + /* Information in each SSL cfilter context: cf->ctx */ struct ssl_connect_data { struct ssl_peer peer; @@ -89,8 +108,14 @@ struct ssl_connect_data { void *backend; /* vtls backend specific props */ struct cf_call_data call_data; /* data handle used in current call */ struct curltime handshake_done; /* time when handshake finished */ + char *alpn_negotiated; /* negotiated ALPN value or NULL */ + struct bufq earlydata; /* earlydata to be send to peer */ + size_t earlydata_max; /* max earlydata allowed by peer */ + size_t earlydata_skip; /* sending bytes to skip when earlydata + * is accepted by peer */ ssl_connection_state state; ssl_connect_state connecting_state; + ssl_earlydata_state earlydata_state; int io_need; /* TLS signals special SEND/RECV needs */ BIT(use_alpn); /* if ALPN shall be used in handshake */ BIT(peer_closed); /* peer has closed connection */ @@ -193,15 +218,23 @@ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf); * Caller must make sure that the ownership of returned sessionid object * is properly taken (e.g. its refcount is incremented * under sessionid mutex). + * @param cf the connection filter wanting to use it + * @param data the transfer involved + * @param peer the peer the filter wants to talk to + * @param sessionid on return the TLS session + * @param idsize on return the size of the TLS session data + * @param palpn on return the ALPN string used by the session, + * set to NULL when not interested */ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, const struct ssl_peer *peer, void **ssl_sessionid, - size_t *idsize); /* set 0 if unknown */ + size_t *idsize, /* set 0 if unknown */ + char **palpn); /* Set a TLS session ID for `peer`. Replaces an existing session ID if - * not already the very same. + * not already the same. * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). * Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb` * to deallocate it. Is called in all outcomes, either right away or @@ -212,19 +245,11 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, struct Curl_easy *data, const struct ssl_peer *peer, + const char *alpn, void *sessionid, size_t sessionid_size, Curl_ssl_sessionid_dtor *sessionid_free_cb); -#include "openssl.h" /* OpenSSL versions */ -#include "gtls.h" /* GnuTLS versions */ -#include "wolfssl.h" /* wolfSSL versions */ -#include "schannel.h" /* Schannel SSPI version */ -#include "sectransp.h" /* SecureTransport (Darwin) version */ -#include "mbedtls.h" /* mbedTLS versions */ -#include "bearssl.h" /* BearSSL versions */ -#include "rustls.h" /* Rustls versions */ - #endif /* USE_SSL */ #endif /* HEADER_CURL_VTLS_INT_H */ diff --git a/libs/libcurl/src/vtls/wolfssl.c b/libs/libcurl/src/vtls/wolfssl.c index 458219ef61..9a05f82946 100644 --- a/libs/libcurl/src/vtls/wolfssl.c +++ b/libs/libcurl/src/vtls/wolfssl.c @@ -97,9 +97,25 @@ #endif #endif -#if defined(HAVE_WOLFSSL_FULL_BIO) && HAVE_WOLFSSL_FULL_BIO +#ifdef HAVE_WOLFSSL_BIO #define USE_BIO_CHAIN -#else +#ifdef HAVE_WOLFSSL_FULL_BIO +#define USE_FULL_BIO +#else /* HAVE_WOLFSSL_FULL_BIO */ +#undef USE_FULL_BIO +#endif +/* wolfSSL 5.7.4 and older do not have these symbols, but only the + * OpenSSL ones. */ +#ifndef WOLFSSL_BIO_CTRL_GET_CLOSE +#define WOLFSSL_BIO_CTRL_GET_CLOSE BIO_CTRL_GET_CLOSE +#define WOLFSSL_BIO_CTRL_SET_CLOSE BIO_CTRL_SET_CLOSE +#define WOLFSSL_BIO_CTRL_FLUSH BIO_CTRL_FLUSH +#define WOLFSSL_BIO_CTRL_DUP BIO_CTRL_DUP +#define wolfSSL_BIO_set_retry_write BIO_set_retry_write +#define wolfSSL_BIO_set_retry_read BIO_set_retry_read +#endif /* !WOLFSSL_BIO_CTRL_GET_CLOSE */ + +#else /* HAVE_WOLFSSL_BIO */ #undef USE_BIO_CHAIN #endif @@ -163,7 +179,7 @@ wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret, #endif /* defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) */ static void -wolfssl_log_tls12_secret(SSL *ssl) +wolfssl_log_tls12_secret(WOLFSSL *ssl) { unsigned char *ms, *sr, *cr; unsigned int msLen, srLen, crLen, i, x = 0; @@ -187,7 +203,7 @@ wolfssl_log_tls12_secret(SSL *ssl) #endif if(wolfSSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != - SSL_SUCCESS) { + WOLFSSL_SUCCESS) { return; } @@ -208,11 +224,11 @@ wolfssl_log_tls12_secret(SSL *ssl) static int wolfssl_do_file_type(const char *type) { if(!type || !type[0]) - return SSL_FILETYPE_PEM; + return WOLFSSL_FILETYPE_PEM; if(strcasecompare(type, "PEM")) - return SSL_FILETYPE_PEM; + return WOLFSSL_FILETYPE_PEM; if(strcasecompare(type, "DER")) - return SSL_FILETYPE_ASN1; + return WOLFSSL_FILETYPE_ASN1; return -1; } @@ -237,7 +253,9 @@ static const struct group_name_map gnm[] = { static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio) { +#ifdef USE_FULL_BIO wolfSSL_BIO_set_shutdown(bio, 1); +#endif wolfSSL_BIO_set_data(bio, NULL); return 1; } @@ -251,28 +269,35 @@ static int wolfssl_bio_cf_destroy(WOLFSSL_BIO *bio) static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr) { - struct Curl_cfilter *cf = BIO_get_data(bio); + struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); long ret = 1; (void)cf; (void)ptr; + (void)num; switch(cmd) { - case BIO_CTRL_GET_CLOSE: + case WOLFSSL_BIO_CTRL_GET_CLOSE: +#ifdef USE_FULL_BIO ret = (long)wolfSSL_BIO_get_shutdown(bio); +#else + ret = 0; +#endif break; - case BIO_CTRL_SET_CLOSE: + case WOLFSSL_BIO_CTRL_SET_CLOSE: +#ifdef USE_FULL_BIO wolfSSL_BIO_set_shutdown(bio, (int)num); +#endif break; - case BIO_CTRL_FLUSH: + case WOLFSSL_BIO_CTRL_FLUSH: /* we do no delayed writes, but if we ever would, this * needs to trigger it. */ ret = 1; break; - case BIO_CTRL_DUP: + case WOLFSSL_BIO_CTRL_DUP: ret = 1; break; -#ifdef BIO_CTRL_EOF - case BIO_CTRL_EOF: +#ifdef WOLFSSL_BIO_CTRL_EOF + case WOLFSSL_BIO_CTRL_EOF: /* EOF has been reached on input? */ return (!cf->next || !cf->next->connected); #endif @@ -309,9 +334,11 @@ static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio, backend->io_result = result; CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d", blen, nwritten, result); +#ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); +#endif if(nwritten < 0 && CURLE_AGAIN == result) { - BIO_set_retry_write(bio); + wolfSSL_BIO_set_retry_write(bio); if(backend->shutting_down && !backend->io_send_blocked_len) backend->io_send_blocked_len = blen; } @@ -338,9 +365,11 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); backend->io_result = result; CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %zd, %d", blen, nread, result); +#ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); +#endif if(nread < 0 && CURLE_AGAIN == result) - BIO_set_retry_read(bio); + wolfSSL_BIO_set_retry_read(bio); else if(nread == 0) connssl->peer_closed = TRUE; return (int)nread; @@ -350,7 +379,8 @@ static WOLFSSL_BIO_METHOD *wolfssl_bio_cf_method = NULL; static void wolfssl_bio_cf_init_methods(void) { - wolfssl_bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO"); + wolfssl_bio_cf_method = wolfSSL_BIO_meth_new(WOLFSSL_BIO_MEMORY, + "wolfSSL CF BIO"); wolfSSL_BIO_meth_set_write(wolfssl_bio_cf_method, &wolfssl_bio_cf_out_write); wolfSSL_BIO_meth_set_read(wolfssl_bio_cf_method, &wolfssl_bio_cf_in_read); wolfSSL_BIO_meth_set_ctrl(wolfssl_bio_cf_method, &wolfssl_bio_cf_ctrl); @@ -370,9 +400,114 @@ static void wolfssl_bio_cf_free_methods(void) #endif /* !USE_BIO_CHAIN */ +static void wolfssl_session_free(void *sdata, size_t slen) +{ + (void)slen; + free(sdata); +} + +CURLcode wssl_cache_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + WOLFSSL_SESSION *session) +{ + CURLcode result = CURLE_OK; + unsigned char *sdata = NULL; + unsigned int slen; + + if(!session) + goto out; + + slen = wolfSSL_i2d_SSL_SESSION(session, NULL); + if(slen <= 0) { + CURL_TRC_CF(data, cf, "fail to assess session length: %u", slen); + result = CURLE_FAILED_INIT; + goto out; + } + sdata = calloc(1, slen); + if(!sdata) { + failf(data, "unable to allocate session buffer of %u bytes", slen); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + slen = wolfSSL_i2d_SSL_SESSION(session, &sdata); + if(slen <= 0) { + CURL_TRC_CF(data, cf, "fail to serialize session: %u", slen); + result = CURLE_FAILED_INIT; + goto out; + } + + Curl_ssl_sessionid_lock(data); + result = Curl_ssl_set_sessionid(cf, data, peer, NULL, + sdata, slen, wolfssl_session_free); + Curl_ssl_sessionid_unlock(data); + if(result) + failf(data, "failed to add new ssl session to cache (%d)", result); + else { + CURL_TRC_CF(data, cf, "added new session to cache"); + sdata = NULL; + } + +out: + free(sdata); + return 0; +} + +static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) +{ + struct Curl_cfilter *cf; + + cf = (struct Curl_cfilter*)wolfSSL_get_app_data(ssl); + DEBUGASSERT(cf != NULL); + if(cf && session) { + struct ssl_connect_data *connssl = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + DEBUGASSERT(connssl); + DEBUGASSERT(data); + if(connssl && data) { + (void)wssl_cache_session(cf, data, &connssl->peer, session); + } + } + return 0; +} + +CURLcode wssl_setup_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wss, + struct ssl_peer *peer) +{ + void *psdata; + const unsigned char *sdata = NULL; + size_t slen = 0; + CURLcode result = CURLE_OK; + + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(cf, data, peer, &psdata, &slen, NULL)) { + WOLFSSL_SESSION *session; + sdata = psdata; + session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)slen); + if(session) { + int ret = wolfSSL_set_session(wss->handle, session); + if(ret != WOLFSSL_SUCCESS) { + Curl_ssl_delsessionid(data, psdata); + infof(data, "previous session not accepted (%d), " + "removing from cache", ret); + } + else + infof(data, "SSL reusing session ID"); + wolfSSL_SESSION_free(session); + } + else { + failf(data, "could not decode previous session"); + } + } + Curl_ssl_sessionid_unlock(data); + return result; +} + static CURLcode populate_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, - X509_STORE *store, + WOLFSSL_X509_STORE *store, struct wolfssl_ctx *wssl) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); @@ -382,7 +517,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, (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; + bool imported_native_ca = FALSE; #if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS) /* load native CA certificates */ @@ -391,7 +526,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, infof(data, "error importing native CA store, continuing anyway"); } else { - imported_native_ca = true; + imported_native_ca = TRUE; infof(data, "successfully imported native CA store"); wssl->x509_store_setup = TRUE; } @@ -402,7 +537,8 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, 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) { + WOLFSSL_FILETYPE_PEM) != + WOLFSSL_SUCCESS) { if(imported_native_ca) { infof(data, "error importing CA certificate blob, continuing anyway"); } @@ -421,7 +557,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, /* 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); + ssl_cafile ? ssl_cafile : "none", !!ca_info_blob); if(!store) return CURLE_OUT_OF_MEMORY; @@ -431,7 +567,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, ssl_cafile, ssl_capath, WOLFSSL_LOAD_FLAG_IGNORE_ERR); - if(SSL_SUCCESS != rc) { + if(WOLFSSL_SUCCESS != rc) { if(conn_config->verifypeer) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:" @@ -493,7 +629,7 @@ cached_x509_store_expired(const struct Curl_easy *data, timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; if(timeout_ms < 0) - return false; + return FALSE; return elapsed_ms >= timeout_ms; } @@ -509,17 +645,17 @@ cached_x509_store_different(struct Curl_cfilter *cf, return strcmp(mb->CAfile, conn_config->CAfile); } -static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data) +static WOLFSSL_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; + 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)) { @@ -531,7 +667,7 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, static void set_cached_x509_store(struct Curl_cfilter *cf, const struct Curl_easy *data, - X509_STORE *store) + WOLFSSL_X509_STORE *store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_multi *multi = data->multi; @@ -563,13 +699,13 @@ static void set_cached_x509_store(struct Curl_cfilter *cf, if(conn_config->CAfile) { CAfile = strdup(conn_config->CAfile); if(!CAfile) { - X509_STORE_free(store); + wolfSSL_X509_STORE_free(store); return; } } if(share->store) { - X509_STORE_free(share->store); + wolfSSL_X509_STORE_free(share->store); free(share->CAfile); } @@ -609,7 +745,7 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, 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(); + WOLFSSL_X509_STORE *store = wolfSSL_X509_STORE_new(); if(!store) { failf(data, "SSL: could not create a X509 store"); return CURLE_OUT_OF_MEMORY; @@ -623,7 +759,7 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, } else { /* We never share the CTX's store, use it. */ - X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx); + WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx); result = populate_x509_store(cf, data, store, wssl); } @@ -631,36 +767,35 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, } #ifdef WOLFSSL_TLS13 -static size_t -wssl_get_default_ciphers(bool tls13, char *buf, size_t size) +static CURLcode +wssl_add_default_ciphers(bool tls13, struct dynbuf *buf) { - size_t len = 0; - char *term = buf; int i; char *str; - size_t n; for(i = 0; (str = wolfSSL_get_cipher_list(i)); i++) { + size_t n; 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 = ':'; + /* if there already is data in the string, add colon separator */ + if(Curl_dyn_len(buf)) { + CURLcode result = Curl_dyn_addn(buf, ":", 1); + if(result) + return result; } - len += n + 1; - } - if(buf) - *term = '\0'; + n = strlen(str); + if(Curl_dyn_addn(buf, str, n)) + return CURLE_OUT_OF_MEMORY; + } - return len > 0 ? len - 1 : 0; + return CURLE_OK; } #endif -#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ +/* 4.2.0 (2019) */ +#if LIBWOLFSSL_VERSION_HEX < 0x04002000 || !defined(OPENSSL_EXTRA) static int wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX* ctx, int version) { @@ -707,7 +842,7 @@ static CURLcode wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { int res; - char *ciphers, *curves; + char *curves; struct ssl_connect_data *connssl = cf->ctx; struct wolfssl_ctx *backend = (struct wolfssl_ctx *)connssl->backend; @@ -798,50 +933,50 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #ifndef WOLFSSL_TLS13 - ciphers = conn_config->cipher_list; - if(ciphers) { - if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { - failf(data, "failed setting cipher list: %s", ciphers); - return CURLE_SSL_CIPHER; + { + char *ciphers = conn_config->cipher_list; + if(ciphers) { + if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { + failf(data, "failed setting cipher list: %s", ciphers); + return CURLE_SSL_CIPHER; + } + infof(data, "Cipher selection: %s", ciphers); } - infof(data, "Cipher selection: %s", ciphers); } #else +#define MAX_CIPHER_LEN 4096 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; + struct dynbuf c; + CURLcode result; + Curl_dyn_init(&c, MAX_CIPHER_LEN); if(ciphers13) - memcpy(ciphers, ciphers13, len13); + result = Curl_dyn_add(&c, ciphers13); else - wssl_get_default_ciphers(true, ciphers, len13 + 1); - ciphers[len13] = ':'; + result = wssl_add_default_ciphers(TRUE, &c); - 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(!result) { + if(ciphers12) { + if(Curl_dyn_len(&c)) + result = Curl_dyn_addn(&c, ":", 1); + if(!result) + result = Curl_dyn_add(&c, ciphers12); + } + else + result = wssl_add_default_ciphers(FALSE, &c); + } + if(result) + return result; - if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { - failf(data, "failed setting cipher list: %s", ciphers); - free(ciphers); + if(!wolfSSL_CTX_set_cipher_list(backend->ctx, Curl_dyn_ptr(&c))) { + failf(data, "failed setting cipher list: %s", Curl_dyn_ptr(&c)); + Curl_dyn_free(&c); return CURLE_SSL_CIPHER; } - infof(data, "Cipher selection: %s", ciphers); - free(ciphers); + infof(data, "Cipher selection: %s", Curl_dyn_ptr(&c)); + Curl_dyn_free(&c); } #endif @@ -859,7 +994,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(pqkem == 0) #endif { - if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { + if(!wolfSSL_CTX_set1_curves_list(backend->ctx, curves)) { failf(data, "failed setting curves list: '%s'", curves); return CURLE_SSL_CIPHER; } @@ -960,8 +1095,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ wolfSSL_CTX_set_verify(backend->ctx, - conn_config->verifypeer?SSL_VERIFY_PEER: - SSL_VERIFY_NONE, NULL); + conn_config->verifypeer ? WOLFSSL_VERIFY_PEER : + WOLFSSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI if(connssl->peer.sni) { @@ -1028,7 +1163,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(result || wolfSSL_UseALPN(backend->handle, (char *)proto.data, (unsigned int)proto.len, - WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { + WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != WOLFSSL_SUCCESS) { failf(data, "SSL: failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } @@ -1056,20 +1191,11 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* 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, &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, "cannot use session ID, going on without"); - } - else - infof(data, "SSL reusing session ID"); - } - Curl_ssl_sessionid_unlock(data); + /* Set session from cache if there is one */ + (void)wssl_setup_session(cf, data, backend, &connssl->peer); + /* Register to get notified when a new session is received */ + wolfSSL_set_app_data(backend->handle, cf); + wolfSSL_CTX_sess_set_new_cb(backend->ctx, wssl_vtls_new_session_cb); } #ifdef USE_ECH @@ -1152,7 +1278,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { WOLFSSL_BIO *bio; - bio = BIO_new(wolfssl_bio_cf_method); + bio = wolfSSL_BIO_new(wolfssl_bio_cf_method); if(!bio) return CURLE_OUT_OF_MEMORY; @@ -1200,8 +1326,8 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) (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]: + 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]; @@ -1213,9 +1339,9 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* Enable RFC2818 checks */ if(conn_config->verifyhost) { - char *snihost = connssl->peer.sni? - connssl->peer.sni : connssl->peer.hostname; - if(wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE) + char *snihost = connssl->peer.sni ? + connssl->peer.sni : connssl->peer.hostname; + if(wolfSSL_check_domain_name(backend->handle, snihost) == WOLFSSL_FAILURE) return CURLE_SSL_CONNECT_ERROR; } @@ -1244,7 +1370,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) * Note that OpenSSL SSL_want_read() is always true here. If wolfSSL ever * changes, the worst case is that no key is logged on error. */ - if(ret == SSL_SUCCESS || + if(ret == WOLFSSL_SUCCESS || (!wolfSSL_want_read(backend->handle) && !wolfSSL_want_write(backend->handle))) { wolfssl_log_tls12_secret(backend->handle); @@ -1258,11 +1384,11 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) if(ret != 1) { int detail = wolfSSL_get_error(backend->handle, ret); - if(SSL_ERROR_WANT_READ == detail) { + if(WOLFSSL_ERROR_WANT_READ == detail) { connssl->io_need = CURL_SSL_IO_NEED_RECV; return CURLE_OK; } - else if(SSL_ERROR_WANT_WRITE == detail) { + else if(WOLFSSL_ERROR_WANT_WRITE == detail) { connssl->io_need = CURL_SSL_IO_NEED_SEND; return CURLE_OK; } @@ -1354,7 +1480,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) if(pinnedpubkey) { #ifdef KEEP_PEER_CERT - X509 *x509; + WOLFSSL_X509 *x509; const char *x509_der; int x509_der_len; struct Curl_X509certificate x509_parsed; @@ -1406,12 +1532,12 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len); - if(rc == SSL_SUCCESS) { - Curl_alpn_set_negotiated(cf, data, (const unsigned char *)protocol, - protocol_len); + if(rc == WOLFSSL_SUCCESS) { + Curl_alpn_set_negotiated(cf, data, connssl, + (const unsigned char *)protocol, protocol_len); } - else if(rc == SSL_ALPN_NOT_FOUND) - Curl_alpn_set_negotiated(cf, data, NULL, 0); + else if(rc == WOLFSSL_ALPN_NOT_FOUND) + Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0); else { failf(data, "ALPN, failure getting protocol, error %d", rc); return CURLE_SSL_CONNECT_ERROR; @@ -1431,50 +1557,6 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_OK; } - -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_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.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); - /* 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(result) { - failf(data, "failed to store ssl session"); - return result; - } - } - } - - connssl->connecting_state = ssl_connect_done; - - return result; -} - - static ssize_t wolfssl_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *mem, @@ -1496,8 +1578,8 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, int err = wolfSSL_get_error(backend->handle, rc); switch(err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: + case WOLFSSL_ERROR_WANT_READ: + case WOLFSSL_ERROR_WANT_WRITE: /* there is data pending, re-invoke SSL_write() */ CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len); *curlcode = CURLE_AGAIN; @@ -1546,14 +1628,14 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf, wctx->shutting_down = TRUE; connssl->io_need = CURL_SSL_IO_NEED_NONE; *done = FALSE; - if(!(wolfSSL_get_shutdown(wctx->handle) & SSL_SENT_SHUTDOWN)) { + if(!(wolfSSL_get_shutdown(wctx->handle) & WOLFSSL_SENT_SHUTDOWN)) { /* We have not started the shutdown from our side yet. Check * if the server already sent us one. */ - ERR_clear_error(); + wolfSSL_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) { + if(!nread && err == WOLFSSL_ERROR_ZERO_RETURN) { bool input_pending; /* Yes, it did. */ if(!send_shutdown) { @@ -1576,13 +1658,13 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf, /* 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(); + wolfSSL_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)) { + if(WOLFSSL_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; @@ -1592,25 +1674,25 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf, } for(i = 0; i < 10; ++i) { - ERR_clear_error(); + wolfSSL_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 */ + case WOLFSSL_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: + case WOLFSSL_ERROR_NONE: /* just did not get anything */ + case WOLFSSL_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: + case WOLFSSL_ERROR_WANT_WRITE: CURL_TRC_CF(data, cf, "SSL shutdown send blocked"); connssl->io_need = CURL_SSL_IO_NEED_SEND; break; @@ -1671,13 +1753,18 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, int err = wolfSSL_get_error(backend->handle, nread); switch(err) { - case SSL_ERROR_ZERO_RETURN: /* no more data */ + case WOLFSSL_ERROR_ZERO_RETURN: /* no more data */ CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen); *curlcode = CURLE_OK; return 0; - case SSL_ERROR_NONE: - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: + case WOLFSSL_ERROR_NONE: + case WOLFSSL_ERROR_WANT_READ: + case WOLFSSL_ERROR_WANT_WRITE: + if(!backend->io_result && connssl->peer_closed) { + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen); + *curlcode = CURLE_OK; + return 0; + } /* there is data pending, re-invoke wolfSSL_read() */ CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen); *curlcode = CURLE_AGAIN; @@ -1688,7 +1775,12 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, *curlcode = CURLE_AGAIN; return -1; } - { + else if(!backend->io_result && connssl->peer_closed) { + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen); + *curlcode = CURLE_OK; + return 0; + } + else { char error_buffer[256]; failf(data, "SSL read: %s, errno %d", wolfssl_strerror((unsigned long)err, error_buffer, @@ -1721,7 +1813,7 @@ static int wolfssl_init(void) #ifdef OPENSSL_EXTRA Curl_tls_keylog_open(); #endif - ret = (wolfSSL_Init() == SSL_SUCCESS); + ret = (wolfSSL_Init() == WOLFSSL_SUCCESS); wolfssl_bio_cf_init_methods(); return ret; } @@ -1748,7 +1840,7 @@ static bool wolfssl_data_pending(struct Curl_cfilter *cf, backend = (struct wolfssl_ctx *)ctx->backend; if(backend->handle) /* SSL is in use */ - return (0 != wolfSSL_pending(backend->handle)) ? TRUE : FALSE; + return wolfSSL_pending(backend->handle); else return FALSE; } @@ -1762,7 +1854,6 @@ wolfssl_connect_common(struct Curl_cfilter *cf, CURLcode result; struct ssl_connect_data *connssl = cf->ctx; curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - int what; /* check if the connection has already been established */ if(ssl_connection_complete == connssl->state) { @@ -1798,14 +1889,12 @@ wolfssl_connect_common(struct Curl_cfilter *cf, /* if ssl is expecting something, check if it is available. */ if(connssl->io_need) { - - 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); + 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; + int what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + nonblocking ? 0 : timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -1838,9 +1927,9 @@ wolfssl_connect_common(struct Curl_cfilter *cf, } /* repeat step2 until all transactions are done. */ if(ssl_connect_3 == connssl->connecting_state) { - result = wolfssl_connect_step3(cf, data); - if(result) - return result; + /* In other backends, this is where we verify the certificate, but + * wolfSSL already does that as part of the handshake. */ + connssl->connecting_state = ssl_connect_done; } if(ssl_connect_done == connssl->connecting_state) { diff --git a/libs/libcurl/src/vtls/wolfssl.h b/libs/libcurl/src/vtls/wolfssl.h index 0e0ffb6e70..0a4d253a58 100644 --- a/libs/libcurl/src/vtls/wolfssl.h +++ b/libs/libcurl/src/vtls/wolfssl.h @@ -26,13 +26,16 @@ #include "curl_setup.h" #ifdef USE_WOLFSSL -#include -#include -#include -#include #include "urldata.h" +struct WOLFSSL; +typedef struct WOLFSSL WOLFSSL; +struct WOLFSSL_CTX; +typedef struct WOLFSSL_CTX WOLFSSL_CTX; +struct WOLFSSL_SESSION; +typedef struct WOLFSSL_SESSION WOLFSSL_SESSION; + extern const struct Curl_ssl Curl_ssl_wolfssl; struct wolfssl_ctx { @@ -48,5 +51,16 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, struct wolfssl_ctx *wssl); +CURLcode wssl_setup_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wss, + struct ssl_peer *peer); + +CURLcode wssl_cache_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + WOLFSSL_SESSION *session); + + #endif /* USE_WOLFSSL */ #endif /* HEADER_CURL_WOLFSSL_H */ diff --git a/libs/libcurl/src/vtls/x509asn1.c b/libs/libcurl/src/vtls/x509asn1.c index e92449fb2e..6e1fbfab49 100644 --- a/libs/libcurl/src/vtls/x509asn1.c +++ b/libs/libcurl/src/vtls/x509asn1.c @@ -270,7 +270,7 @@ static CURLcode bool2str(struct dynbuf *store, { if(end - beg != 1) return CURLE_BAD_FUNCTION_ARGUMENT; - return Curl_dyn_add(store, *beg? "TRUE": "FALSE"); + return Curl_dyn_add(store, *beg ? "TRUE": "FALSE"); } /* @@ -323,7 +323,7 @@ static CURLcode int2str(struct dynbuf *store, do val = (val << 8) | *(const unsigned char *) beg++; while(beg < end); - return Curl_dyn_addf(store, "%s%x", val >= 10? "0x": "", val); + return Curl_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val); } /* @@ -551,7 +551,7 @@ static CURLcode GTime2str(struct dynbuf *store, "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", beg, beg + 4, beg + 6, beg + 8, beg + 10, sec1, sec2, - fracl? ".": "", (int)fracl, fracp, + fracl ? ".": "", (int)fracl, fracp, sep, (int)tzl, tzp); } -- cgit v1.2.3