From 86465c1dacb704a5e95cd3311595f27c323e0507 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Mon, 25 Apr 2022 16:47:06 +0300 Subject: [PATCH] DoT: implement TLS client session resumption This commit extends DoT code with TLS client session resumption support implemented on top of the TLS client session cache. --- bin/dig/dighost.c | 2 +- bin/tests/test_client.c | 2 +- lib/dns/xfrin.c | 2 +- lib/isc/include/isc/netmgr.h | 3 +- lib/isc/netmgr/netmgr-int.h | 2 + lib/isc/netmgr/tlsdns.c | 79 +++++++++++++++++++++++++++++++----- lib/isc/tests/netmgr_test.c | 18 ++++---- 7 files changed, 85 insertions(+), 23 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index ce550fe7bf..14f6267859 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -3000,7 +3000,7 @@ start_tcp(dig_query_t *query) { isc_nm_tlsdnsconnect(netmgr, &localaddr, &query->sockaddr, tcp_connected, connectquery, local_timeout, - tlsctx); + tlsctx, sess_cache); #if HAVE_LIBNGHTTP2 } else if (query->lookup->https_mode) { char uri[4096] = { 0 }; diff --git a/bin/tests/test_client.c b/bin/tests/test_client.c index 226aac7290..c7615e27d9 100644 --- a/bin/tests/test_client.c +++ b/bin/tests/test_client.c @@ -407,7 +407,7 @@ run(void) { isc_tlsctx_createclient(&tls_ctx); isc_nm_tlsdnsconnect(netmgr, &sockaddr_local, &sockaddr_remote, - connect_cb, NULL, timeout, tls_ctx); + connect_cb, NULL, timeout, tls_ctx, NULL); break; } #if HAVE_LIBNGHTTP2 diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 42f8114e9d..c3971b8ffa 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -1118,7 +1118,7 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { } isc_nm_tlsdnsconnect(xfr->netmgr, &xfr->sourceaddr, &xfr->primaryaddr, xfrin_connect_done, - connect_xfr, 30000, tlsctx); + connect_xfr, 30000, tlsctx, sess_cache); } break; default: UNREACHABLE(); diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index 830fc327a8..71878f1099 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -484,7 +484,8 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, void isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg, unsigned int timeout, - isc_tlsctx_t *sslctx); + isc_tlsctx_t *sslctx, + isc_tlsctx_client_session_cache_t *client_sess_cache); /*%< * Establish a DNS client connection via a TCP or TLS connection, bound to * the address 'local' and connected to the address 'peer'. diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index b6a10a16f5..1a9114f8ae 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -932,6 +932,8 @@ struct isc_nmsocket { struct tls { isc_tls_t *tls; isc_tlsctx_t *ctx; + isc_tlsctx_client_session_cache_t *client_sess_cache; + bool client_session_saved; BIO *app_rbio; BIO *app_wbio; BIO *ssl_rbio; diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c index 7e5fd597b9..b316449647 100644 --- a/lib/isc/netmgr/tlsdns.c +++ b/lib/isc/netmgr/tlsdns.c @@ -79,6 +79,14 @@ tls_cycle(isc_nmsocket_t *sock); static void call_pending_send_callbacks(isc_nmsocket_t *sock, const isc_result_t result); +static void +tlsdns_keep_client_tls_session(isc_nmsocket_t *sock); + +static void +tlsdns_set_tls_shutdown(isc_tls_t *tls) { + (void)SSL_set_shutdown(tls, SSL_SENT_SHUTDOWN); +} + static bool peer_verification_has_failed(isc_nmsocket_t *sock) { if (sock->tls.tls != NULL && sock->tls.state == TLS_STATE_HANDSHAKE && @@ -294,11 +302,17 @@ tlsdns_connect_cb(uv_connect_t *uvreq, int status) { SSL_set_bio(sock->tls.tls, sock->tls.ssl_rbio, sock->tls.ssl_wbio); #endif - SSL_set_connect_state(sock->tls.tls); - result = isc_sockaddr_fromsockaddr(&sock->peer, (struct sockaddr *)&ss); RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (sock->tls.client_sess_cache != NULL) { + isc_tlsctx_client_session_cache_reuse_sockaddr( + sock->tls.client_sess_cache, &sock->peer, + sock->tls.tls); + } + + SSL_set_connect_state(sock->tls.tls); + /* Setting pending req */ sock->tls.pending_req = req; @@ -319,7 +333,8 @@ error: void isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg, unsigned int timeout, - isc_tlsctx_t *sslctx) { + isc_tlsctx_t *sslctx, + isc_tlsctx_client_session_cache_t *client_sess_cache) { isc_result_t result = ISC_R_SUCCESS; isc_nmsocket_t *sock = NULL; isc__netievent_tlsdnsconnect_t *ievent = NULL; @@ -349,6 +364,13 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, req->local = *local; req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface); + if (client_sess_cache != NULL) { + INSIST(isc_tlsctx_client_session_cache_getctx( + client_sess_cache) == sslctx); + isc_tlsctx_client_session_cache_attach( + client_sess_cache, &sock->tls.client_sess_cache); + } + result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock->fd); if (result != ISC_R_SUCCESS) { goto failure; @@ -1008,6 +1030,11 @@ isc__nm_tlsdns_processbuffer(isc_nmsocket_t *sock) { isc_nmhandle_detach(&handle); + if (isc__nmsocket_closing(sock)) { + tlsdns_set_tls_shutdown(sock->tls.tls); + tlsdns_keep_client_tls_session(sock); + } + return (ISC_R_SUCCESS); } @@ -1102,6 +1129,8 @@ tls_cycle_input(isc_nmsocket_t *sock) { const unsigned char *alpn = NULL; unsigned int alpnlen = 0; + isc__nmsocket_log_tls_session_reuse(sock, sock->tls.tls); + isc_tls_get_selected_alpn(sock->tls.tls, &alpn, &alpnlen); if (alpn != NULL && alpnlen == ISC_TLS_DOT_PROTO_ALPN_ID_LEN && memcmp(ISC_TLS_DOT_PROTO_ALPN_ID, alpn, @@ -1835,6 +1864,12 @@ tlsdns_close_sock(isc_nmsocket_t *sock) { atomic_store(&sock->connected, false); if (sock->tls.tls != NULL) { + /* + * Let's shutdown the TLS session properly so that the session + * will remain resumable, if required. + */ + tlsdns_set_tls_shutdown(sock->tls.tls); + tlsdns_keep_client_tls_session(sock); isc_tls_free(&sock->tls.tls); } @@ -2015,7 +2050,7 @@ isc__nm_tlsdns_shutdown(isc_nmsocket_t *sock) { if (sock->tls.tls) { /* Shutdown any active TLS connections */ - (void)SSL_shutdown(sock->tls.tls); + tlsdns_set_tls_shutdown(sock->tls.tls); } if (atomic_load(&sock->accepting)) { @@ -2146,11 +2181,35 @@ isc__nm_async_tlsdns_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx, void isc__nm_tlsdns_cleanup_data(isc_nmsocket_t *sock) { - if ((sock->type == isc_nm_tlsdnslistener || - sock->type == isc_nm_tlsdnssocket) && - sock->tls.ctx != NULL) - { - INSIST(ISC_LIST_EMPTY(sock->tls.sendreqs)); - isc_tlsctx_free(&sock->tls.ctx); + if (sock->type == isc_nm_tlsdnslistener || + sock->type == isc_nm_tlsdnssocket) { + if (sock->tls.client_sess_cache != NULL) { + INSIST(atomic_load(&sock->client)); + INSIST(sock->type == isc_nm_tlsdnssocket); + isc_tlsctx_client_session_cache_detach( + &sock->tls.client_sess_cache); + } + if (sock->tls.ctx != NULL) { + INSIST(ISC_LIST_EMPTY(sock->tls.sendreqs)); + isc_tlsctx_free(&sock->tls.ctx); + } + } +} + +static void +tlsdns_keep_client_tls_session(isc_nmsocket_t *sock) { + /* + * Ensure that the isc_tls_t is being accessed from + * within the worker thread the socket is bound to. + */ + REQUIRE(sock->tid == isc_nm_tid()); + if (sock->tls.client_sess_cache != NULL && + sock->tls.client_session_saved == false) + { + INSIST(atomic_load(&sock->client)); + isc_tlsctx_client_session_cache_keep_sockaddr( + sock->tls.client_sess_cache, &sock->peer, + sock->tls.tls); + sock->tls.client_session_saved = true; } } diff --git a/lib/isc/tests/netmgr_test.c b/lib/isc/tests/netmgr_test.c index 8cf0187cec..6388aa4270 100644 --- a/lib/isc/tests/netmgr_test.c +++ b/lib/isc/tests/netmgr_test.c @@ -2331,7 +2331,7 @@ static void tlsdns_connect(isc_nm_t *nm) { isc_nm_tlsdnsconnect(nm, &tcp_connect_addr, &tcp_listen_addr, connect_connect_cb, NULL, T_CONNECT, - tcp_connect_tlsctx); + tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); } static void @@ -2353,7 +2353,7 @@ tlsdns_noop(void **state __attribute__((unused))) { isc_refcount_increment0(&active_cconnects); isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, connect_connect_cb, NULL, T_CONNECT, - tcp_connect_tlsctx); + tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); isc__netmgr_shutdown(connect_nm); @@ -2382,7 +2382,7 @@ tlsdns_noresponse(void **state __attribute__((unused))) { isc_refcount_increment0(&active_cconnects); isc_nm_tlsdnsconnect(connect_nm, &connect_addr, &tcp_listen_addr, connect_connect_cb, NULL, T_CONNECT, - tcp_connect_tlsctx); + tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); WAIT_FOR_EQ(cconnects, 1); WAIT_FOR_EQ(csends, 1); @@ -2437,7 +2437,7 @@ tlsdns_timeout_recovery(void **state __attribute__((unused))) { isc_refcount_increment0(&active_cconnects); isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, connect_connect_cb, NULL, T_SOFT, - tcp_connect_tlsctx); + tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); WAIT_FOR_EQ(cconnects, 1); WAIT_FOR_GE(csends, 1); @@ -2469,7 +2469,7 @@ tlsdns_recv_one(void **state __attribute__((unused))) { isc_refcount_increment0(&active_cconnects); isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, connect_connect_cb, NULL, T_CONNECT, - tcp_connect_tlsctx); + tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); WAIT_FOR_EQ(cconnects, 1); WAIT_FOR_LE(nsends, 0); @@ -2512,14 +2512,14 @@ tlsdns_recv_two(void **state __attribute__((unused))) { isc_refcount_increment0(&active_cconnects); isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, connect_connect_cb, NULL, T_CONNECT, - tcp_connect_tlsctx); + tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); WAIT_FOR_EQ(cconnects, 1); isc_refcount_increment0(&active_cconnects); isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, connect_connect_cb, NULL, T_CONNECT, - tcp_connect_tlsctx); + tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); WAIT_FOR_EQ(cconnects, 2); @@ -2788,7 +2788,7 @@ tlsdns_connect_noalpn(void **state __attribute__((unused))) { isc_refcount_increment0(&active_cconnects); isc_nm_tlsdnsconnect(connect_nm, &connect_addr, &tcp_listen_addr, tlsdns_connect_connect_noalpn, NULL, T_CONNECT, - connect_tlsctx_noalpn); + connect_tlsctx_noalpn, NULL); WAIT_FOR_EQ(active_cconnects, 0); @@ -2855,7 +2855,7 @@ tlsdns_listen_noalpn(void **state __attribute__((unused))) { isc_refcount_increment0(&active_cconnects); isc_nm_tlsdnsconnect(connect_nm, &connect_addr, &tcp_listen_addr, connect_connect_cb, NULL, T_CONNECT, - tcp_connect_tlsctx); + tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); WAIT_FOR_EQ(saccepts, 1); WAIT_FOR_EQ(cconnects, 1);