From 1bccee76c8211da3233f08444abacd7cc00d33d4 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 17 Aug 2023 11:16:11 +0200 Subject: [PATCH] bearssl: handshake fix, provide proper get_select_socks() implementation - bring bearssl handshake times down from +200ms down to other TLS backends - vtls: improve generic get_select_socks() implementation - tests: provide Apache with a suitable ssl session cache Closes #11675 --- lib/vtls/bearssl.c | 51 +++++++++++++++++++++++++++++++++++-- lib/vtls/vtls.c | 38 ++++++++++++++------------- tests/http/testenv/httpd.py | 2 ++ 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c index 2fbbde6027..9ab55dca8c 100644 --- a/lib/vtls/bearssl.c +++ b/lib/vtls/bearssl.c @@ -587,6 +587,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, const bool verifyhost = conn_config->verifyhost; CURLcode ret; unsigned version_min, version_max; + int session_set = 0; #ifdef ENABLE_IPV6 struct in6_addr addr; #else @@ -594,6 +595,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, #endif DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step1"); switch(conn_config->version) { case CURL_SSLVERSION_SSLv2: @@ -631,6 +633,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, source.data = ca_info_blob->data; source.len = ca_info_blob->len; + CURL_TRC_CF(data, cf, "connect_step1, load ca_info_blob"); ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); if(ret != CURLE_OK) { failf(data, "error importing CA certificate blob"); @@ -644,6 +647,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, source.data = ssl_cafile; source.len = 0; + CURL_TRC_CF(data, cf, "connect_step1, load cafile"); ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); if(ret != CURLE_OK) { failf(data, "error setting certificate verify locations." @@ -663,6 +667,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, if(conn_config->cipher_list) { /* Override the ciphers as specified. For the default cipher list see the BearSSL source code of br_ssl_client_init_full() */ + CURL_TRC_CF(data, cf, "connect_step1, set ciphers"); ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng, conn_config->cipher_list); if(ret) @@ -678,9 +683,11 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, if(ssl_config->primary.sessionid) { void *session; + CURL_TRC_CF(data, cf, "connect_step1, check session cache"); Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, &session, NULL)) { br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); + session_set = 1; infof(data, "BearSSL: re-using session ID"); } Curl_ssl_sessionid_unlock(data); @@ -718,6 +725,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } hostname = snihost; + CURL_TRC_CF(data, cf, "connect_step1, SNI set"); } /* give application a chance to interfere with SSL set up. */ @@ -732,7 +740,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, } } - if(!br_ssl_client_reset(&backend->ctx, hostname, 1)) + if(!br_ssl_client_reset(&backend->ctx, hostname, session_set)) return CURLE_FAILED_INIT; backend->active = TRUE; @@ -741,6 +749,28 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_OK; } +static int bearssl_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) +{ + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + + if(sock == CURL_SOCKET_BAD) + return GETSOCK_BLANK; + else { + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + unsigned state = br_ssl_engine_current_state(&backend->ctx.eng); + if(state & BR_SSL_SENDREC) { + socks[0] = sock; + return GETSOCK_WRITESOCK(0); + } + } + socks[0] = sock; + return GETSOCK_READSOCK(0); +} + static CURLcode bearssl_run_until(struct Curl_cfilter *cf, struct Curl_easy *data, unsigned target) @@ -792,6 +822,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, if(state & BR_SSL_SENDREC) { buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len); ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, &result); + CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result); if(ret <= 0) { return result; } @@ -800,6 +831,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, else if(state & BR_SSL_RECVREC) { buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len); ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result); + CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %zd, %d", len, ret, result); if(ret == 0) { failf(data, "SSL: EOF without close notify"); return CURLE_READ_ERROR; @@ -821,16 +853,26 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, CURLcode ret; DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step2"); ret = bearssl_run_until(cf, data, BR_SSL_SENDAPP | BR_SSL_RECVAPP); if(ret == CURLE_AGAIN) return CURLE_OK; if(ret == CURLE_OK) { + unsigned int tver; if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { failf(data, "SSL: connection closed during handshake"); return CURLE_SSL_CONNECT_ERROR; } connssl->connecting_state = ssl_connect_3; + /* Informational message */ + tver = br_ssl_engine_get_version(&backend->ctx.eng); + if(tver == 0x0303) + infof(data, "SSL connection using TLSv1.2"); + else if(tver == 0x0304) + infof(data, "SSL connection using TLSv1.3"); + else + infof(data, "SSL connection using TLS 0x%x", tver); } return ret; } @@ -846,6 +888,7 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step3"); if(connssl->alpn) { const char *proto; @@ -954,8 +997,10 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, timediff_t timeout_ms; int what; + CURL_TRC_CF(data, cf, "connect_common(blocking=%d)", !nonblocking); /* check if the connection has already been established */ if(ssl_connection_complete == connssl->state) { + CURL_TRC_CF(data, cf, "connect_common, connected"); *done = TRUE; return CURLE_OK; } @@ -987,8 +1032,10 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, curl_socket_t readfd = ssl_connect_2_reading == connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + CURL_TRC_CF(data, cf, "connect_common, check socket"); what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, nonblocking?0:timeout_ms); + CURL_TRC_CF(data, cf, "connect_common, check socket -> %d", what); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -1163,7 +1210,7 @@ const struct Curl_ssl Curl_ssl_bearssl = { Curl_none_cert_status_request, /* cert_status_request */ bearssl_connect, /* connect */ bearssl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + bearssl_get_select_socks, /* getsock */ bearssl_get_internals, /* get_internals */ bearssl_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 2dbf27bcb8..68caadda0e 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -635,19 +635,16 @@ int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_connect_data *connssl = cf->ctx; curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); - if(sock != CURL_SOCKET_BAD) { - if(connssl->connecting_state == ssl_connect_2_writing) { - /* write mode */ - socks[0] = sock; - return GETSOCK_WRITESOCK(0); - } - if(connssl->connecting_state == ssl_connect_2_reading) { - /* read mode */ - socks[0] = sock; - return GETSOCK_READSOCK(0); - } + if(sock == CURL_SOCKET_BAD) + return GETSOCK_BLANK; + + if(connssl->connecting_state == ssl_connect_2_writing) { + /* we are only interested in writing */ + socks[0] = sock; + return GETSOCK_WRITESOCK(0); } - return GETSOCK_BLANK; + socks[0] = sock; + return GETSOCK_READSOCK(0); } /* Selects an SSL crypto engine @@ -1512,6 +1509,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, } CF_DATA_SAVE(save, cf, data); + CURL_TRC_CF(data, cf, "cf_connect()"); (void)connssl; DEBUGASSERT(data->conn); DEBUGASSERT(data->conn == cf->conn); @@ -1541,6 +1539,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, DEBUGASSERT(connssl->state == ssl_connection_complete); } out: + CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done); CF_DATA_RESTORE(cf, save); return result; } @@ -1601,12 +1600,17 @@ static int ssl_cf_get_select_socks(struct Curl_cfilter *cf, curl_socket_t *socks) { struct cf_call_data save; - int result; + int fds = GETSOCK_BLANK; - CF_DATA_SAVE(save, cf, data); - result = Curl_ssl->get_select_socks(cf, data, socks); - CF_DATA_RESTORE(cf, save); - return result; + if(!cf->next->connected) { + fds = cf->next->cft->get_select_socks(cf->next, data, socks); + } + else if(!cf->connected) { + CF_DATA_SAVE(save, cf, data); + fds = Curl_ssl->get_select_socks(cf, data, socks); + CF_DATA_RESTORE(cf, save); + } + return fds; } static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, diff --git a/tests/http/testenv/httpd.py b/tests/http/testenv/httpd.py index ecc7c6c475..38a8a8b6a4 100644 --- a/tests/http/testenv/httpd.py +++ b/tests/http/testenv/httpd.py @@ -48,6 +48,7 @@ class Httpd: 'authz_user', 'authz_core', 'authz_host', 'auth_basic', 'auth_digest', 'env', 'filter', 'headers', 'mime', + 'socache_shmcb', 'rewrite', 'http2', 'ssl', 'proxy', 'proxy_http', 'proxy_connect', 'mpm_event', ] @@ -251,6 +252,7 @@ class Httpd: f'Listen {self.env.proxy_port}', f'Listen {self.env.proxys_port}', f'TypesConfig "{self._conf_dir}/mime.types', + f'SSLSessionCache "shmcb:ssl_gcache_data(32000)"', ] if 'base' in self._extra_configs: conf.extend(self._extra_configs['base']) -- 2.47.3