Merge branch '3529-tls-transport-support-in-dns_request-and-dns_dispatch' into 'main'

Resolve "Implement TLS transport support for dns_request"

Closes #3529

See merge request isc-projects/bind9!6751
This commit is contained in:
Arаm Sаrgsyаn 2022-09-19 17:14:18 +00:00
commit b574904041
17 changed files with 478 additions and 292 deletions

View file

@ -1,3 +1,6 @@
5975. [func] Implement TLS transport support for dns_request and
dns_dispatch. [GL #3529]
5974. [bug] Fix an assertion failure in dispatch caused by
extra read callback call. [GL #3545]

View file

@ -2480,10 +2480,10 @@ send_update(dns_name_t *zone, isc_sockaddr_t *primary) {
updatemsg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
}
result = dns_request_create(requestmgr, updatemsg, srcaddr, primary, -1,
options, tsigkey, timeout, udp_timeout,
udp_retries, global_task, update_completed,
NULL, &request);
result = dns_request_create(requestmgr, updatemsg, srcaddr, primary,
NULL, NULL, -1, options, tsigkey, timeout,
udp_timeout, udp_retries, global_task,
update_completed, NULL, &request);
check_result(result, "dns_request_create");
if (debugging) {
@ -2589,10 +2589,10 @@ recvsoa(isc_task_t *task, isc_event_t *event) {
srcaddr = localaddr4;
}
result = dns_request_create(requestmgr, soaquery, srcaddr, addr,
-1, 0, NULL, FIND_TIMEOUT * 20,
FIND_TIMEOUT, 3, global_task,
recvsoa, reqinfo, &request);
result = dns_request_create(
requestmgr, soaquery, srcaddr, addr, NULL, NULL, -1, 0,
NULL, FIND_TIMEOUT * 20, FIND_TIMEOUT, 3, global_task,
recvsoa, reqinfo, &request);
check_result(result, "dns_request_create");
requests++;
return;
@ -2808,10 +2808,10 @@ sendrequest(isc_sockaddr_t *destaddr, dns_message_t *msg,
srcaddr = localaddr4;
}
result = dns_request_create(requestmgr, msg, srcaddr, destaddr, -1, 0,
default_servers ? NULL : tsigkey,
FIND_TIMEOUT * 20, FIND_TIMEOUT, 3,
global_task, recvsoa, reqinfo, request);
result = dns_request_create(
requestmgr, msg, srcaddr, destaddr, NULL, NULL, -1, 0,
default_servers ? NULL : tsigkey, FIND_TIMEOUT * 20,
FIND_TIMEOUT, 3, global_task, recvsoa, reqinfo, request);
check_result(result, "dns_request_create");
requests++;
}
@ -3010,10 +3010,10 @@ send_gssrequest(isc_sockaddr_t *destaddr, dns_message_t *msg,
srcaddr = localaddr4;
}
result = dns_request_create(requestmgr, msg, srcaddr, destaddr, -1,
options, tsigkey, FIND_TIMEOUT * 20,
FIND_TIMEOUT, 3, global_task, recvgss,
reqinfo, request);
result = dns_request_create(requestmgr, msg, srcaddr, destaddr, NULL,
NULL, -1, options, tsigkey,
FIND_TIMEOUT * 20, FIND_TIMEOUT, 3,
global_task, recvgss, reqinfo, request);
check_result(result, "dns_request_create");
if (debugging) {
show_message(stdout, msg, "Outgoing update query:");

View file

@ -169,10 +169,10 @@ sendquery(isc_task_t *task) {
ISC_LIST_APPEND(qname->list, qrdataset, link);
dns_message_addname(message, qname, DNS_SECTION_QUESTION);
result = dns_request_create(requestmgr, message,
have_src ? &srcaddr : NULL, &dstaddr, -1,
DNS_REQUESTOPT_TCP, NULL, TIMEOUT, 0, 0,
task, recvresponse, message, &request);
result = dns_request_create(
requestmgr, message, have_src ? &srcaddr : NULL, &dstaddr, NULL,
NULL, -1, DNS_REQUESTOPT_TCP, NULL, TIMEOUT, 0, 0, task,
recvresponse, message, &request);
CHECK("dns_request_create", result);
return (ISC_R_SUCCESS);

View file

@ -178,9 +178,10 @@ sendquery(void *arg) {
DNS_TSIG_HMACMD5_NAME, &nonce, 3600);
CHECK("dns_tkey_builddhquery", result);
result = dns_request_create(requestmgr, query, NULL, &address, -1,
DNS_REQUESTOPT_TCP, initialkey, TIMEOUT, 0,
0, task, recvquery, query, &request);
result = dns_request_create(requestmgr, query, NULL, &address, NULL,
NULL, -1, DNS_REQUESTOPT_TCP, initialkey,
TIMEOUT, 0, 0, task, recvquery, query,
&request);
CHECK("dns_request_create", result);
}

View file

@ -122,9 +122,10 @@ sendquery(void *arg) {
result = dns_tkey_builddeletequery(query, tsigkey);
CHECK("dns_tkey_builddeletequery", result);
result = dns_request_create(requestmgr, query, NULL, &address, -1,
DNS_REQUESTOPT_TCP, tsigkey, TIMEOUT, 0, 0,
task, recvquery, query, &request);
result = dns_request_create(requestmgr, query, NULL, &address, NULL,
NULL, -1, DNS_REQUESTOPT_TCP, tsigkey,
TIMEOUT, 0, 0, task, recvquery, query,
&request);
CHECK("dns_request_create", result);
}

View file

@ -756,8 +756,8 @@ sendquery(struct query *query) {
}
result = dns_request_create(
requestmgr, message, have_src ? &srcaddr : NULL, &dstaddr, dscp,
options, NULL, query->timeout, query->udptimeout,
requestmgr, message, have_src ? &srcaddr : NULL, &dstaddr, NULL,
NULL, dscp, options, NULL, query->timeout, query->udptimeout,
query->udpretries, global_task, recvresponse, message,
&request);
CHECK("dns_request_create", result);

View file

@ -30,6 +30,7 @@
#include <isc/stats.h>
#include <isc/string.h>
#include <isc/time.h>
#include <isc/tls.h>
#include <isc/util.h>
#include <dns/acl.h>
@ -37,6 +38,7 @@
#include <dns/log.h>
#include <dns/message.h>
#include <dns/stats.h>
#include <dns/transport.h>
#include <dns/types.h>
typedef ISC_LIST(dns_dispentry_t) dns_displist_t;
@ -75,6 +77,8 @@ struct dns_dispentry {
isc_refcount_t references;
dns_dispatch_t *disp;
isc_nmhandle_t *handle; /*%< netmgr handle for UDP connection */
dns_transport_t *transport;
isc_tlsctx_cache_t *tlsctx_cache;
unsigned int bucket;
unsigned int retries;
unsigned int timeout;
@ -407,6 +411,14 @@ dispentry_destroy(dns_dispentry_t *resp) {
isc_nmhandle_detach(&resp->handle);
}
if (resp->tlsctx_cache != NULL) {
isc_tlsctx_cache_detach(&resp->tlsctx_cache);
}
if (resp->transport != NULL) {
dns_transport_detach(&resp->transport);
}
isc_refcount_destroy(&resp->references);
isc_mem_put(disp->mgr->mctx, resp, sizeof(*resp));
@ -1384,6 +1396,7 @@ dns_dispatch_detach(dns_dispatch_t **dispp) {
isc_result_t
dns_dispatch_add(dns_dispatch_t *disp, unsigned int options,
unsigned int timeout, const isc_sockaddr_t *dest,
dns_transport_t *transport, isc_tlsctx_cache_t *tlsctx_cache,
dispatch_cb_t connected, dispatch_cb_t sent,
dispatch_cb_t response, void *arg, dns_messageid_t *idp,
dns_dispentry_t **resp) {
@ -1486,6 +1499,14 @@ dns_dispatch_add(dns_dispatch_t *disp, unsigned int options,
return (ISC_R_NOMORE);
}
if (transport != NULL) {
dns_transport_attach(transport, &res->transport);
}
if (tlsctx_cache != NULL) {
isc_tlsctx_cache_attach(tlsctx_cache, &res->tlsctx_cache);
}
dns_dispatch_attach(disp, &res->disp);
res->id = id;
@ -1791,16 +1812,45 @@ isc_result_t
dns_dispatch_connect(dns_dispentry_t *resp) {
dns_dispatch_t *disp = NULL;
uint_fast32_t tcpstate = DNS_DISPATCHSTATE_NONE;
dns_transport_type_t transport_type = DNS_TRANSPORT_NONE;
isc_tlsctx_t *tlsctx = NULL;
isc_tlsctx_client_session_cache_t *sess_cache = NULL;
REQUIRE(VALID_RESPONSE(resp));
disp = resp->disp;
if (disp->socktype == isc_socktype_tcp) {
transport_type = DNS_TRANSPORT_TCP;
} else if (disp->socktype == isc_socktype_udp) {
transport_type = DNS_TRANSPORT_UDP;
} else {
return (ISC_R_NOTIMPLEMENTED);
}
if (resp->transport != NULL) {
transport_type = dns_transport_get_type(resp->transport);
}
if (transport_type == DNS_TRANSPORT_TLS) {
isc_result_t result;
result = dns_transport_get_tlsctx(
resp->transport, &resp->peer, resp->tlsctx_cache,
resp->disp->mgr->mctx, &tlsctx, &sess_cache);
if (result != ISC_R_SUCCESS) {
return (result);
}
INSIST(tlsctx != NULL);
}
/* This will be detached once we've connected. */
dispentry_attach(resp, &(dns_dispentry_t *){ NULL });
switch (disp->socktype) {
case isc_socktype_tcp:
switch (transport_type) {
case DNS_TRANSPORT_TCP:
case DNS_TRANSPORT_TLS:
/*
* Check whether the dispatch is already connecting
* or connected.
@ -1815,9 +1865,17 @@ dns_dispatch_connect(dns_dispentry_t *resp) {
ISC_LIST_APPEND(disp->pending, resp, plink);
UNLOCK(&disp->lock);
dns_dispatch_attach(disp, &(dns_dispatch_t *){ NULL });
isc_nm_tcpdnsconnect(disp->mgr->nm, &disp->local,
&disp->peer, tcp_connected, disp,
resp->timeout);
if (transport_type == DNS_TRANSPORT_TLS) {
isc_nm_tlsdnsconnect(
disp->mgr->nm, &disp->local,
&disp->peer, tcp_connected, disp,
resp->timeout, tlsctx, sess_cache);
} else {
isc_nm_tcpdnsconnect(disp->mgr->nm,
&disp->local, &disp->peer,
tcp_connected, disp,
resp->timeout);
}
break;
case DNS_DISPATCHSTATE_CONNECTING:
@ -1841,7 +1899,7 @@ dns_dispatch_connect(dns_dispentry_t *resp) {
break;
case isc_socktype_udp:
case DNS_TRANSPORT_UDP:
isc_nm_udpconnect(disp->mgr->nm, &resp->local, &resp->peer,
udp_connected, resp, resp->timeout);
break;

View file

@ -266,6 +266,7 @@ typedef void (*dispatch_cb_t)(isc_result_t eresult, isc_region_t *region,
isc_result_t
dns_dispatch_add(dns_dispatch_t *disp, unsigned int options,
unsigned int timeout, const isc_sockaddr_t *dest,
dns_transport_t *transport, isc_tlsctx_cache_t *tlsctx_cache,
dispatch_cb_t connected, dispatch_cb_t sent,
dispatch_cb_t response, void *arg, dns_messageid_t *idp,
dns_dispentry_t **resp);

View file

@ -130,7 +130,8 @@ dns_requestmgr_detach(dns_requestmgr_t **requestmgrp);
isc_result_t
dns_request_create(dns_requestmgr_t *requestmgr, dns_message_t *message,
const isc_sockaddr_t *srcaddr,
const isc_sockaddr_t *destaddr, isc_dscp_t dscp,
const isc_sockaddr_t *destaddr, dns_transport_t *transport,
isc_tlsctx_cache_t *tlsctx_cache, isc_dscp_t dscp,
unsigned int options, dns_tsigkey_t *key,
unsigned int timeout, unsigned int udptimeout,
unsigned int udpretries, isc_task_t *task,
@ -174,7 +175,9 @@ dns_request_create(dns_requestmgr_t *requestmgr, dns_message_t *message,
isc_result_t
dns_request_createraw(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
const isc_sockaddr_t *srcaddr,
const isc_sockaddr_t *destaddr, isc_dscp_t dscp,
const isc_sockaddr_t *destaddr,
dns_transport_t *transport,
isc_tlsctx_cache_t *tlsctx_cache, isc_dscp_t dscp,
unsigned int options, unsigned int timeout,
unsigned int udptimeout, unsigned int udpretries,
isc_task_t *task, isc_taskaction_t action, void *arg,

View file

@ -13,7 +13,9 @@
#pragma once
#include <dns/name.h>
#include <isc/tls.h>
#include <dns/types.h>
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

View file

@ -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;

View file

@ -22,6 +22,7 @@
#include <isc/result.h>
#include <isc/task.h>
#include <isc/thread.h>
#include <isc/tls.h>
#include <isc/util.h>
#include <dns/acl.h>
@ -33,6 +34,7 @@
#include <dns/rdata.h>
#include <dns/rdatastruct.h>
#include <dns/request.h>
#include <dns/transport.h>
#include <dns/tsig.h>
#define REQUESTMGR_MAGIC ISC_MAGIC('R', 'q', 'u', 'M')
@ -407,7 +409,9 @@ get_dispatch(bool tcp, bool newtcp, dns_requestmgr_t *requestmgr,
isc_result_t
dns_request_createraw(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
const isc_sockaddr_t *srcaddr,
const isc_sockaddr_t *destaddr, isc_dscp_t dscp,
const isc_sockaddr_t *destaddr,
dns_transport_t *transport,
isc_tlsctx_cache_t *tlsctx_cache, isc_dscp_t dscp,
unsigned int options, unsigned int timeout,
unsigned int udptimeout, unsigned int udpretries,
isc_task_t *task, isc_taskaction_t action, void *arg,
@ -504,9 +508,9 @@ again:
}
result = dns_dispatch_add(request->dispatch, dispopt, request->timeout,
destaddr, req_connected, req_senddone,
req_response, request, &id,
&request->dispentry);
destaddr, transport, tlsctx_cache,
req_connected, req_senddone, req_response,
request, &id, &request->dispentry);
if (result != ISC_R_SUCCESS) {
if ((options & DNS_REQUESTOPT_FIXEDID) != 0 && !newtcp) {
newtcp = true;
@ -572,7 +576,8 @@ cleanup:
isc_result_t
dns_request_create(dns_requestmgr_t *requestmgr, dns_message_t *message,
const isc_sockaddr_t *srcaddr,
const isc_sockaddr_t *destaddr, isc_dscp_t dscp,
const isc_sockaddr_t *destaddr, dns_transport_t *transport,
isc_tlsctx_cache_t *tlsctx_cache, isc_dscp_t dscp,
unsigned int options, dns_tsigkey_t *key,
unsigned int timeout, unsigned int udptimeout,
unsigned int udpretries, isc_task_t *task,
@ -659,9 +664,10 @@ again:
goto detach;
}
result = dns_dispatch_add(
request->dispatch, 0, request->timeout, destaddr, req_connected,
req_senddone, req_response, request, &id, &request->dispentry);
result = dns_dispatch_add(request->dispatch, 0, request->timeout,
destaddr, transport, tlsctx_cache,
req_connected, req_senddone, req_response,
request, &id, &request->dispentry);
if (result != ISC_R_SUCCESS) {
goto detach;
}

View file

@ -2283,7 +2283,7 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
/* Set up the dispatch and set the query ID */
result = dns_dispatch_add(
query->dispatch, 0, isc_interval_ms(&fctx->interval),
&query->addrinfo->sockaddr, resquery_connected,
&query->addrinfo->sockaddr, NULL, NULL, resquery_connected,
resquery_senddone, resquery_response, query, &query->id,
&query->dispentry);
if (result != ISC_R_SUCCESS) {

View file

@ -15,9 +15,11 @@
#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>
@ -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);

View file

@ -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;
}

View file

@ -12788,10 +12788,10 @@ notify_send_toaddr(isc_task_t *task, isc_event_t *event) {
if (DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_DIALNOTIFY)) {
timeout = 30;
}
result = dns_request_create(notify->zone->view->requestmgr, message,
&src, &notify->dst, dscp, options, key,
timeout * 3, timeout, 2, notify->zone->task,
notify_done, notify, &notify->request);
result = dns_request_create(
notify->zone->view->requestmgr, message, &src, &notify->dst,
NULL, NULL, dscp, options, key, timeout * 3, timeout, 2,
notify->zone->task, notify_done, notify, &notify->request);
if (result == ISC_R_SUCCESS) {
if (isc_sockaddr_pf(&notify->dst) == AF_INET) {
inc_stats(notify->zone,
@ -13498,7 +13498,7 @@ stub_request_nameserver_address(struct stub_cb_args *args, bool ipv4,
result = dns_request_create(
zone->view->requestmgr, message, &zone->sourceaddr,
&zone->primaryaddr, args->dscp, DNS_REQUESTOPT_TCP,
&zone->primaryaddr, NULL, NULL, args->dscp, DNS_REQUESTOPT_TCP,
args->tsig_key, args->timeout * 3, args->timeout, 2, zone->task,
stub_glue_response_cb, request, &request->request);
@ -14741,8 +14741,8 @@ again:
}
result = dns_request_create(
zone->view->requestmgr, message, &zone->sourceaddr,
&zone->primaryaddr, dscp, options, key, timeout * 3, timeout, 2,
zone->task, refresh_callback, zone, &zone->request);
&zone->primaryaddr, NULL, NULL, dscp, options, key, timeout * 3,
timeout, 2, zone->task, refresh_callback, zone, &zone->request);
if (result != ISC_R_SUCCESS) {
zone_idetach(&dummy);
zone_debuglog(zone, __func__, 1,
@ -15024,10 +15024,11 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
cb_args->timeout = timeout;
cb_args->reqnsid = reqnsid;
result = dns_request_create(
zone->view->requestmgr, message, &zone->sourceaddr,
&zone->primaryaddr, dscp, DNS_REQUESTOPT_TCP, key, timeout * 3,
timeout, 2, zone->task, stub_callback, cb_args, &zone->request);
result = dns_request_create(zone->view->requestmgr, message,
&zone->sourceaddr, &zone->primaryaddr, NULL,
NULL, dscp, DNS_REQUESTOPT_TCP, key,
timeout * 3, timeout, 2, zone->task,
stub_callback, cb_args, &zone->request);
if (result != ISC_R_SUCCESS) {
zone_debuglog(zone, __func__, 1,
"dns_request_create() failed: %s",
@ -18292,11 +18293,11 @@ sendtoprimary(dns_forward_t *forward) {
result = ISC_R_NOTIMPLEMENTED;
goto unlock;
}
result = dns_request_createraw(forward->zone->view->requestmgr,
forward->msgbuf, &src, &forward->addr,
dscp, forward->options, 15 /* XXX */, 0,
0, forward->zone->task, forward_callback,
forward, &forward->request);
result = dns_request_createraw(
forward->zone->view->requestmgr, forward->msgbuf, &src,
&forward->addr, NULL, NULL, dscp, forward->options,
15 /* XXX */, 0, 0, forward->zone->task, forward_callback,
forward, &forward->request);
if (result == ISC_R_SUCCESS) {
if (!ISC_LINK_LINKED(forward, link)) {
ISC_LIST_APPEND(forward->zone->forwards, forward, link);
@ -21217,7 +21218,7 @@ checkds_send_toaddr(isc_task_t *task, isc_event_t *event) {
options |= DNS_REQUESTOPT_TCP;
result = dns_request_create(
checkds->zone->view->requestmgr, message, &src, &checkds->dst,
dscp, options, key, timeout * 3, timeout, 2,
NULL, NULL, dscp, options, key, timeout * 3, timeout, 2,
checkds->zone->task, checkds_done, checkds, &checkds->request);
if (result != ISC_R_SUCCESS) {
dns_zone_log(checkds->zone, ISC_LOG_DEBUG(3),

View file

@ -27,6 +27,7 @@
#include <isc/buffer.h>
#include <isc/managers.h>
#include <isc/refcount.h>
#include <isc/tls.h>
#include <isc/util.h>
#include <isc/uv.h>
@ -54,6 +55,15 @@ static isc_sockaddr_t udp_server_addr;
static isc_sockaddr_t udp_connect_addr;
static isc_sockaddr_t tcp_server_addr;
static isc_sockaddr_t tcp_connect_addr;
static isc_sockaddr_t tls_server_addr;
static isc_sockaddr_t tls_connect_addr;
static isc_tlsctx_cache_t *tls_tlsctx_client_cache = NULL;
static isc_tlsctx_t *tls_listen_tlsctx = NULL;
static dns_name_t tls_name;
static const char *tls_name_str = "ephemeral";
static dns_transport_t *tls_transport = NULL;
static dns_transport_list_t *transport_list = NULL;
static dns_dispatchmgr_t *dispatchmgr = NULL;
static dns_dispatch_t *dispatch = NULL;
@ -116,6 +126,9 @@ setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) {
static int
setup_test(void **state) {
isc_buffer_t namesrc, namebuf;
char namedata[DNS_NAME_FORMATSIZE + 1];
uv_os_sock_t socket = -1;
setup_loopmgr(state);
@ -129,6 +142,9 @@ setup_test(void **state) {
tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
tls_connect_addr = (isc_sockaddr_t){ .length = 0 };
isc_sockaddr_fromin6(&tls_connect_addr, &in6addr_loopback, 0);
udp_server_addr = (isc_sockaddr_t){ .length = 0 };
socket = setup_ephemeral_port(&udp_server_addr, SOCK_DGRAM);
if (socket < 0) {
@ -143,6 +159,13 @@ setup_test(void **state) {
}
close(socket);
tls_server_addr = (isc_sockaddr_t){ .length = 0 };
socket = setup_ephemeral_port(&tls_server_addr, SOCK_STREAM);
if (socket < 0) {
return (-1);
}
close(socket);
isc_nm_settimeouts(netmgr, T_SERVER_INIT, T_SERVER_IDLE,
T_SERVER_KEEPALIVE, T_SERVER_ADVERTISED);
@ -158,11 +181,36 @@ setup_test(void **state) {
testdata.region.length = sizeof(testdata.rbuf);
memset(testdata.message, 0, sizeof(testdata.message));
tls_tlsctx_client_cache = isc_tlsctx_cache_new(mctx);
if (isc_tlsctx_createserver(NULL, NULL, &tls_listen_tlsctx) !=
ISC_R_SUCCESS) {
return (-1);
}
dns_name_init(&tls_name, NULL);
isc_buffer_constinit(&namesrc, tls_name_str, strlen(tls_name_str));
isc_buffer_add(&namesrc, strlen(tls_name_str));
isc_buffer_init(&namebuf, namedata, sizeof(namedata));
if (dns_name_fromtext(&tls_name, &namesrc, dns_rootname,
DNS_NAME_DOWNCASE, &namebuf) != ISC_R_SUCCESS)
{
return (-1);
}
transport_list = dns_transport_list_new(mctx);
tls_transport = dns_transport_new(&tls_name, DNS_TRANSPORT_TLS,
transport_list);
dns_transport_set_tlsname(tls_transport, tls_name_str);
return (0);
}
static int
teardown_test(void **state) {
dns_transport_list_detach(&transport_list);
isc_tlsctx_cache_detach(&tls_tlsctx_client_cache);
isc_tlsctx_free(&tls_listen_tlsctx);
isc_netmgr_destroy(&connect_nm);
teardown_netmgr(state);
@ -409,10 +457,10 @@ ISC_LOOP_TEST_IMPL(dispatch_timeout_tcp_connect) {
assert_int_equal(result, ISC_R_SUCCESS);
dns_dispatchmgr_detach(&dispatchmgr);
result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
&tcp_server_addr, timeout_connected,
client_senddone, response_timeout,
&testdata.region, &id, &dispentry);
result = dns_dispatch_add(
dispatch, 0, T_CLIENT_CONNECT, &tcp_server_addr, NULL, NULL,
timeout_connected, client_senddone, response_timeout,
&testdata.region, &id, &dispentry);
assert_int_equal(result, ISC_R_SUCCESS);
dns_dispatch_detach(&dispatch);
@ -454,9 +502,9 @@ ISC_LOOP_TEST_IMPL(dispatch_timeout_tcp_response) {
dns_dispatchmgr_detach(&dispatchmgr);
result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
&tcp_server_addr, connected, client_senddone,
response_timeout, &testdata.region, &id,
&dispentry);
&tcp_server_addr, NULL, NULL, connected,
client_senddone, response_timeout,
&testdata.region, &id, &dispentry);
assert_int_equal(result, ISC_R_SUCCESS);
dns_dispatch_detach(&dispatch);
@ -488,8 +536,46 @@ ISC_LOOP_TEST_IMPL(dispatch_tcp_response) {
dns_dispatchmgr_detach(&dispatchmgr);
result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
&tcp_server_addr, connected, client_senddone,
response, &testdata.region, &id, &dispentry);
&tcp_server_addr, NULL, NULL, connected,
client_senddone, response, &testdata.region,
&id, &dispentry);
assert_int_equal(result, ISC_R_SUCCESS);
dns_dispatch_detach(&dispatch);
testdata.message[0] = (id >> 8) & 0xff;
testdata.message[1] = id & 0xff;
dns_dispatch_connect(dispentry);
}
ISC_LOOP_TEST_IMPL(dispatch_tls_response) {
isc_result_t result;
uint16_t id;
/* Server */
result = isc_nm_listentlsdns(
netmgr, ISC_NM_LISTEN_ONE, &tls_server_addr, nameserver, NULL,
accept_cb, NULL, 0, NULL, tls_listen_tlsctx, &sock);
assert_int_equal(result, ISC_R_SUCCESS);
isc_loop_teardown(isc_loop_main(loopmgr), stop_listening, sock);
/* Client */
testdata.region.base = testdata.message;
testdata.region.length = sizeof(testdata.message);
result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
assert_int_equal(result, ISC_R_SUCCESS);
result = dns_dispatch_createtcp(dispatchmgr, &tls_connect_addr,
&tls_server_addr, -1, &dispatch);
assert_int_equal(result, ISC_R_SUCCESS);
dns_dispatchmgr_detach(&dispatchmgr);
result = dns_dispatch_add(
dispatch, 0, T_CLIENT_CONNECT, &tls_server_addr, tls_transport,
tls_tlsctx_client_cache, connected, client_senddone, response,
&testdata.region, &id, &dispentry);
assert_int_equal(result, ISC_R_SUCCESS);
dns_dispatch_detach(&dispatch);
@ -521,9 +607,9 @@ ISC_LOOP_TEST_IMPL(dispatch_timeout_udp_response) {
dns_dispatchmgr_detach(&dispatchmgr);
result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
&udp_server_addr, connected, client_senddone,
response_timeout, &testdata.region, &id,
&dispentry);
&udp_server_addr, NULL, NULL, connected,
client_senddone, response_timeout,
&testdata.region, &id, &dispentry);
assert_int_equal(result, ISC_R_SUCCESS);
dns_dispatch_detach(&dispatch);
@ -555,9 +641,9 @@ ISC_LOOP_TEST_IMPL(dispatch_getnext) {
dns_dispatchmgr_detach(&dispatchmgr);
result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
&udp_server_addr, connected, client_senddone,
response_getnext, &testdata.region, &id,
&dispentry);
&udp_server_addr, NULL, NULL, connected,
client_senddone, response_getnext,
&testdata.region, &id, &dispentry);
assert_int_equal(result, ISC_R_SUCCESS);
dns_dispatch_detach(&dispatch);
@ -574,6 +660,7 @@ ISC_TEST_ENTRY_CUSTOM(dispatchset_get, setup_test, teardown_test)
ISC_TEST_ENTRY_CUSTOM(dispatch_timeout_tcp_response, setup_test, teardown_test)
ISC_TEST_ENTRY_CUSTOM(dispatch_timeout_tcp_connect, setup_test, teardown_test)
ISC_TEST_ENTRY_CUSTOM(dispatch_tcp_response, setup_test, teardown_test)
ISC_TEST_ENTRY_CUSTOM(dispatch_tls_response, setup_test, teardown_test)
ISC_TEST_ENTRY_CUSTOM(dispatch_getnext, setup_test, teardown_test)
ISC_TEST_LIST_END