mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-28 04:34:54 -04:00
Add support for Strict/Mutual TLS into BIND
This commit adds support for Strict/Mutual TLS into BIND. It does so by implementing the backing code for 'hostname' and 'ca-file' options of the 'tls' statement. The commit also updates the documentation accordingly.
This commit is contained in:
parent
05091f0095
commit
6c05fb09c3
5 changed files with 166 additions and 13 deletions
|
|
@ -11106,8 +11106,8 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
|
|||
const cfg_obj_t *http_server = NULL;
|
||||
in_port_t port = 0;
|
||||
isc_dscp_t dscp = -1;
|
||||
const char *key = NULL, *cert = NULL, *dhparam_file = NULL,
|
||||
*ciphers = NULL;
|
||||
const char *key = NULL, *cert = NULL, *ca_file = NULL,
|
||||
*dhparam_file = NULL, *ciphers = NULL;
|
||||
bool tls_prefer_server_ciphers = false,
|
||||
tls_prefer_server_ciphers_set = false;
|
||||
bool tls_session_tickets = false, tls_session_tickets_set = false;
|
||||
|
|
@ -11132,7 +11132,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
|
|||
do_tls = true;
|
||||
} else {
|
||||
const cfg_obj_t *keyobj = NULL, *certobj = NULL,
|
||||
*dhparam_obj = NULL;
|
||||
*ca_obj = NULL, *dhparam_obj = NULL;
|
||||
const cfg_obj_t *tlsmap = NULL;
|
||||
const cfg_obj_t *tls_proto_list = NULL;
|
||||
const cfg_obj_t *ciphers_obj = NULL;
|
||||
|
|
@ -11155,6 +11155,11 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
|
|||
CHECK(cfg_map_get(tlsmap, "cert-file", &certobj));
|
||||
cert = cfg_obj_asstring(certobj);
|
||||
|
||||
if (cfg_map_get(tlsmap, "ca-file", &ca_obj) ==
|
||||
ISC_R_SUCCESS) {
|
||||
ca_file = cfg_obj_asstring(ca_obj);
|
||||
}
|
||||
|
||||
if (cfg_map_get(tlsmap, "protocols", &tls_proto_list) ==
|
||||
ISC_R_SUCCESS) {
|
||||
const cfg_listelt_t *proto = NULL;
|
||||
|
|
@ -11210,6 +11215,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
|
|||
.name = tlsname,
|
||||
.key = key,
|
||||
.cert = cert,
|
||||
.ca_file = ca_file,
|
||||
.protocols = tls_protos,
|
||||
.dhparam_file = dhparam_file,
|
||||
.ciphers = ciphers,
|
||||
|
|
|
|||
|
|
@ -4832,7 +4832,13 @@ The following options can be specified in a ``tls`` statement:
|
|||
the connection.
|
||||
|
||||
``ca-file``
|
||||
Path to a file containing trusted TLS certificates.
|
||||
Path to a file containing trusted CA-authorities TLS
|
||||
certificates used to verify remote peer certificates. Specifying
|
||||
this option enables remote peer certificates verification. For
|
||||
incoming connections specifying this option will make BIND require
|
||||
a valid TLS certificate from a client. In the case of outgoing
|
||||
connections, if ``hostname`` is not specified, then the remote
|
||||
server IP address is used instead.
|
||||
|
||||
``dhparam-file``
|
||||
Path to a file containing Diffie-Hellman parameters,
|
||||
|
|
@ -4842,7 +4848,13 @@ The following options can be specified in a ``tls`` statement:
|
|||
ciphers in TLSv1.2.
|
||||
|
||||
``hostname``
|
||||
The hostname associated with the certificate.
|
||||
The expected hostname in the TLS certificate of the
|
||||
remote server. This option enables a remote server certificate
|
||||
verification. If ``ca-file`` is not specified, then the
|
||||
platform-specific certificates store is used for
|
||||
verification. This option is used when connecting to a remote peer
|
||||
only and, thus, is ignored when ``tls`` statements are referenced
|
||||
by ``listen-on`` or ``listen-on-v6`` statements.
|
||||
|
||||
``protocols``
|
||||
Allowed versions of the TLS protocol. TLS version 1.2 and higher are
|
||||
|
|
|
|||
|
|
@ -933,6 +933,7 @@ xfrin_start(dns_xfrin_ctx_t *xfr) {
|
|||
dns_xfrin_ctx_t *connect_xfr = NULL;
|
||||
dns_transport_type_t transport_type = DNS_TRANSPORT_TCP;
|
||||
isc_tlsctx_t *tlsctx = NULL, *found = NULL;
|
||||
isc_tls_cert_store_t *store = NULL, *found_store = NULL;
|
||||
|
||||
(void)isc_refcount_increment0(&xfr->connects);
|
||||
dns_xfrin_attach(xfr, &connect_xfr);
|
||||
|
|
@ -973,8 +974,19 @@ xfrin_start(dns_xfrin_ctx_t *xfr) {
|
|||
*/
|
||||
result = isc_tlsctx_cache_find(xfr->tlsctx_cache, tlsname,
|
||||
isc_tlsctx_cache_tls, family,
|
||||
&tlsctx, NULL);
|
||||
&tlsctx, &found_store);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
const char *hostname =
|
||||
dns_transport_get_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
|
||||
|
|
@ -997,12 +1009,80 @@ xfrin_start(dns_xfrin_ctx_t *xfr) {
|
|||
isc_tlsctx_prefer_server_ciphers(
|
||||
tlsctx, prefer_server_ciphers);
|
||||
}
|
||||
|
||||
if (hostname != NULL || ca_file != NULL) {
|
||||
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);
|
||||
|
||||
found_store = NULL;
|
||||
result = isc_tlsctx_cache_add(
|
||||
xfr->tlsctx_cache, tlsname,
|
||||
isc_tlsctx_cache_tls, family, tlsctx, NULL,
|
||||
&found, NULL);
|
||||
isc_tlsctx_cache_tls, family, tlsctx, store,
|
||||
&found, &found_store);
|
||||
if (result == ISC_R_EXISTS) {
|
||||
/*
|
||||
* It seems the entry has just been created
|
||||
|
|
@ -1019,6 +1099,7 @@ xfrin_start(dns_xfrin_ctx_t *xfr) {
|
|||
*/
|
||||
INSIST(found != NULL);
|
||||
isc_tlsctx_free(&tlsctx);
|
||||
isc_tls_cert_store_free(&store);
|
||||
tlsctx = found;
|
||||
} else {
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
|
|
@ -1043,6 +1124,10 @@ failure:
|
|||
if (tlsctx != NULL && found != tlsctx) {
|
||||
isc_tlsctx_free(&tlsctx);
|
||||
}
|
||||
|
||||
if (store != NULL && store != found_store) {
|
||||
isc_tls_cert_store_free(&store);
|
||||
}
|
||||
isc_refcount_decrement0(&xfr->connects);
|
||||
dns_xfrin_detach(&connect_xfr);
|
||||
return (result);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ typedef struct ns_listen_tls_params {
|
|||
const char *name;
|
||||
const char *key;
|
||||
const char *cert;
|
||||
const char *ca_file;
|
||||
uint32_t protocols;
|
||||
const char *dhparam_file;
|
||||
const char *ciphers;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
|
|||
ns_listenelt_t *elt = NULL;
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
isc_tlsctx_t *sslctx = NULL;
|
||||
isc_tls_cert_store_t *store = NULL, *found_store = NULL;
|
||||
|
||||
REQUIRE(target != NULL && *target == NULL);
|
||||
REQUIRE(!tls || (tls_params != NULL && tlsctx_cache != NULL));
|
||||
|
|
@ -48,7 +49,7 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
|
|||
*/
|
||||
result = isc_tlsctx_cache_find(tlsctx_cache, tls_params->name,
|
||||
transport, family, &sslctx,
|
||||
NULL);
|
||||
&found_store);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
/*
|
||||
* The lookup failed, let's try to create a new context
|
||||
|
|
@ -60,7 +61,39 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
|
|||
result = isc_tlsctx_createserver(
|
||||
tls_params->key, tls_params->cert, &sslctx);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
goto tls_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* If CA-bundle file is specified - enable client
|
||||
* certificates validation.
|
||||
*/
|
||||
if (tls_params->ca_file != NULL) {
|
||||
if (found_store == NULL) {
|
||||
result = isc_tls_cert_store_create(
|
||||
tls_params->ca_file, &store);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto tls_error;
|
||||
}
|
||||
} else {
|
||||
store = found_store;
|
||||
}
|
||||
|
||||
result = isc_tlsctx_enable_peer_verification(
|
||||
sslctx, true, store, NULL, false);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto tls_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the list of allowed client certificate
|
||||
* issuers to send to TLS clients.
|
||||
*/
|
||||
result = isc_tlsctx_load_client_ca_names(
|
||||
sslctx, tls_params->ca_file);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto tls_error;
|
||||
}
|
||||
}
|
||||
|
||||
if (tls_params->protocols != 0) {
|
||||
|
|
@ -71,8 +104,8 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
|
|||
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_R_FAILURE;
|
||||
goto tls_error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,10 +139,17 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
|
|||
* The storing in the cache should not fail because the
|
||||
* (re)initialisation happens from within a single
|
||||
* thread.
|
||||
*
|
||||
* Taking into account that the most recent call to
|
||||
* 'isc_tlsctx_cache_find()' has failed, it means that
|
||||
* the TLS context has not been found. Considering that
|
||||
* the initialisation happens from within the context of
|
||||
* a single thread, the call to 'isc_tlsctx_cache_add()'
|
||||
* is expected not to fail.
|
||||
*/
|
||||
RUNTIME_CHECK(isc_tlsctx_cache_add(
|
||||
tlsctx_cache, tls_params->name,
|
||||
transport, family, sslctx, NULL,
|
||||
transport, family, sslctx, store,
|
||||
NULL, NULL) == ISC_R_SUCCESS);
|
||||
} else {
|
||||
INSIST(sslctx != NULL);
|
||||
|
|
@ -134,6 +174,15 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
|
|||
|
||||
*target = elt;
|
||||
return (ISC_R_SUCCESS);
|
||||
tls_error:
|
||||
if (sslctx != NULL) {
|
||||
isc_tlsctx_free(&sslctx);
|
||||
}
|
||||
|
||||
if (store != NULL && store != found_store) {
|
||||
isc_tls_cert_store_free(&store);
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
|
|
|
|||
Loading…
Reference in a new issue