diff --git a/CHANGES b/CHANGES index df26bf014c..053fec41c9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +5784. [func] Implement TLS-contexts reuse. Reusing the + previously created TLS context objects can reduce + initialisation time for some configurations and enables + TLS session resumption for incoming zone transfers over + TLS (XoT). [GL #3067] + 5783. [func] named is now able to log TLS pre-master secrets for debugging purposes. This requires setting the SSLKEYLOGFILE environment variable appropriately. diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index 089145c29c..869f5f764e 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,9 @@ struct named_server { dns_dtenv_t *dtenv; /*%< Dnstap environment */ 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 d4641516fc..8e27090da8 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -403,19 +403,21 @@ named_server_reload(isc_task_t *task, isc_event_t *event); #ifdef HAVE_LIBNGHTTP2 static isc_result_t -listenelt_http(const cfg_obj_t *http, bool tls, - const ns_listen_tls_params_t *tls_params, in_port_t port, +listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls, + const ns_listen_tls_params_t *tls_params, + isc_tlsctx_cache_t *tlsctx_cache, in_port_t port, isc_mem_t *mctx, ns_listenelt_t **target); #endif static isc_result_t listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, - ns_listenelt_t **target); + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target); static isc_result_t listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenlist_t **target); static isc_result_t @@ -8422,6 +8424,22 @@ load_configuration(const char *filename, named_server_t *server, */ CHECK(bind9_check_namedconf(config, false, named_g_lctx, named_g_mctx)); + /* Let's recreate the TLS context cache */ + if (server->tlsctx_server_cache != NULL) { + isc_tlsctx_cache_detach(&server->tlsctx_server_cache); + } + + 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. */ @@ -8874,13 +8892,15 @@ load_configuration(const char *filename, named_server_t *server, if (clistenon != NULL) { CHECK(listenlist_fromconfig( clistenon, config, named_g_aclconfctx, - named_g_mctx, AF_INET, &listenon)); + named_g_mctx, AF_INET, + server->tlsctx_server_cache, &listenon)); } else { /* * Not specified, use default. */ CHECK(ns_listenlist_default(named_g_mctx, listen_port, - -1, true, &listenon)); + -1, true, AF_INET, + &listenon)); } if (listenon != NULL) { ns_interfacemgr_setlistenon4(server->interfacemgr, @@ -8901,13 +8921,15 @@ load_configuration(const char *filename, named_server_t *server, if (clistenon != NULL) { CHECK(listenlist_fromconfig( clistenon, config, named_g_aclconfctx, - named_g_mctx, AF_INET6, &listenon)); + named_g_mctx, AF_INET6, + server->tlsctx_server_cache, &listenon)); } else { /* * Not specified, use default. */ CHECK(ns_listenlist_default(named_g_mctx, listen_port, - -1, true, &listenon)); + -1, true, AF_INET6, + &listenon)); } if (listenon != NULL) { ns_interfacemgr_setlistenon6(server->interfacemgr, @@ -10167,6 +10189,10 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) { server->dtenv = NULL; server->magic = NAMED_SERVER_MAGIC; + + server->tlsctx_server_cache = NULL; + server->tlsctx_client_cache = NULL; + *serverp = server; } @@ -10221,6 +10247,14 @@ named_server_destroy(named_server_t **serverp) { INSIST(ISC_LIST_EMPTY(server->viewlist)); INSIST(ISC_LIST_EMPTY(server->cachelist)); + if (server->tlsctx_server_cache != NULL) { + 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; @@ -10860,6 +10894,7 @@ named_server_togglequerylog(named_server_t *server, isc_lex_t *lex) { static isc_result_t listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenlist_t **target) { isc_result_t result; const cfg_listelt_t *element; @@ -10878,7 +10913,7 @@ listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, ns_listenelt_t *delt = NULL; const cfg_obj_t *listener = cfg_listelt_value(element); result = listenelt_fromconfig(listener, config, actx, mctx, - family, &delt); + family, tlsctx_cache, &delt); if (result != ISC_R_SUCCESS) { goto cleanup; } @@ -10925,6 +10960,7 @@ find_maplist(const cfg_obj_t *config, const char *listname, const char *name) { static isc_result_t listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target) { isc_result_t result; const cfg_obj_t *ltup = NULL; @@ -10942,6 +10978,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, ns_listenelt_t *delt = NULL; uint32_t tls_protos = 0; ns_listen_tls_params_t tls_params = { 0 }; + const char *tlsname = NULL; REQUIRE(target != NULL && *target == NULL); @@ -10950,7 +10987,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, tlsobj = cfg_tuple_get(ltup, "tls"); if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) { - const char *tlsname = cfg_obj_asstring(tlsobj); + tlsname = cfg_obj_asstring(tlsobj); if (strcasecmp(tlsname, "none") == 0) { no_tls = true; @@ -11033,6 +11070,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, } tls_params = (ns_listen_tls_params_t){ + .name = tlsname, .key = key, .cert = cert, .protocols = tls_protos, @@ -11126,14 +11164,15 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, #ifdef HAVE_LIBNGHTTP2 if (http) { - CHECK(listenelt_http(http_server, do_tls, &tls_params, port, - mctx, &delt)); + CHECK(listenelt_http(http_server, family, do_tls, &tls_params, + tlsctx_cache, port, mctx, &delt)); } #endif /* HAVE_LIBNGHTTP2 */ if (!http) { - CHECK(ns_listenelt_create(mctx, port, dscp, NULL, do_tls, - &tls_params, &delt)); + CHECK(ns_listenelt_create(mctx, port, dscp, NULL, family, + do_tls, &tls_params, tlsctx_cache, + &delt)); } result = cfg_acl_fromconfig2(cfg_tuple_get(listener, "acl"), config, @@ -11151,8 +11190,9 @@ cleanup: #ifdef HAVE_LIBNGHTTP2 static isc_result_t -listenelt_http(const cfg_obj_t *http, bool tls, - const ns_listen_tls_params_t *tls_params, in_port_t port, +listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls, + const ns_listen_tls_params_t *tls_params, + isc_tlsctx_cache_t *tlsctx_cache, in_port_t port, isc_mem_t *mctx, ns_listenelt_t **target) { isc_result_t result = ISC_R_SUCCESS; ns_listenelt_t *delt = NULL; @@ -11224,9 +11264,9 @@ listenelt_http(const cfg_obj_t *http, bool tls, quota = isc_mem_get(mctx, sizeof(isc_quota_t)); isc_quota_init(quota, max_clients); } - result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, tls, - tls_params, endpoints, len, quota, - max_streams, &delt); + result = ns_listenelt_create_http( + mctx, port, named_g_dscp, NULL, family, tls, tls_params, + tlsctx_cache, endpoints, len, quota, max_streams, &delt); if (result != ISC_R_SUCCESS) { goto error; } diff --git a/bin/named/transportconf.c b/bin/named/transportconf.c index 61c7462f0b..40c21a6cd0 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 91fe382630..69ed013c87 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 d080b089cb..6cbac87f90 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 d82d6eb0da..5211740406 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 9f7e2bdc79..36df552634 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 bdda30cbe2..d48cebbed1 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 08112ac2b5..2e54038b3c 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); +} diff --git a/lib/isc/include/isc/tls.h b/lib/isc/include/isc/tls.h index fcc484df47..ec7382901d 100644 --- a/lib/isc/include/isc/tls.h +++ b/lib/isc/include/isc/tls.h @@ -158,7 +158,6 @@ isc_tlsctx_enable_http2client_alpn(isc_tlsctx_t *ctx); void isc_tlsctx_enable_http2server_alpn(isc_tlsctx_t *ctx); /*%< - * * Enable HTTP/2 Application Layer Protocol Negotation for 'ctx'. * * Requires: @@ -178,9 +177,118 @@ isc_tlsctx_enable_dot_client_alpn(isc_tlsctx_t *ctx); void isc_tlsctx_enable_dot_server_alpn(isc_tlsctx_t *ctx); /*%< - * * Enable DoT Application Layer Protocol Negotation for 'ctx'. * * Requires: *\li 'ctx' is not NULL. */ + +typedef struct isc_tlsctx_cache isc_tlsctx_cache_t; +/*%< + * The TLS context cache is an object which allows retrieving a + * previously created TLS context based on the following tuple: + * + * 1. The name of a TLS entry, as defined in the configuration file; + * 2. A transport type. Currently, only TLS (DoT) and HTTPS (DoH) are + * supported; + * 3. An IP address family (AF_INET or AF_INET6). + * + * There are multiple uses for this object: + * + * First, it allows reuse of client-side contexts during zone transfers. + * That, in turn, allows use of session caches associated with these + * contexts, which enables TLS session resumption, making establishment + * of XoT connections faster and computationally cheaper. + * + * Second, it can be extended to be used as storage for TLS context related + * data, as defined in 'tls' statements in the configuration file (for + * example, CA-bundle intermediate certificate storage, client-side contexts + * with pre-loaded certificates in a case of Mutual TLS, etc). This will + * be used to implement Strict/Mutual TLS. + * + * Third, it avoids creating an excessive number of server-side TLS + * contexts, which might help to reduce the number of contexts + * created during server initialisation and reconfiguration. + */ + +typedef enum { + isc_tlsctx_cache_none = 0, + isc_tlsctx_cache_tls, + isc_tlsctx_cache_https, + isc_tlsctx_cache_count +} isc_tlsctx_cache_transport_t; +/*%< TLS context cache transport type values. */ + +isc_tlsctx_cache_t * +isc_tlsctx_cache_new(isc_mem_t *mctx); +/*%< + * Create a new TLS context cache object. + * + * Requires: + *\li 'mctx' is a valid memory context. + */ + +void +isc_tlsctx_cache_attach(isc_tlsctx_cache_t *source, + isc_tlsctx_cache_t **targetp); +/*%< + * Create a reference to the TLS context cache object. + * + * Requires: + *\li 'source' is a valid TLS context cache object; + *\li 'targetp' is a valid pointer to a pointer which must equal NULL. + */ + +void +isc_tlsctx_cache_detach(isc_tlsctx_cache_t **pcache); +/*%< + * Remove a reference to the TLS context cache object. + * + * Requires: + *\li 'pcache' is a valid pointer to a pointer which must point to a + * valid TLS context cache object. + */ + +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_tlsctx_t **pfound); +/*%< + * + * 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. + * + * 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. + * + * Returns: + *\li #ISC_R_EXISTS - node of the same key already exists; + *\li #ISC_R_SUCCESS - the new entry has been added successfully. + */ + +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); +/*%< + * Look up a TLS context in the TLS context cache. + * + * 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' - 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. + * + * Returns: + *\li #ISC_R_SUCCESS - the context has been found; + *\li #ISC_R_NOTFOUND - the context has not been found. + */ diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index 539fef6fd3..e31180bd02 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -2454,7 +2454,6 @@ isc_nm_listenhttp(isc_nm_t *mgr, isc_sockaddr_t *iface, int backlog, isc_nm_http_endpoints_attach(eps, &sock->h2.listener_endpoints); if (ctx != NULL) { - isc_tlsctx_enable_http2server_alpn(ctx); result = isc_nm_listentls(mgr, iface, httplisten_acceptcb, sock, sizeof(isc_nm_http_session_t), backlog, quota, ctx, &sock->outer); diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c index 0e34b95011..54848102b2 100644 --- a/lib/isc/netmgr/tlsdns.c +++ b/lib/isc/netmgr/tlsdns.c @@ -494,8 +494,6 @@ isc_nm_listentlsdns(isc_nm_t *mgr, isc_sockaddr_t *iface, sock->tid = 0; sock->fd = -1; - isc_tlsctx_enable_dot_server_alpn(sslctx); - #if !HAVE_SO_REUSEPORT_LB fd = isc__nm_tlsdns_lb_socket(iface->type.sa.sa_family); #endif diff --git a/lib/isc/tests/doh_test.c b/lib/isc/tests/doh_test.c index 7a2c06571b..a5694979f8 100644 --- a/lib/isc/tests/doh_test.c +++ b/lib/isc/tests/doh_test.c @@ -332,6 +332,7 @@ nm_setup(void **state) { server_tlsctx = NULL; isc_tlsctx_createserver(NULL, NULL, &server_tlsctx); + isc_tlsctx_enable_http2server_alpn(server_tlsctx); client_tlsctx = NULL; isc_tlsctx_createclient(&client_tlsctx); isc_tlsctx_enable_http2client_alpn(client_tlsctx); diff --git a/lib/isc/tls.c b/lib/isc/tls.c index 7515d833cf..cf781a2302 100644 --- a/lib/isc/tls.c +++ b/lib/isc/tls.c @@ -12,6 +12,7 @@ #include #include #include +#include #if HAVE_LIBNGHTTP2 #include #endif /* HAVE_LIBNGHTTP2 */ @@ -27,10 +28,14 @@ #include #include +#include #include +#include #include #include #include +#include +#include #include #include #include @@ -885,3 +890,200 @@ isc_tlsctx_enable_dot_server_alpn(isc_tlsctx_t *tls) { SSL_CTX_set_alpn_select_cb(tls, dot_alpn_select_proto_cb, NULL); #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L } + +#define TLSCTX_CACHE_MAGIC ISC_MAGIC('T', 'l', 'S', 'c') +#define VALID_TLSCTX_CACHE(t) ISC_MAGIC_VALID(t, TLSCTX_CACHE_MAGIC) + +typedef struct isc_tlsctx_cache_entry { + /* + * We need a TLS context entry for each transport on both IPv4 and + * IPv6 in order to avoid cluttering a context-specific + * session-resumption cache. + */ + isc_tlsctx_t *ctx[isc_tlsctx_cache_count - 1][2]; + /* + * TODO: add a certificate store for an intermediate certificates + * from a CA-bundle file. One is enough for all the contexts defined + * above. We will need that for validation. + * + * X509_STORE *ca_bundle_store; // TODO: define the utilities to + * operate on these ones + */ +} isc_tlsctx_cache_entry_t; + +struct isc_tlsctx_cache { + uint32_t magic; + isc_refcount_t references; + isc_mem_t *mctx; + + isc_rwlock_t rwlock; + isc_ht_t *data; +}; + +isc_tlsctx_cache_t * +isc_tlsctx_cache_new(isc_mem_t *mctx) { + isc_tlsctx_cache_t *nc; + + nc = isc_mem_get(mctx, sizeof(*nc)); + + *nc = (isc_tlsctx_cache_t){ .magic = TLSCTX_CACHE_MAGIC }; + isc_refcount_init(&nc->references, 1); + isc_mem_attach(mctx, &nc->mctx); + + RUNTIME_CHECK(isc_ht_init(&nc->data, mctx, 5) == ISC_R_SUCCESS); + isc_rwlock_init(&nc->rwlock, 0, 0); + + return (nc); +} + +void +isc_tlsctx_cache_attach(isc_tlsctx_cache_t *source, + isc_tlsctx_cache_t **targetp) { + REQUIRE(VALID_TLSCTX_CACHE(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + isc_refcount_increment(&source->references); + + *targetp = source; +} + +static void +tlsctx_cache_entry_destroy(isc_mem_t *mctx, isc_tlsctx_cache_entry_t *entry) { + size_t i, k; + + for (i = 0; i < (isc_tlsctx_cache_count - 1); i++) { + for (k = 0; k < 2; k++) { + if (entry->ctx[i][k] != NULL) { + isc_tlsctx_free(&entry->ctx[i][k]); + } + } + } + isc_mem_put(mctx, entry, sizeof(*entry)); +} + +void +isc_tlsctx_cache_detach(isc_tlsctx_cache_t **pcache) { + isc_tlsctx_cache_t *cache; + isc_ht_iter_t *it = NULL; + isc_result_t result; + REQUIRE(pcache != NULL); + cache = *pcache; + REQUIRE(VALID_TLSCTX_CACHE(cache)); + + if (isc_refcount_decrement(&cache->references) > 1) { + return; + } + + RUNTIME_CHECK(isc_ht_iter_create(cache->data, &it) == ISC_R_SUCCESS); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_delcurrent_next(it)) + { + isc_tlsctx_cache_entry_t *entry = NULL; + isc_ht_iter_current(it, (void **)&entry); + tlsctx_cache_entry_destroy(cache->mctx, entry); + } + isc_ht_iter_destroy(&it); + isc_ht_destroy(&cache->data); + + isc_rwlock_destroy(&cache->rwlock); + cache->magic = 0; + isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache)); + *pcache = NULL; +} + +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_tlsctx_t **pfound) { + 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(name != NULL && *name != '\0'); + REQUIRE(transport > isc_tlsctx_cache_none && + transport < isc_tlsctx_cache_count); + REQUIRE(family == AF_INET || family == AF_INET6); + REQUIRE(ctx != NULL); + + tr_offset = (transport - 1); + ipv6 = (family == AF_INET6); + + RWLOCK(&cache->rwlock, isc_rwlocktype_write); + + name_len = strlen(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) { + /* The entry exists. */ + if (pfound != NULL) { + INSIST(*pfound == NULL); + *pfound = entry->ctx[tr_offset][ipv6]; + } + result = ISC_R_EXISTS; + } else if (result == ISC_R_SUCCESS && + entry->ctx[tr_offset][ipv6] == NULL) { + /* + * The hast table entry exists, but is not filled for this + * particular transport/IP type combination. + */ + entry->ctx[tr_offset][ipv6] = ctx; + result = ISC_R_SUCCESS; + } else { + /* + * The hash table entry does not exist, let's create one. + */ + INSIST(result != ISC_R_SUCCESS); + entry = isc_mem_get(cache->mctx, sizeof(*entry)); + /* Oracle/Red Hat Linux, GCC bug #53119 */ + memset(entry, 0, sizeof(*entry)); + entry->ctx[tr_offset][ipv6] = ctx; + RUNTIME_CHECK(isc_ht_add(cache->data, (const uint8_t *)name, + name_len, + (void *)entry) == ISC_R_SUCCESS); + result = ISC_R_SUCCESS; + } + + RWUNLOCK(&cache->rwlock, isc_rwlocktype_write); + + return (result); +} + +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_result_t result = ISC_R_FAILURE; + size_t tr_offset; + isc_tlsctx_cache_entry_t *entry = NULL; + bool ipv6; + + REQUIRE(VALID_TLSCTX_CACHE(cache)); + REQUIRE(name != NULL && *name != '\0'); + REQUIRE(transport > isc_tlsctx_cache_none && + transport < isc_tlsctx_cache_count); + REQUIRE(family == AF_INET || family == AF_INET6); + REQUIRE(pctx != NULL && *pctx == NULL); + + tr_offset = (transport - 1); + ipv6 = (family == AF_INET6); + + RWLOCK(&cache->rwlock, isc_rwlocktype_read); + + result = isc_ht_find(cache->data, (const uint8_t *)name, strlen(name), + (void **)&entry); + if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) { + *pctx = entry->ctx[tr_offset][ipv6]; + } else if (result == ISC_R_SUCCESS && + entry->ctx[tr_offset][ipv6] == NULL) { + result = ISC_R_NOTFOUND; + } else { + INSIST(result != ISC_R_SUCCESS); + } + + RWUNLOCK(&cache->rwlock, isc_rwlocktype_read); + + return (result); +} diff --git a/lib/ns/include/ns/listenlist.h b/lib/ns/include/ns/listenlist.h index 0b052b6a70..936061e457 100644 --- a/lib/ns/include/ns/listenlist.h +++ b/lib/ns/include/ns/listenlist.h @@ -40,15 +40,16 @@ typedef struct ns_listenlist ns_listenlist_t; struct ns_listenelt { isc_mem_t *mctx; - in_port_t port; - bool is_http; - isc_dscp_t dscp; /* -1 = not set, 0..63 */ + in_port_t port; + bool is_http; + isc_dscp_t dscp; /* -1 = not set, 0..63 */ dns_acl_t *acl; - isc_tlsctx_t *sslctx; - char **http_endpoints; - size_t http_endpoints_number; - isc_quota_t *http_quota; - uint32_t max_concurrent_streams; + isc_tlsctx_t *sslctx; + isc_tlsctx_cache_t *sslctx_cache; + char **http_endpoints; + size_t http_endpoints_number; + isc_quota_t *http_quota; + uint32_t max_concurrent_streams; ISC_LINK(ns_listenelt_t) link; }; @@ -59,6 +60,7 @@ struct ns_listenlist { }; typedef struct ns_listen_tls_params { + const char *name; const char *key; const char *cert; uint32_t protocols; @@ -76,24 +78,25 @@ typedef struct ns_listen_tls_params { isc_result_t ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, - dns_acl_t *acl, bool tls, + dns_acl_t *acl, const uint16_t family, bool tls, const ns_listen_tls_params_t *tls_params, - ns_listenelt_t **target); + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target); /*%< * Create a listen-on list element. * * Requires: * \li 'targetp' is a valid pointer to a pointer containing 'NULL'; * \li 'tls_params' is a valid, non-'NULL' pointer if 'tls' equals 'true'. + * \li 'tlsctx_cache' is a valid, non-'NULL' pointer if 'tls' equals 'true'. */ isc_result_t ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp, - dns_acl_t *acl, bool tls, + dns_acl_t *acl, const uint16_t family, bool tls, const ns_listen_tls_params_t *tls_params, - char **endpoints, size_t nendpoints, - isc_quota_t *quota, const uint32_t max_streams, - ns_listenelt_t **target); + isc_tlsctx_cache_t *tlsctx_cache, char **endpoints, + size_t nendpoints, isc_quota_t *quota, + const uint32_t max_streams, ns_listenelt_t **target); /*%< * Create a listen-on list element for HTTP(S). */ @@ -124,7 +127,8 @@ ns_listenlist_detach(ns_listenlist_t **listp); isc_result_t ns_listenlist_default(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, - bool enabled, ns_listenlist_t **target); + bool enabled, const uint16_t family, + ns_listenlist_t **target); /*%< * Create a listen-on list with default contents, matching * all addresses with port 'port' (if 'enabled' is true), diff --git a/lib/ns/listenlist.c b/lib/ns/listenlist.c index 6aa67dbd2b..602d9c3807 100644 --- a/lib/ns/listenlist.c +++ b/lib/ns/listenlist.c @@ -24,50 +24,92 @@ static void destroy(ns_listenlist_t *list); -isc_result_t -ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, - dns_acl_t *acl, bool tls, - const ns_listen_tls_params_t *tls_params, - ns_listenelt_t **target) { +static isc_result_t +listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, + dns_acl_t *acl, const uint16_t family, const bool is_http, + bool tls, const ns_listen_tls_params_t *tls_params, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target) { ns_listenelt_t *elt = NULL; isc_result_t result = ISC_R_SUCCESS; isc_tlsctx_t *sslctx = NULL; REQUIRE(target != NULL && *target == NULL); - REQUIRE(!tls || tls_params != NULL); + REQUIRE(!tls || (tls_params != NULL && tlsctx_cache != NULL)); if (tls) { - result = isc_tlsctx_createserver(tls_params->key, - tls_params->cert, &sslctx); + const isc_tlsctx_cache_transport_t transport = + is_http ? isc_tlsctx_cache_https : isc_tlsctx_cache_tls; + + /* + * Let's try to reuse the existing context from the cache in + * order to avoid excessive TLS contexts creation. + */ + result = isc_tlsctx_cache_find(tlsctx_cache, tls_params->name, + transport, family, &sslctx); if (result != ISC_R_SUCCESS) { - return (result); - } + /* + * The lookup failed, let's try to create a new context + * and store it within the cache. + */ + INSIST(tls_params->name != NULL && + *tls_params->name != '\0'); - if (tls_params->protocols != 0) { - isc_tlsctx_set_protocols(sslctx, tls_params->protocols); - } - - if (tls_params->dhparam_file != NULL) { - if (!isc_tlsctx_load_dhparams(sslctx, - tls_params->dhparam_file)) - { - isc_tlsctx_free(&sslctx); - return (ISC_R_FAILURE); + result = isc_tlsctx_createserver( + tls_params->key, tls_params->cert, &sslctx); + if (result != ISC_R_SUCCESS) { + return (result); } - } - if (tls_params->ciphers != NULL) { - isc_tlsctx_set_cipherlist(sslctx, tls_params->ciphers); - } + if (tls_params->protocols != 0) { + isc_tlsctx_set_protocols(sslctx, + tls_params->protocols); + } - if (tls_params->prefer_server_ciphers_set) { - isc_tlsctx_prefer_server_ciphers( - sslctx, tls_params->prefer_server_ciphers); - } + if (tls_params->dhparam_file != NULL) { + if (!isc_tlsctx_load_dhparams( + sslctx, tls_params->dhparam_file)) { + isc_tlsctx_free(&sslctx); + return (ISC_R_FAILURE); + } + } - if (tls_params->session_tickets_set) { - isc_tlsctx_session_tickets(sslctx, - tls_params->session_tickets); + if (tls_params->ciphers != NULL) { + isc_tlsctx_set_cipherlist(sslctx, + tls_params->ciphers); + } + + if (tls_params->prefer_server_ciphers_set) { + isc_tlsctx_prefer_server_ciphers( + sslctx, + tls_params->prefer_server_ciphers); + } + + if (tls_params->session_tickets_set) { + isc_tlsctx_session_tickets( + sslctx, tls_params->session_tickets); + } + +#ifdef HAVE_LIBNGHTTP2 + if (is_http) { + isc_tlsctx_enable_http2server_alpn(sslctx); + } +#endif /* HAVE_LIBNGHTTP2 */ + + if (!is_http) { + isc_tlsctx_enable_dot_server_alpn(sslctx); + } + + /* + * The storing in the cache should not fail because the + * (re)initialisation happens from within a single + * thread. + */ + RUNTIME_CHECK(isc_tlsctx_cache_add( + tlsctx_cache, tls_params->name, + transport, family, sslctx, + NULL) == ISC_R_SUCCESS); + } else { + INSIST(sslctx != NULL); } } @@ -79,6 +121,10 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, elt->dscp = dscp; elt->acl = acl; elt->sslctx = sslctx; + elt->sslctx_cache = NULL; + if (sslctx != NULL && tlsctx_cache != NULL) { + isc_tlsctx_cache_attach(tlsctx_cache, &elt->sslctx_cache); + } elt->http_endpoints = NULL; elt->http_endpoints_number = 0; elt->http_quota = NULL; @@ -87,21 +133,30 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, return (ISC_R_SUCCESS); } +isc_result_t +ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, + dns_acl_t *acl, const uint16_t family, bool tls, + const ns_listen_tls_params_t *tls_params, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target) { + return listenelt_create(mctx, port, dscp, acl, family, false, tls, + tls_params, tlsctx_cache, target); +} + isc_result_t ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp, - dns_acl_t *acl, bool tls, + dns_acl_t *acl, const uint16_t family, bool tls, const ns_listen_tls_params_t *tls_params, - char **endpoints, size_t nendpoints, - isc_quota_t *quota, const uint32_t max_streams, - ns_listenelt_t **target) { + isc_tlsctx_cache_t *tlsctx_cache, char **endpoints, + size_t nendpoints, isc_quota_t *quota, + const uint32_t max_streams, ns_listenelt_t **target) { isc_result_t result; REQUIRE(target != NULL && *target == NULL); REQUIRE(endpoints != NULL && *endpoints != NULL); REQUIRE(nendpoints > 0); - result = ns_listenelt_create(mctx, http_port, dscp, acl, tls, - tls_params, target); + result = listenelt_create(mctx, http_port, dscp, acl, family, true, tls, + tls_params, tlsctx_cache, target); if (result == ISC_R_SUCCESS) { (*target)->is_http = true; (*target)->http_endpoints = endpoints; @@ -123,8 +178,11 @@ ns_listenelt_destroy(ns_listenelt_t *elt) { if (elt->acl != NULL) { dns_acl_detach(&elt->acl); } - if (elt->sslctx != NULL) { - isc_tlsctx_free(&elt->sslctx); + + elt->sslctx = NULL; /* this one is going to be destroyed alongside the + sslctx_cache */ + if (elt->sslctx_cache != NULL) { + isc_tlsctx_cache_detach(&elt->sslctx_cache); } if (elt->http_endpoints != NULL) { size_t i; @@ -179,7 +237,8 @@ ns_listenlist_detach(ns_listenlist_t **listp) { isc_result_t ns_listenlist_default(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, - bool enabled, ns_listenlist_t **target) { + bool enabled, const uint16_t family, + ns_listenlist_t **target) { isc_result_t result; dns_acl_t *acl = NULL; ns_listenelt_t *elt = NULL; @@ -195,7 +254,8 @@ ns_listenlist_default(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, goto cleanup; } - result = ns_listenelt_create(mctx, port, dscp, acl, false, NULL, &elt); + result = ns_listenelt_create(mctx, port, dscp, acl, family, false, NULL, + NULL, &elt); if (result != ISC_R_SUCCESS) { goto cleanup_acl; } diff --git a/lib/ns/tests/listenlist_test.c b/lib/ns/tests/listenlist_test.c index 94012e9fdc..a197590f0a 100644 --- a/lib/ns/tests/listenlist_test.c +++ b/lib/ns/tests/listenlist_test.c @@ -71,7 +71,7 @@ ns_listenlist_default_test(void **state) { UNUSED(state); - result = ns_listenlist_default(mctx, port, -1, false, &list); + result = ns_listenlist_default(mctx, port, -1, false, AF_INET, &list); assert_int_equal(result, ISC_R_SUCCESS); assert_non_null(list); @@ -98,7 +98,7 @@ ns_listenlist_default_test(void **state) { ns_listenlist_detach(&list); - result = ns_listenlist_default(mctx, port, -1, true, &list); + result = ns_listenlist_default(mctx, port, -1, true, AF_INET, &list); assert_int_equal(result, ISC_R_SUCCESS); assert_false(ISC_LIST_EMPTY(list->elts)); diff --git a/lib/ns/tests/nstest.c b/lib/ns/tests/nstest.c index 3c458ca1d6..5202b2e9b3 100644 --- a/lib/ns/tests/nstest.c +++ b/lib/ns/tests/nstest.c @@ -11,7 +11,6 @@ /*! \file */ -#include "nstest.h" #include #include #include @@ -50,6 +49,8 @@ #include #include +#include "nstest.h" + isc_mem_t *mctx = NULL; isc_log_t *lctx = NULL; isc_nm_t *netmgr = NULL; @@ -236,7 +237,7 @@ create_managers(void) { dispatchmgr, maintask, NULL, ncpus, false, &interfacemgr)); - CHECK(ns_listenlist_default(mctx, port, -1, true, &listenon)); + CHECK(ns_listenlist_default(mctx, port, -1, true, AF_INET, &listenon)); ns_interfacemgr_setlistenon4(interfacemgr, listenon); ns_listenlist_detach(&listenon);