diff --git a/Makefile.in b/Makefile.in index c4d17c82f..6b602e11e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -24,6 +24,7 @@ logfile = @logfile@ xfrdir = @xfrdir@ xfrdfile = @xfrdfile@ zonelistfile = @zonelistfile@ +cookiesecretsfile = @cookiesecretsfile@ nsdconfigfile = @nsd_conf_file@ zonesdir = @zonesdir@ chrootdir= @chrootdir@ @@ -73,6 +74,7 @@ EDIT = $(SED) \ -e 's,@xfrdir\@,$(xfrdir),g' \ -e 's,@xfrdfile\@,$(xfrdfile),g' \ -e 's,@zonelistfile\@,$(zonelistfile),g' \ + -e 's,@cookiesecretsfile\@,$(cookiesecretsfile),g' \ -e 's,@nsdconfigfile\@,$(nsdconfigfile),g' \ -e 's,@shell\@,$(SHELL),g' \ -e 's,@ratelimit_default\@,@ratelimit_default@,g' \ @@ -167,6 +169,7 @@ install: all $(INSTALL) -d $(DESTDIR)$(xfrdir) $(INSTALL) -d `dirname $(DESTDIR)$(xfrdfile)` $(INSTALL) -d `dirname $(DESTDIR)$(zonelistfile)` + $(INSTALL) -d `dirname $(DESTDIR)$(cookiesecretsfile)` $(INSTALL) -d $(DESTDIR)$(mandir) $(INSTALL) -d $(DESTDIR)$(mandir)/man8 $(INSTALL) -d $(DESTDIR)$(mandir)/man5 @@ -189,7 +192,7 @@ uninstall: rm -f -- $(DESTDIR)$(mandir)/man8/nsd-checkconf.8 $(DESTDIR)$(mandir)/man8/nsd-checkzone.8 $(DESTDIR)$(mandir)/man8/nsd-control.8 rm -f -- $(DESTDIR)$(pidfile) @echo - @echo "You still need to remove $(DESTDIR)$(configdir), $(DESTDIR)$(piddir), $(DESTDIR)$(xfrdfile), $(DESTDIR)$(zonelistfile) directory by hand." + @echo "You still need to remove $(DESTDIR)$(configdir), $(DESTDIR)$(piddir), $(DESTDIR)$(xfrdfile), $(DESTDIR)$(zonelistfile) $(DESTDIR)$(cookiesecretsfile) directory by hand." test: diff --git a/configlexer.lex b/configlexer.lex index 415beeeba..e3dbc9cc5 100644 --- a/configlexer.lex +++ b/configlexer.lex @@ -313,6 +313,7 @@ proxy-protocol-port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PROXY_PROTOC answer-cookie{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ANSWER_COOKIE;} cookie-secret{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_COOKIE_SECRET;} cookie-secret-file{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_COOKIE_SECRET_FILE;} +cookie-staging-secret{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_COOKIE_STAGING_SECRET;} xfrd-tcp-max{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_TCP_MAX;} xfrd-tcp-pipeline{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_TCP_PIPELINE;} verify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERIFY; } diff --git a/configparser.y b/configparser.y index f9e6c29d6..9204bb7fd 100644 --- a/configparser.y +++ b/configparser.y @@ -196,6 +196,7 @@ struct component { %token VAR_ANSWER_COOKIE %token VAR_COOKIE_SECRET %token VAR_COOKIE_SECRET_FILE +%token VAR_COOKIE_STAGING_SECRET %token VAR_MAX_REFRESH_TIME %token VAR_MIN_REFRESH_TIME %token VAR_MAX_RETRY_TIME @@ -518,9 +519,39 @@ server_option: | VAR_ANSWER_COOKIE boolean { cfg_parser->opt->answer_cookie = $2; } | VAR_COOKIE_SECRET STRING - { cfg_parser->opt->cookie_secret = region_strdup(cfg_parser->opt->region, $2); } + { + uint8_t secret[32]; + ssize_t len = hex_pton($2, secret, NSD_COOKIE_SECRET_SIZE); + + if(len != NSD_COOKIE_SECRET_SIZE) { + yyerror("expected a 128 bit hex string"); + } else { + cfg_parser->opt->cookie_secret = region_strdup(cfg_parser->opt->region, $2); + } + } + | VAR_COOKIE_STAGING_SECRET STRING + { + uint8_t secret[32]; + ssize_t len = hex_pton($2, secret, NSD_COOKIE_SECRET_SIZE); + + if(len != NSD_COOKIE_SECRET_SIZE) { + yyerror("expected a 128 bit hex string"); + } else { + cfg_parser->opt->cookie_staging_secret = region_strdup(cfg_parser->opt->region, $2); + } + } | VAR_COOKIE_SECRET_FILE STRING - { cfg_parser->opt->cookie_secret_file = region_strdup(cfg_parser->opt->region, $2); } + { + /* Empty filename means explicitly disabled cookies from file, internally + * represented as NULL. + * Note that after parsing, if no value was configured, then + * cookie_secret_file_is_default is still 1, then the default cookie + * secret file value will be assigned to cookie_secret_file. + */ + if(*$2) cfg_parser->opt->cookie_secret_file = region_strdup(cfg_parser->opt->region, $2); + cfg_parser->opt->cookie_secret_file_is_default = 0; + } + | VAR_XFRD_TCP_MAX number { cfg_parser->opt->xfrd_tcp_max = (int)$2; } | VAR_XFRD_TCP_PIPELINE number diff --git a/configure.ac b/configure.ac index 88059c113..3ebcbbf51 100644 --- a/configure.ac +++ b/configure.ac @@ -126,6 +126,12 @@ AC_ARG_WITH([zonelistfile], AS_HELP_STRING([--with-zonelistfile=path],[Pathname AC_DEFINE_UNQUOTED(ZONELISTFILE, ["`eval echo $zonelistfile`"], [Pathname to the NSD zone list file.]) AC_SUBST(zonelistfile) +# default cookiesecrets file location. +cookiesecretsfile=${dbdir}/cookiesecrets.txt +AC_ARG_WITH([cookiesecretsfile], AS_HELP_STRING([--with-cookiesecretsfile=path],[Pathname to the NSD cookie secrets file]), [cookiesecretsfile=$withval]) +AC_DEFINE_UNQUOTED(COOKIESECRETSFILE, ["`eval echo $cookiesecretsfile`"], [Pathname to the NSD cookies secrets file.]) +AC_SUBST(cookiesecretsfile) + # default xfr dir location. xfrdir="/tmp" AC_ARG_WITH([xfrdir], AS_HELP_STRING([--with-xfrdir=path],[Pathname to where the NSD transfer dir is created]), [xfrdir=$withval]) diff --git a/difffile.c b/difffile.c index c4d18ad96..501d6e697 100644 --- a/difffile.c +++ b/difffile.c @@ -1714,44 +1714,25 @@ void task_new_del_key(udb_base* udb, udb_ptr* last, const char* name) udb_ptr_unlink(&e, udb); } -void task_new_add_cookie_secret(udb_base* udb, udb_ptr* last, - const char* secret) { +void task_new_cookies(udb_base* udb, udb_ptr* last, int answer_cookie, + size_t cookie_count, void* cookie_secrets) { udb_ptr e; char* p; - size_t const secret_size = strlen(secret) + 1; + size_t const secrets_size = sizeof(cookie_secrets_type); - DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add task add_cookie_secret")); + DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add task cookies")); if(!task_create_new_elem(udb, last, &e, - sizeof(struct task_list_d) + secret_size, NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add add_cookie_secret"); + sizeof(struct task_list_d) + secrets_size, NULL)) { + log_msg(LOG_ERR, "tasklist: out of space, cannot add cookies"); return; } - TASKLIST(&e)->task_type = task_add_cookie_secret; + TASKLIST(&e)->task_type = task_cookies; + TASKLIST(&e)->newserial = (uint32_t) answer_cookie; + TASKLIST(&e)->yesno = (uint64_t) cookie_count; p = (char*)TASKLIST(&e)->zname; - memmove(p, secret, secret_size); - udb_ptr_unlink(&e, udb); -} - -void task_new_drop_cookie_secret(udb_base* udb, udb_ptr* last) { - udb_ptr e; - DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add task drop_cookie_secret")); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d), NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add drop_cookie_secret"); - return; - } - TASKLIST(&e)->task_type = task_drop_cookie_secret; - udb_ptr_unlink(&e, udb); -} + memmove(p, cookie_secrets, secrets_size); -void task_new_activate_cookie_secret(udb_base* udb, udb_ptr* last) { - udb_ptr e; - DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add task activate_cookie_secret")); - if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d), NULL)) { - log_msg(LOG_ERR, "tasklist: out of space, cannot add activate_cookie_secret"); - return; - } - TASKLIST(&e)->task_type = task_activate_cookie_secret; udb_ptr_unlink(&e, udb); } @@ -1988,53 +1969,14 @@ task_process_del_key(struct nsd* nsd, struct task_list_d* task) } static void -task_process_add_cookie_secret(struct nsd* nsd, struct task_list_d* task) { - uint8_t secret_tmp[NSD_COOKIE_SECRET_SIZE]; - ssize_t decoded_len; - char* secret = (char*)task->zname; - - DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add_cookie_secret task %s", secret)); - - if( strlen(secret) != 32 ) { - log_msg(LOG_ERR, "invalid cookie secret: %s", secret); - explicit_bzero(secret, strlen(secret)); - return; - } - - decoded_len = hex_pton(secret, secret_tmp, NSD_COOKIE_SECRET_SIZE); - if( decoded_len != 16 ) { - explicit_bzero(secret_tmp, NSD_COOKIE_SECRET_SIZE); - log_msg(LOG_ERR, "unable to parse cookie secret: %s", secret); - explicit_bzero(secret, strlen(secret)); - return; - } - explicit_bzero(secret, strlen(secret)); - add_cookie_secret(nsd, secret_tmp); - explicit_bzero(secret_tmp, NSD_COOKIE_SECRET_SIZE); -} - -static void -task_process_drop_cookie_secret(struct nsd* nsd, struct task_list_d* task) -{ - (void)task; - DEBUG(DEBUG_IPC, 1, (LOG_INFO, "drop_cookie_secret task")); - if( nsd->cookie_count <= 1 ) { - log_msg(LOG_ERR, "can not drop the only active cookie secret"); - return; - } - drop_cookie_secret(nsd); -} - -static void -task_process_activate_cookie_secret(struct nsd* nsd, struct task_list_d* task) -{ - (void)task; - DEBUG(DEBUG_IPC, 1, (LOG_INFO, "activate_cookie_secret task")); - if( nsd->cookie_count <= 1 ) { - log_msg(LOG_ERR, "can not activate the only active cookie secret"); - return; - } - activate_cookie_secret(nsd); +task_process_cookies(struct nsd* nsd, struct task_list_d* task) { + DEBUG(DEBUG_IPC, 1, (LOG_INFO, "cookies task answer: %s, count: %d", + task->newserial ? "yes" : "no", (int)task->yesno)); + + nsd->do_answer_cookie = (int) task->newserial; + nsd->cookie_count = (size_t) task->yesno; + memmove(nsd->cookie_secrets, task->zname, sizeof(nsd->cookie_secrets)); + explicit_bzero(task->zname, sizeof(nsd->cookie_secrets)); } static void @@ -2179,14 +2121,8 @@ void task_process_in_reload(struct nsd* nsd, udb_base* udb, udb_ptr *last_task, case task_apply_xfr: task_process_apply_xfr(nsd, udb, last_task, task); break; - case task_add_cookie_secret: - task_process_add_cookie_secret(nsd, TASKLIST(task)); - break; - case task_drop_cookie_secret: - task_process_drop_cookie_secret(nsd, TASKLIST(task)); - break; - case task_activate_cookie_secret: - task_process_activate_cookie_secret(nsd, TASKLIST(task)); + case task_cookies: + task_process_cookies(nsd, TASKLIST(task)); break; default: log_msg(LOG_WARNING, "unhandled task in reload type %d", diff --git a/difffile.h b/difffile.h index 4a88d4b1d..1490f1ac8 100644 --- a/difffile.h +++ b/difffile.h @@ -111,12 +111,8 @@ struct task_list_d { task_opt_change, /** zonestat increment */ task_zonestat_inc, - /** add a new cookie secret */ - task_add_cookie_secret, - /** drop the oldest cookie secret */ - task_drop_cookie_secret, - /** make staging cookie secret active */ - task_activate_cookie_secret, + /** cookies */ + task_cookies, } task_type; uint32_t size; /* size of this struct */ @@ -152,9 +148,8 @@ void task_new_add_pattern(udb_base* udb, udb_ptr* last, void task_new_del_pattern(udb_base* udb, udb_ptr* last, const char* name); void task_new_opt_change(udb_base* udb, udb_ptr* last, struct nsd_options* opt); void task_new_zonestat_inc(udb_base* udb, udb_ptr* last, unsigned sz); -void task_new_add_cookie_secret(udb_base* udb, udb_ptr* last, const char* secret); -void task_new_drop_cookie_secret(udb_base* udb, udb_ptr* last); -void task_new_activate_cookie_secret(udb_base* udb, udb_ptr* last); +void task_new_cookies(udb_base* udb, udb_ptr* last, int answer_cookie, + size_t cookie_count, void* cookie_secrets); int task_new_apply_xfr(udb_base* udb, udb_ptr* last, const dname_type* zone, uint32_t old_serial, uint32_t new_serial, uint64_t filenumber); void task_process_in_reload(struct nsd* nsd, udb_base* udb, udb_ptr *last_task, diff --git a/nsd-checkconf.c b/nsd-checkconf.c index 4ce3e50a8..f4a2801d4 100644 --- a/nsd-checkconf.c +++ b/nsd-checkconf.c @@ -440,6 +440,7 @@ config_print_zone(nsd_options_type* opt, const char* k, int s, const char *o, SERV_GET_STR(tls_port, o); SERV_GET_STR(tls_cert_bundle, o); SERV_GET_STR(cookie_secret, o); + SERV_GET_STR(cookie_staging_secret, o); SERV_GET_STR(cookie_secret_file, o); SERV_GET_BIN(answer_cookie, o); /* int */ @@ -718,10 +719,15 @@ config_test_print_server(nsd_options_type* opt) print_string_var("tls-port:", opt->tls_port); print_string_var("tls-cert-bundle:", opt->tls_cert_bundle); printf("\tanswer-cookie: %s\n", opt->answer_cookie?"yes":"no"); - if (opt->cookie_secret) - print_string_var("cookie-secret:", opt->cookie_secret); - if (opt->cookie_secret_file) + print_string_var("cookie-secret:", opt->cookie_secret); + print_string_var("cookie-staging-secret:", opt->cookie_staging_secret); + if(opt->cookie_secret_file_is_default) { + print_string_var("#cookie-secret-file:", opt->cookie_secret_file); + } else if(opt->cookie_secret_file) { print_string_var("cookie-secret-file:", opt->cookie_secret_file); + } else { + print_string_var("cookie-secret-file:", ""); + } if(opt->proxy_protocol_port) { struct proxy_protocol_port_list* p; for(p = opt->proxy_protocol_port; p; p = p->next) diff --git a/nsd-control.8.in b/nsd-control.8.in index 0625eb913..4920e7ad8 100644 --- a/nsd-control.8.in +++ b/nsd-control.8.in @@ -210,7 +210,7 @@ be dropped with the \fBdrop_cookie_secret\fR command. .sp Persistence is accomplished by writing to a file which if configured with the \fBcookie\-secret\-file\fR option in the server section of the config file. -The default value for that is: @configdir@/nsd_cookiesecrets.txt . +The default value for that is: @cookiesecretsfile@ . .TP .B drop_cookie_secret Drop the \fIstaging\fR cookie secret. diff --git a/nsd.c b/nsd.c index 1cd968c55..c049675e2 100644 --- a/nsd.c +++ b/nsd.c @@ -902,40 +902,6 @@ bind8_stats (struct nsd *nsd) } #endif /* BIND8_STATS */ -static -int cookie_secret_file_read(nsd_type* nsd) { - char secret[NSD_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/]; - char const* file = nsd->options->cookie_secret_file; - FILE* f; - int corrupt = 0; - size_t count; - - assert( nsd->options->cookie_secret_file != NULL ); - f = fopen(file, "r"); - /* a non-existing cookie file is not an error */ - if( f == NULL ) { return errno != EPERM; } - /* cookie secret file exists and is readable */ - nsd->cookie_count = 0; - for( count = 0; count < NSD_COOKIE_HISTORY_SIZE; count++ ) { - size_t secret_len = 0; - ssize_t decoded_len = 0; - if( fgets(secret, sizeof(secret), f) == NULL ) { break; } - secret_len = strlen(secret); - if( secret_len == 0 ) { break; } - assert( secret_len <= sizeof(secret) ); - secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len; - if( secret_len != NSD_COOKIE_SECRET_SIZE * 2 ) { corrupt++; break; } - /* needed for `hex_pton`; stripping potential `\n` */ - secret[secret_len] = '\0'; - decoded_len = hex_pton(secret, nsd->cookie_secrets[count].cookie_secret, - NSD_COOKIE_SECRET_SIZE); - if( decoded_len != NSD_COOKIE_SECRET_SIZE ) { corrupt++; break; } - nsd->cookie_count++; - } - fclose(f); - return corrupt == 0; -} - extern char *optarg; extern int optind; @@ -977,14 +943,16 @@ main(int argc, char *argv[]) nsd.chrootdir = 0; nsd.nsid = NULL; nsd.nsid_len = 0; + nsd.do_answer_cookie = 0; nsd.cookie_count = 0; + nsd.cookie_secrets_source = COOKIE_SECRETS_NONE; + nsd.cookie_secrets_filename = NULL; nsd.child_count = 0; nsd.maximum_tcp_count = 0; nsd.current_tcp_count = 0; nsd.file_rotation_ok = 0; - nsd.do_answer_cookie = 1; /* Set up our default identity to gethostname(2) */ if (gethostname(hostname, MAXHOSTNAMELEN) == 0) { @@ -1261,35 +1229,7 @@ main(int argc, char *argv[]) #endif /* IPV6 MTU) */ #endif /* defined(INET6) */ - nsd.do_answer_cookie = nsd.options->answer_cookie; - if (nsd.cookie_count > 0) - ; /* pass */ - - else if (nsd.options->cookie_secret) { - ssize_t len = hex_pton(nsd.options->cookie_secret, - nsd.cookie_secrets[0].cookie_secret, NSD_COOKIE_SECRET_SIZE); - if (len != NSD_COOKIE_SECRET_SIZE ) { - error("A cookie secret must be a " - "128 bit hex string"); - } - nsd.cookie_count = 1; - } else { - size_t j; - size_t const cookie_secret_len = NSD_COOKIE_SECRET_SIZE; - /* Calculate a new random secret */ - srandom(getpid() ^ time(NULL)); - - for( j = 0; j < NSD_COOKIE_HISTORY_SIZE; j++) { -#if defined(HAVE_SSL) - if (!RAND_status() - || !RAND_bytes(nsd.cookie_secrets[j].cookie_secret, cookie_secret_len)) -#endif - for (i = 0; i < cookie_secret_len; i++) - nsd.cookie_secrets[j].cookie_secret[i] = random_generate(256); - } - // XXX: all we have is a random cookie, still pretend we have one - nsd.cookie_count = 1; - } + reconfig_cookies(&nsd, nsd.options); if (nsd.nsid_len == 0 && nsd.options->nsid) { if (strlen(nsd.options->nsid) % 2 != 0) { @@ -1592,11 +1532,6 @@ main(int argc, char *argv[]) } #endif /* HAVE_SSL */ - if(nsd.options->cookie_secret_file && nsd.options->cookie_secret_file[0] - && !cookie_secret_file_read(&nsd) ) { - log_msg(LOG_ERR, "cookie secret file corrupt or not readable"); - } - /* Unless we're debugging, fork... */ if (!nsd.debug) { int fd; diff --git a/nsd.conf.5.in b/nsd.conf.5.in index e6e4e7d8f..6c13847ea 100644 --- a/nsd.conf.5.in +++ b/nsd.conf.5.in @@ -481,25 +481,56 @@ With the value 0 the rate is unlimited. .B answer\-cookie:\fR Enable to answer to requests containing DNS Cookies as specified in RFC7873. Default is no. +.sp +DNS Cookies increase transaction security and provide limited protection +against denial-off-service amplification attacks. Server cookies will be +created and included in responses. Server cookies are created based on the +client cookie in the request, the current time, the client's IP address and +a secret. When a client includes a valid server cookie in successive requests, +the client will not be subjected to Request Rate Limiting (see \fBrrl\-ratelimit\fR). +.sp +Servers in an anycast deployment need to be able to verify each other's server +cookies. For this they need to share the secret used to construct and verify +the cookies. These cookie secrets can be specified in the configuration files +with the \fBcookie\-secret\fR and \fBcookie\-staging\-secret\fR options. +.sp +If no cookie secrets are provided via configuration file, server cookie secrets +can be added, dropped and activated with the \fInsd\-control\fR(8) tool. +These secrets will be stored persistently in the cookie secret file for which +the location can be specified with the \fBcookie\-secret\-file\fR option. +.sp +If no cookie secrets are provided via configuration file, and there is no or an +empty cookie secret file, a random cookie secret is generated. .TP .B cookie\-secret:\fR <128 bit hex string> -Servers in an anycast deployment need to be able to verify each other's DNS -Server Cookies. For this they need to share the secret used to construct and -verify the DNS Cookies. Default is a 128 bits random secret generated at -startup time. This option is ignored if a \fBcookie\-secret\-file\fR is -present. In that case the secrets from that file are used in DNS Cookie -calculations. +The cookie secret with which server cookies are created and can be verified. +If a \fBcookie\-secret\fR is specified via configuration file, cookie secrets +from the cookie secret file will be ignored. +.TP +.B cookie\-staging\-secret:\fR <128 bit hex string> +A cookie secret with which server cookies can be verified, but will not be +created. This is helpful in rolling cookie secrets in anycast setups. +.sp +A \fBcookie\-staging\-secret\fR can only be configured when there is also a +\fBcookie\-secret\fR configured. .TP .B cookie\-secret\-file:\fR -File from which the secrets are read used in DNS Cookie calculations. When this -file exists, the secrets in this file are used and the secret specified by the -\fBcookie-secret\fR option is ignored. -Default is @configdir@/nsd_cookiesecrets.txt -.sp -The content of this file must be manipulated with the \fBadd_cookie_secret\fR, -\fBdrop_cookie_secret\fR and \fBactivate_cookie_secret\fR commands to the -\fInsd\-control\fR(8) tool. Please see that manpage how to perform a safe -cookie secret rollover. +File from which the secrets are read used in DNS Cookie calculations. Secrets +will only be read from this file if no cookie secrets are given in the +configuration file via the \fBcookie\-secret\fR and +\fBcookie\-staging\-secret\fR options. +Default is "@cookiesecretsfile@" +.sp +In NSD version 4.10.1 and earlier, the default location of the cookie secret +file was "@configdir@/nsd_cookiesecrets.txt". For migration purposes, cookie +secrets will be read from that location if no value is given for the +\fBcookie\-secret\-file\fR option and when the current default location +("@cookiesecretsfile@") does not exist. +.sp +The content of the cookie secret file must be manipulated with the +\fBadd_cookie_secret\fR, \fBdrop_cookie_secret\fR and +\fBactivate_cookie_secret\fR commands to the \fInsd\-control\fR(8) tool. +Please see that manpage how to perform a safe cookie secret rollover. .TP .B tls\-service\-key:\fR If enabled, the server provides TLS service on TCP sockets with the TLS diff --git a/nsd.h b/nsd.h index d5f3bdf34..2401865f5 100644 --- a/nsd.h +++ b/nsd.h @@ -206,11 +206,20 @@ struct nsd_child #define NSD_COOKIE_HISTORY_SIZE 2 #define NSD_COOKIE_SECRET_SIZE 16 -typedef struct cookie_secret cookie_secret_type; struct cookie_secret { /** cookie secret */ uint8_t cookie_secret[NSD_COOKIE_SECRET_SIZE]; }; +typedef struct cookie_secret cookie_secret_type; +typedef cookie_secret_type cookie_secrets_type[NSD_COOKIE_HISTORY_SIZE]; + +enum cookie_secrets_source { + COOKIE_SECRETS_NONE = 0, + COOKIE_SECRETS_GENERATED = 1, + COOKIE_SECRETS_FROM_FILE = 2, + COOKIE_SECRETS_FROM_CONFIG = 3 +}; +typedef enum cookie_secrets_source cookie_secrets_source_type; /* NSD configuration and run-time variables */ typedef struct nsd nsd_type; @@ -359,15 +368,22 @@ struct nsd /* ratelimit for errors, packet count */ unsigned int err_limit_count; - /** do answer with server cookie when request contained cookie option */ + /* do answer with server cookie when request contained cookie option */ int do_answer_cookie; - /** how many cookies are there in the cookies array */ + /* how many cookies are there in the cookies array */ size_t cookie_count; /* keep track of the last `NSD_COOKIE_HISTORY_SIZE` * cookies as per rfc requirement .*/ - cookie_secret_type cookie_secrets[NSD_COOKIE_HISTORY_SIZE]; + cookie_secrets_type cookie_secrets; + + /* From where came the configured cookies */ + cookie_secrets_source_type cookie_secrets_source; + + /* The cookie secrets filename when they came from file; when + * cookie_secrets_source == COOKIE_SECRETS_FROM_FILE */ + char* cookie_secrets_filename; struct nsd_options* options; diff --git a/options.c b/options.c index fa39a642d..dcae09344 100644 --- a/options.c +++ b/options.c @@ -152,7 +152,9 @@ nsd_options_create(region_type* region) opt->proxy_protocol_port = NULL; opt->answer_cookie = 0; opt->cookie_secret = NULL; - opt->cookie_secret_file = CONFIGDIR"/nsd_cookiesecrets.txt"; + opt->cookie_staging_secret = NULL; + opt->cookie_secret_file = NULL; + opt->cookie_secret_file_is_default = 1; opt->control_enable = 0; opt->control_interface = NULL; opt->control_port = NSD_CONTROL_PORT; @@ -257,6 +259,16 @@ parse_options_file(struct nsd_options* opt, const char* file, opt->configfile = region_strdup(opt->region, file); + /* Set default cookie_secret_file value */ + if(opt->cookie_secret_file_is_default && !opt->cookie_secret_file) { + opt->cookie_secret_file = + region_strdup(opt->region, COOKIESECRETSFILE); + } + /* Semantic errors */ + if(opt->cookie_staging_secret && !opt->cookie_secret) { + c_error("a cookie-staging-secret cannot be configured without " + "also providing a cookie-secret"); + } RBTREE_FOR(pat, struct pattern_options*, opt->patterns) { struct pattern_options* old_pat = diff --git a/options.h b/options.h index 3ff1cbcf8..2567a62d8 100644 --- a/options.h +++ b/options.h @@ -209,8 +209,12 @@ struct nsd_options { int answer_cookie; /** cookie secret */ char *cookie_secret; + /** cookie staging secret */ + char *cookie_staging_secret; /** path to cookie secret store */ - char const* cookie_secret_file; + char *cookie_secret_file; + /** set when the cookie_secret_file whas not explicitely configured */ + uint8_t cookie_secret_file_is_default; /** enable verify */ int verify_enable; /** list of ip addresses used to serve zones for verification */ diff --git a/region-allocator.c b/region-allocator.c index f53841ad1..e4c75a6ab 100644 --- a/region-allocator.c +++ b/region-allocator.c @@ -428,6 +428,25 @@ region_strdup(region_type *region, const char *string) return (char *) region_alloc_init(region, string, strlen(string) + 1); } +void +region_str_replace(region_type *region, char **to_replace, const char *string) +{ + assert(to_replace); + if(!*to_replace) { + if(!string) + return; + *to_replace = region_strdup(region, string); + } + else if(!string) { + region_recycle(region, *to_replace, strlen(*to_replace) + 1); + *to_replace = NULL; + } + else if(strcmp(*to_replace, string)) { + region_recycle(region, *to_replace, strlen(*to_replace) + 1); + *to_replace = region_strdup(region, string); + } +} + void region_recycle(region_type *region, void *block, size_t size) { diff --git a/region-allocator.h b/region-allocator.h index 99af23350..fc6496ffd 100644 --- a/region-allocator.h +++ b/region-allocator.h @@ -129,6 +129,11 @@ void region_free_all(region_type *region); */ char *region_strdup(region_type *region, const char *string); +/* + * Replace a string on the to_replace location, if string is different + */ +void region_str_replace(region_type* region, char **to_replace, + const char *string); /* * Recycle an allocated memory block. Pass size used to alloc it. * Does nothing if recycling is not enabled for the region. diff --git a/remote.c b/remote.c index 2e79b3a4a..83d791954 100644 --- a/remote.c +++ b/remote.c @@ -1969,21 +1969,66 @@ repat_options_changed(xfrd_state_type* xfrd, struct nsd_options* newopt) return 0; } +static int opt_str_changed(const char* old, const char* new) +{ return !old ? ( !new ? 0 : 1 ) : ( !new ? 1 : strcasecmp(old, new) ); } + +/** true if cookie options are different that can be set via repat. */ +static int +repat_cookie_options_changed(struct nsd_options* old, struct nsd_options* new) +{ + return old->answer_cookie != new->answer_cookie + || opt_str_changed( old->cookie_secret + , new->cookie_secret) + || opt_str_changed( old->cookie_staging_secret + , new->cookie_staging_secret) + || old->cookie_secret_file_is_default != + new->cookie_secret_file_is_default + || opt_str_changed( old->cookie_secret_file + , new->cookie_secret_file); +} + /** check if global options have changed */ static void repat_options(xfrd_state_type* xfrd, struct nsd_options* newopt) { + struct nsd_options* oldopt = xfrd->nsd->options; + if(repat_options_changed(xfrd, newopt)) { /* update our options */ #ifdef RATELIMIT - xfrd->nsd->options->rrl_ratelimit = newopt->rrl_ratelimit; - xfrd->nsd->options->rrl_whitelist_ratelimit = newopt->rrl_whitelist_ratelimit; - xfrd->nsd->options->rrl_slip = newopt->rrl_slip; + oldopt->rrl_ratelimit = newopt->rrl_ratelimit; + oldopt->rrl_whitelist_ratelimit = newopt->rrl_whitelist_ratelimit; + oldopt->rrl_slip = newopt->rrl_slip; #endif task_new_opt_change(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task, newopt); xfrd_set_reload_now(xfrd); } + if(repat_cookie_options_changed(oldopt, newopt)) { + /* update our options */ + oldopt->answer_cookie = newopt->answer_cookie; + region_str_replace( oldopt->region + , &oldopt->cookie_secret + , newopt->cookie_secret); + region_str_replace( oldopt->region + , &oldopt->cookie_staging_secret + , newopt->cookie_staging_secret); + oldopt->cookie_secret_file_is_default = + newopt->cookie_secret_file_is_default; + region_str_replace( oldopt->region + , &oldopt->cookie_secret_file + , newopt->cookie_secret_file); + + xfrd->nsd->cookie_count = 0; + xfrd->nsd->cookie_secrets_source = COOKIE_SECRETS_NONE; + reconfig_cookies(xfrd->nsd, newopt); + task_new_cookies( xfrd->nsd->task[xfrd->nsd->mytask] + , xfrd->last_task + , xfrd->nsd->do_answer_cookie + , xfrd->nsd->cookie_count + , xfrd->nsd->cookie_secrets); + xfrd_set_reload_now(xfrd); + } } /** print errors over ssl, gets pointer-to-pointer to ssl, so it can set @@ -2340,19 +2385,42 @@ do_del_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg) { send_ok(ssl); } + +static int +can_dump_cookie_secrets(RES* ssl, nsd_type* const nsd) +{ + if(!nsd->options->cookie_secret_file) + (void)ssl_printf(ssl, "error: empty cookie-secret-file\n"); + + else if(nsd->cookie_secrets_source == COOKIE_SECRETS_FROM_CONFIG) + (void)ssl_printf(ssl, "error: cookie secrets are already " + "configured. Remove \"cookie-secret:\" and " + "\"cookie-staging-secret:\" entries from configuration " + "first (and reconfig) before managing cookies with " + "nsd-control\n"); + else + return 1; + return 0; + +} + /* returns `0` on failure */ static int -cookie_secret_file_dump(RES* ssl, nsd_type const* nsd) { - char const* secret_file = nsd->options->cookie_secret_file; +cookie_secret_file_dump_and_reload(RES* ssl, nsd_type* const nsd) { char secret_hex[NSD_COOKIE_SECRET_SIZE * 2 + 1]; FILE* f; size_t i; - assert( secret_file != NULL ); /* open write only and truncate */ - if((f = fopen(secret_file, "w")) == NULL ) { - (void)ssl_printf(ssl, "unable to open cookie secret file %s: %s", - secret_file, strerror(errno)); + if(!nsd->options->cookie_secret_file) { + (void)ssl_printf(ssl, "cookie-secret-file empty\n"); + return 0; + } + else if((f = fopen(nsd->options->cookie_secret_file, "w")) == NULL ) { + (void)ssl_printf( ssl + , "unable to open cookie secret file %s: %s\n" + , nsd->options->cookie_secret_file + , strerror(errno)); return 0; } for(i = 0; i < nsd->cookie_count; i++) { @@ -2366,65 +2434,77 @@ cookie_secret_file_dump(RES* ssl, nsd_type const* nsd) { } explicit_bzero(secret_hex, sizeof(secret_hex)); fclose(f); + nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; + region_str_replace(nsd->region, &nsd->cookie_secrets_filename + , nsd->options->cookie_secret_file); + task_new_cookies(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task, + nsd->do_answer_cookie, nsd->cookie_count, nsd->cookie_secrets); + xfrd_set_reload_now(xfrd); + send_ok(ssl); return 1; } static void do_activate_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { nsd_type* nsd = xrfd->nsd; + size_t backup_cookie_count; + cookie_secrets_type backup_cookie_secrets; (void)arg; + if(!can_dump_cookie_secrets(ssl, xfrd->nsd)) + return; + if(nsd->cookie_count <= 1 ) { (void)ssl_printf(ssl, "error: no staging cookie secret to activate\n"); return; } - if(!nsd->options->cookie_secret_file || !nsd->options->cookie_secret_file[0]) { - (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); - return; - } - if(!cookie_secret_file_dump(ssl, nsd)) { - (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", - nsd->options->cookie_secret_file); - return; - } + backup_cookie_count = nsd->cookie_count; + memcpy( backup_cookie_secrets, nsd->cookie_secrets + , sizeof(cookie_secrets_type)); activate_cookie_secret(nsd); - (void)cookie_secret_file_dump(ssl, nsd); - task_new_activate_cookie_secret(xfrd->nsd->task[xfrd->nsd->mytask], - xfrd->last_task); - xfrd_set_reload_now(xfrd); - send_ok(ssl); + if(!cookie_secret_file_dump_and_reload(ssl, nsd)) { + memcpy( nsd->cookie_secrets, backup_cookie_secrets + , sizeof(cookie_secrets_type)); + nsd->cookie_count = backup_cookie_count; + } + explicit_bzero(backup_cookie_secrets, sizeof(cookie_secrets_type)); } static void do_drop_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { nsd_type* nsd = xrfd->nsd; + size_t backup_cookie_count; + cookie_secrets_type backup_cookie_secrets; (void)arg; + if(!can_dump_cookie_secrets(ssl, xfrd->nsd)) + return; + if(nsd->cookie_count <= 1 ) { (void)ssl_printf(ssl, "error: can not drop the currently active cookie secret\n"); return; } - if(!nsd->options->cookie_secret_file || !nsd->options->cookie_secret_file[0]) { - (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); - return; - } - if(!cookie_secret_file_dump(ssl, nsd)) { - (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", - nsd->options->cookie_secret_file); - return; - } + backup_cookie_count = nsd->cookie_count; + memcpy( backup_cookie_secrets, nsd->cookie_secrets + , sizeof(cookie_secrets_type)); drop_cookie_secret(nsd); - (void)cookie_secret_file_dump(ssl, nsd); - task_new_drop_cookie_secret(xfrd->nsd->task[xfrd->nsd->mytask], - xfrd->last_task); - xfrd_set_reload_now(xfrd); - send_ok(ssl); + if(!cookie_secret_file_dump_and_reload(ssl, nsd)) { + memcpy( nsd->cookie_secrets, backup_cookie_secrets + , sizeof(cookie_secrets_type)); + nsd->cookie_count = backup_cookie_count; + } + explicit_bzero(backup_cookie_secrets, sizeof(cookie_secrets_type)); } static void do_add_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { nsd_type* nsd = xrfd->nsd; uint8_t secret[NSD_COOKIE_SECRET_SIZE]; + size_t backup_cookie_count; + cookie_secrets_type backup_cookie_secrets; + + if(!can_dump_cookie_secrets(ssl, xfrd->nsd)) + return; if(*arg == '\0') { (void)ssl_printf(ssl, "error: missing argument (cookie_secret)\n"); @@ -2443,27 +2523,26 @@ do_add_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { (void)ssl_printf(ssl, "please provide a 128bit hex encoded secret\n"); return; } - if(!nsd->options->cookie_secret_file || !nsd->options->cookie_secret_file[0]) { - explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE); - explicit_bzero(arg, strlen(arg)); - (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); - return; - } - if(!cookie_secret_file_dump(ssl, nsd)) { - explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE); - explicit_bzero(arg, strlen(arg)); - (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", - nsd->options->cookie_secret_file); - return; + explicit_bzero(arg, strlen(arg)); + + backup_cookie_count = nsd->cookie_count; + memcpy( backup_cookie_secrets, nsd->cookie_secrets + , sizeof(cookie_secrets_type)); + if(nsd->cookie_secrets_source != COOKIE_SECRETS_FROM_FILE + && nsd->cookie_secrets_source != COOKIE_SECRETS_FROM_CONFIG) { + nsd->cookie_count = 0; } add_cookie_secret(nsd, secret); explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE); - (void)cookie_secret_file_dump(ssl, nsd); - task_new_add_cookie_secret(xfrd->nsd->task[xfrd->nsd->mytask], - xfrd->last_task, arg); - explicit_bzero(arg, strlen(arg)); - xfrd_set_reload_now(xfrd); - send_ok(ssl); + if(!cookie_secret_file_dump_and_reload(ssl, nsd)) { + explicit_bzero(arg, strlen(arg)); + (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n" + , nsd->options->cookie_secret_file); + memcpy( nsd->cookie_secrets, backup_cookie_secrets + , sizeof(cookie_secrets_type)); + nsd->cookie_count = backup_cookie_count; + } + explicit_bzero(backup_cookie_secrets, sizeof(cookie_secrets_type)); } static void @@ -2473,7 +2552,23 @@ do_print_cookie_secrets(RES* ssl, xfrd_state_type* xrfd, char* arg) { int i; (void)arg; - /* (void)ssl_printf(ssl, "cookie_secret_count=%zu\n", nsd->cookie_count); */ + switch(nsd->cookie_secrets_source){ + case COOKIE_SECRETS_NONE: + break; + case COOKIE_SECRETS_GENERATED: + ssl_printf(ssl, "source : random generated\n"); + break; + case COOKIE_SECRETS_FROM_FILE: + ssl_printf( ssl, "source : \"%s\"\n" + , nsd->cookie_secrets_filename); + break; + case COOKIE_SECRETS_FROM_CONFIG: + ssl_printf(ssl, "source : configuration\n"); + break; + default: + ssl_printf(ssl, "source : unknown\n"); + break; + } for(i = 0; (size_t)i < nsd->cookie_count; i++) { struct cookie_secret const* cs = &nsd->cookie_secrets[i]; ssize_t const len = hex_ntop(cs->cookie_secret, NSD_COOKIE_SECRET_SIZE, diff --git a/tpkg/checkconf.tdir/checkconf.check b/tpkg/checkconf.tdir/checkconf.check index 5cd52731f..f24511053 100644 --- a/tpkg/checkconf.tdir/checkconf.check +++ b/tpkg/checkconf.tdir/checkconf.check @@ -57,7 +57,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -192,7 +194,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -270,7 +274,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -357,7 +363,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -488,7 +496,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -561,7 +571,7 @@ checkconf.nsd09.conf:9: error: expected yes or no read checkconf.nsd09.conf failed: 1 errors in configuration file checkconf.nsd10.conf:10: error: expected a number read checkconf.nsd10.conf failed: 1 errors in configuration file -# Read file checkconf.nsd11.conf: 1 patterns, 0 fixed-zones, 0 keys, 0 tls-auth. +# Read file checkconf.nsd11.conf: 5 patterns, 1 fixed-zones, 0 keys, 0 tls-auth. # Config settings. server: debug-mode: no @@ -617,7 +627,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -635,7 +647,23 @@ verify: verifier-feed-zone: yes verifier-timeout: 0 +pattern: + name: "group1" + catalog-producer-zone: "producer.catalog.invalid" + +pattern: + name: "group2" + catalog-producer-zone: "producer.catalog.invalid" + +pattern: + name: "group3" + catalog-producer-zone: "producer.catalog.invalid" + pattern: name: "myzones" catalog: consumer catalog-member-pattern: "mycatalog2" + +zone: + name: "producer.catalog.invalid" + catalog: producer diff --git a/tpkg/checkconf.tdir/checkconf.check2.in b/tpkg/checkconf.tdir/checkconf.check2.in index 072edcc22..5f01c8e1f 100644 --- a/tpkg/checkconf.tdir/checkconf.check2.in +++ b/tpkg/checkconf.tdir/checkconf.check2.in @@ -57,7 +57,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -192,7 +194,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -270,7 +274,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -357,7 +363,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -488,7 +496,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -617,7 +627,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no diff --git a/tpkg/dns-cookies.tdir/dns-cookies.by-config.conf b/tpkg/dns-cookies.tdir/dns-cookies.by-config.conf new file mode 100644 index 000000000..e3e28b3d1 --- /dev/null +++ b/tpkg/dns-cookies.tdir/dns-cookies.by-config.conf @@ -0,0 +1,15 @@ +server: + username: "" + zonesdir: "" + xfrdfile: "" + logfile: "nsd.log" + ip-address: lo + +include: "cookiesecrets.conf" + +remote-control: + control-enable: yes + control-key-file: "dns-cookies.nsd_control.key" + control-cert-file: "dns-cookies.nsd_control.pem" + server-key-file: "dns-cookies.nsd_server.key" + server-cert-file: "dns-cookies.nsd_server.pem" diff --git a/tpkg/dns-cookies.tdir/dns-cookies.by-nsd-control.conf b/tpkg/dns-cookies.tdir/dns-cookies.by-nsd-control.conf new file mode 100644 index 000000000..c06757e9f --- /dev/null +++ b/tpkg/dns-cookies.tdir/dns-cookies.by-nsd-control.conf @@ -0,0 +1,15 @@ +server: + username: "" + zonesdir: "" + xfrdfile: "" + cookie-secret-file: "cookiesecrets.txt" + answer-cookie: yes + logfile: "nsd.log" + ip-address: lo + +remote-control: + control-enable: yes + control-key-file: "dns-cookies.nsd_control.key" + control-cert-file: "dns-cookies.nsd_control.pem" + server-key-file: "dns-cookies.nsd_server.key" + server-cert-file: "dns-cookies.nsd_server.pem" diff --git a/tpkg/dns-cookies.tdir/dns-cookies.conf b/tpkg/dns-cookies.tdir/dns-cookies.conf new file mode 100644 index 000000000..4af1baaf7 --- /dev/null +++ b/tpkg/dns-cookies.tdir/dns-cookies.conf @@ -0,0 +1,18 @@ +server: + username: "" + zonesdir: "" + xfrdfile: "" + cookie-secret-file: "cookiesecrets.txt" + answer-cookie: yes + logfile: "nsd.log" + ip-address: lo + + cookie-secret: "e5e973e5a6b2a43f48e7dc849e37bfcf" + cookie-staging-secret: "342ed7de87073f9011b5a6c849d91caa" + +remote-control: + control-enable: yes + control-key-file: "dns-cookies.nsd_control.key" + control-cert-file: "dns-cookies.nsd_control.pem" + server-key-file: "dns-cookies.nsd_server.key" + server-cert-file: "dns-cookies.nsd_server.pem" diff --git a/tpkg/dns-cookies.tdir/dns-cookies.defaults.conf b/tpkg/dns-cookies.tdir/dns-cookies.defaults.conf new file mode 100644 index 000000000..ef90ffd62 --- /dev/null +++ b/tpkg/dns-cookies.tdir/dns-cookies.defaults.conf @@ -0,0 +1,7 @@ +server: + username: "" + answer-cookie: yes + ip-address: lo + +remote-control: + control-enable: yes diff --git a/tpkg/dns-cookies.tdir/dns-cookies.dsc b/tpkg/dns-cookies.tdir/dns-cookies.dsc index 48bf8b8b3..6ab32b8af 100644 --- a/tpkg/dns-cookies.tdir/dns-cookies.dsc +++ b/tpkg/dns-cookies.tdir/dns-cookies.dsc @@ -1,11 +1,12 @@ BaseName: dns-cookies Version: 1.0 -Description: Test DNS Cookies +Description: Execute tests from Appendix A of RFC 9018 and tests involving + configuring DNS cookie secrets. CreationDate: Mon Jun 21 13:39:31 UTC 2021 Maintainer: Tom Carpay Category: Component: -CmdDepends: +CmdDepends: unshare, faketime, ip Depends: Help: Pre: diff --git a/tpkg/dns-cookies.tdir/nsd_control.key b/tpkg/dns-cookies.tdir/dns-cookies.nsd_control.key similarity index 100% rename from tpkg/dns-cookies.tdir/nsd_control.key rename to tpkg/dns-cookies.tdir/dns-cookies.nsd_control.key diff --git a/tpkg/dns-cookies.tdir/nsd_control.pem b/tpkg/dns-cookies.tdir/dns-cookies.nsd_control.pem similarity index 100% rename from tpkg/dns-cookies.tdir/nsd_control.pem rename to tpkg/dns-cookies.tdir/dns-cookies.nsd_control.pem diff --git a/tpkg/dns-cookies.tdir/nsd_server.key b/tpkg/dns-cookies.tdir/dns-cookies.nsd_server.key similarity index 100% rename from tpkg/dns-cookies.tdir/nsd_server.key rename to tpkg/dns-cookies.tdir/dns-cookies.nsd_server.key diff --git a/tpkg/dns-cookies.tdir/nsd_server.pem b/tpkg/dns-cookies.tdir/dns-cookies.nsd_server.pem similarity index 100% rename from tpkg/dns-cookies.tdir/nsd_server.pem rename to tpkg/dns-cookies.tdir/dns-cookies.nsd_server.pem diff --git a/tpkg/dns-cookies.tdir/dns-cookies.test b/tpkg/dns-cookies.tdir/dns-cookies.test index bafaab76f..827964fc8 100644 --- a/tpkg/dns-cookies.tdir/dns-cookies.test +++ b/tpkg/dns-cookies.tdir/dns-cookies.test @@ -8,6 +8,11 @@ if test ! -x "`which unshare 2>&1`"; then echo "no unshare (from util-linux package) available, skip test" exit 0 fi +if test ! -x "`which faketime 2>&1`"; then + echo "no faketime available, skip test" + exit 0 +fi + echo "Starting test A1" unshare -rUn bash dns-cookies.test.A1 || exit 1 @@ -20,3 +25,10 @@ unshare -rUn bash dns-cookies.test.A3 || exit 1 echo "Starting test A4" unshare -rUn bash dns-cookies.test.A4 || exit 1 + +echo "Starting test C1" +unshare -rUn bash dns-cookies.test.C1 || exit 1 + +echo "Starting test C2" +unshare -mrUn bash dns-cookies.test.C2 || exit 1 + diff --git a/tpkg/dns-cookies.tdir/dns-cookies.test.A1 b/tpkg/dns-cookies.tdir/dns-cookies.test.A1 index 2a0d5af95..7cf100651 100644 --- a/tpkg/dns-cookies.tdir/dns-cookies.test.A1 +++ b/tpkg/dns-cookies.tdir/dns-cookies.test.A1 @@ -15,7 +15,7 @@ TPKG_NSD_PID="nsd.pid.$$" TPKG_NSD="$PRE/nsd" # start nsd with faketime -TZ=UTC faketime -f '2019-06-05 10:53:05' $TPKG_NSD -c dns-cookies.test.conf -P $TPKG_NSD_PID & +TZ=UTC faketime -f '2019-06-05 10:53:05' $TPKG_NSD -c dns-cookies.conf -P $TPKG_NSD_PID & wait_nsd_up nsd.log echo "faketime nsd instance A1 running" diff --git a/tpkg/dns-cookies.tdir/dns-cookies.test.A2 b/tpkg/dns-cookies.tdir/dns-cookies.test.A2 index 7d68a946e..f63de0712 100644 --- a/tpkg/dns-cookies.tdir/dns-cookies.test.A2 +++ b/tpkg/dns-cookies.tdir/dns-cookies.test.A2 @@ -15,7 +15,7 @@ TPKG_NSD_PID="nsd.pid.$$" TPKG_NSD="$PRE/nsd" # start nsd with faketime -TZ=UTC faketime -f '2019-06-05 11:33:05' $TPKG_NSD -c dns-cookies.test.conf -P $TPKG_NSD_PID & +TZ=UTC faketime -f '2019-06-05 11:33:05' $TPKG_NSD -c dns-cookies.conf -P $TPKG_NSD_PID & wait_nsd_up nsd.log echo "faketime nsd instance A2 running" diff --git a/tpkg/dns-cookies.tdir/dns-cookies.test.A3 b/tpkg/dns-cookies.tdir/dns-cookies.test.A3 index bc20ef01a..898bd73fb 100644 --- a/tpkg/dns-cookies.tdir/dns-cookies.test.A3 +++ b/tpkg/dns-cookies.tdir/dns-cookies.test.A3 @@ -15,7 +15,7 @@ TPKG_NSD_PID="nsd.pid.$$" TPKG_NSD="$PRE/nsd" # start nsd with faketime -TZ=UTC faketime -f '2019-06-05 11:38:20' $TPKG_NSD -c dns-cookies.test.conf -P $TPKG_NSD_PID & +TZ=UTC faketime -f '2019-06-05 11:38:20' $TPKG_NSD -c dns-cookies.conf -P $TPKG_NSD_PID & wait_nsd_up nsd.log echo "faketime nsd instance A3 running" diff --git a/tpkg/dns-cookies.tdir/dns-cookies.test.A4 b/tpkg/dns-cookies.tdir/dns-cookies.test.A4 index 2a507bdd5..8c85d6f32 100644 --- a/tpkg/dns-cookies.tdir/dns-cookies.test.A4 +++ b/tpkg/dns-cookies.tdir/dns-cookies.test.A4 @@ -20,21 +20,21 @@ TPKG_NSD_CONTROL="$PRE/nsd-control" # option in the config file, but no cookies configured. touch cookiesecrets.txt # start nsd with faketime -TZ=UTC faketime -f '2019-06-05 13:39:21' $TPKG_NSD -c dns-cookies.test.conf -P $TPKG_NSD_PRI_PID & +TZ=UTC faketime -f '2019-06-05 13:39:21' $TPKG_NSD -c dns-cookies.by-nsd-control.conf -P $TPKG_NSD_PRI_PID & wait_nsd_up nsd.log echo "faketime nsd instance A4 running" # add secret -$TPKG_NSD_CONTROL -c dns-cookies.test.conf add_cookie_secret dd3bdf9344b678b185a6f5cb60fca715 -$TPKG_NSD_CONTROL -c dns-cookies.test.conf add_cookie_secret 445536bcd2513298075a5d379663c962 +$TPKG_NSD_CONTROL -c dns-cookies.by-nsd-control.conf add_cookie_secret dd3bdf9344b678b185a6f5cb60fca715 +$TPKG_NSD_CONTROL -c dns-cookies.by-nsd-control.conf add_cookie_secret 445536bcd2513298075a5d379663c962 -$TPKG_NSD_CONTROL -c dns-cookies.test.conf print_cookie_secrets > cookie_secrets.a4-1 +$TPKG_NSD_CONTROL -c dns-cookies.by-nsd-control.conf print_cookie_secrets > cookie_secrets.a4-1 if ! grep -q "445536bcd2513298075a5d379663c962" cookie_secrets.a4-1 \ || ! grep -q "dd3bdf9344b678b185a6f5cb60fca715" cookie_secrets.a4-1 then sleep 1 - $TPKG_NSD_CONTROL -c dns-cookies.test.conf print_cookie_secrets > cookie_secrets.a4-1 + $TPKG_NSD_CONTROL -c dns-cookies.by-nsd-control.conf print_cookie_secrets > cookie_secrets.a4-1 if ! grep -q "445536bcd2513298075a5d379663c962" cookie_secrets.a4-1 \ || ! grep -q "dd3bdf9344b678b185a6f5cb60fca715" cookie_secrets.a4-1 then @@ -56,13 +56,13 @@ else fi # secret rollover -$TPKG_NSD_CONTROL -c dns-cookies.test.conf activate_cookie_secret +$TPKG_NSD_CONTROL -c dns-cookies.by-nsd-control.conf activate_cookie_secret -$TPKG_NSD_CONTROL -c dns-cookies.test.conf print_cookie_secrets > cookie_secrets.a4-2 +$TPKG_NSD_CONTROL -c dns-cookies.by-nsd-control.conf print_cookie_secrets > cookie_secrets.a4-2 if ! grep -q "^active.*445536bcd2513298075a5d379663c962" cookie_secrets.a4-2 then sleep 1 - $TPKG_NSD_CONTROL -c dns-cookies.test.conf print_cookie_secrets > cookie_secrets.a4-2 + $TPKG_NSD_CONTROL -c dns-cookies.by-nsd-control.conf print_cookie_secrets > cookie_secrets.a4-2 if ! grep -q "^active.*445536bcd2513298075a5d379663c962" cookie_secrets.a4-2 then cat cookie_secrets.a4-2 diff --git a/tpkg/dns-cookies.tdir/dns-cookies.test.C1 b/tpkg/dns-cookies.tdir/dns-cookies.test.C1 new file mode 100644 index 000000000..06a4d9292 --- /dev/null +++ b/tpkg/dns-cookies.tdir/dns-cookies.test.C1 @@ -0,0 +1,196 @@ +# #-- dns-cookies.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test +. ../common.sh + +# set environment interfaces +ip address add 198.51.100.100 dev lo +ip link set dev lo up + +# set NSD environment variables +PRE="../.." +TPKG_NSD_PID="nsd.pid.$$" +TPKG_NSD="$PRE/nsd" +TPKG_NSD_CONTROL="$PRE/nsd-control" + + +cat << EOF > cookiesecrets.conf +server: + answer-cookie: yes + cookie-secret-file: "cookiesecrets.txt" + cookie-secret: c0be1977faa8661a00944c337c1a07e9 + cookie-staging-secret: 1f4fd8475cb97ed70da5d7a019ad53f1 +EOF + +# start nsd with faketime +TZ=UTC faketime -f '2024-08-26 15:37:05' $TPKG_NSD -c dns-cookies.by-config.conf -P $TPKG_NSD_PID & +wait_nsd_up nsd.log +NSD_PID=`cat $TPKG_NSD_PID` +echo "faketime nsd instance C1 running" + +if $TPKG_NSD_CONTROL -c dns-cookies.by-config.conf add_cookie_secret dd3bdf9344b678b185a6f5cb60fca715 >/dev/null +then + echo "C.1.1 Adding cookies by nsd-control should not be allowed with configured cookies" + # kill NSD + kill_pid $NSD_PID + rm -f nsd.log + exit 1 +else + echo "C.1.1 Adding cookies by nsd-control is not allowed with configured cookies" +fi + +# dig @198.51.100.100 +cookie=2464c4abcf10c957 +# $TPKG_NSD_CONTROL -c dns-cookies.by-config.conf print_cookie_secrets +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.2 + +cat << EOF > cookiesecrets.conf +server: + answer-cookie: yes + cookie-secret-file: "cookiesecrets.txt" + cookie-staging-secret: c0be1977faa8661a00944c337c1a07e9 + cookie-secret: 1f4fd8475cb97ed70da5d7a019ad53f1 +EOF +$TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig + +sleep .1 + +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.3 + +cat << EOF > cookiesecrets.conf +server: + answer-cookie: yes + cookie-secret-file: "cookiesecrets.txt" +EOF +$TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig + +sleep .1 + +$TPKG_NSD_CONTROL -c dns-cookies.by-nsd-control.conf add_cookie_secret dd3bdf9344b678b185a6f5cb60fca715 + +sleep .1 + +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.4 + +cat << EOF > cookiesecrets.conf +server: + answer-cookie: yes + cookie-secret-file: "cookiesecrets.txt" + cookie-staging-secret: c0be1977faa8661a00944c337c1a07e9 + cookie-secret: 1f4fd8475cb97ed70da5d7a019ad53f1 +EOF +$TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig + +sleep .1 + +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.5 + +cat << EOF > cookiesecrets.conf +server: + answer-cookie: yes + cookie-secret-file: "cookiesecrets.txt" + cookie-staging-secret: c0be1977faa8661a00944c337c1a07e9 + cookie-secret: error +EOF +if $TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig 2>/dev/null +then + echo "C.1.6. Reconfigure was allowed with broken cookies" + # kill NSD + kill_pid $NSD_PID + rm -f nsd.log + exit 1 +else + echo "C.1.6. Cannot reconfigure with broken cookies" +fi + +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.7 + +cat << EOF > cookiesecrets.conf +server: + answer-cookie: yes + cookie-secret-file: "cookiesecrets.txt" + cookie-staging-secret: c0be1977faa8661a00944c337c1a07e9 +EOF +if $TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig 2>/dev/null +then + echo "C.1.8. Reconfigure was allowed with only a staging secret" + # kill NSD + kill_pid $NSD_PID + rm -f nsd.log + exit 1 +else + echo "C.1.8. Cannot reconfigure with only a staging secret" +fi + +cat << EOF > cookiesecrets.conf +server: + answer-cookie: yes + cookie-secret-file: "cookiesecrets.txt" +EOF +$TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig + +sleep .1 + +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.9 + +# kill NSD +kill_pid $NSD_PID +rm -f nsd.log + +ERRORS=0 +if grep -q "2464c4abcf10c9570100000066cca121c6adacef8ac2bcdd" dig.output.c1.2 +then + echo "C.1.2. Active server cookie matched" +else + echo "C.1.2. Active server cookie failed to match" + cat dig.output.c1.2 + ERRORS=1 +fi + +if grep -q "2464c4abcf10c9570100000066cca121b789adffadb783df" dig.output.c1.3 +then + echo "C.1.3. Active server cookie matched" +else + echo "C.1.3. Active server cookie failed to match" + cat dig.output.c1.3 + ERRORS=1 +fi + +if grep -q "2464c4abcf10c9570100000066cca1218cf0b73f53bc2520" dig.output.c1.4 +then + echo "C.1.4. Active server cookie from file matched" +else + echo "C.1.4. Active server cookie from file failed to match" + cat dig.output.c1.4 + ERRORS=1 +fi + +if grep -q "2464c4abcf10c9570100000066cca121b789adffadb783df" dig.output.c1.5 +then + echo "C.1.5. Configured secrets prefered over dynamic secrets" +else + echo "C.1.5. Error on configured secrets over dynamic secrets preference" + cat dig.output.c1.5 + ERRORS=1 +fi + +if grep -q "2464c4abcf10c9570100000066cca121b789adffadb783df" dig.output.c1.7 +then + echo "C.1.7. Cookie secrets remain after broken reconfig" +else + echo "C.1.7. Cookie secrets did not remain after broken reconfig" + cat dig.output.c1.7 + ERRORS=1 +fi + +if grep -q "2464c4abcf10c9570100000066cca1218cf0b73f53bc2520" dig.output.c1.9 +then + echo "C.1.9. Active server cookie from file matched again" +else + echo "C.1.9. Active server cookie from file failed to match again" + cat dig.output.c1.9 + ERRORS=1 +fi + +exit $ERRORS diff --git a/tpkg/dns-cookies.tdir/dns-cookies.test.C2 b/tpkg/dns-cookies.tdir/dns-cookies.test.C2 new file mode 100644 index 000000000..8b1dc8a7e --- /dev/null +++ b/tpkg/dns-cookies.tdir/dns-cookies.test.C2 @@ -0,0 +1,89 @@ +# #-- dns-cookies.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test +. ../common.sh + +mkdir -p var/db/nsd +mkdir -p var/run +mkdir -p var/log +mkdir -p etc/nsd + +mount --bind var /var +mount --bind etc /etc + +cp -p dns-cookies.nsd_control.key /etc/nsd/nsd_control.key +cp -p dns-cookies.nsd_control.pem /etc/nsd/nsd_control.pem +cp -p dns-cookies.nsd_server.key /etc/nsd/nsd_server.key +cp -p dns-cookies.nsd_server.pem /etc/nsd/nsd_server.pem + +cat << EOF > /etc/nsd/nsd_cookiesecrets.txt +dd3bdf9344b678b185a6f5cb60fca715 +445536bcd2513298075a5d379663c962 +EOF + +# set environment interfaces +ip address add 198.51.100.100 dev lo +ip link set dev lo up + +# set NSD environment variables +PRE="../.." +TPKG_NSD_PID=`grep '^#define PIDFILE ' ../../config.h | sed -e 's/^[^"]*"//g' -e 's/"[^"]*$//g'` +TPKG_NSD="$PRE/nsd" +TPKG_NSD_CONTROL="$PRE/nsd-control" + + +# start nsd with faketime +TZ=UTC faketime -f '2024-08-28 14:49:05' $TPKG_NSD -c dns-cookies.defaults.conf & +sleep .1 + +echo "faketime nsd instance C2 running" + +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c2.1 + +$TPKG_NSD_CONTROL -c dns-cookies.defaults.conf activate_cookie_secret + +sleep .1 + +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c2.2 + +kill `cat /var/run/nsd.pid` +sleep .1 + +TZ=UTC faketime -f '2024-08-28 14:49:05' $TPKG_NSD -c dns-cookies.defaults.conf & +sleep .1 + +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c2.3 + +kill `cat /var/run/nsd.pid` +sleep .1 +rm -fr etc +rm -fr var +ERRORS=0 + +if grep -q "2464c4abcf10c9570100000066cf38e136666f01c0260ed1" dig.output.c2.1 +then + echo "C.2.1 Old location cookie matched" +else + echo "C.2.1 Old location cookie failed to match" + cat dig.output.c2.1 + ERRORS=1 +fi +if grep -q "2464c4abcf10c9570100000066cf38e1a75842528f29e33d" dig.output.c2.2 +then + echo "C.2.2 Automatic cookie secret file migration worked" +else + echo "C.2.2 Automatic cookie secret file migration did not worked" + cat dig.output.c2.2 + ERRORS=1 +fi +if grep -q "2464c4abcf10c9570100000066cf38e1a75842528f29e33d" dig.output.c2.3 +then + echo "C.2.3 Restart read cookies from new default secret file location" +else + echo "C.2.3 Restart did not read cookies from new default secret file location" + cat dig.output.c2.3 + ERRORS=1 +fi +exit $ERRORS diff --git a/tpkg/dns-cookies.tdir/dns-cookies.test.conf b/tpkg/dns-cookies.tdir/dns-cookies.test.conf deleted file mode 100644 index a14750f57..000000000 --- a/tpkg/dns-cookies.tdir/dns-cookies.test.conf +++ /dev/null @@ -1,17 +0,0 @@ -server: - username: "" - zonesdir: "" - xfrdfile: "" - cookie-secret-file: "cookiesecrets.txt" - answer-cookie: yes - logfile: "nsd.log" - ip-address: lo - - cookie-secret: "e5e973e5a6b2a43f48e7dc849e37bfcf" - -remote-control: - control-enable: yes - control-key-file: "nsd_control.key" - control-cert-file: "nsd_control.pem" - server-key-file: "nsd_server.key" - server-cert-file: "nsd_server.pem" diff --git a/util.c b/util.c index 624f72d0d..e598695b7 100644 --- a/util.c +++ b/util.c @@ -12,6 +12,9 @@ #include #include #include +#ifdef HAVE_OPENSSL_RAND_H +#include +#endif #include #include #include @@ -37,6 +40,7 @@ #include "rdata.h" #include "zonec.h" #include "nsd.h" +#include "options.h" #ifdef USE_MMAP_ALLOC #include @@ -1170,3 +1174,133 @@ void drop_cookie_secret(struct nsd* nsd) , NSD_COOKIE_SECRET_SIZE); nsd->cookie_count -= 1; } + +void reconfig_cookies(struct nsd* nsd, struct nsd_options* options) +{ + cookie_secret_type cookie_secrets[NSD_COOKIE_HISTORY_SIZE]; + char secret[NSD_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/]; + FILE* f = NULL; + size_t count = 0; + const char* fn; + size_t i, j; + + nsd->do_answer_cookie = options->answer_cookie; + + /* Cookie secrets in the configuration file take precedence */ + if(options->cookie_secret) { + ssize_t len = hex_pton(options->cookie_secret, + nsd->cookie_secrets[0].cookie_secret, + NSD_COOKIE_SECRET_SIZE); + + /* Cookie length guaranteed in configparser.y */ + assert(len == NSD_COOKIE_SECRET_SIZE); + nsd->cookie_count = 1; + if(options->cookie_staging_secret) { + len = hex_pton(options->cookie_staging_secret, + nsd->cookie_secrets[1].cookie_secret, + NSD_COOKIE_SECRET_SIZE); + /* Cookie length guaranteed in configparser.y */ + assert(len == NSD_COOKIE_SECRET_SIZE); + nsd->cookie_count = 2; + } + /*************************************************************/ + nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_CONFIG; + return; + /*************************************************************/ + } + /* Are cookies from file explicitly disabled? */ + if(!(fn = nsd->options->cookie_secret_file)) + goto generate_cookie_secrets; + + else if((f = fopen(fn, "r")) != NULL) + ; /* pass */ + + /* a non-existing cookie file is not necessarily an error */ + else if(errno != ENOENT) { + log_msg( LOG_ERR + , "error reading cookie secret file \"%s\": \"%s\"" + , fn, strerror(errno)); + goto generate_cookie_secrets; + + /* Only at startup cookie_secrets_source == COOKIE_SECRETS_NONE. + * Only then the previous default file location will be tried + * when the current default file location didn't exist. + */ + } else if(nsd->cookie_secrets_source == COOKIE_SECRETS_NONE + && nsd->options->cookie_secret_file_is_default + && (f = fopen((fn = CONFIGDIR"/nsd_cookiesecrets.txt"),"r"))) + ; /* pass */ + + else if(errno != ENOENT) { + log_msg( LOG_ERR + , "error reading cookie secret file \"%s\": \"%s\"" + , fn, strerror(errno)); + goto generate_cookie_secrets; + } else + goto generate_cookie_secrets; + + /* cookie secret file exists and is readable */ + for( count = 0; count < NSD_COOKIE_HISTORY_SIZE; count++ ) { + size_t secret_len = 0; + ssize_t decoded_len = 0; + if( fgets(secret, sizeof(secret), f) == NULL ) { break; } + secret_len = strlen(secret); + if( secret_len == 0 ) { break; } + assert( secret_len <= sizeof(secret) ); + secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len; + if( secret_len != NSD_COOKIE_SECRET_SIZE * 2 ) { + fclose(f); + log_msg( LOG_ERR + , "error parsing cookie secret file \"%s\"" + , fn); + explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); + explicit_bzero(secret, sizeof(secret)); + goto generate_cookie_secrets; + } + /* needed for `hex_pton`; stripping potential `\n` */ + secret[secret_len] = '\0'; + decoded_len = hex_pton(secret, cookie_secrets[count].cookie_secret, + NSD_COOKIE_SECRET_SIZE); + if( decoded_len != NSD_COOKIE_SECRET_SIZE ) { + fclose(f); + log_msg( LOG_ERR + , "error parsing cookie secret file \"%s\"" + , fn); + explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); + explicit_bzero(secret, sizeof(secret)); + goto generate_cookie_secrets; + } + explicit_bzero(secret, sizeof(secret)); + } + fclose(f); + if(count) { + nsd->cookie_count = count; + memcpy(nsd->cookie_secrets, cookie_secrets, sizeof(cookie_secrets)); + region_str_replace( nsd->region + , &nsd->cookie_secrets_filename, fn ); + explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); + /*************************************************************/ + nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; + return; + /*************************************************************/ + } + explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); + +generate_cookie_secrets: + /* Calculate a new random secret */ + srandom(getpid() ^ time(NULL)); + + for( j = 0; j < NSD_COOKIE_HISTORY_SIZE; j++) { +#if defined(HAVE_SSL) + if (!RAND_status() + || !RAND_bytes(nsd->cookie_secrets[j].cookie_secret, NSD_COOKIE_SECRET_SIZE)) +#endif + for (i = 0; i < NSD_COOKIE_SECRET_SIZE; i++) + nsd->cookie_secrets[j].cookie_secret[i] = random_generate(256); + } + nsd->cookie_count = 1; + /*********************************************************************/ + nsd->cookie_secrets_source = COOKIE_SECRETS_GENERATED; + /*********************************************************************/ +} + diff --git a/util.h b/util.h index b18901464..aedc94cb9 100644 --- a/util.h +++ b/util.h @@ -18,6 +18,7 @@ struct rr; struct buffer; struct region; struct nsd; +struct nsd_options; #ifdef HAVE_SYSLOG_H # include @@ -438,4 +439,7 @@ void activate_cookie_secret(struct nsd* nsd); /* Drop a cookie secret. Drops the staging secret. An active secret will not * be dropped. */ void drop_cookie_secret(struct nsd* nsd); +/* Configure nsd struct with how to respond to DNS Cookies based on options */ +void reconfig_cookies(struct nsd* nsd, struct nsd_options* options); + #endif /* UTIL_H */