From: Stefan Eissing Date: Fri, 22 Mar 2024 12:07:25 +0000 (+0100) Subject: tls: use shared init code for TCP+QUIC X-Git-Tag: curl-8_8_0~266 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3210101088dfa3d6a125;p=thirdparty%2Fcurl.git tls: use shared init code for TCP+QUIC Closes #13172 --- diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index ed6322c10d..689db97891 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -1068,7 +1068,7 @@ static void cf_h1_proxy_close(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_h1_proxy = { "H1-PROXY", - CF_TYPE_IP_CONNECT, + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, 0, cf_h1_proxy_destroy, cf_h1_proxy_connect, diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 78dc222fa8..0bff15f38c 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -1532,7 +1532,7 @@ static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_h2_proxy = { "H2-PROXY", - CF_TYPE_IP_CONNECT, + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, CURL_LOG_LVL_NONE, cf_h2_proxy_destroy, cf_h2_proxy_connect, diff --git a/lib/cf-haproxy.c b/lib/cf-haproxy.c index 404392291c..2abc4d754e 100644 --- a/lib/cf-haproxy.c +++ b/lib/cf-haproxy.c @@ -189,7 +189,7 @@ static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_haproxy = { "HAPROXY", - 0, + CF_TYPE_PROXY, 0, cf_haproxy_destroy, cf_haproxy_connect, diff --git a/lib/cfilters.h b/lib/cfilters.h index c90b1f4bc2..2d6c7b6afb 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -178,10 +178,12 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, * connection, etc. * CF_TYPE_SSL: provide SSL/TLS * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles + * CF_TYPE_PROXY provides proxying */ #define CF_TYPE_IP_CONNECT (1 << 0) #define CF_TYPE_SSL (1 << 1) #define CF_TYPE_MULTIPLEX (1 << 2) +#define CF_TYPE_PROXY (1 << 3) /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 113c43a413..7c035d4b60 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -293,7 +293,7 @@ static void http_proxy_cf_close(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_http_proxy = { "HTTP-PROXY", - CF_TYPE_IP_CONNECT, + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, 0, http_proxy_cf_destroy, http_proxy_cf_connect, diff --git a/lib/socks.c b/lib/socks.c index bd5962a870..e6c6a6480f 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -1244,7 +1244,7 @@ static void socks_cf_get_host(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_socks_proxy = { "SOCKS-PROXYY", - CF_TYPE_IP_CONNECT, + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, 0, socks_proxy_cf_destroy, socks_proxy_cf_connect, diff --git a/lib/urldata.h b/lib/urldata.h index 95a3add703..90f4d1bb57 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -279,6 +279,8 @@ struct ssl_peer { char *dispname; /* display version of hostname */ char *sni; /* SNI version of hostname or NULL if not usable */ ssl_peer_type type; /* type of the peer information */ + int port; /* port we are talking to */ + int transport; /* TCP or QUIC */ }; struct ssl_primary_config { @@ -344,6 +346,7 @@ struct Curl_ssl_session { long age; /* just a number, the higher the more recent */ int remote_port; /* remote port */ int conn_to_port; /* remote port for the connection (may be -1) */ + int transport; /* TCP or QUIC */ struct ssl_primary_config ssl_config; /* setup for this session */ }; diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index d2e0a12658..ed53f88a27 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -113,7 +113,7 @@ void Curl_ngtcp2_ver(char *p, size_t len) struct cf_ngtcp2_ctx { struct cf_quic_ctx q; struct ssl_peer peer; - struct quic_tls_ctx tls; + struct curl_tls_ctx tls; ngtcp2_path connected_path; ngtcp2_conn *qconn; ngtcp2_cid dcid; @@ -1909,25 +1909,60 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) (void)save; } -static CURLcode tls_ctx_setup(struct quic_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data) +#ifdef USE_OPENSSL +/* The "new session" callback must return zero if the session can be removed + * or non-zero if the session has been put into the session cache. + */ +static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) { + struct Curl_cfilter *cf; + struct cf_ngtcp2_ctx *ctx; + struct Curl_easy *data; + ngtcp2_crypto_conn_ref *cref; + + cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); + cf = cref? cref->user_data : NULL; + ctx = cf? cf->ctx : NULL; + data = cf? CF_DATA_CURRENT(cf) : NULL; + if(cf && data && ctx) { + CURLcode result = Curl_ossl_add_session(cf, data, &ctx->peer, + ssl_sessionid); + return result? 0 : 1; + } + return 0; +} +#endif /* USE_OPENSSL */ + +static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data) +{ + struct curl_tls_ctx *ctx = user_data; (void)cf; #ifdef USE_OPENSSL #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) - if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ssl_ctx) != 0) { + if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) + != 0) { failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); return CURLE_FAILED_INIT; } #else - if(ngtcp2_crypto_quictls_configure_client_context(ctx->ssl_ctx) != 0) { + if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); return CURLE_FAILED_INIT; } #endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ + /* Enable the session cache because it's a prerequisite for the + * "new session" callback. Use the "external storage" mode to prevent + * OpenSSL from creating an internal session cache. + */ + SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, ossl_new_session_cb); + #elif defined(USE_GNUTLS) - if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) { + if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) { failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); return CURLE_FAILED_INIT; } @@ -1960,17 +1995,21 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); - result = Curl_ssl_peer_init(&ctx->peer, cf); + result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); if(result) return result; #define H3_ALPN "\x2h3\x5h3-29" result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, H3_ALPN, sizeof(H3_ALPN) - 1, - tls_ctx_setup, &ctx->conn_ref); + tls_ctx_setup, &ctx->tls, &ctx->conn_ref); if(result) return result; +#ifdef USE_OPENSSL + SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); +#endif + ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); if(result) @@ -2012,8 +2051,10 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, if(rc) return CURLE_QUIC_CONNECT_ERROR; -#ifdef USE_GNUTLS - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls->session); +#ifdef USE_OPENSSL + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); +#elif defined(USE_GNUTLS) + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); #else ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ssl); #endif diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c index 61434e37d9..b7ca302230 100644 --- a/lib/vquic/curl_osslq.c +++ b/lib/vquic/curl_osslq.c @@ -281,7 +281,7 @@ static void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3) struct cf_osslq_ctx { struct cf_quic_ctx q; struct ssl_peer peer; - struct quic_tls_ctx tls; + struct curl_tls_ctx tls; struct cf_call_data call_data; struct cf_osslq_h3conn h3; struct curltime started_at; /* time the current attempt started */ @@ -318,7 +318,7 @@ static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - if(ctx && ctx->tls.ssl) { + if(ctx && ctx->tls.ossl.ssl) { /* TODO: send connection close */ CURL_TRC_CF(data, cf, "cf_osslq_close()"); cf_osslq_ctx_clear(ctx); @@ -403,7 +403,7 @@ static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { result = CURLE_PEER_FAILED_VERIFICATION; - lerr = SSL_get_verify_result(ctx->tls.ssl); + lerr = SSL_get_verify_result(ctx->tls.ossl.ssl); if(lerr != X509_V_OK) { ssl_config->certverifyresult = lerr; msnprintf(ebuf, sizeof(ebuf), @@ -1047,14 +1047,14 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); - result = Curl_ssl_peer_init(&ctx->peer, cf); + result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); if(result) goto out; #define H3_ALPN "\x2h3" result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, H3_ALPN, sizeof(H3_ALPN) - 1, - NULL, NULL); + NULL, NULL, NULL); if(result) goto out; @@ -1098,12 +1098,12 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, goto out; } - if(!SSL_set1_initial_peer_addr(ctx->tls.ssl, baddr)) { + if(!SSL_set1_initial_peer_addr(ctx->tls.ossl.ssl, baddr)) { failf(data, "failed to set the initial peer address"); result = CURLE_FAILED_INIT; goto out; } - if(!SSL_set_blocking_mode(ctx->tls.ssl, 0)) { + if(!SSL_set_blocking_mode(ctx->tls.ossl.ssl, 0)) { failf(data, "failed to turn off blocking mode"); result = CURLE_FAILED_INIT; goto out; @@ -1111,7 +1111,8 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, #ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT /* Added in OpenSSL v3.3.x */ - if(!SSL_set_feature_request_uint(ctx->tls.ssl, SSL_VALUE_QUIC_IDLE_TIMEOUT, + if(!SSL_set_feature_request_uint(ctx->tls.ossl.ssl, + SSL_VALUE_QUIC_IDLE_TIMEOUT, CURL_QUIC_MAX_IDLE_MS)) { CURL_TRC_CF(data, cf, "error setting idle timeout, "); result = CURLE_FAILED_INIT; @@ -1119,13 +1120,13 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, } #endif - SSL_set_bio(ctx->tls.ssl, bio, bio); + SSL_set_bio(ctx->tls.ossl.ssl, bio, bio); bio = NULL; - SSL_set_connect_state(ctx->tls.ssl); - SSL_set_incoming_stream_policy(ctx->tls.ssl, + SSL_set_connect_state(ctx->tls.ossl.ssl); + SSL_set_incoming_stream_policy(ctx->tls.ossl.ssl, SSL_INCOMING_STREAM_POLICY_ACCEPT, 0); /* setup the H3 things on top of the QUIC connection */ - result = cf_osslq_h3conn_init(ctx, ctx->tls.ssl, cf); + result = cf_osslq_h3conn_init(ctx, ctx->tls.ossl.ssl, cf); out: if(bio) @@ -1288,22 +1289,23 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, struct cf_osslq_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - if(!ctx->tls.ssl) + if(!ctx->tls.ossl.ssl) goto out; ERR_clear_error(); /* 1. Check for new incoming streams */ while(1) { - SSL *snew = SSL_accept_stream(ctx->tls.ssl, SSL_ACCEPT_STREAM_NO_BLOCK); + SSL *snew = SSL_accept_stream(ctx->tls.ossl.ssl, + SSL_ACCEPT_STREAM_NO_BLOCK); if(!snew) break; (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data); } - if(!SSL_handle_events(ctx->tls.ssl)) { - int detail = SSL_get_error(ctx->tls.ssl, 0); + if(!SSL_handle_events(ctx->tls.ossl.ssl)) { + int detail = SSL_get_error(ctx->tls.ossl.ssl, 0); result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR); } @@ -1370,7 +1372,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, struct cf_osslq_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - if(!ctx->tls.ssl || !ctx->h3.conn) + if(!ctx->tls.ossl.ssl || !ctx->h3.conn) goto out; for(;;) { @@ -1493,7 +1495,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, struct cf_osslq_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - if(!ctx->tls.ssl) + if(!ctx->tls.ossl.ssl) goto out; ERR_clear_error(); @@ -1501,8 +1503,8 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, if(result) goto out; - if(!SSL_handle_events(ctx->tls.ssl)) { - int detail = SSL_get_error(ctx->tls.ssl, 0); + if(!SSL_handle_events(ctx->tls.ossl.ssl)) { + int detail = SSL_get_error(ctx->tls.ossl.ssl, 0); result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR); } @@ -1522,8 +1524,8 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, timediff_t timeoutms; int is_infinite = TRUE; - if(ctx->tls.ssl && - SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite) && + if(ctx->tls.ossl.ssl && + SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite) && !is_infinite) { timeoutms = curlx_tvtoms(&tv); /* QUIC want to be called again latest at the returned timeout */ @@ -1534,7 +1536,7 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, result = cf_progress_egress(cf, data); if(result) goto out; - if(SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite)) { + if(SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite)) { timeoutms = curlx_tvtoms(&tv); } } @@ -1579,7 +1581,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, goto out; } - if(!ctx->tls.ssl) { + if(!ctx->tls.ossl.ssl) { ctx->started_at = now; result = cf_osslq_ctx_start(cf, data); if(result) @@ -1595,7 +1597,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, } ERR_clear_error(); - err = SSL_do_handshake(ctx->tls.ssl); + err = SSL_do_handshake(ctx->tls.ossl.ssl); if(err == 1) { /* connected */ @@ -1613,7 +1615,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, } } else { - int detail = SSL_get_error(ctx->tls.ssl, err); + int detail = SSL_get_error(ctx->tls.ossl.ssl, err); switch(detail) { case SSL_ERROR_WANT_READ: ctx->q.last_io = now; @@ -1644,7 +1646,8 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, } out: - if(result == CURLE_RECV_ERROR && ctx->tls.ssl && ctx->protocol_shutdown) { + if(result == CURLE_RECV_ERROR && ctx->tls.ossl.ssl && + ctx->protocol_shutdown) { /* When a QUIC server instance is shutting down, it may send us a * CONNECTION_CLOSE right away. Our connection then enters the DRAINING * state. The CONNECT may work in the near future again. Indicate @@ -1732,7 +1735,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, } DEBUGASSERT(stream->s.id == -1); - *err = cf_osslq_stream_open(&stream->s, ctx->tls.ssl, 0, + *err = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0, &ctx->stream_bufcp, data); if(*err) { failf(data, "can't get bidi streams"); @@ -1810,7 +1813,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx->tls.ssl); + DEBUGASSERT(ctx->tls.ossl.ssl); DEBUGASSERT(ctx->h3.conn); *err = CURLE_OK; @@ -1952,7 +1955,7 @@ static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); DEBUGASSERT(ctx); - DEBUGASSERT(ctx->tls.ssl); + DEBUGASSERT(ctx->tls.ossl.ssl); DEBUGASSERT(ctx->h3.conn); *err = CURLE_OK; @@ -2088,7 +2091,7 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); *input_pending = FALSE; - if(!ctx->tls.ssl) + if(!ctx->tls.ossl.ssl) goto out; #ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT @@ -2096,7 +2099,8 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf, { timediff_t idletime; uint64_t idle_ms = ctx->max_idle_ms; - if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_FEATURE_NEGOTIATED, + if(!SSL_get_value_uint(ctx->tls.ossl.ssl, + SSL_VALUE_CLASS_FEATURE_NEGOTIATED, SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) { CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, " "assume connection is dead."); @@ -2136,15 +2140,15 @@ static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf, { struct cf_osslq_ctx *ctx = cf->ctx; - if(!ctx->tls.ssl) { + if(!ctx->tls.ossl.ssl) { /* NOP */ } else if(!cf->connected) { /* during handshake, transfer has not started yet. we always * add our socket for polling if SSL wants to send/recv */ Curl_pollset_set(data, ps, ctx->q.sockfd, - SSL_net_read_desired(ctx->tls.ssl), - SSL_net_write_desired(ctx->tls.ssl)); + SSL_net_read_desired(ctx->tls.ossl.ssl), + SSL_net_write_desired(ctx->tls.ossl.ssl)); } else { /* once connected, we only modify the socket if it is present. @@ -2153,8 +2157,8 @@ static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf, Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); if(want_recv || want_send) { Curl_pollset_set(data, ps, ctx->q.sockfd, - SSL_net_read_desired(ctx->tls.ssl), - SSL_net_write_desired(ctx->tls.ssl)); + SSL_net_read_desired(ctx->tls.ossl.ssl), + SSL_net_write_desired(ctx->tls.ossl.ssl)); } } } @@ -2170,7 +2174,7 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf, #ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL /* Added in OpenSSL v3.3.x */ uint64_t v; - if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_GENERIC, + if(!SSL_get_value_uint(ctx->tls.ossl.ssl, SSL_VALUE_CLASS_GENERIC, SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) { CURL_TRC_CF(data, cf, "error getting available local bidi streams"); return CURLE_HTTP3; diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 3015db063a..aca1001623 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -88,7 +88,7 @@ void Curl_quiche_ver(char *p, size_t len) struct cf_quiche_ctx { struct cf_quic_ctx q; struct ssl_peer peer; - struct quic_tls_ctx tls; + struct curl_tls_ctx tls; quiche_conn *qconn; quiche_config *cfg; quiche_h3_conn *h3c; @@ -123,8 +123,8 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) quiche_conn_free(ctx->qconn); if(ctx->cfg) quiche_config_free(ctx->cfg); - /* quiche just freed ctx->tls.ssl */ - ctx->tls.ssl = NULL; + /* quiche just freed it */ + ctx->tls.ossl.ssl = NULL; Curl_vquic_tls_cleanup(&ctx->tls); Curl_ssl_peer_cleanup(&ctx->peer); vquic_ctx_free(&ctx->q); @@ -565,7 +565,7 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, return CURLE_OK; } else if(QUICHE_ERR_TLS_FAIL == nread) { - long verify_ok = SSL_get_verify_result(ctx->tls.ssl); + long verify_ok = SSL_get_verify_result(ctx->tls.ossl.ssl); if(verify_ok != X509_V_OK) { failf(r->data, "SSL certificate problem: %s", X509_verify_cert_error_string(verify_ok)); @@ -1202,7 +1202,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, if(result) return result; - result = Curl_ssl_peer_init(&ctx->peer, cf); + result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); if(result) return result; @@ -1237,7 +1237,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, QUICHE_H3_APPLICATION_PROTOCOL, sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1, - NULL, cf); + NULL, NULL, cf); if(result) return result; @@ -1257,7 +1257,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, (struct sockaddr *)&ctx->q.local_addr, ctx->q.local_addrlen, &sockaddr->sa_addr, sockaddr->addrlen, - ctx->cfg, ctx->tls.ssl, false); + ctx->cfg, ctx->tls.ossl.ssl, false); if(!ctx->qconn) { failf(data, "can't create quiche connection"); return CURLE_OUT_OF_MEMORY; diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index ecba607a9f..191ac22cec 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -61,277 +61,12 @@ #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) #endif -#ifdef USE_OPENSSL -#define QUIC_CIPHERS \ - "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ - "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" -#define QUIC_GROUPS "P-256:X25519:P-384:P-521" -#elif defined(USE_GNUTLS) -#define QUIC_PRIORITY \ - "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ - "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ - "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ - "%DISABLE_TLS13_COMPAT_MODE" -#elif defined(USE_WOLFSSL) +#if defined(USE_WOLFSSL) + #define QUIC_CIPHERS \ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" #define QUIC_GROUPS "P-256:P-384:P-521" -#endif - - -#ifdef USE_OPENSSL - -static void keylog_callback(const SSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} - -static CURLcode curl_ossl_init_ctx(struct quic_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - Curl_vquic_tls_ctx_setup *ctx_setup) -{ - struct ssl_primary_config *conn_config; - CURLcode result = CURLE_FAILED_INIT; - - DEBUGASSERT(!ctx->ssl_ctx); -#ifdef USE_OPENSSL_QUIC - ctx->ssl_ctx = SSL_CTX_new(OSSL_QUIC_client_method()); -#else - ctx->ssl_ctx = SSL_CTX_new(TLS_method()); -#endif - if(!ctx->ssl_ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - conn_config = Curl_ssl_cf_get_primary_config(cf); - if(!conn_config) { - result = CURLE_FAILED_INIT; - goto out; - } - - if(ctx_setup) { - result = ctx_setup(ctx, cf, data); - if(result) - goto out; - } - - SSL_CTX_set_default_verify_paths(ctx->ssl_ctx); - - { - const char *curves = conn_config->curves ? - conn_config->curves : QUIC_GROUPS; - if(!SSL_CTX_set1_curves_list(ctx->ssl_ctx, curves)) { - failf(data, "failed setting curves list for QUIC: '%s'", curves); - return CURLE_SSL_CIPHER; - } - } - -#ifndef OPENSSL_IS_BORINGSSL - { - const char *ciphers13 = conn_config->cipher_list13 ? - conn_config->cipher_list13 : QUIC_CIPHERS; - if(SSL_CTX_set_ciphersuites(ctx->ssl_ctx, ciphers13) != 1) { - failf(data, "failed setting QUIC cipher suite: %s", ciphers13); - return CURLE_SSL_CIPHER; - } - infof(data, "QUIC cipher selection: %s", ciphers13); - } -#endif - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback); - } - - /* OpenSSL always tries to verify the peer, this only says whether it should - * fail to connect if the verification fails, or if it should continue - * anyway. In the latter case the result of the verification is checked with - * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(ctx->ssl_ctx, conn_config->verifypeer ? - SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); - - /* give application a chance to interfere with SSL set up. */ - if(data->set.ssl.fsslctx) { - /* When a user callback is installed to modify the SSL_CTX, - * we need to do the full initialization before calling it. - * See: #11800 */ - if(!ctx->x509_store_setup) { - result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx); - if(result) - goto out; - ctx->x509_store_setup = TRUE; - } - Curl_set_in_callback(data, true); - result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx, - data->set.ssl.fsslctxp); - Curl_set_in_callback(data, false); - if(result) { - failf(data, "error signaled by ssl ctx callback"); - goto out; - } - } - result = CURLE_OK; - -out: - if(result && ctx->ssl_ctx) { - SSL_CTX_free(ctx->ssl_ctx); - ctx->ssl_ctx = NULL; - } - return result; -} - -static CURLcode curl_ossl_set_client_cert(struct quic_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - SSL_CTX *ssl_ctx = ctx->ssl_ctx; - const struct ssl_config_data *ssl_config; - - ssl_config = Curl_ssl_cf_get_config(cf, data); - DEBUGASSERT(ssl_config); - - if(ssl_config->primary.clientcert || - ssl_config->primary.cert_blob || - ssl_config->cert_type) { - return Curl_ossl_set_client_cert( - data, ssl_ctx, ssl_config->primary.clientcert, - ssl_config->primary.cert_blob, ssl_config->cert_type, - ssl_config->key, ssl_config->key_blob, - ssl_config->key_type, ssl_config->key_passwd); - } - - return CURLE_OK; -} - -/** SSL callbacks ***/ - -static CURLcode curl_ossl_init_ssl(struct quic_tls_ctx *ctx, - struct Curl_easy *data, - struct ssl_peer *peer, - const char *alpn, size_t alpn_len, - void *user_data) -{ - DEBUGASSERT(!ctx->ssl); - ctx->ssl = SSL_new(ctx->ssl_ctx); - - SSL_set_app_data(ctx->ssl, user_data); - SSL_set_connect_state(ctx->ssl); -#ifndef USE_OPENSSL_QUIC - SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0); -#endif - - if(alpn) - SSL_set_alpn_protos(ctx->ssl, (const uint8_t *)alpn, (int)alpn_len); - - if(peer->sni) { - if(!SSL_set_tlsext_host_name(ctx->ssl, peer->sni)) { - failf(data, "Failed set SNI"); - SSL_free(ctx->ssl); - ctx->ssl = NULL; - return CURLE_QUIC_CONNECT_ERROR; - } - } - return CURLE_OK; -} - -#elif defined(USE_GNUTLS) -static int keylog_callback(gnutls_session_t session, const char *label, - const gnutls_datum_t *secret) -{ - gnutls_datum_t crandom; - gnutls_datum_t srandom; - - gnutls_session_get_random(session, &crandom, &srandom); - if(crandom.size != 32) { - return -1; - } - - Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); - return 0; -} - -static CURLcode curl_gtls_init_ctx(struct quic_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - const char *alpn, size_t alpn_len, - Curl_vquic_tls_ctx_setup *ctx_setup, - void *user_data) -{ - struct ssl_primary_config *conn_config; - CURLcode result; - gnutls_datum_t alpns[5]; - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - long * const pverifyresult = &data->set.ssl.certverifyresult; - int rc; - - conn_config = Curl_ssl_cf_get_primary_config(cf); - if(!conn_config) - return CURLE_FAILED_INIT; - - DEBUGASSERT(ctx->gtls == NULL); - ctx->gtls = calloc(1, sizeof(*(ctx->gtls))); - if(!ctx->gtls) - return CURLE_OUT_OF_MEMORY; - - result = gtls_client_init(data, conn_config, &data->set.ssl, - peer, ctx->gtls, pverifyresult); - if(result) - return result; - - gnutls_session_set_ptr(ctx->gtls->session, user_data); - - if(ctx_setup) { - result = ctx_setup(ctx, cf, data); - if(result) - return result; - } - - rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL); - if(rc < 0) { - CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n", - gnutls_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - gnutls_session_set_keylog_function(ctx->gtls->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) { - 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(ctx->gtls->session, - alpns, (unsigned int)i, - GNUTLS_ALPN_MANDATORY); - } - } - - return CURLE_OK; -} -#elif defined(USE_WOLFSSL) #if defined(HAVE_SECRET_CALLBACK) static void keylog_callback(const WOLFSSL *ssl, const char *line) @@ -341,10 +76,11 @@ static void keylog_callback(const WOLFSSL *ssl, const char *line) } #endif -static CURLcode curl_wssl_init_ctx(struct quic_tls_ctx *ctx, +static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, - Curl_vquic_tls_ctx_setup *ctx_setup) + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data) { struct ssl_primary_config *conn_config; CURLcode result = CURLE_FAILED_INIT; @@ -361,8 +97,8 @@ static CURLcode curl_wssl_init_ctx(struct quic_tls_ctx *ctx, goto out; } - if(ctx_setup) { - result = ctx_setup(ctx, cf, data); + if(cb_setup) { + result = cb_setup(cf, data, cb_user_data); if(result) goto out; } @@ -458,7 +194,7 @@ out: /** SSL callbacks ***/ -static CURLcode curl_wssl_init_ssl(struct quic_tls_ctx *ctx, +static CURLcode curl_wssl_init_ssl(struct curl_tls_ctx *ctx, struct Curl_easy *data, struct ssl_peer *peer, const char *alpn, size_t alpn_len, @@ -486,57 +222,50 @@ static CURLcode curl_wssl_init_ssl(struct quic_tls_ctx *ctx, } #endif /* defined(USE_WOLFSSL) */ -CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, const char *alpn, size_t alpn_len, - Curl_vquic_tls_ctx_setup *ctx_setup, - void *user_data) + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data, void *ssl_user_data) { CURLcode result; #ifdef USE_OPENSSL - result = curl_ossl_init_ctx(ctx, cf, data, ctx_setup); - if(result) - return result; - - result = curl_ossl_set_client_cert(ctx, cf, data); - if(result) - return result; - - return curl_ossl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data); + (void)result; + return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, TRNSPRT_QUIC, + (const unsigned char *)alpn, alpn_len, + cb_setup, cb_user_data, NULL, ssl_user_data); #elif defined(USE_GNUTLS) (void)result; - return curl_gtls_init_ctx(ctx, cf, data, peer, alpn, alpn_len, - ctx_setup, user_data); + return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, + (const unsigned char *)alpn, alpn_len, + cb_setup, cb_user_data, ssl_user_data); #elif defined(USE_WOLFSSL) - result = curl_wssl_init_ctx(ctx, cf, data, ctx_setup); + result = curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data); if(result) return result; - return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data); + return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data); #else #error "no TLS lib in used, should not happen" return CURLE_FAILED_INIT; #endif } -void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx) +void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx) { #ifdef USE_OPENSSL - if(ctx->ssl) - SSL_free(ctx->ssl); - if(ctx->ssl_ctx) - SSL_CTX_free(ctx->ssl_ctx); + if(ctx->ossl.ssl) + SSL_free(ctx->ossl.ssl); + if(ctx->ossl.ssl_ctx) + SSL_CTX_free(ctx->ossl.ssl_ctx); #elif defined(USE_GNUTLS) - if(ctx->gtls) { - if(ctx->gtls->cred) - gnutls_certificate_free_credentials(ctx->gtls->cred); - if(ctx->gtls->session) - gnutls_deinit(ctx->gtls->session); - free(ctx->gtls); - } + if(ctx->gtls.cred) + gnutls_certificate_free_credentials(ctx->gtls.cred); + if(ctx->gtls.session) + gnutls_deinit(ctx->gtls.session); #elif defined(USE_WOLFSSL) if(ctx->ssl) wolfSSL_free(ctx->ssl); @@ -546,16 +275,16 @@ void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx) memset(ctx, 0, sizeof(*ctx)); } -CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data) { #ifdef USE_OPENSSL - if(!ctx->x509_store_setup) { - CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx); + if(!ctx->ossl.x509_store_setup) { + CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ossl.ssl_ctx); if(result) return result; - ctx->x509_store_setup = TRUE; + ctx->ossl.x509_store_setup = TRUE; } #else (void)ctx; (void)cf; (void)data; @@ -563,7 +292,7 @@ CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx, return CURLE_OK; } -CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer) @@ -575,36 +304,24 @@ CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx, if(!conn_config) return CURLE_FAILED_INIT; - if(conn_config->verifyhost) { #ifdef USE_OPENSSL - X509 *server_cert; - server_cert = SSL_get1_peer_certificate(ctx->ssl); - if(!server_cert) { - return CURLE_PEER_FAILED_VERIFICATION; - } - result = Curl_ossl_verifyhost(data, cf->conn, peer, server_cert); - X509_free(server_cert); - if(result) - return result; + (void)conn_config; + result = Curl_oss_check_peer_cert(cf, data, &ctx->ossl, peer); #elif defined(USE_GNUTLS) - result = Curl_gtls_verifyserver(data, ctx->gtls->session, + if(conn_config->verifyhost) { + result = Curl_gtls_verifyserver(data, ctx->gtls.session, conn_config, &data->set.ssl, peer, data->set.str[STRING_SSL_PINNEDPUBLICKEY]); if(result) return result; + } #elif defined(USE_WOLFSSL) + (void)data; + if(conn_config->verifyhost) { if(!peer->sni || wolfSSL_check_domain_name(ctx->ssl, peer->sni) == SSL_FAILURE) return CURLE_PEER_FAILED_VERIFICATION; -#endif - infof(data, "Verified certificate just fine"); } - else - infof(data, "Skipped certificate verification"); -#ifdef USE_OPENSSL - if(data->set.ssl.certinfo) - /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, ctx->ssl); #endif return result; } diff --git a/lib/vquic/vquic-tls.h b/lib/vquic/vquic-tls.h index 9c0dfd8d52..be18fccc5e 100644 --- a/lib/vquic/vquic-tls.h +++ b/lib/vquic/vquic-tls.h @@ -26,21 +26,20 @@ #include "curl_setup.h" #include "bufq.h" +#include "vtls/openssl.h" #if defined(ENABLE_QUIC) && \ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) -struct quic_tls_ctx { +struct curl_tls_ctx { #ifdef USE_OPENSSL - SSL_CTX *ssl_ctx; - SSL *ssl; + struct ossl_ctx ossl; #elif defined(USE_GNUTLS) - struct gtls_instance *gtls; + struct gtls_ctx gtls; #elif defined(USE_WOLFSSL) WOLFSSL_CTX *ssl_ctx; WOLFSSL *ssl; #endif - BIT(x509_store_setup); /* if x509 store has been set up */ }; /** @@ -50,9 +49,9 @@ struct quic_tls_ctx { * - openssl/wolfssl: SSL_CTX* has just been created * - gnutls: gtls_client_init() has run */ -typedef CURLcode Curl_vquic_tls_ctx_setup(struct quic_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data); +typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *cb_user_data); /** * Initialize the QUIC TLS instances based of the SSL configurations @@ -64,23 +63,25 @@ typedef CURLcode Curl_vquic_tls_ctx_setup(struct quic_tls_ctx *ctx, * @param alpn the ALPN string in protocol format ((len+bytes+)+), * may be NULL * @param alpn_len the overall number of bytes in `alpn` - * @param ctx_setup optional callback for very early TLS config - * @param user_data optional pointer to set in TLS application context + * @param cb_setup optional callback for very early TLS config + ± @param cb_user_data user_data param for callback + * @param ssl_user_data optional pointer to set in TLS application context */ -CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, const char *alpn, size_t alpn_len, - Curl_vquic_tls_ctx_setup *ctx_setup, - void *user_data); + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data, + void *ssl_user_data); /** * Cleanup all data that has been initialized. */ -void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx); +void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx); -CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data); @@ -88,7 +89,7 @@ CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx, * After the QUIC basic handshake has been, verify that the peer * (and its certificate) fulfill our requirements. */ -CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer); diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c index 9b003d6958..e9e2ce86cc 100644 --- a/lib/vtls/bearssl.c +++ b/lib/vtls/bearssl.c @@ -686,7 +686,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "connect_step1, check session cache"); Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &session, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &session, NULL)) { br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); session_set = 1; infof(data, "BearSSL: reusing session ID"); @@ -905,10 +905,11 @@ 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); - incache = !(Curl_ssl_getsessionid(cf, data, &oldsession, NULL)); + incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, + &oldsession, NULL)); if(incache) Curl_ssl_delsessionid(data, oldsession); - ret = Curl_ssl_addsessionid(cf, data, session, 0, &added); + ret = Curl_ssl_addsessionid(cf, data, &connssl->peer, session, 0, &added); Curl_ssl_sessionid_unlock(data); if(!added) free(session); diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 7db1ce9daf..16d44c17c0 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -43,6 +43,7 @@ #include "urldata.h" #include "sendf.h" #include "inet_pton.h" +#include "keylog.h" #include "gtls.h" #include "vtls.h" #include "vtls_int.h" @@ -59,6 +60,16 @@ /* The last #include file should be: */ #include "memdebug.h" +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#define QUIC_PRIORITY \ + "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ + "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ + "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ + "%DISABLE_TLS13_COMPAT_MODE" + /* Enable GnuTLS debugging by defining GTLSDEBUG */ /*#define GTLSDEBUG */ @@ -77,7 +88,7 @@ static bool gtls_inited = FALSE; # include struct gtls_ssl_backend_data { - struct gtls_instance gtls; + struct gtls_ctx gtls; }; static ssize_t gtls_push(void *s, const void *buf, size_t blen) @@ -330,6 +341,7 @@ static gnutls_x509_crt_fmt_t do_file_type(const char *type) static CURLcode set_ssl_version_min_max(struct Curl_easy *data, + struct ssl_peer *peer, struct ssl_primary_config *conn_config, const char **prioritylist, const char *tls13support) @@ -337,6 +349,16 @@ set_ssl_version_min_max(struct Curl_easy *data, long ssl_version = conn_config->version; long ssl_version_max = conn_config->version_max; + if(peer->transport == TRNSPRT_QUIC) { + if((ssl_version != CURL_SSLVERSION_DEFAULT) && + (ssl_version < CURL_SSLVERSION_TLSv1_3)) { + failf(data, "QUIC needs at least TLS version 1.3"); + return CURLE_SSL_CONNECT_ERROR; + } + *prioritylist = QUIC_PRIORITY; + return CURLE_OK; + } + if((ssl_version == CURL_SSLVERSION_DEFAULT) || (ssl_version == CURL_SSLVERSION_TLSv1)) ssl_version = CURL_SSLVERSION_TLSv1_0; @@ -401,12 +423,12 @@ set_ssl_version_min_max(struct Curl_easy *data, return CURLE_SSL_CONNECT_ERROR; } -CURLcode gtls_client_init(struct Curl_easy *data, - struct ssl_primary_config *config, - struct ssl_config_data *ssl_config, - struct ssl_peer *peer, - struct gtls_instance *gtls, - long *pverifyresult) +static CURLcode gtls_client_init(struct Curl_easy *data, + struct ssl_primary_config *config, + struct ssl_config_data *ssl_config, + struct ssl_peer *peer, + struct gtls_ctx *gtls, + long *pverifyresult) { unsigned int init_flags; int rc; @@ -578,7 +600,8 @@ CURLcode gtls_client_init(struct Curl_easy *data, } /* At this point we know we have a supported TLS version, so set it */ - result = set_ssl_version_min_max(data, config, &prioritylist, tls13support); + result = set_ssl_version_min_max(data, peer, + config, &prioritylist, tls13support); if(result) return result; @@ -677,46 +700,81 @@ CURLcode gtls_client_init(struct Curl_easy *data, return CURLE_OK; } -static CURLcode -gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +static int keylog_callback(gnutls_session_t session, const char *label, + const gnutls_datum_t *secret) +{ + gnutls_datum_t crandom; + gnutls_datum_t srandom; + + gnutls_session_get_random(session, &crandom, &srandom); + if(crandom.size != 32) { + return -1; + } + + Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); + return 0; +} + +CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const unsigned char *alpn, size_t alpn_len, + Curl_gtls_ctx_setup_cb *cb_setup, + void *cb_user_data, + void *ssl_user_data) { - struct ssl_connect_data *connssl = cf->ctx; - struct gtls_ssl_backend_data *backend = - (struct gtls_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); long * const pverifyresult = &ssl_config->certverifyresult; CURLcode result; - DEBUGASSERT(backend); - - if(connssl->state == ssl_connection_complete) - /* to make us tolerant against being called more than once for the - same connection */ - return CURLE_OK; + DEBUGASSERT(gctx); - result = gtls_client_init(data, conn_config, ssl_config, - &connssl->peer, - &backend->gtls, pverifyresult); + result = gtls_client_init(data, conn_config, ssl_config, peer, + gctx, pverifyresult); if(result) return result; - if(connssl->alpn) { - struct alpn_proto_buf proto; - gnutls_datum_t alpn[ALPN_ENTRIES_MAX]; - size_t i; + gnutls_session_set_ptr(gctx->session, ssl_user_data); + + if(cb_setup) { + result = cb_setup(cf, data, cb_user_data); + if(result) + return result; + } - for(i = 0; i < connssl->alpn->count; ++i) { - alpn[i].data = (unsigned char *)connssl->alpn->entries[i]; - alpn[i].size = (unsigned)strlen(connssl->alpn->entries[i]); + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + 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) { + 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(gnutls_alpn_set_protocols(backend->gtls.session, alpn, - (unsigned)connssl->alpn->count, 0)) { + 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; } - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } /* This might be a reconnect, so we check for a session ID in the cache @@ -726,16 +784,49 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) size_t ssl_idsize; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, &ssl_idsize)) { + if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, &ssl_idsize)) { /* we got a session id, use it! */ - gnutls_session_set_data(backend->gtls.session, - ssl_sessionid, ssl_idsize); + gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize); /* Informational message */ infof(data, "SSL reusing session ID"); } Curl_ssl_sessionid_unlock(data); } + return CURLE_OK; +} + +static CURLcode +gtls_connect_step1(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; + struct alpn_proto_buf proto; + CURLcode result; + + DEBUGASSERT(backend); + DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); + + if(connssl->state == ssl_connection_complete) + /* to make us tolerant against being called more than once for the + same connection */ + return CURLE_OK; + + memset(&proto, 0, sizeof(proto)); + if(connssl->alpn) { + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result) { + failf(data, "Error determining ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + } + + result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer, + proto.data, proto.len, NULL, NULL, cf); + if(result) + return result; + /* register callback functions and handle to send and receive data. */ gnutls_transport_set_ptr(backend->gtls.session, cf); @@ -1291,7 +1382,8 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, gnutls_session_get_data(session, connect_sessionid, &connect_idsize); Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)); + incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, + &ssl_sessionid, NULL)); if(incache) { /* there was one before in the cache, so instead of risking that the previous one was rejected, we just kill that and store the new */ @@ -1299,8 +1391,9 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, } /* store this session id */ - result = Curl_ssl_addsessionid(cf, data, connect_sessionid, - connect_idsize, &added); + result = Curl_ssl_addsessionid(cf, data, &connssl->peer, + connect_sessionid, connect_idsize, + &added); Curl_ssl_sessionid_unlock(data); if(!added) free(connect_sessionid); diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h index 1a81c01e93..766cd40ac8 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -45,7 +45,7 @@ struct ssl_primary_config; struct ssl_config_data; struct ssl_peer; -struct gtls_instance { +struct gtls_ctx { gnutls_session_t session; gnutls_certificate_credentials_t cred; #ifdef USE_GNUTLS_SRP @@ -53,13 +53,18 @@ struct gtls_instance { #endif }; -CURLcode -gtls_client_init(struct Curl_easy *data, - struct ssl_primary_config *config, - struct ssl_config_data *ssl_config, - struct ssl_peer *peer, - struct gtls_instance *gtls, - long *pverifyresult); +typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data); + +CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const unsigned char *alpn, size_t alpn_len, + Curl_gtls_ctx_setup_cb *cb_setup, + void *cb_user_data, + void *ssl_user_data); CURLcode Curl_gtls_verifyserver(struct Curl_easy *data, diff --git a/lib/vtls/keylog.c b/lib/vtls/keylog.c index fbcb25cfb6..ab7baaaeca 100644 --- a/lib/vtls/keylog.c +++ b/lib/vtls/keylog.c @@ -24,6 +24,7 @@ #include "curl_setup.h" #if defined(USE_OPENSSL) || \ + defined(USE_GNUTLS) || \ defined(USE_WOLFSSL) || \ (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ defined(USE_QUICHE) diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 438cb7c6fa..4967beac36 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -589,7 +589,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #endif - infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->port); + infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->peer.port); mbedtls_ssl_config_init(&backend->config); ret = mbedtls_ssl_config_defaults(&backend->config, @@ -667,7 +667,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) void *old_session = NULL; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &old_session, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &old_session, NULL)) { ret = mbedtls_ssl_set_session(&backend->ssl, old_session); if(ret) { Curl_ssl_sessionid_unlock(data); @@ -955,11 +955,12 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) /* If there's already a matching session in the cache, delete it */ Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)) + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + &old_ssl_sessionid, NULL)) Curl_ssl_delsessionid(data, old_ssl_sessionid); - retcode = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, - 0, &added); + retcode = Curl_ssl_addsessionid(cf, data, &connssl->peer, + our_ssl_sessionid, 0, &added); Curl_ssl_sessionid_unlock(data); if(!added) { mbedtls_ssl_session_free(our_ssl_sessionid); diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 84b95ae8ad..58395cb68e 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -298,20 +298,6 @@ typedef unsigned long sslerr_t; #define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L) #endif /* !LIBRESSL_VERSION_NUMBER */ -struct ossl_ssl_backend_data { - /* these ones requires specific SSL-types */ - SSL_CTX* ctx; - SSL* handle; - X509* server_cert; - BIO_METHOD *bio_method; - CURLcode io_result; /* result of last BIO cfilter operation */ -#ifndef HAVE_KEYLOG_CALLBACK - /* Set to true once a valid keylog entry has been created to avoid dupes. */ - bool keylog_done; -#endif - bool x509_store_setup; /* x509 store has been set up */ -}; - #if defined(HAVE_SSL_X509_STORE_SHARE) struct multi_ssl_backend_data { char *CAfile; /* CAfile path used to generate X509 store */ @@ -726,8 +712,7 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen) { struct Curl_cfilter *cf = BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result = CURLE_SEND_ERROR; @@ -737,7 +722,7 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen) CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, err=%d", blen, (int)nwritten, result); BIO_clear_retry_flags(bio); - backend->io_result = result; + octx->io_result = result; if(nwritten < 0) { if(CURLE_AGAIN == result) BIO_set_retry_write(bio); @@ -749,8 +734,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) { struct Curl_cfilter *cf = BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result = CURLE_RECV_ERROR; @@ -764,7 +748,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, err=%d", blen, (int)nread, result); BIO_clear_retry_flags(bio); - backend->io_result = result; + octx->io_result = result; if(nread < 0) { if(CURLE_AGAIN == result) BIO_set_retry_read(bio); @@ -775,13 +759,13 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) /* Before returning server replies to the SSL instance, we need * to have setup the x509 store or verification will fail. */ - if(!backend->x509_store_setup) { - result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); + if(!octx->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); if(result) { - backend->io_result = result; + octx->io_result = result; return -1; } - backend->x509_store_setup = TRUE; + octx->x509_store_setup = TRUE; } return (int)nread; @@ -1883,13 +1867,12 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data) static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; (void)data; - DEBUGASSERT(backend); + DEBUGASSERT(octx); - if(backend->handle) { + if(octx->ssl) { /* Send the TLS shutdown if we are still connected *and* if * the peer did not already close the connection. */ if(cf->next && cf->next->connected && !connssl->peer_closed) { @@ -1900,8 +1883,8 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) /* Maybe the server has already sent a close notify alert. Read it to avoid an RST on the TCP connection. */ ERR_clear_error(); - nread = SSL_read(backend->handle, buf, (int)sizeof(buf)); - err = SSL_get_error(backend->handle, nread); + nread = SSL_read(octx->ssl, buf, (int)sizeof(buf)); + err = SSL_get_error(octx->ssl, nread); if(!nread && err == SSL_ERROR_ZERO_RETURN) { CURLcode result; ssize_t n; @@ -1924,12 +1907,12 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) CURL_TRC_CF(data, cf, "not from sending TLS shutdown on " "connection closed by peer"); } - else if(SSL_shutdown(backend->handle) == 1) { + else if(SSL_shutdown(octx->ssl) == 1) { CURL_TRC_CF(data, cf, "SSL shutdown finished"); } else { - nread = SSL_read(backend->handle, buf, (int)sizeof(buf)); - err = SSL_get_error(backend->handle, nread); + nread = SSL_read(octx->ssl, buf, (int)sizeof(buf)); + err = SSL_get_error(octx->ssl, nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ case SSL_ERROR_ZERO_RETURN: /* no more data */ @@ -1955,20 +1938,20 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) } ERR_clear_error(); - SSL_set_connect_state(backend->handle); + SSL_set_connect_state(octx->ssl); } - SSL_free(backend->handle); - backend->handle = NULL; + SSL_free(octx->ssl); + octx->ssl = NULL; } - if(backend->ctx) { - SSL_CTX_free(backend->ctx); - backend->ctx = NULL; - backend->x509_store_setup = FALSE; + if(octx->ssl_ctx) { + SSL_CTX_free(octx->ssl_ctx); + octx->ssl_ctx = NULL; + octx->x509_store_setup = FALSE; } - if(backend->bio_method) { - ossl_bio_cf_method_free(backend->bio_method); - backend->bio_method = NULL; + if(octx->bio_method) { + ossl_bio_cf_method_free(octx->bio_method); + octx->bio_method = NULL; } } @@ -1988,11 +1971,10 @@ static int ossl_shutdown(struct Curl_cfilter *cf, int buffsize; int err; bool done = FALSE; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; int loop = 10; - DEBUGASSERT(backend); + DEBUGASSERT(octx); #ifndef CURL_DISABLE_FTP /* This has only been tested on the proftpd server, and the mod_tls code @@ -2001,10 +1983,10 @@ static int ossl_shutdown(struct Curl_cfilter *cf, we do not send one. Let's hope other servers do the same... */ if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - (void)SSL_shutdown(backend->handle); + (void)SSL_shutdown(octx->ssl); #endif - if(backend->handle) { + if(octx->ssl) { buffsize = (int)sizeof(buf); while(!done && loop--) { int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), @@ -2014,8 +1996,8 @@ static int ossl_shutdown(struct Curl_cfilter *cf, /* Something to read, let's do it and hope that it is the close notify alert from the server */ - nread = SSL_read(backend->handle, buf, buffsize); - err = SSL_get_error(backend->handle, nread); + nread = SSL_read(octx->ssl, buf, buffsize); + err = SSL_get_error(octx->ssl, nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -2060,7 +2042,7 @@ static int ossl_shutdown(struct Curl_cfilter *cf, if(data->set.verbose) { #ifdef HAVE_SSL_GET_SHUTDOWN - switch(SSL_get_shutdown(backend->handle)) { + switch(SSL_get_shutdown(octx->ssl)) { case SSL_SENT_SHUTDOWN: infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN"); break; @@ -2075,8 +2057,8 @@ static int ossl_shutdown(struct Curl_cfilter *cf, #endif } - SSL_free(backend->handle); - backend->handle = NULL; + SSL_free(octx->ssl); + octx->ssl = NULL; } return retval; } @@ -2381,8 +2363,7 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, OCSP_BASICRESP *br = NULL; X509_STORE *st = NULL; STACK_OF(X509) *ch = NULL; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; X509 *cert; OCSP_CERTID *id = NULL; int cert_status, crl_reason; @@ -2390,9 +2371,9 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, int ret; long len; - DEBUGASSERT(backend); + DEBUGASSERT(octx); - len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status); + len = SSL_get_tlsext_status_ocsp_resp(octx->ssl, &status); if(!status) { failf(data, "No OCSP response received"); @@ -2422,13 +2403,13 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, goto end; } - ch = SSL_get_peer_cert_chain(backend->handle); + ch = SSL_get_peer_cert_chain(octx->ssl); if(!ch) { failf(data, "Could not get peer certificate chain"); result = CURLE_SSL_INVALIDCERTSTATUS; goto end; } - st = SSL_CTX_get_cert_store(backend->ctx); + st = SSL_CTX_get_cert_store(octx->ssl_ctx); #if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \ (defined(LIBRESSL_VERSION_NUMBER) && \ @@ -2465,7 +2446,7 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, } /* Compute the certificate's ID */ - cert = SSL_get1_peer_certificate(backend->handle); + cert = SSL_get1_peer_certificate(octx->ssl); if(!cert) { failf(data, "Error getting peer certificate"); result = CURLE_SSL_INVALIDCERTSTATUS; @@ -2880,10 +2861,9 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, #ifdef TLS1_3_VERSION { struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - DEBUGASSERT(backend); - SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + DEBUGASSERT(octx); + SSL_CTX_set_max_proto_version(octx->ssl_ctx, TLS1_3_VERSION); *ctx_options |= SSL_OP_NO_TLSv1_2; } #else @@ -2941,24 +2921,17 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, } #endif -/* The "new session" callback must return zero if the session can be removed - * or non-zero if the session has been put into the session cache. - */ -static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) +CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + SSL_SESSION *ssl_sessionid) { - int res = 0; - struct Curl_easy *data; - struct Curl_cfilter *cf; const struct ssl_config_data *config; - struct ssl_connect_data *connssl; bool isproxy; + CURLcode result = CURLE_WRITE_ERROR; - cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); - connssl = cf? cf->ctx : NULL; - data = connssl? CF_DATA_CURRENT(cf) : NULL; - /* The sockindex has been stored as a pointer to an array element */ if(!cf || !data) - return 0; + return result; isproxy = Curl_ssl_cf_is_proxy(cf); @@ -2972,7 +2945,8 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) if(isproxy) incache = FALSE; else - incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)); + incache = !(Curl_ssl_getsessionid(cf, data, peer, + &old_ssl_sessionid, NULL)); if(incache) { if(old_ssl_sessionid != ssl_sessionid) { infof(data, "old SSL session ID is stale, removing"); @@ -2982,11 +2956,11 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) } if(!incache) { - if(!Curl_ssl_addsessionid(cf, data, ssl_sessionid, + if(!Curl_ssl_addsessionid(cf, data, peer, ssl_sessionid, 0 /* unknown size */, &added)) { if(added) { /* the session has been put into the session cache */ - res = 1; + result = CURLE_OK; } } else @@ -2995,7 +2969,24 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) Curl_ssl_sessionid_unlock(data); } - return res; + return result; +} + +/* The "new session" callback must return zero if the session can be removed + * or non-zero if the session has been put into the session cache. + */ +static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) +{ + struct Curl_cfilter *cf; + struct Curl_easy *data; + struct ssl_connect_data *connssl; + CURLcode result; + + cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); + connssl = cf? cf->ctx : NULL; + data = connssl? CF_DATA_CURRENT(cf) : NULL; + result = Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid); + return result? 0 : 1; } static CURLcode load_cacert_from_memory(X509_STORE *store, @@ -3493,29 +3484,30 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, } #endif /* HAVE_SSL_X509_STORE_SHARE */ -static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, - struct Curl_easy *data) +CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + int transport, /* TCP or QUIC */ + const unsigned char *alpn, size_t alpn_len, + Curl_ossl_ctx_setup_cb *cb_setup, + void *cb_user_data, + Curl_ossl_new_session_cb *cb_new_session, + void *ssl_user_data) { CURLcode result = CURLE_OK; - char *ciphers; + const char *ciphers; SSL_METHOD_QUAL SSL_METHOD *req_method = NULL; - struct ssl_connect_data *connssl = cf->ctx; ctx_option_t ctx_options = 0; void *ssl_sessionid = NULL; 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); - BIO *bio; const long int ssl_version = conn_config->version; char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; const char * const ssl_cert_type = ssl_config->cert_type; const bool verifypeer = conn_config->verifypeer; char error_buffer[256]; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - - DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); - DEBUGASSERT(backend); /* Make funny stuff to get random input */ result = ossl_seed(data); @@ -3524,52 +3516,72 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, ssl_config->certverifyresult = !X509_V_OK; - /* check to see if we've been told to use an explicit SSL/TLS version */ - - switch(ssl_version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - /* it will be handled later with the context options */ -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) - req_method = TLS_client_method(); + switch(transport) { + case TRNSPRT_TCP: + /* check to see if we've been told to use an explicit SSL/TLS version */ + switch(ssl_version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + case CURL_SSLVERSION_TLSv1_3: + /* it will be handled later with the context options */ + #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + req_method = TLS_client_method(); + #else + req_method = SSLv23_client_method(); + #endif + break; + case CURL_SSLVERSION_SSLv2: + failf(data, "No SSLv2 support"); + return CURLE_NOT_BUILT_IN; + case CURL_SSLVERSION_SSLv3: + failf(data, "No SSLv3 support"); + return CURLE_NOT_BUILT_IN; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + break; + case TRNSPRT_QUIC: + if((ssl_version != CURL_SSLVERSION_DEFAULT) && + (ssl_version < CURL_SSLVERSION_TLSv1_3)) { + failf(data, "QUIC needs at least TLS version 1.3"); + return CURLE_SSL_CONNECT_ERROR; + } +#ifdef USE_OPENSSL_QUIC + req_method = OSSL_QUIC_client_method(); #else - req_method = SSLv23_client_method(); + req_method = TLS_method(); #endif break; - case CURL_SSLVERSION_SSLv2: - failf(data, "No SSLv2 support"); - return CURLE_NOT_BUILT_IN; - case CURL_SSLVERSION_SSLv3: - failf(data, "No SSLv3 support"); - return CURLE_NOT_BUILT_IN; default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + failf(data, "unsupported transport %d in SSL init", transport); return CURLE_SSL_CONNECT_ERROR; } - if(backend->ctx) { - /* This happens when an error was encountered before in this - * step and we are called to do it again. Get rid of any leftover - * from the previous call. */ - ossl_close(cf, data); - } - backend->ctx = SSL_CTX_new(req_method); - if(!backend->ctx) { + DEBUGASSERT(!octx->ssl_ctx); + octx->ssl_ctx = SSL_CTX_new(req_method); + + if(!octx->ssl_ctx) { failf(data, "SSL: couldn't create a context: %s", ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer))); return CURLE_OUT_OF_MEMORY; } + if(cb_setup) { + result = cb_setup(cf, data, cb_user_data); + if(result) + return result; + } + #ifdef SSL_CTRL_SET_MSG_CALLBACK if(data->set.fdebug && data->set.verbose) { /* the SSL trace callback is only used for verbose logging */ - SSL_CTX_set_msg_callback(backend->ctx, ossl_trace); - SSL_CTX_set_msg_callback_arg(backend->ctx, cf); + SSL_CTX_set_msg_callback(octx->ssl_ctx, ossl_trace); + SSL_CTX_set_msg_callback_arg(octx->ssl_ctx, cf); } #endif @@ -3649,7 +3661,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, ctx_options |= SSL_OP_NO_SSLv3; #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ - result = ossl_set_ssl_version_min_max(cf, backend->ctx); + result = ossl_set_ssl_version_min_max(cf, octx->ssl_ctx); #else result = ossl_set_ssl_version_min_max_legacy(&ctx_options, cf, data); #endif @@ -3662,26 +3674,20 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - SSL_CTX_set_options(backend->ctx, ctx_options); + SSL_CTX_set_options(octx->ssl_ctx, ctx_options); #ifdef HAS_ALPN - if(connssl->alpn) { - struct alpn_proto_buf proto; - - result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); - if(result || - SSL_CTX_set_alpn_protos(backend->ctx, proto.data, proto.len)) { + if(alpn) { + if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) { failf(data, "Error setting ALPN"); return CURLE_SSL_CONNECT_ERROR; } - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } #endif if(ssl_cert || ssl_cert_blob || ssl_cert_type) { if(!result && - !cert_stuff(data, backend->ctx, + !cert_stuff(data, octx->ssl_ctx, ssl_cert, ssl_cert_blob, ssl_cert_type, ssl_config->key, ssl_config->key_blob, ssl_config->key_type, ssl_config->key_passwd)) @@ -3692,10 +3698,10 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } ciphers = conn_config->cipher_list; - if(!ciphers) - ciphers = (char *)DEFAULT_CIPHER_SELECTION; + if(!ciphers && (peer->transport != TRNSPRT_QUIC)) + ciphers = DEFAULT_CIPHER_SELECTION; if(ciphers) { - if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { + if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); return CURLE_SSL_CIPHER; } @@ -3704,9 +3710,9 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES { - char *ciphers13 = conn_config->cipher_list13; + const char *ciphers13 = conn_config->cipher_list13; if(ciphers13) { - if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) { + if(!SSL_CTX_set_ciphersuites(octx->ssl_ctx, ciphers13)) { failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13); return CURLE_SSL_CIPHER; } @@ -3717,14 +3723,14 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH /* OpenSSL 1.1.1 requires clients to opt-in for PHA */ - SSL_CTX_set_post_handshake_auth(backend->ctx, 1); + SSL_CTX_set_post_handshake_auth(octx->ssl_ctx, 1); #endif #ifdef HAVE_SSL_CTX_SET_EC_CURVES { - char *curves = conn_config->curves; + const char *curves = conn_config->curves; if(curves) { - if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { + if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, curves)) { failf(data, "failed setting curves list: '%s'", curves); return CURLE_SSL_CIPHER; } @@ -3738,18 +3744,18 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, char * const ssl_password = ssl_config->primary.password; infof(data, "Using TLS-SRP username: %s", ssl_username); - if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) { + if(!SSL_CTX_set_srp_username(octx->ssl_ctx, ssl_username)) { failf(data, "Unable to set SRP user name"); return CURLE_BAD_FUNCTION_ARGUMENT; } - if(!SSL_CTX_set_srp_password(backend->ctx, ssl_password)) { + if(!SSL_CTX_set_srp_password(octx->ssl_ctx, ssl_password)) { failf(data, "failed setting SRP password"); return CURLE_BAD_FUNCTION_ARGUMENT; } if(!conn_config->cipher_list) { infof(data, "Setting cipher list SRP"); - if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) { + if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, "SRP")) { failf(data, "failed setting SRP cipher list"); return CURLE_SSL_CIPHER; } @@ -3761,38 +3767,40 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(backend->ctx, + SSL_CTX_set_verify(octx->ssl_ctx, verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */ #ifdef HAVE_KEYLOG_CALLBACK if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(backend->ctx, ossl_keylog_callback); + SSL_CTX_set_keylog_callback(octx->ssl_ctx, ossl_keylog_callback); } #endif - /* Enable the session cache because it's a prerequisite for the "new session" - * callback. Use the "external storage" mode to prevent OpenSSL from creating - * an internal session cache. - */ - SSL_CTX_set_session_cache_mode(backend->ctx, - SSL_SESS_CACHE_CLIENT | - SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_new_cb(backend->ctx, ossl_new_session_cb); + if(cb_new_session) { + /* Enable the session cache because it's a prerequisite for the + * "new session" callback. Use the "external storage" mode to prevent + * OpenSSL from creating an internal session cache. + */ + SSL_CTX_set_session_cache_mode(octx->ssl_ctx, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(octx->ssl_ctx, cb_new_session); + } /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { /* When a user callback is installed to modify the SSL_CTX, * we need to do the full initialization before calling it. * See: #11800 */ - if(!backend->x509_store_setup) { - result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); + if(!octx->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); if(result) return result; - backend->x509_store_setup = TRUE; + octx->x509_store_setup = TRUE; } Curl_set_in_callback(data, true); - result = (*data->set.ssl.fsslctx)(data, backend->ctx, + result = (*data->set.ssl.fsslctx)(data, octx->ssl_ctx, data->set.ssl.fsslctxp); Curl_set_in_callback(data, false); if(result) { @@ -3802,47 +3810,45 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } /* Let's make an SSL structure */ - if(backend->handle) - SSL_free(backend->handle); - backend->handle = SSL_new(backend->ctx); - if(!backend->handle) { + if(octx->ssl) + SSL_free(octx->ssl); + octx->ssl = SSL_new(octx->ssl_ctx); + if(!octx->ssl) { failf(data, "SSL: couldn't create a context (handle)"); return CURLE_OUT_OF_MEMORY; } - SSL_set_app_data(backend->handle, cf); + SSL_set_app_data(octx->ssl, ssl_user_data); #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) if(conn_config->verifystatus) - SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp); + SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp); #endif #if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \ defined(ALLOW_RENEG) - SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely); + SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely); #endif - SSL_set_connect_state(backend->handle); + SSL_set_connect_state(octx->ssl); - backend->server_cert = 0x0; + octx->server_cert = 0x0; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if(connssl->peer.sni) { - if(!SSL_set_tlsext_host_name(backend->handle, connssl->peer.sni)) { + if(peer->sni) { + if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) { failf(data, "Failed set SNI"); return CURLE_SSL_CONNECT_ERROR; } } #endif - SSL_set_app_data(backend->handle, cf); - - connssl->reused_session = FALSE; - if(ssl_config->primary.sessionid) { + octx->reused_session = FALSE; + if(ssl_config->primary.sessionid && transport == TRNSPRT_TCP) { Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, NULL)) { /* we got a session id, use it! */ - if(!SSL_set_session(backend->handle, ssl_sessionid)) { + if(!SSL_set_session(octx->ssl, ssl_sessionid)) { Curl_ssl_sessionid_unlock(data); failf(data, "SSL: SSL_set_session failed: %s", ossl_strerror(ERR_get_error(), error_buffer, @@ -3851,15 +3857,46 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } /* Informational message */ infof(data, "SSL reusing session ID"); - connssl->reused_session = TRUE; + octx->reused_session = TRUE; } Curl_ssl_sessionid_unlock(data); } - backend->bio_method = ossl_bio_cf_method_create(); - if(!backend->bio_method) + return CURLE_OK; +} + +static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + struct alpn_proto_buf proto; + BIO *bio; + CURLcode result; + + DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); + DEBUGASSERT(octx); + memset(&proto, 0, sizeof(proto)); +#ifdef HAS_ALPN + if(connssl->alpn) { + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result) { + failf(data, "Error determining ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + } +#endif + + result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP, + proto.data, proto.len, NULL, NULL, + ossl_new_session_cb, cf); + if(result) + return result; + + octx->bio_method = ossl_bio_cf_method_create(); + if(!octx->bio_method) return CURLE_OUT_OF_MEMORY; - bio = BIO_new(backend->bio_method); + bio = BIO_new(octx->bio_method); if(!bio) return CURLE_OUT_OF_MEMORY; @@ -3871,13 +3908,17 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, * We check on the function in configure, since libressl and friends * each have their own versions to add support for this. */ BIO_up_ref(bio); - SSL_set0_rbio(backend->handle, bio); - SSL_set0_wbio(backend->handle, bio); + SSL_set0_rbio(octx->ssl, bio); + SSL_set0_wbio(octx->ssl, bio); #else - SSL_set_bio(backend->handle, bio, bio); + SSL_set_bio(octx->ssl, bio, bio); #endif - connssl->connecting_state = ssl_connect_2; + if(connssl->alpn) { + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } + connssl->connecting_state = ssl_connect_2; return CURLE_OK; } @@ -3886,25 +3927,24 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, { int err; struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); - DEBUGASSERT(backend); + DEBUGASSERT(octx); ERR_clear_error(); - err = SSL_connect(backend->handle); + err = SSL_connect(octx->ssl); - if(!backend->x509_store_setup) { + if(!octx->x509_store_setup) { /* After having send off the ClientHello, we prepare the x509 * store to verify the coming certificate from the server */ - CURLcode result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); + CURLcode result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); if(result) return result; - backend->x509_store_setup = TRUE; + octx->x509_store_setup = TRUE; } #ifndef HAVE_KEYLOG_CALLBACK @@ -3912,7 +3952,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, /* If key logging is enabled, wait for the handshake to complete and then * proceed with logging secrets (for TLS 1.2 or older). */ - ossl_log_tls12_secret(backend->handle, &backend->keylog_done); + ossl_log_tls12_secret(octx->ssl, &octx->keylog_done); } #endif @@ -3920,7 +3960,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, 0 is "not successful but was shut down controlled" <0 is "handshake was not successful, because a fatal error occurred" */ if(1 != err) { - int detail = SSL_get_error(backend->handle, err); + int detail = SSL_get_error(octx->ssl, err); if(SSL_ERROR_WANT_READ == detail) { connssl->connecting_state = ssl_connect_2_reading; @@ -3942,7 +3982,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, return CURLE_OK; } #endif - if(backend->io_result == CURLE_AGAIN) { + if(octx->io_result == CURLE_AGAIN) { return CURLE_OK; } else { @@ -3970,7 +4010,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { result = CURLE_PEER_FAILED_VERIFICATION; - lerr = SSL_get_verify_result(backend->handle); + lerr = SSL_get_verify_result(octx->ssl); if(lerr != X509_V_OK) { ssl_config->certverifyresult = lerr; msnprintf(error_buffer, sizeof(error_buffer), @@ -4012,7 +4052,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, Curl_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - connssl->peer.hostname, connssl->port); + connssl->peer.hostname, connssl->peer.port); return result; } @@ -4030,19 +4070,19 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, connssl->connecting_state = ssl_connect_3; #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) - SSL_get_peer_signature_type_nid(backend->handle, &psigtype_nid); + SSL_get_peer_signature_type_nid(octx->ssl, &psigtype_nid); #if (OPENSSL_VERSION_NUMBER >= 0x30200000L) - negotiated_group_name = SSL_get0_group_name(backend->handle); + negotiated_group_name = SSL_get0_group_name(octx->ssl); #else negotiated_group_name = - OBJ_nid2sn(SSL_get_negotiated_group(backend->handle) & 0x0000FFFF); + OBJ_nid2sn(SSL_get_negotiated_group(octx->ssl) & 0x0000FFFF); #endif #endif /* Informational message */ infof(data, "SSL connection using %s / %s / %s / %s", - SSL_get_version(backend->handle), - SSL_get_cipher(backend->handle), + SSL_get_version(octx->ssl), + SSL_get_cipher(octx->ssl), negotiated_group_name? negotiated_group_name : "[blank]", OBJ_nid2sn(psigtype_nid)); @@ -4053,7 +4093,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, if(connssl->alpn) { const unsigned char *neg_protocol; unsigned int len; - SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len); + SSL_get0_alpn_selected(octx->ssl, &neg_protocol, &len); return Curl_alpn_set_negotiated(cf, data, neg_protocol, len); } @@ -4190,20 +4230,12 @@ static void infof_certstack(struct Curl_easy *data, const SSL *ssl) #define infof_certstack(data, ssl) #endif -/* - * Get the server cert, verify it and show it, etc., only call failf() if the - * 'strict' argument is TRUE as otherwise all this is for informational - * purposes only! - * - * We check certificates to authenticate the server; otherwise we risk - * man-in-the-middle attack. - */ -static CURLcode servercert(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool strict) +CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ossl_ctx *octx, + struct ssl_peer *peer) { struct connectdata *conn = cf->conn; - struct ssl_connect_data *connssl = cf->ctx; 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; @@ -4215,10 +4247,9 @@ static CURLcode servercert(struct Curl_cfilter *cf, char buffer[2048]; const char *ptr; BIO *mem = BIO_new(BIO_s_mem()); - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + bool strict = (conn_config->verifypeer || conn_config->verifyhost); - DEBUGASSERT(backend); + DEBUGASSERT(octx); if(!mem) { failf(data, @@ -4231,10 +4262,10 @@ static CURLcode servercert(struct Curl_cfilter *cf, if(data->set.ssl.certinfo) /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, backend->handle); + (void)Curl_ossl_certchain(data, octx->ssl); - backend->server_cert = SSL_get1_peer_certificate(backend->handle); - if(!backend->server_cert) { + octx->server_cert = SSL_get1_peer_certificate(octx->ssl); + if(!octx->server_cert) { BIO_free(mem); if(!strict) return CURLE_OK; @@ -4246,19 +4277,19 @@ static CURLcode servercert(struct Curl_cfilter *cf, infof(data, "%s certificate:", Curl_ssl_cf_is_proxy(cf)? "Proxy" : "Server"); - rc = x509_name_oneline(X509_get_subject_name(backend->server_cert), + rc = x509_name_oneline(X509_get_subject_name(octx->server_cert), buffer, sizeof(buffer)); infof(data, " subject: %s", rc?"[NONE]":buffer); #ifndef CURL_DISABLE_VERBOSE_STRINGS { long len; - ASN1_TIME_print(mem, X509_get0_notBefore(backend->server_cert)); + ASN1_TIME_print(mem, X509_get0_notBefore(octx->server_cert)); len = BIO_get_mem_data(mem, (char **) &ptr); infof(data, " start date: %.*s", (int)len, ptr); (void)BIO_reset(mem); - ASN1_TIME_print(mem, X509_get0_notAfter(backend->server_cert)); + ASN1_TIME_print(mem, X509_get0_notAfter(octx->server_cert)); len = BIO_get_mem_data(mem, (char **) &ptr); infof(data, " expire date: %.*s", (int)len, ptr); (void)BIO_reset(mem); @@ -4268,16 +4299,15 @@ static CURLcode servercert(struct Curl_cfilter *cf, BIO_free(mem); if(conn_config->verifyhost) { - result = Curl_ossl_verifyhost(data, conn, &connssl->peer, - backend->server_cert); + result = Curl_ossl_verifyhost(data, conn, peer, octx->server_cert); if(result) { - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return result; } } - rc = x509_name_oneline(X509_get_issuer_name(backend->server_cert), + rc = x509_name_oneline(X509_get_issuer_name(octx->server_cert), buffer, sizeof(buffer)); if(rc) { if(strict) @@ -4301,8 +4331,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, " error %s", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer)) ); - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return CURLE_OUT_OF_MEMORY; } } @@ -4314,8 +4344,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, " error %s", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer)) ); - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return CURLE_OUT_OF_MEMORY; } @@ -4324,8 +4354,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, failf(data, "SSL: Unable to open issuer cert (%s)", conn_config->issuercert); BIO_free(fp); - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } } @@ -4337,19 +4367,19 @@ static CURLcode servercert(struct Curl_cfilter *cf, conn_config->issuercert); BIO_free(fp); X509_free(issuer); - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } - if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) { + if(X509_check_issued(issuer, octx->server_cert) != X509_V_OK) { if(strict) failf(data, "SSL: Certificate issuer check failed (%s)", conn_config->issuercert); BIO_free(fp); X509_free(issuer); - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } @@ -4359,7 +4389,7 @@ static CURLcode servercert(struct Curl_cfilter *cf, X509_free(issuer); } - lerr = SSL_get_verify_result(backend->handle); + lerr = SSL_get_verify_result(octx->ssl); ssl_config->certverifyresult = lerr; if(lerr != X509_V_OK) { if(conn_config->verifypeer) { @@ -4379,11 +4409,11 @@ static CURLcode servercert(struct Curl_cfilter *cf, infof(data, " SSL certificate verify ok."); } - infof_certstack(data, backend->handle); + infof_certstack(data, octx->ssl); #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) - if(conn_config->verifystatus && !connssl->reused_session) { + if(conn_config->verifystatus && !octx->reused_session) { /* don't do this after Session ID reuse */ result = verifystatus(cf, data); if(result) { @@ -4393,7 +4423,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, void *old_ssl_sessionid = NULL; bool incache; Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)); + incache = !(Curl_ssl_getsessionid(cf, data, peer, + &old_ssl_sessionid, NULL)); if(incache) { infof(data, "Remove session ID again from cache"); Curl_ssl_delsessionid(data, old_ssl_sessionid); @@ -4401,8 +4432,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, Curl_ssl_sessionid_unlock(data); } - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return result; } } @@ -4420,14 +4451,13 @@ static CURLcode servercert(struct Curl_cfilter *cf, ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; #endif if(!result && ptr) { - result = ossl_pkp_pin_peer_pubkey(data, backend->server_cert, ptr); + result = ossl_pkp_pin_peer_pubkey(data, octx->server_cert, ptr); if(result) failf(data, "SSL: public key does not match pinned public key"); } - X509_free(backend->server_cert); - backend->server_cert = NULL; - connssl->connecting_state = ssl_connect_done; + X509_free(octx->server_cert); + octx->server_cert = NULL; return result; } @@ -4437,7 +4467,7 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf, { CURLcode result = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -4448,9 +4478,7 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf, * operations. */ - result = servercert(cf, data, conn_config->verifypeer || - conn_config->verifyhost); - + result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer); if(!result) connssl->connecting_state = ssl_connect_done; @@ -4590,12 +4618,11 @@ static bool ossl_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; (void)data; - DEBUGASSERT(connssl && backend); - if(backend->handle && SSL_pending(backend->handle)) + DEBUGASSERT(connssl && octx); + if(octx->ssl && SSL_pending(octx->ssl)) return TRUE; return FALSE; } @@ -4614,19 +4641,18 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, int memlen; int rc; struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; (void)data; - DEBUGASSERT(backend); + DEBUGASSERT(octx); ERR_clear_error(); memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - rc = SSL_write(backend->handle, mem, memlen); + rc = SSL_write(octx->ssl, mem, memlen); if(rc <= 0) { - err = SSL_get_error(backend->handle, rc); + err = SSL_get_error(octx->ssl, rc); switch(err) { case SSL_ERROR_WANT_READ: @@ -4641,7 +4667,7 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, { int sockerr = SOCKERRNO; - if(backend->io_result == CURLE_AGAIN) { + if(octx->io_result == CURLE_AGAIN) { *curlcode = CURLE_AGAIN; rc = -1; goto out; @@ -4698,20 +4724,19 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, int buffsize; struct connectdata *conn = cf->conn; struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; (void)data; - DEBUGASSERT(backend); + DEBUGASSERT(octx); ERR_clear_error(); buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); + nread = (ssize_t)SSL_read(octx->ssl, buf, buffsize); if(nread <= 0) { /* failed SSL_read */ - int err = SSL_get_error(backend->handle, (int)nread); + int err = SSL_get_error(octx->ssl, (int)nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -4733,7 +4758,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return value/errno" */ /* https://www.openssl.org/docs/crypto/ERR_get_error.html */ - if(backend->io_result == CURLE_AGAIN) { + if(octx->io_result == CURLE_AGAIN) { *curlcode = CURLE_AGAIN; nread = -1; goto out; @@ -4924,11 +4949,10 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl, CURLINFO info) { /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */ - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - DEBUGASSERT(backend); + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + DEBUGASSERT(octx); return info == CURLINFO_TLS_SESSION ? - (void *)backend->ctx : (void *)backend->handle; + (void *)octx->ssl_ctx : (void *)octx->ssl; } static void ossl_free_multi_ssl_backend_data( @@ -4958,7 +4982,7 @@ const struct Curl_ssl Curl_ssl_openssl = { #endif SSLSUPP_HTTPS_PROXY, - sizeof(struct ossl_ssl_backend_data), + sizeof(struct ossl_ctx), ossl_init, /* init */ ossl_cleanup, /* cleanup */ diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index e802363a4a..47bb282a9b 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -36,6 +36,39 @@ #include "urldata.h" +/* Struct to hold a Curl OpenSSL instance */ +struct ossl_ctx { + /* these ones requires specific SSL-types */ + SSL_CTX* ssl_ctx; + SSL* ssl; + X509* server_cert; + BIO_METHOD *bio_method; + CURLcode io_result; /* result of last BIO cfilter operation */ +#ifndef HAVE_KEYLOG_CALLBACK + /* Set to true once a valid keylog entry has been created to avoid dupes. */ + BIT(keylog_done); +#endif + BIT(x509_store_setup); /* x509 store has been set up */ + BIT(reused_session); /* session-ID was reused for this */ +}; + +typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data); + +typedef int Curl_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid); + +CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + int transport, /* TCP or QUIC */ + const unsigned char *alpn, size_t alpn_len, + Curl_ossl_ctx_setup_cb *cb_setup, + void *cb_user_data, + Curl_ossl_new_session_cb *cb_new_session, + void *ssl_user_data); + #if (OPENSSL_VERSION_NUMBER < 0x30000000L) #define SSL_get1_peer_certificate SSL_get_peer_certificate #endif @@ -66,5 +99,23 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf, struct Curl_easy *data, SSL_CTX *ssl_ctx); +/* + * Add a new session to the cache. + */ +CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + SSL_SESSION *ssl_sessionid); + +/* + * Get the server cert, verify it and show it, etc., only call failf() if + * ssl config verifypeer or -host is set. Otherwise all this is for + * informational purposes only! + */ +CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ossl_ctx *octx, + struct ssl_peer *peer); + #endif /* USE_OPENSSL */ #endif /* HEADER_CURL_SSLUSE_H */ diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 76b855d928..cec7fa9710 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -1071,7 +1071,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", - connssl->peer.hostname, connssl->port)); + connssl->peer.hostname, connssl->peer.port)); if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, VERSION_LESS_THAN_EQUAL)) { @@ -1129,7 +1129,8 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* check for an existing reusable credential handle */ if(ssl_config->primary.sessionid) { Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + (void **)&old_cred, NULL)) { backend->cred = old_cred; DEBUGF(infof(data, "schannel: reusing existing credential handle")); @@ -1335,7 +1336,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", - connssl->peer.hostname, connssl->port)); + connssl->peer.hostname, connssl->peer.port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1693,7 +1694,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", - connssl->peer.hostname, connssl->port)); + connssl->peer.hostname, connssl->peer.port)); if(!backend->cred) return CURLE_SSL_CONNECT_ERROR; @@ -1755,7 +1756,8 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_schannel_cred *old_cred = NULL; Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL)); + incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, + (void **)&old_cred, NULL)); if(incache) { if(old_cred != backend->cred) { DEBUGF(infof(data, @@ -1766,7 +1768,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) } } if(!incache) { - result = Curl_ssl_addsessionid(cf, data, backend->cred, + result = Curl_ssl_addsessionid(cf, data, &connssl->peer, backend->cred, sizeof(struct Curl_schannel_cred), &added); if(result) { @@ -2493,7 +2495,7 @@ static int schannel_shutdown(struct Curl_cfilter *cf, if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", - connssl->peer.hostname, connssl->port); + connssl->peer.hostname, connssl->peer.port); } if(backend->cred && backend->ctxt) { diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c index 67f534c675..b7fc7c0222 100644 --- a/lib/vtls/sectransp.c +++ b/lib/vtls/sectransp.c @@ -2047,8 +2047,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, size_t ssl_sessionid_len; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, (void **)&ssl_sessionid, - &ssl_sessionid_len)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + (void **)&ssl_sessionid, &ssl_sessionid_len)) { /* we got a session id, use it! */ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); Curl_ssl_sessionid_unlock(data); @@ -2067,7 +2067,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, aprintf("%s:%d:%d:%s:%d", ssl_cafile ? ssl_cafile : "(blob memory)", verifypeer, conn_config->verifyhost, connssl->peer.hostname, - connssl->port); + connssl->peer.port); ssl_sessionid_len = strlen(ssl_sessionid); err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); @@ -2077,7 +2077,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - result = Curl_ssl_addsessionid(cf, data, ssl_sessionid, + result = Curl_ssl_addsessionid(cf, data, &connssl->peer, ssl_sessionid, ssl_sessionid_len, NULL); Curl_ssl_sessionid_unlock(data); if(result) { diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index d13a3cb1b7..28d1595390 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -534,10 +534,10 @@ void Curl_ssl_sessionid_unlock(struct Curl_easy *data) */ 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 */ { - struct ssl_connect_data *connssl = cf->ctx; 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); struct Curl_ssl_session *check; @@ -567,14 +567,15 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, if(!check->sessionid) /* not session ID means blank entry */ continue; - if(strcasecompare(connssl->peer.hostname, check->name) && + if(strcasecompare(peer->hostname, check->name) && ((!cf->conn->bits.conn_to_host && !check->conn_to_host) || (cf->conn->bits.conn_to_host && check->conn_to_host && strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) && ((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) || (cf->conn->bits.conn_to_port && check->conn_to_port != -1 && cf->conn->conn_to_port == check->conn_to_port)) && - (connssl->port == check->remote_port) && + (peer->port == check->remote_port) && + (peer->transport == check->transport) && strcasecompare(cf->conn->handler->scheme, check->scheme) && match_ssl_primary_config(data, conn_config, &check->ssl_config)) { /* yes, we have a session ID! */ @@ -591,8 +592,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d", no_match? "Didn't find": "Found", Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host", - cf->conn->handler->scheme, connssl->peer.hostname, - connssl->port)); + cf->conn->handler->scheme, peer->hostname, peer->port)); return no_match; } @@ -642,11 +642,11 @@ void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid) */ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, + const struct ssl_peer *peer, void *ssl_sessionid, size_t idsize, bool *added) { - struct ssl_connect_data *connssl = cf->ctx; 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); size_t i; @@ -668,7 +668,7 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, (void)ssl_config; DEBUGASSERT(ssl_config->primary.sessionid); - clone_host = strdup(connssl->peer.hostname); + clone_host = strdup(peer->hostname); if(!clone_host) return CURLE_OUT_OF_MEMORY; /* bail out */ @@ -723,8 +723,9 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, store->conn_to_host = clone_conn_to_host; /* clone connect to host name */ store->conn_to_port = conn_to_port; /* connect to port number */ /* port number */ - store->remote_port = connssl->port; + store->remote_port = peer->port; store->scheme = cf->conn->handler->scheme; + store->transport = peer->transport; if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) { Curl_free_primary_ssl_config(&store->ssl_config); @@ -1549,9 +1550,9 @@ static ssl_peer_type get_peer_type(const char *hostname) return CURL_SSL_PEER_DNS; } -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf) +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, + int transport) { - struct ssl_connect_data *connssl = cf->ctx; const char *ehostname, *edispname; int eport; @@ -1614,7 +1615,8 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf) } } - connssl->port = eport; + peer->port = eport; + peer->transport = transport; return CURLE_OK; } @@ -1667,7 +1669,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, goto out; *done = FALSE; - result = Curl_ssl_peer_init(&connssl->peer, cf); + result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP); if(result) goto out; @@ -1857,7 +1859,7 @@ struct Curl_cftype Curl_cft_ssl = { struct Curl_cftype Curl_cft_ssl_proxy = { "SSL-PROXY", - CF_TYPE_SSL, + CF_TYPE_SSL|CF_TYPE_PROXY, CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, @@ -2033,12 +2035,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf) { -#ifndef CURL_DISABLE_PROXY - return (cf->cft == &Curl_cft_ssl_proxy); -#else - (void)cf; - return FALSE; -#endif + return (cf->cft->flags & CF_TYPE_SSL) && (cf->cft->flags & CF_TYPE_PROXY); } struct ssl_config_data * diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 744bbf8fd9..cd7eeeca17 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -107,7 +107,8 @@ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); /** * Init SSL peer information for filter. Can be called repeatedly. */ -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf); +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, + struct Curl_cfilter *cf, int transport); /** * Free all allocated data and reset peer information. */ diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index 0361fa95a8..8aca1d3331 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -73,9 +73,7 @@ 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 */ - int port; /* remote port at origin */ BIT(use_alpn); /* if ALPN shall be used in handshake */ - BIT(reused_session); /* session-ID was reused for this */ BIT(peer_closed); /* peer has closed connection */ }; @@ -181,6 +179,7 @@ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf); */ 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 */ /* add a new session ID @@ -190,6 +189,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, */ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, + const struct ssl_peer *peer, void *ssl_sessionid, size_t idsize, bool *added); diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 31ef8c75b6..48cf132a37 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -409,11 +409,11 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #if defined(WOLFSSL_ALLOW_TLSV10) && !defined(NO_OLD_TLS) req_method = TLSv1_client_method(); use_sni(TRUE); + break; #else failf(data, "wolfSSL does not support TLS 1.0"); return CURLE_NOT_BUILT_IN; #endif - break; case CURL_SSLVERSION_TLSv1_1: #ifndef NO_OLD_TLS req_method = TLSv1_1_client_method(); @@ -481,6 +481,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } #endif + FALLTHROUGH(); default: break; } @@ -711,7 +712,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) void *ssl_sessionid = NULL; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + &ssl_sessionid, NULL)) { /* we got a session id, use it! */ if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_delsessionid(data, ssl_sessionid); @@ -968,7 +970,8 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) if(our_ssl_sessionid) { Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)); + incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, + &old_ssl_sessionid, NULL)); if(incache) { if(old_ssl_sessionid != our_ssl_sessionid) { infof(data, "old SSL session ID is stale, removing"); @@ -978,7 +981,8 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) } if(!incache) { - result = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, 0, NULL); + result = Curl_ssl_addsessionid(cf, data, &connssl->peer, + our_ssl_sessionid, 0, NULL); if(result) { Curl_ssl_sessionid_unlock(data); wolfSSL_SESSION_free(our_ssl_sessionid); diff --git a/tests/http/clients/tls-session-reuse.c b/tests/http/clients/tls-session-reuse.c index 0309276cf8..2750ba7776 100644 --- a/tests/http/clients/tls-session-reuse.c +++ b/tests/http/clients/tls-session-reuse.c @@ -142,7 +142,8 @@ static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *opaque) } static void add_transfer(CURLM *multi, CURLSH *share, - struct curl_slist *resolve, const char *url) + struct curl_slist *resolve, + const char *url, int http_version) { CURL *easy; CURLMcode mc; @@ -159,7 +160,7 @@ static void add_transfer(CURLM *multi, CURLSH *share, curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L); curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, http_version); curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL); curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L); @@ -190,13 +191,19 @@ int main(int argc, char *argv[]) int msgs_in_queue; int add_more, waits, ongoing = 0; char *host, *port; + int http_version = CURL_HTTP_VERSION_1_1; - if(argc != 2) { - fprintf(stderr, "%s URL\n", argv[0]); + if(argc != 3) { + fprintf(stderr, "%s proto URL\n", argv[0]); exit(2); } - url = argv[1]; + if(!strcmp("h2", argv[1])) + http_version = CURL_HTTP_VERSION_2; + else if(!strcmp("h3", argv[1])) + http_version = CURL_HTTP_VERSION_3ONLY; + + url = argv[2]; cu = curl_url(); if(!cu) { fprintf(stderr, "out of memory\n"); @@ -234,7 +241,7 @@ int main(int argc, char *argv[]) curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); - add_transfer(multi, share, &resolve, url); + add_transfer(multi, share, &resolve, url, http_version); ++ongoing; add_more = 6; waits = 3; @@ -260,7 +267,7 @@ int main(int argc, char *argv[]) } else { while(add_more) { - add_transfer(multi, share, &resolve, url); + add_transfer(multi, share, &resolve, url, http_version); ++ongoing; --add_more; } diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index e608c96126..34b863f3c0 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -434,13 +434,14 @@ class TestDownload: assert r.exit_code == 0, f'{client.dump_logs()}' # Special client that tests TLS session reuse in parallel transfers - def test_02_26_session_shared_reuse(self, env: Env, httpd, repeat): - curl = CurlClient(env=env) - url = f'https://{env.domain1}:{env.https_port}/data-100k' + # TODO: just uses a single connection for h2/h3. Not sure how to prevent that + @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) + def test_02_26_session_shared_reuse(self, env: Env, proto, httpd, nghttpx, repeat): + url = f'https://{env.authority_for(env.domain1, proto)}/data-100k' client = LocalClient(name='tls-session-reuse', env=env) if not client.exists(): pytest.skip(f'example client not built: {client.name}') - r = client.run(args=[url]) + r = client.run(args=[proto, url]) r.check_exit_code(0) # test on paused transfers, based on issue #11982