#pragma once
-#include <dns/name.h>
+#include <isc/tls.h>
+
+#include <dns/types.h>
typedef enum {
DNS_TRANSPORT_NONE = 0,
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);
* '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
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;
#include <isc/list.h>
#include <isc/mem.h>
+#include <isc/netaddr.h>
#include <isc/refcount.h>
#include <isc/result.h>
#include <isc/rwlock.h>
+#include <isc/sockaddr.h>
#include <isc/util.h>
#include <dns/name.h>
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);
*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;
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;
}