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
diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c
index 2fbbde6..9ab55dc 100644
--- a/lib/vtls/bearssl.c
+++ b/lib/vtls/bearssl.c
@@ -587,6 +587,7 @@
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 @@
#endif
DEBUGASSERT(backend);
+ CURL_TRC_CF(data, cf, "connect_step1");
switch(conn_config->version) {
case CURL_SSLVERSION_SSLv2:
@@ -631,6 +633,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 2dbf27b..68caadd 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -635,19 +635,16 @@
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 @@
}
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 @@
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 @@
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 ecc7c6c..38a8a8b 100644
--- a/tests/http/testenv/httpd.py
+++ b/tests/http/testenv/httpd.py
@@ -48,6 +48,7 @@
'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 @@
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'])