diff --git a/nsd-control.c b/nsd-control.c index 5de032620..944d3aaee 100644 --- a/nsd-control.c +++ b/nsd-control.c @@ -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 diff --git a/nsd.c b/nsd.c index b8bc1ea41..fa9566d31 100644 --- a/nsd.c +++ b/nsd.c @@ -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; diff --git a/nsd.conf.5.in b/nsd.conf.5.in index 8aad818ce..bc221580d 100644 --- a/nsd.conf.5.in +++ b/nsd.conf.5.in @@ -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 @@ -626,7 +628,8 @@ Requests for zone transfers on other ports are refused. .B tls\-cert\-bundle:\fR 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 The port number for proxy protocol service. If the statement is given multiple @@ -1254,13 +1257,62 @@ be configured here. Also configure a private key with client\-key. .TP .B client\-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: 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: + provide-xfr: "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 @1853 "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 diff --git a/nsd.h b/nsd.h index e55c9ded1..7e9c82bf1 100644 --- a/nsd.h +++ b/nsd.h @@ -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; diff --git a/options.h b/options.h index 5cd1cda5c..484747a74 100644 --- a/options.h +++ b/options.h @@ -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 */ diff --git a/xfrd-tcp.c b/xfrd-tcp.c index acea5e2dc..70f643f62 100644 --- a/xfrd-tcp.c +++ b/xfrd-tcp.c @@ -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) { @@ -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; @@ -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, @@ -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; itcp_max; i++) @@ -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; diff --git a/xfrd-tcp.h b/xfrd-tcp.h index 02970e0d6..11e8d9d85 100644 --- a/xfrd-tcp.h +++ b/xfrd-tcp.h @@ -13,6 +13,7 @@ #include "xfrd.h" #ifdef HAVE_TLS_1_3 #include +void xfrd_tls_auth_load(struct nsd_options* opts); #endif @@ -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; }; @@ -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); diff --git a/xfrd.c b/xfrd.c index 76a6d61dd..05b265f5c 100644 --- a/xfrd.c +++ b/xfrd.c @@ -225,7 +225,7 @@ xfrd_init(int socket, struct nsd* nsd, int shortsoa, int reload_active, daemon_metrics_attach(xfrd->nsd->metrics, xfrd); #endif /* USE_METRICS */ - xfrd->tcp_set = xfrd_tcp_set_create(xfrd->region, nsd->options->tls_cert_bundle, nsd->options->xfrd_tcp_max, nsd->options->xfrd_tcp_pipeline); + xfrd->tcp_set = xfrd_tcp_set_create(xfrd->region, nsd->options->xfrd_tcp_max, nsd->options->xfrd_tcp_pipeline); xfrd->tcp_set->tcp_timeout = nsd->tcp_timeout; #if !defined(HAVE_ARC4RANDOM) && !defined(HAVE_GETRANDOM) srandom((unsigned long) getpid() * (unsigned long) time(NULL)); @@ -440,16 +440,15 @@ xfrd_shutdown() if (xfrd->nsd->tls_ctx) SSL_CTX_free(xfrd->nsd->tls_ctx); # ifdef HAVE_TLS_1_3 - if (xfrd->tcp_set->ssl_ctx) { - int i; - for(i=0; itcp_set->tcp_max; i++) { - if(xfrd->tcp_set->tcp_state[i] && - xfrd->tcp_set->tcp_state[i]->ssl) { - SSL_free(xfrd->tcp_set->tcp_state[i]->ssl); - xfrd->tcp_set->tcp_state[i]->ssl = NULL; + /* Free tls-auth SSL contexts */ + if (xfrd->nsd->options) { + struct tls_auth_options* tls_auth; + RBTREE_FOR(tls_auth, struct tls_auth_options*, + xfrd->nsd->options->tls_auths) { + if (tls_auth->ssl_ctx) { + SSL_CTX_free(tls_auth->ssl_ctx); } } - SSL_CTX_free(xfrd->tcp_set->ssl_ctx); } # endif #endif