diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index d63adac344..bb9e13dcdb 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -2772,7 +2772,8 @@ _cancel_lookup(dig_lookup_t *lookup, const char *file, unsigned int line) { } static isc_tlsctx_t * -get_create_tls_context(dig_query_t *query, const bool is_https) { +get_create_tls_context(dig_query_t *query, const bool is_https, + isc_tlsctx_client_session_cache_t **psess_cache) { isc_result_t result; isc_tlsctx_t *ctx = NULL, *found_ctx = NULL; isc_tls_cert_store_t *store = NULL, *found_store = NULL; @@ -2783,6 +2784,8 @@ get_create_tls_context(dig_query_t *query, const bool is_https) { isc_tlsctx_cache_transport_t transport = is_https ? isc_tlsctx_cache_https : isc_tlsctx_cache_tls; const bool hostname_ignore_subject = !is_https; + isc_tlsctx_client_session_cache_t *sess_cache = NULL, + *found_sess_cache = NULL; if (query->lookup->tls_key_file_set != query->lookup->tls_cert_file_set) { @@ -2793,7 +2796,7 @@ get_create_tls_context(dig_query_t *query, const bool is_https) { result = isc_tlsctx_cache_find(query->lookup->tls_ctx_cache, tlsctxname, transport, family, &found_ctx, - &found_store); + &found_store, &found_sess_cache); if (result != ISC_R_SUCCESS) { if (query->lookup->tls_ca_set) { if (found_store == NULL) { @@ -2852,13 +2855,26 @@ get_create_tls_context(dig_query_t *query, const bool is_https) { } #endif /* HAVE_LIBNGHTTP2 */ - result = isc_tlsctx_cache_add(query->lookup->tls_ctx_cache, - tlsctxname, transport, family, - ctx, store, NULL, NULL); + sess_cache = isc_tlsctx_client_session_cache_new( + mctx, ctx, + ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE); + + result = isc_tlsctx_cache_add( + query->lookup->tls_ctx_cache, tlsctxname, transport, + family, ctx, store, sess_cache, NULL, NULL, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (psess_cache != NULL) { + INSIST(*psess_cache == NULL); + *psess_cache = sess_cache; + } return (ctx); } + if (psess_cache != NULL) { + INSIST(*psess_cache == NULL); + *psess_cache = found_sess_cache; + } + INSIST(!query->lookup->tls_ca_set || found_store != NULL); return (found_ctx); failure: @@ -2868,6 +2884,9 @@ failure: if (store != NULL && store != found_store) { isc_tls_cert_store_free(&store); } + if (sess_cache != NULL && sess_cache != found_sess_cache) { + isc_tlsctx_client_session_cache_detach(&sess_cache); + } return (NULL); } @@ -2886,6 +2905,7 @@ start_tcp(dig_query_t *query) { dig_query_t *connectquery = NULL; isc_tlsctx_t *tlsctx = NULL; bool tls_mode = false; + isc_tlsctx_client_session_cache_t *sess_cache = NULL; REQUIRE(DIG_VALID_QUERY(query)); debug("start_tcp(%p)", query); @@ -2980,7 +3000,8 @@ start_tcp(dig_query_t *query) { query_attach(query, &connectquery); if (tls_mode) { - tlsctx = get_create_tls_context(connectquery, false); + tlsctx = get_create_tls_context(connectquery, false, + &sess_cache); if (tlsctx == NULL) { goto failure_tls; } @@ -2997,8 +3018,8 @@ start_tcp(dig_query_t *query) { uri, sizeof(uri)); if (!query->lookup->http_plain) { - tlsctx = get_create_tls_context(connectquery, - true); + tlsctx = get_create_tls_context( + connectquery, true, &sess_cache); if (tlsctx == NULL) { goto failure_tls; } diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 6c3f8cbd52..6701174525 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -934,6 +934,8 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { dns_transport_type_t transport_type = DNS_TRANSPORT_TCP; isc_tlsctx_t *tlsctx = NULL, *found = NULL; isc_tls_cert_store_t *store = NULL, *found_store = NULL; + isc_tlsctx_client_session_cache_t *sess_cache = NULL, + *found_sess_cache = NULL; (void)isc_refcount_increment0(&xfr->connects); dns_xfrin_attach(xfr, &connect_xfr); @@ -972,9 +974,9 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { * full TLS handshake procedure, making establishing * subsequent TLS connections for XoT faster. */ - result = isc_tlsctx_cache_find(xfr->tlsctx_cache, tlsname, - isc_tlsctx_cache_tls, family, - &tlsctx, &found_store); + result = isc_tlsctx_cache_find( + xfr->tlsctx_cache, tlsname, isc_tlsctx_cache_tls, + family, &tlsctx, &found_store, &found_sess_cache); if (result != ISC_R_SUCCESS) { const char *hostname = dns_transport_get_remote_hostname( @@ -1079,11 +1081,16 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { isc_tlsctx_enable_dot_client_alpn(tlsctx); + sess_cache = isc_tlsctx_client_session_cache_new( + xfr->mctx, tlsctx, + ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE); + found_store = NULL; result = isc_tlsctx_cache_add( xfr->tlsctx_cache, tlsname, isc_tlsctx_cache_tls, family, tlsctx, store, - &found, &found_store); + sess_cache, &found, &found_store, + &found_sess_cache); if (result == ISC_R_EXISTS) { /* * It seems the entry has just been created @@ -1101,7 +1108,10 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { INSIST(found != NULL); isc_tlsctx_free(&tlsctx); isc_tls_cert_store_free(&store); + isc_tlsctx_client_session_cache_detach( + &sess_cache); tlsctx = found; + sess_cache = found_sess_cache; } else { INSIST(result == ISC_R_SUCCESS); } @@ -1129,6 +1139,11 @@ failure: if (store != NULL && store != found_store) { isc_tls_cert_store_free(&store); } + + if (sess_cache != NULL && sess_cache != found_sess_cache) { + isc_tlsctx_client_session_cache_detach(&sess_cache); + } + isc_refcount_decrement0(&xfr->connects); dns_xfrin_detach(&connect_xfr); return (result); diff --git a/lib/isc/include/isc/tls.h b/lib/isc/include/isc/tls.h index ab44a23484..86265ddb7c 100644 --- a/lib/isc/include/isc/tls.h +++ b/lib/isc/include/isc/tls.h @@ -366,7 +366,7 @@ isc_tlsctx_client_session_cache_keep(isc_tlsctx_client_session_cache_t *cache, * * Requires: *\li 'cache' is a pointer to a valid TLS client session cache object; - *\li 'remote_peer_name' is a pointer to a non empty character string. + *\li 'remote_peer_name' is a pointer to a non empty character string; *\li 'tls' is a valid, non-'NULL' pointer to a TLS connection state object. */ @@ -375,11 +375,11 @@ isc_tlsctx_client_session_cache_keep_sockaddr( isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer, isc_tls_t *tls); /*%< - * The same as 'isc_tlsctx_client_session_cache_keep()', but using a + * The same as 'isc_tlsctx_client_session_cache_keep()', but uses a * 'isc_sockaddr_t' as a key, instead of a character string. * * Requires: - *\li 'remote_peer' is a valid, non-'NULL' pointer to an 'isc_sockaddr_t' + *\li 'remote_peer' is a valid, non-'NULL', pointer to an 'isc_sockaddr_t' *object. */ @@ -395,7 +395,7 @@ isc_tlsctx_client_session_cache_reuse(isc_tlsctx_client_session_cache_t *cache, * Requires: *\li 'cache' is a pointer to a valid TLS client session cache object; *\li 'remote_peer_name' is a pointer to a non empty character string; - *\li 'tls' is a valid, non-'NULL', pointer to a TLS connection state object. + *\li 'tls' is a valid, non-'NULL' pointer to a TLS connection state object. */ void @@ -413,6 +413,22 @@ isc_tlsctx_client_session_cache_reuse_sockaddr( const isc_tlsctx_t * isc_tlsctx_client_session_cache_getctx(isc_tlsctx_client_session_cache_t *cache); +/*%< + * Returns a TLS context associated with the given TLS client + * session cache object. The function is intended to be used to + * implement the sanity checks ('INSIST()'s and 'REQUIRE()'s). + * + * Requires: + *\li 'cache' is a pointer to a valid TLS client session cache object. + */ + +#define ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE (150) +/*%< + * The default maximum size of a TLS client session cache. The value + * should be large enough to hold enough sessions to successfully + * re-establish connections to the most remote TLS servers, but not + * too big to avoid keeping too much obsolete sessions. + */ typedef struct isc_tlsctx_cache isc_tlsctx_cache_t; /*%< @@ -481,28 +497,39 @@ isc_tlsctx_cache_detach(isc_tlsctx_cache_t **cachep); */ isc_result_t -isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name, - const isc_tlsctx_cache_transport_t transport, - const uint16_t family, isc_tlsctx_t *ctx, - isc_tls_cert_store_t *store, isc_tlsctx_t **pfound, - isc_tls_cert_store_t **pfound_store); +isc_tlsctx_cache_add( + isc_tlsctx_cache_t *cache, const char *name, + const isc_tlsctx_cache_transport_t transport, const uint16_t family, + isc_tlsctx_t *ctx, isc_tls_cert_store_t *store, + isc_tlsctx_client_session_cache_t *client_sess_cache, + isc_tlsctx_t **pfound, isc_tls_cert_store_t **pfound_store, + isc_tlsctx_client_session_cache_t **pfound_client_sess_cache); /*%< * - * Add a new TLS context to the TLS context cache. 'pfound' is an - * optional pointer, which can be used to retrieve an already - * existing TLS context object in a case it exists. + * Add a new TLS context and its associated data to the TLS context + * cache. 'pfound' is an optional pointer, which can be used to + * retrieve an already existing TLS context object in a case it + * exists. * * The passed certificates store object ('store') possession is * transferred to the cache object in a case of success. In some cases * it might be destroyed immediately upon the call completion. * + * The possession of the passed TLS client session cache + * ('client_sess_cache') is also transferred to the cache object in a + * case of success. + * * Requires: *\li 'cache' is a valid pointer to a TLS context cache object; *\li 'name' is a valid pointer to a non-empty string; *\li 'transport' is a valid transport identifier (currently only * TLS/DoT and HTTPS/DoH are supported); *\li 'family' - either 'AF_INET' or 'AF_INET6'; - *\li 'ctx' - a valid pointer to a valid TLS context object. + *\li 'ctx' - a valid pointer to a valid TLS context object; + *\li 'store' - a valid pointer to a valid TLS certificates store object or + * 'NULL'; + *\li 'client_sess_cache' - a valid pointer to a valid TLS client sessions + *cache object or 'NULL. * * Returns: *\li #ISC_R_EXISTS - node of the same key already exists; @@ -510,12 +537,13 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name, */ isc_result_t -isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name, - const isc_tlsctx_cache_transport_t transport, - const uint16_t family, isc_tlsctx_t **pctx, - isc_tls_cert_store_t **pstore); +isc_tlsctx_cache_find( + isc_tlsctx_cache_t *cache, const char *name, + const isc_tlsctx_cache_transport_t transport, const uint16_t family, + isc_tlsctx_t **pctx, isc_tls_cert_store_t **pstore, + isc_tlsctx_client_session_cache_t **pfound_client_sess_cache); /*%< - * Look up a TLS context in the TLS context cache. + * Look up a TLS context and its associated data in the TLS context cache. * * Requires: *\li 'cache' is a valid pointer to a TLS context cache object; @@ -523,7 +551,10 @@ isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name, *\li 'transport' - a valid transport identifier (currently only * TLS/DoT and HTTPS/DoH are supported; *\li 'family' - either 'AF_INET' or 'AF_INET6'; - *\li 'pctx' - a valid pointer to a non-NULL pointer. + *\li 'pctx' - a valid pointer to a non-NULL pointer; + *\li 'pstore' - a valid pointer to a non-NULL pointer or 'NULL'. + *\li 'pfound_client_sess_cache' - a valid pointer to a non-NULL pointer or + *'NULL'. * * Returns: *\li #ISC_R_SUCCESS - the context has been found; diff --git a/lib/isc/tls.c b/lib/isc/tls.c index a7b9d1b40a..2a6923b73d 100644 --- a/lib/isc/tls.c +++ b/lib/isc/tls.c @@ -1086,6 +1086,8 @@ typedef struct isc_tlsctx_cache_entry { * session-resumption cache. */ isc_tlsctx_t *ctx[isc_tlsctx_cache_count - 1][2]; + isc_tlsctx_client_session_cache_t + *client_sess_cache[isc_tlsctx_cache_count - 1][2]; /* * One certificate store is enough for all the contexts defined * above. We need that for peer validation. @@ -1138,6 +1140,11 @@ tlsctx_cache_entry_destroy(isc_mem_t *mctx, isc_tlsctx_cache_entry_t *entry) { if (entry->ctx[i][k] != NULL) { isc_tlsctx_free(&entry->ctx[i][k]); } + + if (entry->client_sess_cache[i][k] != NULL) { + isc_tlsctx_client_session_cache_detach( + &entry->client_sess_cache[i][k]); + } } } if (entry->ca_store != NULL) { @@ -1187,17 +1194,21 @@ isc_tlsctx_cache_detach(isc_tlsctx_cache_t **cachep) { } isc_result_t -isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name, - const isc_tlsctx_cache_transport_t transport, - const uint16_t family, isc_tlsctx_t *ctx, - isc_tls_cert_store_t *store, isc_tlsctx_t **pfound, - isc_tls_cert_store_t **pfound_store) { +isc_tlsctx_cache_add( + isc_tlsctx_cache_t *cache, const char *name, + const isc_tlsctx_cache_transport_t transport, const uint16_t family, + isc_tlsctx_t *ctx, isc_tls_cert_store_t *store, + isc_tlsctx_client_session_cache_t *client_sess_cache, + isc_tlsctx_t **pfound, isc_tls_cert_store_t **pfound_store, + isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) { isc_result_t result = ISC_R_FAILURE; size_t name_len, tr_offset; isc_tlsctx_cache_entry_t *entry = NULL; bool ipv6; REQUIRE(VALID_TLSCTX_CACHE(cache)); + REQUIRE(client_sess_cache == NULL || + VALID_TLSCTX_CLIENT_SESSION_CACHE(client_sess_cache)); REQUIRE(name != NULL && *name != '\0'); REQUIRE(transport > isc_tlsctx_cache_none && transport < isc_tlsctx_cache_count); @@ -1213,6 +1224,7 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name, result = isc_ht_find(cache->data, (const uint8_t *)name, name_len, (void **)&entry); if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) { + isc_tlsctx_client_session_cache_t *found_client_sess_cache; /* The entry exists. */ if (pfound != NULL) { INSIST(*pfound == NULL); @@ -1223,6 +1235,14 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name, INSIST(*pfound_store == NULL); *pfound_store = entry->ca_store; } + + found_client_sess_cache = + entry->client_sess_cache[tr_offset][ipv6]; + if (pfound_client_sess_cache != NULL && + found_client_sess_cache != NULL) { + INSIST(*pfound_client_sess_cache == NULL); + *pfound_client_sess_cache = found_client_sess_cache; + } result = ISC_R_EXISTS; } else if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] == NULL) { @@ -1231,10 +1251,11 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name, * particular transport/IP type combination. */ entry->ctx[tr_offset][ipv6] = ctx; + entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache; /* - * As the passed certificates store object is supposed to be - * internally managed by the cache object anyway, we might - * destroy the unneeded store object right now. + * As the passed certificates store object is supposed + * to be internally managed by the cache object anyway, + * we might destroy the unneeded store object right now. */ if (store != NULL && store != entry->ca_store) { isc_tls_cert_store_free(&store); @@ -1249,6 +1270,7 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name, /* Oracle/Red Hat Linux, GCC bug #53119 */ memset(entry, 0, sizeof(*entry)); entry->ctx[tr_offset][ipv6] = ctx; + entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache; entry->ca_store = store; RUNTIME_CHECK(isc_ht_add(cache->data, (const uint8_t *)name, name_len, @@ -1262,10 +1284,11 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name, } isc_result_t -isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name, - const isc_tlsctx_cache_transport_t transport, - const uint16_t family, isc_tlsctx_t **pctx, - isc_tls_cert_store_t **pstore) { +isc_tlsctx_cache_find( + isc_tlsctx_cache_t *cache, const char *name, + const isc_tlsctx_cache_transport_t transport, const uint16_t family, + isc_tlsctx_t **pctx, isc_tls_cert_store_t **pstore, + isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) { isc_result_t result = ISC_R_FAILURE; size_t tr_offset; isc_tlsctx_cache_entry_t *entry = NULL; @@ -1292,7 +1315,16 @@ isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name, } if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) { + isc_tlsctx_client_session_cache_t *found_client_sess_cache = + entry->client_sess_cache[tr_offset][ipv6]; + *pctx = entry->ctx[tr_offset][ipv6]; + + if (pfound_client_sess_cache != NULL && + found_client_sess_cache != NULL) { + INSIST(*pfound_client_sess_cache == NULL); + *pfound_client_sess_cache = found_client_sess_cache; + } } else if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] == NULL) { result = ISC_R_NOTFOUND; diff --git a/lib/ns/listenlist.c b/lib/ns/listenlist.c index 32a72888d5..f852f06d56 100644 --- a/lib/ns/listenlist.c +++ b/lib/ns/listenlist.c @@ -49,7 +49,7 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, */ result = isc_tlsctx_cache_find(tlsctx_cache, tls_params->name, transport, family, &sslctx, - &found_store); + &found_store, NULL); if (result != ISC_R_SUCCESS) { /* * The lookup failed, let's try to create a new context @@ -150,7 +150,8 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, RUNTIME_CHECK(isc_tlsctx_cache_add( tlsctx_cache, tls_params->name, transport, family, sslctx, store, - NULL, NULL) == ISC_R_SUCCESS); + NULL, NULL, NULL, + NULL) == ISC_R_SUCCESS); } else { INSIST(sslctx != NULL); }