Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nsd-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ setup_ctx(struct nsd_options* cfg)
cfg->zonesdir, strerror(errno));
}

ctx = SSL_CTX_new(SSLv23_client_method());
ctx = SSL_CTX_new(TLS_client_method());
if(!ctx)
ssl_err("could not allocate SSL_CTX pointer");
#if SSL_OP_NO_SSLv2 != 0
Expand Down
5 changes: 5 additions & 0 deletions nsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1529,6 +1529,11 @@ main(int argc, char *argv[])
}
#endif /* HAVE_SSL */

#ifdef HAVE_TLS_1_3
/* Load client tls-auth SSL ctx(s) */
xfrd_tls_auth_load(nsd.options);
#endif

/* Unless we're debugging, fork... */
if (!nsd.debug) {
int fd;
Expand Down
58 changes: 55 additions & 3 deletions nsd.conf.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,9 @@ appended to the IP-address. This creates the extra socket on which the
DNS over TLS service is provided.
.sp
The file is the private key for the TLS session. The public certificate is
in the tls-service-pem file. Default is "", turned off. Requires a
in the
.BR tls-service-pem
file. Default is "", turned off. Requires a
restart (a reload is not enough) if changed, because the private key is
read while root permissions are held and before chroot (if any).
.TP
Expand Down Expand Up @@ -626,7 +628,8 @@ Requests for zone transfers on other ports are refused.
.B tls\-cert\-bundle:\fR <filename>
If null or "", the default verify locations are used. Set it to the certificate
bundle file, for example "/etc/pki/tls/certs/ca-bundle.crt". These certificates
are used for authenticating Transfer over TLS (XoT) connections.
are loaded before privilege drop and before chroot and are used for authenticating
Transfer over TLS (XoT) connections.
.TP
.B proxy\-protocol\-port:\fR <number>
The port number for proxy protocol service. If the statement is given multiple
Expand Down Expand Up @@ -1254,13 +1257,62 @@ be configured here. Also configure a private key with client\-key.
.TP
.B client\-key: <file name of clientkey.key>
If you want to use mutual TLS authentication, the private key file can
be configured here for the client authentication.
be configured here for the client authentication (the client authenticates
itself to the server).
.sp
NSD requires a restart (a reload is not enough) if
.BR client-key
or
.BR client-cert
is changed, because the files are read while root permissions are held
and before chroot (if any).
.TP
.B client\-key\-pw: <string>
If the client\-key file uses a password to decrypt the key before it can
be used, then the password can be specified here as a string.
It is possible to include other config files with the include: option, and
this can be used to move that sensitive data to another file, if you wish.
.LP
Example of Mutual TLS Authenticated Zone Transfers (XoT).
.sp
.nf
# Server side (primary server)
# chroot: /var/nsd

tls-auth-port: 1853
tls-service-key: /etc/nsd/server.key
tls-service-pem: /etc/nsd/server.pem

# secondary's cert must be included in the tls-cert-bundle
tls-cert-bundle: /etc/nsd/cert-bundle.pem

tls-auth:
name: "secondary-tls"
auth-domain-name: "secondary.example.com"

zone:
name: example.com
zonefile: /var/nsd/zones/%s
notify: <secondary_ip>
provide-xfr: <secondary_ip> <secondary_tsig> "secondary-tls"
.sp
# Client side (secondary server).
# chroot: /var/nsd

# primary's cert must be included in the tls-cert-bundle
tls-cert-bundle: /etc/nsd/cert-bundle.pem

tls-auth:
name: "primary-tls"
client-cert: /etc/nsd/client.pem
client-key: /etc/nsd/client.key
auth-domain-name: "primary.example.com"

zone:
name: example.com
zonefile: /var/nsd/zones/%s
request-xfr: AXFR <primary_ip>@1853 <primary_tsig> "primary-tls"
.fi
.SS DNSTAP Logging Options
DNSTAP support, when compiled in, is enabled in the \fBdnstap:\fR section.
This starts a collector process that writes the log information to the
Expand Down
3 changes: 3 additions & 0 deletions nsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
#ifdef USE_XDP
#include "xdp-server.h"
#endif
#ifdef HAVE_TLS_1_3
#include "xfrd-tcp.h"
#endif
struct netio_handler;
struct nsd_options;
struct udb_base;
Expand Down
4 changes: 4 additions & 0 deletions options.h
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ struct tls_auth_options {
char* client_cert;
char* client_key;
char* client_key_pw;
#ifdef HAVE_TLS_1_3
/* XoT: SSL context */
SSL_CTX* ssl_ctx;
#endif
};

/* proxy protocol port option list */
Expand Down
192 changes: 110 additions & 82 deletions xfrd-tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,35 +123,6 @@ get_cert_info(SSL* ssl, region_type* region, char** cert_serial,
X509_free(cert);
}

static SSL_CTX*
create_ssl_context()
{
SSL_CTX *ctx;
unsigned char protos[] = { 3, 'd', 'o', 't' };
ctx = SSL_CTX_new(TLS_client_method());
if (!ctx) {
log_msg(LOG_ERR, "xfrd tls: Unable to create SSL ctxt");
}
else if (SSL_CTX_set_default_verify_paths(ctx) != 1) {
SSL_CTX_free(ctx);
log_msg(LOG_ERR, "xfrd tls: Unable to set default SSL verify paths");
return NULL;
}
/* Only trust 1.3 as per the specification */
else if (!SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION)) {
SSL_CTX_free(ctx);
log_msg(LOG_ERR, "xfrd tls: Unable to set minimum TLS version 1.3");
return NULL;
}

if (SSL_CTX_set_alpn_protos(ctx, protos, sizeof(protos)) != 0) {
SSL_CTX_free(ctx);
log_msg(LOG_ERR, "xfrd tls: Unable to set ALPN protocols");
return NULL;
}
return ctx;
}

static int
tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
Expand All @@ -169,16 +140,16 @@ tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
}

static int
setup_ssl(struct xfrd_tcp_pipeline* tp, struct xfrd_tcp_set* tcp_set,
setup_ssl(struct xfrd_tcp_pipeline* tp, SSL_CTX* ctx,
const char* auth_domain_name)
{
if (!tcp_set->ssl_ctx) {
if (!ctx) {
log_msg(LOG_ERR, "xfrd tls: No TLS CTX, cannot set up XFR-over-TLS");
return 0;
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: setting up TLS for tls_auth domain name %s",
auth_domain_name));
tp->ssl = SSL_new((SSL_CTX*)tcp_set->ssl_ctx);
tp->ssl = SSL_new(ctx);
if(!tp->ssl) {
log_msg(LOG_ERR, "xfrd tls: Unable to create TLS object");
return 0;
Expand Down Expand Up @@ -254,7 +225,100 @@ xfrd_pipe_cmp(const void* a, const void* b)
return (uintptr_t)x < (uintptr_t)y ? -1 : 1;
}

struct xfrd_tcp_set* xfrd_tcp_set_create(struct region* region, const char *tls_cert_bundle, int tcp_max, int tcp_pipeline)
#ifdef HAVE_TLS_1_3
void
xfrd_tls_auth_load(struct nsd_options* opts)
{
unsigned char protos[] = { 3, 'd', 'o', 't' };
struct tls_auth_options* tls_auth;

RBTREE_FOR(tls_auth, struct tls_auth_options*, opts->tls_auths) {
tls_auth->ssl_ctx = SSL_CTX_new(TLS_client_method());
if (!tls_auth->ssl_ctx) {
log_msg(LOG_ERR, "xfrd tls: Unable to create SSL_CTX "
"for tls-auth %s", tls_auth->name);
continue;
}
/* XoT requires TLS 1.3 or later */
if (!SSL_CTX_set_min_proto_version(tls_auth->ssl_ctx, TLS1_3_VERSION)) {
log_msg(LOG_ERR, "xfrd tls: Unable to set TLS 1.3 minimum "
"for tls-auth %s", tls_auth->name);
SSL_CTX_free(tls_auth->ssl_ctx);
tls_auth->ssl_ctx = NULL;
continue;
}

/* XoT requires DoT-ALPN */
if (SSL_CTX_set_alpn_protos(tls_auth->ssl_ctx, protos, sizeof(protos)) != 0) {
log_msg(LOG_ERR, "xfrd tls: Unable to set ALPN protocols");
SSL_CTX_free(tls_auth->ssl_ctx);
tls_auth->ssl_ctx = NULL;
continue;
}

if (opts->tls_cert_bundle && opts->tls_cert_bundle[0]) {
if (SSL_CTX_load_verify_locations(tls_auth->ssl_ctx,
opts->tls_cert_bundle, NULL) != 1) {
log_msg(LOG_ERR, "xfrd tls: Unable to load cert bundle "
"%s for tls-auth %s",
opts->tls_cert_bundle, tls_auth->name);
SSL_CTX_free(tls_auth->ssl_ctx);
tls_auth->ssl_ctx = NULL;
continue;
}
} else if (SSL_CTX_set_default_verify_paths(tls_auth->ssl_ctx) != 1) {
log_msg(LOG_ERR, "xfrd tls: Unable to set default SSL verify "
"paths for tls-auth %s", tls_auth->name);
SSL_CTX_free(tls_auth->ssl_ctx);
tls_auth->ssl_ctx = NULL;
continue;
}

if (tls_auth->client_cert && tls_auth->client_key) {
if (SSL_CTX_use_certificate_chain_file(tls_auth->ssl_ctx,
tls_auth->client_cert) != 1) {
log_msg(LOG_ERR, "xfrd tls: Unable to load client certificate "
"%s for tls-auth %s",
tls_auth->client_cert, tls_auth->name);
SSL_CTX_free(tls_auth->ssl_ctx);
tls_auth->ssl_ctx = NULL;
continue;
}

if (tls_auth->client_key_pw) {
SSL_CTX_set_default_passwd_cb(tls_auth->ssl_ctx, password_cb);
SSL_CTX_set_default_passwd_cb_userdata(tls_auth->ssl_ctx,
tls_auth->client_key_pw);
}

if (SSL_CTX_use_PrivateKey_file(tls_auth->ssl_ctx,
tls_auth->client_key, SSL_FILETYPE_PEM) != 1) {
log_msg(LOG_ERR, "xfrd tls: Unable to load client private key "
"%s for tls-auth %s",
tls_auth->client_key, tls_auth->name);
SSL_CTX_free(tls_auth->ssl_ctx);
tls_auth->ssl_ctx = NULL;
continue;
}

if (!SSL_CTX_check_private_key(tls_auth->ssl_ctx)) {
log_msg(LOG_ERR, "xfrd tls: Client private key %s does not "
"match certificate %s for tls-auth %s",
tls_auth->client_key, tls_auth->client_cert,
tls_auth->name);
SSL_CTX_free(tls_auth->ssl_ctx);
tls_auth->ssl_ctx = NULL;
continue;
}

VERBOSITY(2, (LOG_INFO, "xfrd tls: loaded cert/key for tls-auth %s",
tls_auth->name));
}
}
}
#endif

struct xfrd_tcp_set* xfrd_tcp_set_create(struct region* region, int tcp_max, int tcp_pipeline)
{
int i;
struct xfrd_tcp_set* tcp_set = region_alloc(region,
Expand All @@ -266,21 +330,6 @@ struct xfrd_tcp_set* xfrd_tcp_set_create(struct region* region, const char *tls_
tcp_set->tcp_count = 0;
tcp_set->tcp_waiting_first = 0;
tcp_set->tcp_waiting_last = 0;
#ifdef HAVE_TLS_1_3
/* Set up SSL context */
tcp_set->ssl_ctx = create_ssl_context();
if (tcp_set->ssl_ctx == NULL)
log_msg(LOG_ERR, "xfrd: XFR-over-TLS not available");

else if (tls_cert_bundle && tls_cert_bundle[0] && SSL_CTX_load_verify_locations(
tcp_set->ssl_ctx, tls_cert_bundle, NULL) != 1) {
log_msg(LOG_ERR, "xfrd tls: Unable to set the certificate bundle file %s",
tls_cert_bundle);
}
#else
(void)tls_cert_bundle;
log_msg(LOG_INFO, "xfrd: No TLS 1.3 support - XFR-over-TLS not available");
#endif
tcp_set->tcp_state = region_alloc(region,
sizeof(*tcp_set->tcp_state)*tcp_set->tcp_max);
for(i=0; i<tcp_set->tcp_max; i++)
Expand Down Expand Up @@ -990,47 +1039,26 @@ xfrd_tcp_open(struct xfrd_tcp_set* set, struct xfrd_tcp_pipeline* tp,
tp->tcp_r->fd = fd;
tp->tcp_w->fd = fd;

#ifdef HAVE_TLS_1_3
/* Check if an tls_auth name is configured which means we should try to
establish an SSL connection */
if (zone->master->tls_auth_options &&
zone->master->tls_auth_options->auth_domain_name) {
#ifdef HAVE_TLS_1_3
/* Load client certificate (if provided) */
if (zone->master->tls_auth_options->client_cert &&
zone->master->tls_auth_options->client_key) {
if (SSL_CTX_use_certificate_chain_file(set->ssl_ctx,
zone->master->tls_auth_options->client_cert) != 1) {
log_msg(LOG_ERR, "xfrd tls: Unable to load client certificate from file %s", zone->master->tls_auth_options->client_cert);
}

if (zone->master->tls_auth_options->client_key_pw) {
SSL_CTX_set_default_passwd_cb(set->ssl_ctx, password_cb);
SSL_CTX_set_default_passwd_cb_userdata(set->ssl_ctx, zone->master->tls_auth_options->client_key_pw);
}

if (SSL_CTX_use_PrivateKey_file(set->ssl_ctx, zone->master->tls_auth_options->client_key, SSL_FILETYPE_PEM) != 1) {
log_msg(LOG_ERR, "xfrd tls: Unable to load private key from file %s", zone->master->tls_auth_options->client_key);
}
zone->master->tls_auth_options->auth_domain_name) {

if (!SSL_CTX_check_private_key(set->ssl_ctx)) {
log_msg(LOG_ERR, "xfrd tls: Client private key from file %s does not match the certificate from file %s",
zone->master->tls_auth_options->client_key,
zone->master->tls_auth_options->client_cert);
}
/* If client certificate/private key loading has failed,
client will not try to authenticate to the server but the connection
will procceed and will be up to the server to allow or deny the
unauthenticated connection. A server that does not enforce authentication
(or a badly configured server?) might allow the transfer.
XXX: Maybe we should close the connection now to make it obvious that
there is something wrong from our side. Alternatively make it obvious
to the operator that we're not being authenticated to the server.
*/
if (!zone->master->tls_auth_options->ssl_ctx) {
log_msg(LOG_ERR, "xfrd: No SSL context for tls-auth %s, "
"zone %s - check startup errors",
zone->master->tls_auth_options->name,
zone->apex_str);
close(fd);
xfrd_set_refresh_now(zone);
return 0;
}

if (!setup_ssl(tp, set, zone->master->tls_auth_options->auth_domain_name)) {
if (!setup_ssl(tp, zone->master->tls_auth_options->ssl_ctx,
zone->master->tls_auth_options->auth_domain_name)) {
log_msg(LOG_ERR, "xfrd: Cannot setup TLS on pipeline for %s to %s",
zone->apex_str, zone->master->ip_address_spec);
zone->apex_str, zone->master->ip_address_spec);
close(fd);
xfrd_set_refresh_now(zone);
return 0;
Expand Down
7 changes: 2 additions & 5 deletions xfrd-tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "xfrd.h"
#ifdef HAVE_TLS_1_3
#include <openssl/ssl.h>
void xfrd_tls_auth_load(struct nsd_options* opts);
#endif


Expand Down Expand Up @@ -43,10 +44,6 @@ struct xfrd_tcp_set {
int tcp_timeout;
/* rbtree with pipelines sorted by master */
rbtree_type* pipetree;
#ifdef HAVE_TLS_1_3
/* XoT: SSL context */
SSL_CTX* ssl_ctx;
#endif
/* double linked list of zones waiting for a TCP connection */
struct xfrd_zone *tcp_waiting_first, *tcp_waiting_last;
};
Expand Down Expand Up @@ -179,7 +176,7 @@ struct xfrd_tcp_pipeline {
};

/* create set of tcp connections */
struct xfrd_tcp_set* xfrd_tcp_set_create(struct region* region, const char *tls_cert_bundle, int tcp_max, int tcp_pipeline);
struct xfrd_tcp_set* xfrd_tcp_set_create(struct region* region, int tcp_max, int tcp_pipeline);

/* init tcp state */
struct xfrd_tcp* xfrd_tcp_create(struct region* region, size_t bufsize);
Expand Down
Loading
Loading