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;
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;
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;
.name = tlsname,
.key = key,
.cert = cert,
+ .ca_file = ca_file,
.protocols = tls_protos,
.dhparam_file = dhparam_file,
.ciphers = ciphers,
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,
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
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);
*/
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
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
*/
INSIST(found != NULL);
isc_tlsctx_free(&tlsctx);
+ isc_tls_cert_store_free(&store);
tlsctx = found;
} else {
INSIST(result == ISC_R_SUCCESS);
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);
const char *name;
const char *key;
const char *cert;
+ const char *ca_file;
uint32_t protocols;
const char *dhparam_file;
const char *ciphers;
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));
*/
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
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) {
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;
}
}
* 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);
*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