From: Artem Boldariev Date: Thu, 23 Dec 2021 14:08:41 +0000 (+0200) Subject: Use the TLS context cache for client-side contexts (XoT) X-Git-Tag: v9.17.22~21^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=64f7c5566214a5fe9b6fac63f53e741b20cbbda0;p=thirdparty%2Fbind9.git Use the TLS context cache for client-side contexts (XoT) This commit enables client-side TLS contexts re-use for zone transfers over TLS. That, in turn, makes it possible to use the internal session cache associated with the contexts, allowing the TLS connections to be established faster and requiring fewer resources by not going through the full TLS handshake procedure. Previously that would recreate the context on every connection, making TLS session resumption impossible. Also, this change lays down a foundation for Strict TLS (when the client validates a server certificate), as the TLS context cache can be extended to store additional data required for validation (like intermediates CA chain). --- diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index dd0d6701185..869f5f764ea 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -112,6 +112,7 @@ struct named_server { char *lockfile; isc_tlsctx_cache_t *tlsctx_server_cache; + isc_tlsctx_cache_t *tlsctx_client_cache; }; #define NAMED_SERVER_MAGIC ISC_MAGIC('S', 'V', 'E', 'R') diff --git a/bin/named/server.c b/bin/named/server.c index 0ac53c9a763..8e27090da85 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -8431,6 +8431,15 @@ load_configuration(const char *filename, named_server_t *server, server->tlsctx_server_cache = isc_tlsctx_cache_new(named_g_mctx); + if (server->tlsctx_client_cache != NULL) { + isc_tlsctx_cache_detach(&server->tlsctx_client_cache); + } + + server->tlsctx_client_cache = isc_tlsctx_cache_new(named_g_mctx); + + dns_zonemgr_set_tlsctx_cache(server->zonemgr, + server->tlsctx_client_cache); + /* * Fill in the maps array, used for resolving defaults. */ @@ -10182,6 +10191,7 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) { server->magic = NAMED_SERVER_MAGIC; server->tlsctx_server_cache = NULL; + server->tlsctx_client_cache = NULL; *serverp = server; } @@ -10241,6 +10251,10 @@ named_server_destroy(named_server_t **serverp) { isc_tlsctx_cache_detach(&server->tlsctx_server_cache); } + if (server->tlsctx_client_cache != NULL) { + isc_tlsctx_cache_detach(&server->tlsctx_client_cache); + } + server->magic = 0; isc_mem_put(server->mctx, server, sizeof(*server)); *serverp = NULL; diff --git a/bin/named/transportconf.c b/bin/named/transportconf.c index 61c7462f0b0..40c21a6cd0e 100644 --- a/bin/named/transportconf.c +++ b/bin/named/transportconf.c @@ -108,6 +108,7 @@ add_doh_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { transport = dns_transport_new(&dohname, DNS_TRANSPORT_HTTP, list); + dns_transport_set_tlsname(transport, dohid); parse_transport_option(doh, transport, "key-file", dns_transport_set_keyfile); parse_transport_option(doh, transport, "cert-file", @@ -165,6 +166,7 @@ add_tls_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { transport = dns_transport_new(&tlsname, DNS_TRANSPORT_TLS, list); + dns_transport_set_tlsname(transport, tlsid); parse_transport_option(tls, transport, "key-file", dns_transport_set_keyfile); parse_transport_option(tls, transport, "cert-file", @@ -228,10 +230,12 @@ static void transport_list_add_ephemeral(dns_transport_list_t *list) { isc_result_t result; dns_name_t tlsname; + dns_transport_t *transport; create_name("ephemeral", &tlsname); - (void)dns_transport_new(&tlsname, DNS_TRANSPORT_TLS, list); + transport = dns_transport_new(&tlsname, DNS_TRANSPORT_TLS, list); + dns_transport_set_tlsname(transport, "ephemeral"); return; failure: diff --git a/lib/dns/include/dns/transport.h b/lib/dns/include/dns/transport.h index 91fe3826307..69ed013c87c 100644 --- a/lib/dns/include/dns/transport.h +++ b/lib/dns/include/dns/transport.h @@ -54,6 +54,8 @@ dns_http_mode_t dns_transport_get_mode(dns_transport_t *transport); char * dns_transport_get_ciphers(dns_transport_t *transport); +char * +dns_transport_get_tlsname(dns_transport_t *transport); uint32_t dns_transport_get_tls_versions(const dns_transport_t *transport); bool @@ -82,6 +84,9 @@ void dns_transport_set_mode(dns_transport_t *transport, dns_http_mode_t mode); void dns_transport_set_ciphers(dns_transport_t *transport, const char *ciphers); +void +dns_transport_set_tlsname(dns_transport_t *transport, const char *tlsname); + void dns_transport_set_tls_versions(dns_transport_t *transport, const uint32_t tls_versions); diff --git a/lib/dns/include/dns/xfrin.h b/lib/dns/include/dns/xfrin.h index d080b089cb3..6cbac87f903 100644 --- a/lib/dns/include/dns/xfrin.h +++ b/lib/dns/include/dns/xfrin.h @@ -25,6 +25,7 @@ ***/ #include +#include #include #include @@ -49,7 +50,8 @@ dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype, const isc_sockaddr_t *primaryaddr, const isc_sockaddr_t *sourceaddr, isc_dscp_t dscp, dns_tsigkey_t *tsigkey, dns_transport_t *transport, - isc_mem_t *mctx, isc_nm_t *netmgr, dns_xfrindone_t done, + isc_tlsctx_cache_t *tlsctx_cache, isc_mem_t *mctx, + isc_nm_t *netmgr, dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp); /*%< * Attempt to start an incoming zone transfer of 'zone' diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index d82d6eb0dad..52117404060 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -2068,6 +2069,18 @@ dns_zonemgr_unreachabledel(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote, *\li 'local' to be a valid sockaddr. */ +void +dns_zonemgr_set_tlsctx_cache(dns_zonemgr_t *zmgr, + isc_tlsctx_cache_t *tlsctx_cache); +/*%< + * Set the TLS client context cache used for zone transfers via + * encrypted transports (e.g. XoT). + * + * Requires: + *\li 'zmgr' is a valid zone manager. + *\li 'tlsctx_cache' is a valid TLS context cache. + */ + void dns_zone_forcereload(dns_zone_t *zone); /*%< diff --git a/lib/dns/transport.c b/lib/dns/transport.c index 9f7e2bdc79f..36df5526340 100644 --- a/lib/dns/transport.c +++ b/lib/dns/transport.c @@ -44,6 +44,7 @@ struct dns_transport { isc_mem_t *mctx; dns_transport_type_t type; struct { + char *tlsname; char *certfile; char *keyfile; char *cafile; @@ -269,6 +270,22 @@ dns_transport_set_ciphers(dns_transport_t *transport, const char *ciphers) { } } +void +dns_transport_set_tlsname(dns_transport_t *transport, const char *tlsname) { + REQUIRE(VALID_TRANSPORT(transport)); + REQUIRE(transport->type == DNS_TRANSPORT_TLS || + transport->type == DNS_TRANSPORT_HTTP); + + if (transport->tls.tlsname != NULL) { + isc_mem_free(transport->mctx, transport->tls.tlsname); + } + + if (tlsname != NULL) { + transport->tls.tlsname = isc_mem_strdup(transport->mctx, + tlsname); + } +} + char * dns_transport_get_ciphers(dns_transport_t *transport) { REQUIRE(VALID_TRANSPORT(transport)); @@ -276,6 +293,13 @@ dns_transport_get_ciphers(dns_transport_t *transport) { return (transport->tls.ciphers); } +char * +dns_transport_get_tlsname(dns_transport_t *transport) { + REQUIRE(VALID_TRANSPORT(transport)); + + return (transport->tls.tlsname); +} + void dns_transport_set_prefer_server_ciphers(dns_transport_t *transport, const bool prefer) { @@ -330,6 +354,10 @@ transport_destroy(dns_transport_t *transport) { isc_mem_free(transport->mctx, transport->tls.ciphers); } + if (transport->tls.tlsname != NULL) { + isc_mem_free(transport->mctx, transport->tls.tlsname); + } + isc_mem_putanddetach(&transport->mctx, transport, sizeof(*transport)); } diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index bdda30cbe2e..d48cebbed11 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -163,7 +163,6 @@ struct dns_xfrin_ctx { unsigned int sincetsig; /*%< recvd since the last TSIG */ dns_transport_t *transport; - isc_tlsctx_t *tlsctx; dns_xfrindone_t done; @@ -183,6 +182,8 @@ struct dns_xfrin_ctx { dns_rdata_t firstsoa; unsigned char *firstsoa_data; + + isc_tlsctx_cache_t *tlsctx_cache; }; #define XFRIN_MAGIC ISC_MAGIC('X', 'f', 'r', 'I') @@ -199,7 +200,7 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_nm_t *netmgr, dns_rdatatype_t reqtype, const isc_sockaddr_t *primaryaddr, const isc_sockaddr_t *sourceaddr, isc_dscp_t dscp, dns_tsigkey_t *tsigkey, dns_transport_t *transport, - dns_xfrin_ctx_t **xfrp); + isc_tlsctx_cache_t *tlsctx_cache, dns_xfrin_ctx_t **xfrp); static isc_result_t axfr_init(dns_xfrin_ctx_t *xfr); @@ -693,7 +694,8 @@ dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype, const isc_sockaddr_t *primaryaddr, const isc_sockaddr_t *sourceaddr, isc_dscp_t dscp, dns_tsigkey_t *tsigkey, dns_transport_t *transport, - isc_mem_t *mctx, isc_nm_t *netmgr, dns_xfrindone_t done, + isc_tlsctx_cache_t *tlsctx_cache, isc_mem_t *mctx, + isc_nm_t *netmgr, dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp) { dns_name_t *zonename = dns_zone_getorigin(zone); dns_xfrin_ctx_t *xfr = NULL; @@ -712,7 +714,7 @@ dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype, xfrin_create(mctx, zone, db, netmgr, zonename, dns_zone_getclass(zone), xfrtype, primaryaddr, sourceaddr, dscp, tsigkey, transport, - &xfr); + tlsctx_cache, &xfr); if (db != NULL) { xfr->zone_had_db = true; @@ -860,7 +862,7 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_nm_t *netmgr, dns_rdatatype_t reqtype, const isc_sockaddr_t *primaryaddr, const isc_sockaddr_t *sourceaddr, isc_dscp_t dscp, dns_tsigkey_t *tsigkey, dns_transport_t *transport, - dns_xfrin_ctx_t **xfrp) { + isc_tlsctx_cache_t *tlsctx_cache, dns_xfrin_ctx_t **xfrp) { dns_xfrin_ctx_t *xfr = NULL; xfr = isc_mem_get(mctx, sizeof(*xfr)); @@ -918,6 +920,8 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_nm_t *netmgr, isc_buffer_init(&xfr->qbuffer, &xfr->qbuffer_data[2], sizeof(xfr->qbuffer_data) - 2); + isc_tlsctx_cache_attach(tlsctx_cache, &xfr->tlsctx_cache); + xfr->magic = XFRIN_MAGIC; *xfrp = xfr; @@ -928,6 +932,7 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { isc_result_t result; dns_xfrin_ctx_t *connect_xfr = NULL; dns_transport_type_t transport_type = DNS_TRANSPORT_TCP; + isc_tlsctx_t *tlsctx = NULL, *found = NULL; (void)isc_refcount_increment0(&xfr->connects); dns_xfrin_attach(xfr, &connect_xfr); @@ -948,32 +953,79 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { break; case DNS_TRANSPORT_TLS: { uint32_t tls_versions; - const char *ciphers; + const char *ciphers = NULL; bool prefer_server_ciphers; - CHECK(isc_tlsctx_createclient(&xfr->tlsctx)); - if (xfr->transport != NULL) { + const uint16_t family = isc_sockaddr_pf(&xfr->primaryaddr) == + PF_INET6 + ? AF_INET6 + : AF_INET; + const char *tlsname = NULL; + + INSIST(xfr->transport != NULL); + tlsname = dns_transport_get_tlsname(xfr->transport); + INSIST(tlsname != NULL && *tlsname != '\0'); + + /* + * Let's try to re-use the already created context. This way + * we have a chance to resume the TLS session, bypassing the + * 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); + if (result != ISC_R_SUCCESS) { + /* + * So, no context exists. Let's create one using the + * parameters from the configuration file and try to + * store it for further reuse. + */ + CHECK(isc_tlsctx_createclient(&tlsctx)); tls_versions = dns_transport_get_tls_versions(xfr->transport); if (tls_versions != 0) { - isc_tlsctx_set_protocols(xfr->tlsctx, - tls_versions); + isc_tlsctx_set_protocols(tlsctx, tls_versions); } ciphers = dns_transport_get_ciphers(xfr->transport); if (ciphers != NULL) { - isc_tlsctx_set_cipherlist(xfr->tlsctx, ciphers); + isc_tlsctx_set_cipherlist(tlsctx, ciphers); } if (dns_transport_get_prefer_server_ciphers( xfr->transport, &prefer_server_ciphers)) { isc_tlsctx_prefer_server_ciphers( - xfr->tlsctx, prefer_server_ciphers); + tlsctx, prefer_server_ciphers); + } + isc_tlsctx_enable_dot_client_alpn(tlsctx); + + result = isc_tlsctx_cache_add( + xfr->tlsctx_cache, tlsname, + isc_tlsctx_cache_tls, family, tlsctx, &found); + if (result == ISC_R_EXISTS) { + /* + * It seems the entry has just been created + * from within another thread while we were + * initialising ours. Although this is + * unlikely, it could happen after + * startup/re-initialisation. In such a case, + * discard the new context and use the already + * established one from now on. + * + * Such situation will not occur after the + * initial 'warm-up', so it is not critical + * performance-wise. + */ + INSIST(found != NULL); + isc_tlsctx_free(&tlsctx); + tlsctx = found; + } else { + INSIST(result == ISC_R_SUCCESS); } } - isc_tlsctx_enable_dot_client_alpn(xfr->tlsctx); isc_nm_tlsdnsconnect(xfr->netmgr, &xfr->sourceaddr, &xfr->primaryaddr, xfrin_connect_done, - connect_xfr, 30000, 0, xfr->tlsctx); + connect_xfr, 30000, 0, tlsctx); } break; default: INSIST(0); @@ -983,8 +1035,13 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { return (ISC_R_SUCCESS); failure: - if (xfr->tlsctx != NULL) { - isc_tlsctx_free(&xfr->tlsctx); + /* + * The 'found' context is being managed by the TLS context cache. + * Thus, we should keep it as it is, as it will get destroyed + * alongside the cache. + */ + if (tlsctx != NULL && found != tlsctx) { + isc_tlsctx_free(&tlsctx); } isc_refcount_decrement0(&xfr->connects); dns_xfrin_detach(&connect_xfr); @@ -1032,10 +1089,6 @@ xfrin_connect_done(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { isc_refcount_decrement0(&xfr->connects); - if (xfr->tlsctx != NULL) { - isc_tlsctx_free(&xfr->tlsctx); - } - if (atomic_load(&xfr->shuttingdown)) { result = ISC_R_SHUTTINGDOWN; } @@ -1670,6 +1723,10 @@ xfrin_destroy(dns_xfrin_ctx_t *xfr) { isc_mem_free(xfr->mctx, xfr->firstsoa_data); } + if (xfr->tlsctx_cache != NULL) { + isc_tlsctx_cache_detach(&xfr->tlsctx_cache); + } + isc_mem_putanddetach(&xfr->mctx, xfr, sizeof(*xfr)); } diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 08112ac2b50..2e54038b3cf 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -629,6 +630,8 @@ struct dns_zonemgr { struct dns_unreachable unreachable[UNREACH_CACHE_SIZE]; dns_keymgmt_t *keymgmt; + + isc_tlsctx_cache_t *tlsctx_cache; }; /*% @@ -18133,7 +18136,8 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) { } CHECK(dns_xfrin_create(zone, xfrtype, &primaryaddr, &sourceaddr, dscp, - zone->tsigkey, zone->transport, zone->mctx, + zone->tsigkey, zone->transport, + zone->zmgr->tlsctx_cache, zone->mctx, zone->zmgr->netmgr, zone_xfrdone, &zone->xfr)); LOCK_ZONE(zone); if (xfrtype == dns_rdatatype_axfr) { @@ -18831,6 +18835,8 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, isc_mutex_init(&zmgr->iolock); + zmgr->tlsctx_cache = NULL; + zmgr->magic = ZONEMGR_MAGIC; *zmgrp = zmgr; @@ -19188,6 +19194,9 @@ zonemgr_free(dns_zonemgr_t *zmgr) { zonemgr_keymgmt_destroy(zmgr); mctx = zmgr->mctx; + if (zmgr->tlsctx_cache != NULL) { + isc_tlsctx_cache_detach(&zmgr->tlsctx_cache); + } isc_mem_put(zmgr->mctx, zmgr, sizeof(*zmgr)); isc_mem_detach(&mctx); } @@ -23627,3 +23636,20 @@ zone_nsecttl(dns_zone_t *zone) { return (ISC_MIN(zone->minimum, zone->soattl)); } + +void +dns_zonemgr_set_tlsctx_cache(dns_zonemgr_t *zmgr, + isc_tlsctx_cache_t *tlsctx_cache) { + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + REQUIRE(tlsctx_cache != NULL); + + RWLOCK(&zmgr->rwlock, isc_rwlocktype_write); + + if (zmgr->tlsctx_cache != NULL) { + isc_tlsctx_cache_detach(&zmgr->tlsctx_cache); + } + + isc_tlsctx_cache_attach(tlsctx_cache, &zmgr->tlsctx_cache); + + RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write); +}