From: Aram Sargsyan Date: Mon, 19 Sep 2022 10:24:20 +0000 (+0000) Subject: Convert xfrin.c:get_create_tlsctx() into a library function X-Git-Tag: v9.19.6~50^2~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=881747218ba0ad411f6f1bf361c2c09c805d4aa8;p=thirdparty%2Fbind9.git Convert xfrin.c:get_create_tlsctx() into a library function In order to make xfrin.c:get_create_tlsctx() reusable, move the function into transport.c, and make changes into its prototype to not use the 'dns_xfrin_ctx_t' type, thus making it more universal. This change prepares ground for adding transport support into the dispatch manager. Also, move the typedefs for 'dns_transport_t' and 'dns_transport_list_t' from transport.h into types.h. --- diff --git a/lib/dns/include/dns/transport.h b/lib/dns/include/dns/transport.h index e74ccd7f970..d3df43d6b8e 100644 --- a/lib/dns/include/dns/transport.h +++ b/lib/dns/include/dns/transport.h @@ -13,7 +13,9 @@ #pragma once -#include +#include + +#include typedef enum { DNS_TRANSPORT_NONE = 0, @@ -29,9 +31,6 @@ typedef enum { DNS_HTTP_POST = 1, } dns_http_mode_t; -typedef struct dns_transport dns_transport_t; -typedef struct dns_transport_list dns_transport_list_t; - dns_transport_t * dns_transport_new(const dns_name_t *name, dns_transport_type_t type, dns_transport_list_t *list); @@ -72,6 +71,24 @@ dns_transport_get_prefer_server_ciphers(const dns_transport_t *transport, * 'preferp' pointer. */ +isc_result_t +dns_transport_get_tlsctx(dns_transport_t *transport, const isc_sockaddr_t *peer, + isc_tlsctx_cache_t *tlsctx_cache, isc_mem_t *mctx, + isc_tlsctx_t **pctx, + isc_tlsctx_client_session_cache_t **psess_cache); +/*%< + * Get the transport's TLS Context and the TLS Client Session Cache associated + * with it. + * + * Requires: + *\li 'transport' is a valid, 'DNS_TRANSPORT_TLS' type transport. + *\li 'peer' is not NULL. + *\li 'tlsctx_cache' is not NULL. + *\li 'mctx' is not NULL. + *\li 'pctx' is not NULL and '*pctx' is NULL. + *\li 'psess_cache' is not NULL and '*psess_cache' is NULL. + */ + void dns_transport_set_certfile(dns_transport_t *transport, const char *certfile); void diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 4d69639f132..b4207adcf2d 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -140,6 +140,8 @@ typedef struct dns_ssutable dns_ssutable_t; typedef struct dns_stats dns_stats_t; typedef uint32_t dns_rdatastatstype_t; typedef struct dns_tkeyctx dns_tkeyctx_t; +typedef struct dns_transport dns_transport_t; +typedef struct dns_transport_list dns_transport_list_t; typedef uint16_t dns_trust_t; typedef struct dns_tsec dns_tsec_t; typedef struct dns_tsig_keyring dns_tsig_keyring_t; diff --git a/lib/dns/transport.c b/lib/dns/transport.c index ae1ab7415b1..8e0a8f66611 100644 --- a/lib/dns/transport.c +++ b/lib/dns/transport.c @@ -15,9 +15,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -332,6 +334,218 @@ dns_transport_get_prefer_server_ciphers(const dns_transport_t *transport, return false; } +isc_result_t +dns_transport_get_tlsctx(dns_transport_t *transport, const isc_sockaddr_t *peer, + isc_tlsctx_cache_t *tlsctx_cache, isc_mem_t *mctx, + isc_tlsctx_t **pctx, + isc_tlsctx_client_session_cache_t **psess_cache) { + isc_result_t result = ISC_R_FAILURE; + 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; + isc_tlsctx_client_session_cache_t *found_sess_cache = NULL; + uint32_t tls_versions; + const char *ciphers = NULL; + bool prefer_server_ciphers; + uint16_t family; + const char *tlsname = NULL; + + REQUIRE(VALID_TRANSPORT(transport)); + REQUIRE(transport->type == DNS_TRANSPORT_TLS); + REQUIRE(peer != NULL); + REQUIRE(tlsctx_cache != NULL); + REQUIRE(mctx != NULL); + REQUIRE(pctx != NULL && *pctx == NULL); + REQUIRE(psess_cache != NULL && *psess_cache == NULL); + + family = (isc_sockaddr_pf(peer) == PF_INET6) ? AF_INET6 : AF_INET; + + tlsname = dns_transport_get_tlsname(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 faster. + */ + result = isc_tlsctx_cache_find(tlsctx_cache, tlsname, + isc_tlsctx_cache_tls, family, &found, + &found_store, &found_sess_cache); + if (result != ISC_R_SUCCESS) { + const char *hostname = + dns_transport_get_remote_hostname(transport); + const char *ca_file = dns_transport_get_cafile(transport); + const char *cert_file = dns_transport_get_certfile(transport); + const char *key_file = dns_transport_get_keyfile(transport); + char peer_addr_str[INET6_ADDRSTRLEN] = { 0 }; + isc_netaddr_t peer_netaddr = { 0 }; + bool hostname_ignore_subject; + + /* + * So, no context exists. Let's create one using the + * parameters from the configuration file and try to + * store it for further reuse. + */ + result = isc_tlsctx_createclient(&tlsctx); + if (result != ISC_R_SUCCESS) { + goto failure; + } + tls_versions = dns_transport_get_tls_versions(transport); + if (tls_versions != 0) { + isc_tlsctx_set_protocols(tlsctx, tls_versions); + } + ciphers = dns_transport_get_ciphers(transport); + if (ciphers != NULL) { + isc_tlsctx_set_cipherlist(tlsctx, ciphers); + } + + if (dns_transport_get_prefer_server_ciphers( + transport, &prefer_server_ciphers)) { + isc_tlsctx_prefer_server_ciphers(tlsctx, + prefer_server_ciphers); + } + + if (hostname != NULL || ca_file != NULL) { + /* + * The situation when 'found_store != NULL' while + * 'found == NULL' may occur as there is a one-to-many + * relation between cert stores and per-transport TLS + * contexts. That is, there could be one store + * shared between multiple contexts. + */ + if (found_store == NULL) { + /* + * 'ca_file' can equal 'NULL' here, in + * which case the store with system-wide + * CA certificates will be created. + */ + result = isc_tls_cert_store_create(ca_file, + &store); + + if (result != ISC_R_SUCCESS) { + goto failure; + } + } else { + store = found_store; + } + + INSIST(store != NULL); + if (hostname == NULL) { + /* + * If CA bundle file is specified, but + * hostname is not, then use the peer + * IP address for validation, just like + * dig does. + */ + INSIST(ca_file != NULL); + isc_netaddr_fromsockaddr(&peer_netaddr, peer); + isc_netaddr_format(&peer_netaddr, peer_addr_str, + sizeof(peer_addr_str)); + hostname = peer_addr_str; + } + + /* + * According to RFC 8310, Subject field MUST NOT + * be inspected when verifying hostname for DoT. + * Only SubjectAltName must be checked. + */ + hostname_ignore_subject = true; + result = isc_tlsctx_enable_peer_verification( + tlsctx, false, store, hostname, + hostname_ignore_subject); + if (result != ISC_R_SUCCESS) { + goto failure; + } + + /* + * Let's load client certificate and enable + * Mutual TLS. We do that only in the case when + * Strict TLS is enabled, because Mutual TLS is + * an extension of it. + */ + if (cert_file != NULL) { + INSIST(key_file != NULL); + + result = isc_tlsctx_load_certificate( + tlsctx, key_file, cert_file); + if (result != ISC_R_SUCCESS) { + goto failure; + } + } + } + + isc_tlsctx_enable_dot_client_alpn(tlsctx); + + sess_cache = isc_tlsctx_client_session_cache_new( + mctx, tlsctx, + ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE); + + found_store = NULL; + result = isc_tlsctx_cache_add(tlsctx_cache, tlsname, + isc_tlsctx_cache_tls, family, + tlsctx, store, sess_cache, &found, + &found_store, &found_sess_cache); + 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 associated data 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); + isc_tls_cert_store_free(&store); + isc_tlsctx_client_session_cache_detach(&sess_cache); + /* Let's return the data from the cache. */ + *psess_cache = found_sess_cache; + *pctx = found; + } else { + /* + * Adding the fresh values into the cache has been + * successful, let's return them + */ + INSIST(result == ISC_R_SUCCESS); + *psess_cache = sess_cache; + *pctx = tlsctx; + } + } else { + /* + * The cache lookup has been successful, let's return the + * results. + */ + INSIST(result == ISC_R_SUCCESS); + *psess_cache = found_sess_cache; + *pctx = found; + } + + return (ISC_R_SUCCESS); + +failure: + if (tlsctx != NULL) { + isc_tlsctx_free(&tlsctx); + } + + /* + * The 'found_store' is being managed by the TLS context + * cache. Thus, we should keep it as it is, as it will get + * destroyed alongside the cache. As there is one store per + * multiple TLS contexts, we need to handle store deletion in a + * special way. + */ + if (store != NULL && store != found_store) { + isc_tls_cert_store_free(&store); + } + + return (result); +} + static void transport_destroy(dns_transport_t *transport) { isc_refcount_destroy(&transport->references); diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index a360263265d..e27d0d69092 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -927,216 +927,6 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_nm_t *netmgr, *xfrp = xfr; } -static isc_result_t -get_create_tlsctx(const dns_xfrin_ctx_t *xfr, isc_tlsctx_t **pctx, - isc_tlsctx_client_session_cache_t **psess_cache) { - isc_result_t result = ISC_R_FAILURE; - 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; - uint32_t tls_versions; - const char *ciphers = NULL; - bool prefer_server_ciphers; - const uint16_t family = isc_sockaddr_pf(&xfr->primaryaddr) == PF_INET6 - ? AF_INET6 - : AF_INET; - const char *tlsname = NULL; - - REQUIRE(psess_cache != NULL && *psess_cache == NULL); - REQUIRE(pctx != NULL && *pctx == 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, &found, - &found_store, &found_sess_cache); - if (result != ISC_R_SUCCESS) { - const char *hostname = - dns_transport_get_remote_hostname(xfr->transport); - const char *ca_file = dns_transport_get_cafile(xfr->transport); - const char *cert_file = - dns_transport_get_certfile(xfr->transport); - const char *key_file = - dns_transport_get_keyfile(xfr->transport); - char primary_addr_str[INET6_ADDRSTRLEN] = { 0 }; - isc_netaddr_t primary_netaddr = { 0 }; - bool hostname_ignore_subject; - /* - * So, no context exists. Let's create one using the - * parameters from the configuration file and try to - * store it for further reuse. - */ - result = isc_tlsctx_createclient(&tlsctx); - if (result != ISC_R_SUCCESS) { - goto failure; - } - tls_versions = dns_transport_get_tls_versions(xfr->transport); - if (tls_versions != 0) { - isc_tlsctx_set_protocols(tlsctx, tls_versions); - } - ciphers = dns_transport_get_ciphers(xfr->transport); - if (ciphers != NULL) { - isc_tlsctx_set_cipherlist(tlsctx, ciphers); - } - - if (dns_transport_get_prefer_server_ciphers( - xfr->transport, &prefer_server_ciphers)) - { - isc_tlsctx_prefer_server_ciphers(tlsctx, - prefer_server_ciphers); - } - - if (hostname != NULL || ca_file != NULL) { - /* - * The situation when 'found_store != NULL' while 'found - * == NULL' might appear as there is one to many - * relation between per transport TLS contexts and cert - * stores. That is, there could be one store shared - * between multiple contexts. - */ - if (found_store == NULL) { - /* - * 'ca_file' can equal 'NULL' here, in - * that case the store with system-wide - * CA certificates will be created, just - * as planned. - */ - result = isc_tls_cert_store_create(ca_file, - &store); - - if (result != ISC_R_SUCCESS) { - goto failure; - } - } else { - store = found_store; - } - - INSIST(store != NULL); - if (hostname == NULL) { - /* - * If CA bundle file is specified, but - * hostname is not, then use the primary - * IP address for validation, just like - * dig does. - */ - INSIST(ca_file != NULL); - isc_netaddr_fromsockaddr(&primary_netaddr, - &xfr->primaryaddr); - isc_netaddr_format(&primary_netaddr, - primary_addr_str, - sizeof(primary_addr_str)); - hostname = primary_addr_str; - } - /* - * According to RFC 8310, Subject field MUST NOT - * be inspected when verifying hostname for DoT. - * Only SubjectAltName must be checked. - */ - hostname_ignore_subject = true; - result = isc_tlsctx_enable_peer_verification( - tlsctx, false, store, hostname, - hostname_ignore_subject); - if (result != ISC_R_SUCCESS) { - goto failure; - } - - /* - * Let's load client certificate and enable - * Mutual TLS. We do that only in the case when - * Strict TLS is enabled, because Mutual TLS is - * an extension of it. - */ - if (cert_file != NULL) { - INSIST(key_file != NULL); - - result = isc_tlsctx_load_certificate( - tlsctx, key_file, cert_file); - if (result != ISC_R_SUCCESS) { - goto failure; - } - } - } - - 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, sess_cache, &found, - &found_store, &found_sess_cache); - 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 associated data 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); - isc_tls_cert_store_free(&store); - isc_tlsctx_client_session_cache_detach(&sess_cache); - /* Let's return the data from the cache. */ - *psess_cache = found_sess_cache; - *pctx = found; - } else { - /* - * Adding the fresh values into the cache has been - * successful, let's return them - */ - INSIST(result == ISC_R_SUCCESS); - *psess_cache = sess_cache; - *pctx = tlsctx; - } - } else { - /* - * The cache lookup has been successful, let's return the - * results. - */ - INSIST(result == ISC_R_SUCCESS); - *psess_cache = found_sess_cache; - *pctx = found; - } - - return (ISC_R_SUCCESS); - -failure: - if (tlsctx != NULL) { - isc_tlsctx_free(&tlsctx); - } - - /* - * The 'found_store' is being managed by the TLS context - * cache. Thus, we should keep it as it is, as it will get - * destroyed alongside the cache. As there is one store per - * multiple TLS contexts, we need to handle store deletion in a - * special way. - */ - if (store != NULL && store != found_store) { - isc_tls_cert_store_free(&store); - } - - return (result); -} - static isc_result_t xfrin_start(dns_xfrin_ctx_t *xfr) { isc_result_t result; @@ -1163,7 +953,9 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { connect_xfr, 30000); break; case DNS_TRANSPORT_TLS: { - result = get_create_tlsctx(xfr, &tlsctx, &sess_cache); + result = dns_transport_get_tlsctx( + xfr->transport, &xfr->primaryaddr, xfr->tlsctx_cache, + xfr->mctx, &tlsctx, &sess_cache); if (result != ISC_R_SUCCESS) { goto failure; }