From 6bc8ae062168040a97d517c5f7e7fc4a9e903f54 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 21 Aug 2024 14:16:24 +0200 Subject: [PATCH 01/10] --with-cookiesecretsfile=path for configure Partly addressing issue #196 --- Makefile.in | 5 ++++- configure.ac | 6 ++++++ nsd-control.8.in | 2 +- nsd.c | 6 +++--- nsd.conf.5.in | 2 +- options.c | 2 +- tpkg/checkconf.tdir/checkconf.check | 12 ++++++------ tpkg/checkconf.tdir/checkconf.check2.in | 12 ++++++------ 8 files changed, 28 insertions(+), 19 deletions(-) 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/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/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..397d656fc 100644 --- a/nsd.c +++ b/nsd.c @@ -905,12 +905,12 @@ bind8_stats (struct nsd *nsd) 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; + char const* file = nsd->options->cookie_secret_file + ? nsd->options->cookie_secret_file : COOKIESECRETSFILE; 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; } @@ -1592,7 +1592,7 @@ main(int argc, char *argv[]) } #endif /* HAVE_SSL */ - if(nsd.options->cookie_secret_file && nsd.options->cookie_secret_file[0] + 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"); } diff --git a/nsd.conf.5.in b/nsd.conf.5.in index 0d61939dc..b6661e02b 100644 --- a/nsd.conf.5.in +++ b/nsd.conf.5.in @@ -490,7 +490,7 @@ calculations. 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 +Default is @cookiesecretsfile@ .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 diff --git a/options.c b/options.c index 29f0eb6d8..5456ea4d7 100644 --- a/options.c +++ b/options.c @@ -151,7 +151,7 @@ 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_secret_file = NULL; opt->control_enable = 0; opt->control_interface = NULL; opt->control_port = NSD_CONTROL_PORT; diff --git a/tpkg/checkconf.tdir/checkconf.check b/tpkg/checkconf.tdir/checkconf.check index bd9c05614..0d7ab56f4 100644 --- a/tpkg/checkconf.tdir/checkconf.check +++ b/tpkg/checkconf.tdir/checkconf.check @@ -56,7 +56,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -190,7 +190,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -267,7 +267,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -353,7 +353,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -483,7 +483,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -611,7 +611,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no diff --git a/tpkg/checkconf.tdir/checkconf.check2.in b/tpkg/checkconf.tdir/checkconf.check2.in index d1dae0e57..793cbceba 100644 --- a/tpkg/checkconf.tdir/checkconf.check2.in +++ b/tpkg/checkconf.tdir/checkconf.check2.in @@ -56,7 +56,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -190,7 +190,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -267,7 +267,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -353,7 +353,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -483,7 +483,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -611,7 +611,7 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no From 7a34e208556906bb6e00082b25bac8dc3f127f8f Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Mon, 26 Aug 2024 11:45:55 +0200 Subject: [PATCH 02/10] Configurable staing cookie --- configlexer.lex | 1 + configparser.y | 23 +++- difffile.c | 103 ++++------------- difffile.h | 13 +-- nsd-checkconf.c | 26 +++-- nsd.c | 56 +++++---- nsd.h | 17 ++- options.c | 1 + options.h | 2 + remote.c | 106 ++++++++++++++---- tpkg/checkconf.tdir/checkconf.check | 42 +++++-- tpkg/checkconf.tdir/checkconf.check2.in | 24 +++- .../dns-cookies.by-nsd-control.conf | 15 +++ tpkg/dns-cookies.tdir/dns-cookies.conf | 18 +++ ...ontrol.key => dns-cookies.nsd_control.key} | 0 ..._server.key => dns-cookies.nsd_server.key} | 0 tpkg/dns-cookies.tdir/dns-cookies.test | 5 + tpkg/dns-cookies.tdir/dns-cookies.test.A1 | 2 +- tpkg/dns-cookies.tdir/dns-cookies.test.A2 | 2 +- tpkg/dns-cookies.tdir/dns-cookies.test.A3 | 2 +- tpkg/dns-cookies.tdir/dns-cookies.test.A4 | 16 +-- tpkg/dns-cookies.tdir/dns-cookies.test.conf | 17 --- tpkg/dns-cookies.tdir/nsd_control.pem | 22 ---- tpkg/dns-cookies.tdir/nsd_server.pem | 22 ---- 24 files changed, 308 insertions(+), 227 deletions(-) create mode 100644 tpkg/dns-cookies.tdir/dns-cookies.by-nsd-control.conf create mode 100644 tpkg/dns-cookies.tdir/dns-cookies.conf rename tpkg/dns-cookies.tdir/{nsd_control.key => dns-cookies.nsd_control.key} (100%) rename tpkg/dns-cookies.tdir/{nsd_server.key => dns-cookies.nsd_server.key} (100%) delete mode 100644 tpkg/dns-cookies.tdir/dns-cookies.test.conf delete mode 100644 tpkg/dns-cookies.tdir/nsd_control.pem delete mode 100644 tpkg/dns-cookies.tdir/nsd_server.pem diff --git a/configlexer.lex b/configlexer.lex index c85afdd1b..9f2019cb2 100644 --- a/configlexer.lex +++ b/configlexer.lex @@ -312,6 +312,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 2c35cb4ef..887b28d15 100644 --- a/configparser.y +++ b/configparser.y @@ -195,6 +195,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 @@ -512,7 +513,27 @@ 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); } | VAR_XFRD_TCP_MAX number diff --git a/difffile.c b/difffile.c index c4d18ad96..c5fcb107e 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,13 @@ 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)); } static void @@ -2179,14 +2120,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 086632ac0..56106daf6 100644 --- a/nsd-checkconf.c +++ b/nsd-checkconf.c @@ -166,13 +166,23 @@ usage(void) } static void -print_string_var(const char* varname, const char* value) +print_string_var_default(const char* varname, const char* value, + const char* default_value) { - if (!value) { - printf("\t#%s\n", varname); - } else { + if (value) { printf("\t%s \"%s\"\n", varname, value); + } else if (default_value) { + printf("\t#%s \"%s\"\n", varname, default_value); + } else { + printf("\t#%s\n", varname); } + +} + +static void +print_string_var(const char* varname, const char* value) +{ + print_string_var_default(varname, value, NULL); } static void @@ -439,6 +449,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 */ @@ -716,10 +727,9 @@ 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-file:", opt->cookie_secret_file); + print_string_var("cookie-secret:", opt->cookie_secret); + print_string_var("cookie-staging-secret:", opt->cookie_staging_secret); + print_string_var_default("cookie-secret-file:", opt->cookie_secret_file, COOKIESECRETSFILE); 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.c b/nsd.c index 397d656fc..e07c6b7cd 100644 --- a/nsd.c +++ b/nsd.c @@ -904,18 +904,17 @@ bind8_stats (struct nsd *nsd) static int cookie_secret_file_read(nsd_type* nsd) { + cookie_secret_type cookie_secrets[NSD_COOKIE_HISTORY_SIZE]; char secret[NSD_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/]; char const* file = nsd->options->cookie_secret_file ? nsd->options->cookie_secret_file : COOKIESECRETSFILE; FILE* f; - int corrupt = 0; - size_t count; + size_t count = 0; 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; @@ -924,16 +923,26 @@ int cookie_secret_file_read(nsd_type* nsd) { 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; } + if( secret_len != NSD_COOKIE_SECRET_SIZE * 2 ) { + fclose(f); + return 0; + } /* needed for `hex_pton`; stripping potential `\n` */ secret[secret_len] = '\0'; - decoded_len = hex_pton(secret, nsd->cookie_secrets[count].cookie_secret, + decoded_len = hex_pton(secret, cookie_secrets[count].cookie_secret, NSD_COOKIE_SECRET_SIZE); - if( decoded_len != NSD_COOKIE_SECRET_SIZE ) { corrupt++; break; } - nsd->cookie_count++; + if( decoded_len != NSD_COOKIE_SECRET_SIZE ) { + fclose(f); + return 0; + } } fclose(f); - return corrupt == 0; + if(count && nsd->cookie_secrets_source <= COOKIE_SECRETS_FROM_FILE) { + nsd->cookie_count = count; + memcpy(nsd->cookie_secrets, cookie_secrets, sizeof(cookie_secrets)); + nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; + } + return 1; } extern char *optarg; @@ -978,6 +987,7 @@ main(int argc, char *argv[]) nsd.nsid = NULL; nsd.nsid_len = 0; nsd.cookie_count = 0; + nsd.cookie_secrets_source = COOKIE_SECRETS_NONE; nsd.child_count = 0; nsd.maximum_tcp_count = 0; @@ -1262,17 +1272,25 @@ main(int argc, char *argv[]) #endif /* defined(INET6) */ nsd.do_answer_cookie = nsd.options->answer_cookie; - if (nsd.cookie_count > 0) - ; /* pass */ - - else if (nsd.options->cookie_secret) { + if(nsd.options->cookie_secret) { ssize_t len = hex_pton(nsd.options->cookie_secret, - nsd.cookie_secrets[0].cookie_secret, NSD_COOKIE_SECRET_SIZE); + 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"); + error("A cookie secret must be a 128 bit hex string"); } nsd.cookie_count = 1; + nsd.cookie_secrets_source = COOKIE_SECRETS_FROM_CONFIG; + if(nsd.options->cookie_staging_secret) { + len = hex_pton(nsd.options->cookie_staging_secret, + nsd.cookie_secrets[1].cookie_secret, + NSD_COOKIE_SECRET_SIZE); + if (len != NSD_COOKIE_SECRET_SIZE ) { + error("A (staging) cookie secret must be a " + "128 bit hex string"); + } else + nsd.cookie_count = 2; + } } else { size_t j; size_t const cookie_secret_len = NSD_COOKIE_SECRET_SIZE; @@ -1287,8 +1305,8 @@ main(int argc, char *argv[]) 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; + nsd.cookie_secrets_source = COOKIE_SECRETS_GENERATED; } if (nsd.nsid_len == 0 && nsd.options->nsid) { @@ -1592,11 +1610,11 @@ 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) ) { + if(nsd.cookie_secrets_source < COOKIE_SECRETS_FROM_FILE + && (!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.h b/nsd.h index d5f3bdf34..742281863 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; @@ -367,7 +376,11 @@ struct nsd /* 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; struct nsd_options* options; diff --git a/options.c b/options.c index 5456ea4d7..335172ce0 100644 --- a/options.c +++ b/options.c @@ -151,6 +151,7 @@ nsd_options_create(region_type* region) opt->proxy_protocol_port = NULL; opt->answer_cookie = 0; opt->cookie_secret = NULL; + opt->cookie_staging_secret = NULL; opt->cookie_secret_file = NULL; opt->control_enable = 0; opt->control_interface = NULL; diff --git a/options.h b/options.h index f58a91837..999da0b69 100644 --- a/options.h +++ b/options.h @@ -208,6 +208,8 @@ 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; /** enable verify */ diff --git a/remote.c b/remote.c index 2e79b3a4a..b3fb4065b 100644 --- a/remote.c +++ b/remote.c @@ -2342,7 +2342,7 @@ do_del_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg) { /* returns `0` on failure */ static int -cookie_secret_file_dump(RES* ssl, nsd_type const* nsd) { +cookie_secret_file_dump(RES* ssl, nsd_type* const nsd) { char const* secret_file = nsd->options->cookie_secret_file; char secret_hex[NSD_COOKIE_SECRET_SIZE * 2 + 1]; FILE* f; @@ -2372,25 +2372,41 @@ cookie_secret_file_dump(RES* ssl, nsd_type const* nsd) { 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(nsd->cookie_secrets_source == COOKIE_SECRETS_FROM_CONFIG) { + (void)ssl_printf(ssl, "error: cookie secrets already configured."); + (void)ssl_printf(ssl, " Remove \"cookie-secret:\" and " + "\"cookie-staging-secret:\" entries from configuration " + "first (and reconfig) before managing cookies with " + "nsd-control\n"); + 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]) { + if(nsd->options->cookie_secret_file && !nsd->options->cookie_secret_file[0]) { (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); return; } + backup_cookie_count = nsd->cookie_count; + memcpy( backup_cookie_secrets, nsd->cookie_secrets + , sizeof(cookie_secrets_type)); + activate_cookie_secret(nsd); if(!cookie_secret_file_dump(ssl, nsd)) { (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; return; } - 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); + nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_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); } @@ -2398,25 +2414,41 @@ do_activate_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { 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(nsd->cookie_secrets_source == COOKIE_SECRETS_FROM_CONFIG) { + (void)ssl_printf(ssl, "error: cookie secrets already configured."); + (void)ssl_printf(ssl, " Remove \"cookie-secret:\" and " + "\"cookie-staging-secret:\" entries from configuration " + "first (and reconfig) before managing cookies with " + "nsd-control\n"); + 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]) { + if(nsd->options->cookie_secret_file && !nsd->options->cookie_secret_file[0]) { (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); return; } + backup_cookie_count = nsd->cookie_count; + memcpy( backup_cookie_secrets, nsd->cookie_secrets + , sizeof(cookie_secrets_type)); + drop_cookie_secret(nsd); if(!cookie_secret_file_dump(ssl, nsd)) { (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; return; } - 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); + nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_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); } @@ -2425,7 +2457,17 @@ 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(nsd->cookie_secrets_source == COOKIE_SECRETS_FROM_CONFIG) { + (void)ssl_printf(ssl, "error: cookie secrets already configured."); + (void)ssl_printf(ssl, " Remove \"cookie-secret:\" and " + "\"cookie-staging-secret:\" entries from configuration " + "first (and reconfig) before managing cookies with " + "nsd-control\n"); + return; + } if(*arg == '\0') { (void)ssl_printf(ssl, "error: missing argument (cookie_secret)\n"); return; @@ -2443,24 +2485,32 @@ 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]) { + 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; } + 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_count = 0; + } + add_cookie_secret(nsd, secret); + explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE); 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); + memcpy( nsd->cookie_secrets, backup_cookie_secrets + , sizeof(cookie_secrets_type)); + nsd->cookie_count = backup_cookie_count; return; } - 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); + nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; + task_new_cookies(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task, + nsd->do_answer_cookie, nsd->cookie_count, nsd->cookie_secrets); explicit_bzero(arg, strlen(arg)); xfrd_set_reload_now(xfrd); send_ok(ssl); @@ -2473,7 +2523,25 @@ 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->options->cookie_secret_file + ? nsd->options->cookie_secret_file + : COOKIESECRETSFILE); + 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 0d7ab56f4..4d7fff996 100644 --- a/tpkg/checkconf.tdir/checkconf.check +++ b/tpkg/checkconf.tdir/checkconf.check @@ -56,7 +56,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -190,7 +192,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -267,7 +271,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -353,7 +359,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -483,7 +491,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -556,7 +566,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 @@ -611,7 +621,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "/var/db/nsd/cookiesecrets.txt" remote-control: control-enable: no @@ -629,7 +641,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 793cbceba..9d7010e87 100644 --- a/tpkg/checkconf.tdir/checkconf.check2.in +++ b/tpkg/checkconf.tdir/checkconf.check2.in @@ -56,7 +56,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@cookiesecretsfile@" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -190,7 +192,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@cookiesecretsfile@" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -267,7 +271,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@cookiesecretsfile@" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -353,7 +359,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@cookiesecretsfile@" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -483,7 +491,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@cookiesecretsfile@" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no @@ -611,7 +621,9 @@ server: tls-port: "853" #tls-cert-bundle: answer-cookie: no - cookie-secret-file: "@cookiesecretsfile@" + #cookie-secret: + #cookie-staging-secret: + #cookie-secret-file: "@cookiesecretsfile@" remote-control: control-enable: no 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/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_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/dns-cookies.test b/tpkg/dns-cookies.tdir/dns-cookies.test index bafaab76f..d01bda0de 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 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.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/tpkg/dns-cookies.tdir/nsd_control.pem b/tpkg/dns-cookies.tdir/nsd_control.pem deleted file mode 100644 index 93e7ca511..000000000 --- a/tpkg/dns-cookies.tdir/nsd_control.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDqzCCAhMCFBPBN+c2KtT5mAvLhxdCKNiMyNAKMA0GCSqGSIb3DQEBCwUAMA4x -DDAKBgNVBAMMA25zZDAeFw0xOTA0MjUxNTEzMjdaFw0yOTA0MjIxNTEzMjdaMBYx -FDASBgNVBAMMC25zZC1jb250cm9sMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB -igKCAYEAp6tA9X7sVaSMYtQh8FkQLhXERl05wDDxKr+Sqap/mctNoh27f7Y37act -gIkjw0FqJByrg73j1bqoVvRwJMWCrQCYP/oAw66rG4FDSN3jBg8lCeXz4qJETiZn -ZQRdWeJyjap6mm6Gj1CzWqmKq9bH+sJEEpXEA/rQM8PL1fSm5RXExSo47RoemL8j -afZd3j/JPUSunj/2/TLRKvJsCJV2JLFLQFvt/4G56C++ExQKsuN6QpfExZmC5cEH -hpYQBk5AfvbSqwzxGgldFQLyboA22spvh3uxVrYJj1bMDfO8Ij5d0Ku/rulsZVoa -ukudr/hhmI56A2IkYZemYdTMAeCrflYJD9TPx0eXzPtLoPJIyUlJ7MCu0rgN+FbH -7jDM1GC9zpQyBtLr97/rNHzk+z2N2WcgYbDhBW13Dv9QkIb2+ZPxhu2W5OTNEdu4 -+SHZOtYdPXN7NssiS2ZSIp8+ohkRzZgc8hsPr0yjt78IlzR5DVayz6vev9aRjdao -0vlAroIvAgMBAAEwDQYJKoZIhvcNAQELBQADggGBADc3NTIs6gf+//qcdyTR4hYc -oF53cJ4nWRMmoOJIk4ojF77rzoD2UjQye1X0gpZx55UYyu6O/Mq40krPX4FbMSzA -zLAyzuUPwJGdcgjvvlmSym/LAwTXwGYF32S/qKtpJKFSWny08JBt7DT8fV2P00zE -bV6ozg5HKukEcd2dQczsE3+gHwx7P+jsBBnWZzIgCkPwp4amzpahbQZRH2v9OboX -c1zR/8xXaYbes/BdqzzudhFV/Xd9wHH2EV4mRKPvQMY87FV6eGKYNL2UvPfr4/SH -yiIvd4Rw/vgMtpH9cYMov1v2Qkk9qvV9w5OPYu/4HzCVGssbkUPQX0AAWrUYIdo4 -ZqEAzyr1/IevTRYtsA4qkqJqgFfwRmNQH2blRX3vzgNvj2+v4DjvC+HYHuwzkwl8 -rbbs+9Ta+SBljSIPFb1+OMBAvgin/kAaYRxfQs6yrIO5abIBm+gfEiFLaP9yqerE -L+oT94EwsPMPVJ09yuVrjVUU430GMS2uqgTIj35TPQ== ------END CERTIFICATE----- diff --git a/tpkg/dns-cookies.tdir/nsd_server.pem b/tpkg/dns-cookies.tdir/nsd_server.pem deleted file mode 100644 index 5d41ad2d6..000000000 --- a/tpkg/dns-cookies.tdir/nsd_server.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDozCCAgsCFCAZislHgIerlrBBkLFt/ZOkKYVZMA0GCSqGSIb3DQEBCwUAMA4x -DDAKBgNVBAMMA25zZDAeFw0xOTA0MjUxNTEzMjdaFw0yOTA0MjIxNTEzMjdaMA4x -DDAKBgNVBAMMA25zZDCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAMS8 -uXxVCNTo112z3ENd99N/yuQJYk6umwp0ue3nc0CG5j2Y+12xmdEZ1OO3GddRkgCP -abQyK0onTU7B82qlmZ+N9fyFY8KyJEYM/ef2jlkaL+H8JnEuar22FetJd/ODWS6p -heAQw8UrdjC09vo1sRRCtolJtmF2XsIn0PM2h5o3wFpaPE5SuY87dEHNKj7+Zwgu -psLY2+zh7pdzd0KJVuoWTnNSmVRHgXRC3naFPU8jMs4Twb5KgF1qLYvwsFo92DE4 -/y+8zX0PRdR1up+u2PpbTvCGnSCz4mYogFSKy6lIdxC9l36vgXCd0LwA48i/ReUx -9wGlqIuomIDBnIHaivZOoY/s4rjMV+jrIM388UxM2Qh4TxK9/Ul+o/3x8yT+GhkM -QABywJr9qfT/6BGXmbL1j7AVmKVCyoDGdCQ2YX9unXawdZqU2qkULBt/OHVGUfX/ -4ss07M8IHNj0OyRuoFYO9tyqAYZQ9IPg5QvF3cYkcmg2yT180qF4C1rpsdNjoQID -AQABMA0GCSqGSIb3DQEBCwUAA4IBgQB+WGMopDqNkv7yDAO8Ik2EWieDqxTshqR4 -bT1do9zsC9WDrIVxoVcn+dtlIpEQl8MN9U5DTKBbRgk3grOwUsg2kC0Gujv3vAyQ -bF+jxjHWd1xzrbQ+QUgz07P1OMFWxMzECL2L2078UZbawFqKqlmNv5avUk27G8nB -GrujT/pUOIpRXC+rao8e14R84dPJLZuGm9IAeEBQIIdhY9sjFRyoQdCUubyKPpkm -/fpcDMkt7PzZ4nTovj4NUxnnoUGonpXuj0pHA/RDDJkPYaRrND4OGldQXdZ9LJNM -pROL6aCZ5iog74OY8yutVzCgGge9vZLkysceVP7Lyks9/fEAtIuozmulp9TUQAeR -MVdDOcREWRd0vFNtAC9xSloRqV+66CzrFHwkSMpLo+gdgcAZ8s33rgQk+I4gfavU -jPWMZVcZHXevtWuTRnxfOpMkbwiRyr2J8m549K7OKZgr+JRhdJTev4lvXVyfFia4 -zr6UOK4exZWP6VDXb4IyZbJh+LMjmws= ------END CERTIFICATE----- From 65a5d64e5dc8f4c9e0912e0422b579cfbceeca54 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 27 Aug 2024 17:12:22 +0200 Subject: [PATCH 03/10] Allow cookie settings to be reconfigurable --- nsd.c | 88 +------- options.c | 5 + options.h | 2 +- remote.c | 65 +++++- .../dns-cookies.by-config.conf | 15 ++ .../dns-cookies.nsd_control.pem | 22 ++ .../dns-cookies.nsd_server.pem | 22 ++ tpkg/dns-cookies.tdir/dns-cookies.test | 5 + tpkg/dns-cookies.tdir/dns-cookies.test.C1 | 190 ++++++++++++++++++ util.c | 96 +++++++++ util.h | 4 + 11 files changed, 424 insertions(+), 90 deletions(-) create mode 100644 tpkg/dns-cookies.tdir/dns-cookies.by-config.conf create mode 100644 tpkg/dns-cookies.tdir/dns-cookies.nsd_control.pem create mode 100644 tpkg/dns-cookies.tdir/dns-cookies.nsd_server.pem create mode 100644 tpkg/dns-cookies.tdir/dns-cookies.test.C1 diff --git a/nsd.c b/nsd.c index e07c6b7cd..66ff73ff5 100644 --- a/nsd.c +++ b/nsd.c @@ -902,49 +902,6 @@ bind8_stats (struct nsd *nsd) } #endif /* BIND8_STATS */ -static -int cookie_secret_file_read(nsd_type* nsd) { - cookie_secret_type cookie_secrets[NSD_COOKIE_HISTORY_SIZE]; - char secret[NSD_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/]; - char const* file = nsd->options->cookie_secret_file - ? nsd->options->cookie_secret_file : COOKIESECRETSFILE; - FILE* f; - size_t count = 0; - - 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 */ - 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); - return 0; - } - /* 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); - return 0; - } - } - fclose(f); - if(count && nsd->cookie_secrets_source <= COOKIE_SECRETS_FROM_FILE) { - nsd->cookie_count = count; - memcpy(nsd->cookie_secrets, cookie_secrets, sizeof(cookie_secrets)); - nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; - } - return 1; -} - extern char *optarg; extern int optind; @@ -986,6 +943,7 @@ 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; @@ -994,7 +952,6 @@ main(int argc, char *argv[]) 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) { @@ -1271,43 +1228,7 @@ main(int argc, char *argv[]) #endif /* IPV6 MTU) */ #endif /* defined(INET6) */ - nsd.do_answer_cookie = nsd.options->answer_cookie; - 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; - nsd.cookie_secrets_source = COOKIE_SECRETS_FROM_CONFIG; - if(nsd.options->cookie_staging_secret) { - len = hex_pton(nsd.options->cookie_staging_secret, - nsd.cookie_secrets[1].cookie_secret, - NSD_COOKIE_SECRET_SIZE); - if (len != NSD_COOKIE_SECRET_SIZE ) { - error("A (staging) cookie secret must be a " - "128 bit hex string"); - } else - nsd.cookie_count = 2; - } - } 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); - } - nsd.cookie_count = 1; - nsd.cookie_secrets_source = COOKIE_SECRETS_GENERATED; - } + reconfig_cookies(&nsd, nsd.options); if (nsd.nsid_len == 0 && nsd.options->nsid) { if (strlen(nsd.options->nsid) % 2 != 0) { @@ -1610,11 +1531,6 @@ main(int argc, char *argv[]) } #endif /* HAVE_SSL */ - if(nsd.cookie_secrets_source < COOKIE_SECRETS_FROM_FILE - && (!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/options.c b/options.c index 335172ce0..fe3d0dd8b 100644 --- a/options.c +++ b/options.c @@ -257,6 +257,11 @@ parse_options_file(struct nsd_options* opt, const char* file, opt->configfile = region_strdup(opt->region, file); + /* 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 999da0b69..7c785bb39 100644 --- a/options.h +++ b/options.h @@ -211,7 +211,7 @@ struct nsd_options { /** cookie staging secret */ char *cookie_staging_secret; /** path to cookie secret store */ - char const* cookie_secret_file; + char *cookie_secret_file; /** enable verify */ int verify_enable; /** list of ip addresses used to serve zones for verification */ diff --git a/remote.c b/remote.c index b3fb4065b..abcb33c64 100644 --- a/remote.c +++ b/remote.c @@ -1969,21 +1969,80 @@ 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) + || opt_str_changed( old->cookie_secret_file + , new->cookie_secret_file); +} + +static void opt_strcpy(region_type* region, char** dst, const char* src) +{ + assert(dst); + + if(!*dst) { + if(src) + *dst = region_strdup(region, src); + + } else if(!src) { + region_recycle(region, *dst, strlen(*dst)+1); + *dst = NULL; + + } else if(strcasecmp(*dst, src)) { + region_recycle(region, *dst, strlen(*dst)+1); + *dst = region_strdup(region, src); + } +} + /** 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; + opt_strcpy( oldopt->region + , &oldopt->cookie_secret + , newopt->cookie_secret); + opt_strcpy( oldopt->region + , &oldopt->cookie_staging_secret + , newopt->cookie_staging_secret); + opt_strcpy( 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 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.nsd_control.pem b/tpkg/dns-cookies.tdir/dns-cookies.nsd_control.pem new file mode 100644 index 000000000..93e7ca511 --- /dev/null +++ b/tpkg/dns-cookies.tdir/dns-cookies.nsd_control.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqzCCAhMCFBPBN+c2KtT5mAvLhxdCKNiMyNAKMA0GCSqGSIb3DQEBCwUAMA4x +DDAKBgNVBAMMA25zZDAeFw0xOTA0MjUxNTEzMjdaFw0yOTA0MjIxNTEzMjdaMBYx +FDASBgNVBAMMC25zZC1jb250cm9sMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB +igKCAYEAp6tA9X7sVaSMYtQh8FkQLhXERl05wDDxKr+Sqap/mctNoh27f7Y37act +gIkjw0FqJByrg73j1bqoVvRwJMWCrQCYP/oAw66rG4FDSN3jBg8lCeXz4qJETiZn +ZQRdWeJyjap6mm6Gj1CzWqmKq9bH+sJEEpXEA/rQM8PL1fSm5RXExSo47RoemL8j +afZd3j/JPUSunj/2/TLRKvJsCJV2JLFLQFvt/4G56C++ExQKsuN6QpfExZmC5cEH +hpYQBk5AfvbSqwzxGgldFQLyboA22spvh3uxVrYJj1bMDfO8Ij5d0Ku/rulsZVoa +ukudr/hhmI56A2IkYZemYdTMAeCrflYJD9TPx0eXzPtLoPJIyUlJ7MCu0rgN+FbH +7jDM1GC9zpQyBtLr97/rNHzk+z2N2WcgYbDhBW13Dv9QkIb2+ZPxhu2W5OTNEdu4 ++SHZOtYdPXN7NssiS2ZSIp8+ohkRzZgc8hsPr0yjt78IlzR5DVayz6vev9aRjdao +0vlAroIvAgMBAAEwDQYJKoZIhvcNAQELBQADggGBADc3NTIs6gf+//qcdyTR4hYc +oF53cJ4nWRMmoOJIk4ojF77rzoD2UjQye1X0gpZx55UYyu6O/Mq40krPX4FbMSzA +zLAyzuUPwJGdcgjvvlmSym/LAwTXwGYF32S/qKtpJKFSWny08JBt7DT8fV2P00zE +bV6ozg5HKukEcd2dQczsE3+gHwx7P+jsBBnWZzIgCkPwp4amzpahbQZRH2v9OboX +c1zR/8xXaYbes/BdqzzudhFV/Xd9wHH2EV4mRKPvQMY87FV6eGKYNL2UvPfr4/SH +yiIvd4Rw/vgMtpH9cYMov1v2Qkk9qvV9w5OPYu/4HzCVGssbkUPQX0AAWrUYIdo4 +ZqEAzyr1/IevTRYtsA4qkqJqgFfwRmNQH2blRX3vzgNvj2+v4DjvC+HYHuwzkwl8 +rbbs+9Ta+SBljSIPFb1+OMBAvgin/kAaYRxfQs6yrIO5abIBm+gfEiFLaP9yqerE +L+oT94EwsPMPVJ09yuVrjVUU430GMS2uqgTIj35TPQ== +-----END CERTIFICATE----- diff --git a/tpkg/dns-cookies.tdir/dns-cookies.nsd_server.pem b/tpkg/dns-cookies.tdir/dns-cookies.nsd_server.pem new file mode 100644 index 000000000..5d41ad2d6 --- /dev/null +++ b/tpkg/dns-cookies.tdir/dns-cookies.nsd_server.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDozCCAgsCFCAZislHgIerlrBBkLFt/ZOkKYVZMA0GCSqGSIb3DQEBCwUAMA4x +DDAKBgNVBAMMA25zZDAeFw0xOTA0MjUxNTEzMjdaFw0yOTA0MjIxNTEzMjdaMA4x +DDAKBgNVBAMMA25zZDCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAMS8 +uXxVCNTo112z3ENd99N/yuQJYk6umwp0ue3nc0CG5j2Y+12xmdEZ1OO3GddRkgCP +abQyK0onTU7B82qlmZ+N9fyFY8KyJEYM/ef2jlkaL+H8JnEuar22FetJd/ODWS6p +heAQw8UrdjC09vo1sRRCtolJtmF2XsIn0PM2h5o3wFpaPE5SuY87dEHNKj7+Zwgu +psLY2+zh7pdzd0KJVuoWTnNSmVRHgXRC3naFPU8jMs4Twb5KgF1qLYvwsFo92DE4 +/y+8zX0PRdR1up+u2PpbTvCGnSCz4mYogFSKy6lIdxC9l36vgXCd0LwA48i/ReUx +9wGlqIuomIDBnIHaivZOoY/s4rjMV+jrIM388UxM2Qh4TxK9/Ul+o/3x8yT+GhkM +QABywJr9qfT/6BGXmbL1j7AVmKVCyoDGdCQ2YX9unXawdZqU2qkULBt/OHVGUfX/ +4ss07M8IHNj0OyRuoFYO9tyqAYZQ9IPg5QvF3cYkcmg2yT180qF4C1rpsdNjoQID +AQABMA0GCSqGSIb3DQEBCwUAA4IBgQB+WGMopDqNkv7yDAO8Ik2EWieDqxTshqR4 +bT1do9zsC9WDrIVxoVcn+dtlIpEQl8MN9U5DTKBbRgk3grOwUsg2kC0Gujv3vAyQ +bF+jxjHWd1xzrbQ+QUgz07P1OMFWxMzECL2L2078UZbawFqKqlmNv5avUk27G8nB +GrujT/pUOIpRXC+rao8e14R84dPJLZuGm9IAeEBQIIdhY9sjFRyoQdCUubyKPpkm +/fpcDMkt7PzZ4nTovj4NUxnnoUGonpXuj0pHA/RDDJkPYaRrND4OGldQXdZ9LJNM +pROL6aCZ5iog74OY8yutVzCgGge9vZLkysceVP7Lyks9/fEAtIuozmulp9TUQAeR +MVdDOcREWRd0vFNtAC9xSloRqV+66CzrFHwkSMpLo+gdgcAZ8s33rgQk+I4gfavU +jPWMZVcZHXevtWuTRnxfOpMkbwiRyr2J8m549K7OKZgr+JRhdJTev4lvXVyfFia4 +zr6UOK4exZWP6VDXb4IyZbJh+LMjmws= +-----END CERTIFICATE----- diff --git a/tpkg/dns-cookies.tdir/dns-cookies.test b/tpkg/dns-cookies.tdir/dns-cookies.test index d01bda0de..01e749232 100644 --- a/tpkg/dns-cookies.tdir/dns-cookies.test +++ b/tpkg/dns-cookies.tdir/dns-cookies.test @@ -25,3 +25,8 @@ 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 + + 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..5b91879e3 --- /dev/null +++ b/tpkg/dns-cookies.tdir/dns-cookies.test.C1 @@ -0,0 +1,190 @@ +# #-- 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. Adding cookies by nsd-control should not be allowed with configured cookies" + # kill NSD + kill_pid $NSD_PID + rm -f nsd.log + exit 1 +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.c2 + +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.c3 + +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.c4 + +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.c5 + +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.6. Cannot reconfigure with broken cookies" + # kill NSD + kill_pid $NSD_PID + rm -f nsd.log + exit 1 +fi + +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c7 + +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.8. Cannot reconfigure with only a staging secret" + # kill NSD + kill_pid $NSD_PID + rm -f nsd.log + exit 1 +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.c9 + +# kill NSD +kill_pid $NSD_PID +rm -f nsd.log + +ERRORS=0 +if grep -q "2464c4abcf10c9570100000066cca121c6adacef8ac2bcdd" dig.output.c2 +then + echo "C.2. Active server cookie matched" +else + echo "C.2. Active server cookie failed to match" + cat dig.output.c2 + ERRORS=1 +fi + +if grep -q "2464c4abcf10c9570100000066cca121b789adffadb783df" dig.output.c3 +then + echo "C.3. Active server cookie matched" +else + echo "C.3. Active server cookie failed to match" + cat dig.output.c3 + ERRORS=1 +fi + +if grep -q "2464c4abcf10c9570100000066cca1218cf0b73f53bc2520" dig.output.c4 +then + echo "C.4. Active server cookie from file matched" +else + echo "C.4. Active server cookie from file failed to match" + cat dig.output.c4 + ERRORS=1 +fi + +if grep -q "2464c4abcf10c9570100000066cca121b789adffadb783df" dig.output.c5 +then + echo "C.5. Configured secrets prefered over dynamic secrets" +else + echo "C.5. Error on configured secrets over dynamic secrets preference" + cat dig.output.c5 + ERRORS=1 +fi + +if grep -q "2464c4abcf10c9570100000066cca121b789adffadb783df" dig.output.c7 +then + echo "C.7. Cookie secrets remain after broken reconfig" +else + echo "C.7. Cookie secrets did not remain after broken reconfig" + cat dig.output.c7 + ERRORS=1 +fi + +if grep -q "2464c4abcf10c9570100000066cca1218cf0b73f53bc2520" dig.output.c9 +then + echo "C.9. Active server cookie from file matched again" +else + echo "C.9. Active server cookie from file failed to match again" + cat dig.output.c9 + ERRORS=1 +fi + +exit $ERRORS diff --git a/util.c b/util.c index f417db7f1..f036a7994 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 @@ -1145,3 +1149,95 @@ void drop_cookie_secret(struct nsd* nsd) , NSD_COOKIE_SECRET_SIZE); nsd->cookie_count -= 1; } + +static +int cookie_secret_file_read(nsd_type* nsd, const char* cookie_secret_file) { + cookie_secret_type cookie_secrets[NSD_COOKIE_HISTORY_SIZE]; + char secret[NSD_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/]; + char const* file = cookie_secret_file + ? cookie_secret_file : COOKIESECRETSFILE; + FILE* f; + size_t count = 0; + + 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 */ + 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); + return 0; + } + /* 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); + return 0; + } + } + fclose(f); + if(count && nsd->cookie_secrets_source <= COOKIE_SECRETS_FROM_FILE) { + nsd->cookie_count = count; + memcpy(nsd->cookie_secrets, cookie_secrets, sizeof(cookie_secrets)); + nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; + } + return 1; +} + + +void reconfig_cookies(struct nsd* nsd, struct nsd_options* options) +{ + nsd->do_answer_cookie = options->answer_cookie; + 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; + nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_CONFIG; + 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; + } + } else { + size_t i, j; + + /* 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; + if((!options->cookie_secret_file || options->cookie_secret_file[0]) + && !cookie_secret_file_read(nsd, options->cookie_secret_file)) { + log_msg( LOG_ERR, "cookie secret file \"%s\" corrupt " + "or not readable" + , options->cookie_secret_file + ? options->cookie_secret_file + : COOKIESECRETSFILE); + } + } +} + diff --git a/util.h b/util.h index 6cb73233f..a0febd371 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 @@ -435,4 +436,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 */ From efe4cf69d981407613690c9e30df003732bb2d8e Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 28 Aug 2024 15:45:59 +0200 Subject: [PATCH 04/10] Automatic migration of cookie secrets from the old to the new default location --- remote.c | 24 +++-- .../dns-cookies.defaults.conf | 7 ++ tpkg/dns-cookies.tdir/dns-cookies.test | 2 + tpkg/dns-cookies.tdir/dns-cookies.test.C2 | 89 +++++++++++++++++++ util.c | 31 +++++-- 5 files changed, 136 insertions(+), 17 deletions(-) create mode 100644 tpkg/dns-cookies.tdir/dns-cookies.defaults.conf create mode 100644 tpkg/dns-cookies.tdir/dns-cookies.test.C2 diff --git a/remote.c b/remote.c index abcb33c64..25a190644 100644 --- a/remote.c +++ b/remote.c @@ -2402,7 +2402,9 @@ do_del_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg) { /* 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; + char const* secret_file = nsd->options->cookie_secret_file + ? nsd->options->cookie_secret_file + : COOKIESECRETSFILE; char secret_hex[NSD_COOKIE_SECRET_SIZE * 2 + 1]; FILE* f; size_t i; @@ -2410,7 +2412,7 @@ cookie_secret_file_dump(RES* ssl, nsd_type* const nsd) { /* open write only and truncate */ if((f = fopen(secret_file, "w")) == NULL ) { - (void)ssl_printf(ssl, "unable to open cookie secret file %s: %s", + (void)ssl_printf(ssl, "unable to open cookie secret file %s: %s\n", secret_file, strerror(errno)); return 0; } @@ -2456,8 +2458,10 @@ do_activate_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { , sizeof(cookie_secrets_type)); activate_cookie_secret(nsd); if(!cookie_secret_file_dump(ssl, nsd)) { - (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", - nsd->options->cookie_secret_file); + (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n" + , nsd->options->cookie_secret_file + ? nsd->options->cookie_secret_file + : COOKIESECRETSFILE); memcpy( nsd->cookie_secrets, backup_cookie_secrets , sizeof(cookie_secrets_type)); nsd->cookie_count = backup_cookie_count; @@ -2498,8 +2502,10 @@ do_drop_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { , sizeof(cookie_secrets_type)); drop_cookie_secret(nsd); if(!cookie_secret_file_dump(ssl, nsd)) { - (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", - nsd->options->cookie_secret_file); + (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n" + , nsd->options->cookie_secret_file + ? nsd->options->cookie_secret_file + : COOKIESECRETSFILE); memcpy( nsd->cookie_secrets, backup_cookie_secrets , sizeof(cookie_secrets_type)); nsd->cookie_count = backup_cookie_count; @@ -2560,8 +2566,10 @@ do_add_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE); if(!cookie_secret_file_dump(ssl, nsd)) { explicit_bzero(arg, strlen(arg)); - (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", - nsd->options->cookie_secret_file); + (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n" + , nsd->options->cookie_secret_file + ? nsd->options->cookie_secret_file + : COOKIESECRETSFILE); memcpy( nsd->cookie_secrets, backup_cookie_secrets , sizeof(cookie_secrets_type)); nsd->cookie_count = backup_cookie_count; 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.test b/tpkg/dns-cookies.tdir/dns-cookies.test index 01e749232..827964fc8 100644 --- a/tpkg/dns-cookies.tdir/dns-cookies.test +++ b/tpkg/dns-cookies.tdir/dns-cookies.test @@ -29,4 +29,6 @@ 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.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/util.c b/util.c index f036a7994..6bd449d3b 100644 --- a/util.c +++ b/util.c @@ -1161,7 +1161,19 @@ int cookie_secret_file_read(nsd_type* nsd, const char* cookie_secret_file) { f = fopen(file, "r"); /* a non-existing cookie file is not an error */ - if( f == NULL ) { return errno != EPERM; } + if(f != NULL) + ; /* pass */ + + else if(errno != ENOENT) { + log_msg( LOG_ERR + , "error reading cookie secret file \"%s\": \"%s\"" + , file, strerror(errno)); + return 0; + } + else if(cookie_secret_file != NULL + ||!(f = fopen((file = CONFIGDIR"/nsd_cookiesecrets.txt"), "r"))) + return 1; + /* cookie secret file exists and is readable */ for( count = 0; count < NSD_COOKIE_HISTORY_SIZE; count++ ) { size_t secret_len = 0; @@ -1173,6 +1185,9 @@ int cookie_secret_file_read(nsd_type* nsd, const char* cookie_secret_file) { 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\"" + , file); return 0; } /* needed for `hex_pton`; stripping potential `\n` */ @@ -1181,6 +1196,9 @@ int cookie_secret_file_read(nsd_type* nsd, const char* cookie_secret_file) { NSD_COOKIE_SECRET_SIZE); if( decoded_len != NSD_COOKIE_SECRET_SIZE ) { fclose(f); + log_msg( LOG_ERR + , "error parsing cookie secret file \"%s\"" + , file); return 0; } } @@ -1189,6 +1207,7 @@ int cookie_secret_file_read(nsd_type* nsd, const char* cookie_secret_file) { nsd->cookie_count = count; memcpy(nsd->cookie_secrets, cookie_secrets, sizeof(cookie_secrets)); nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; + } return 1; } @@ -1230,14 +1249,8 @@ void reconfig_cookies(struct nsd* nsd, struct nsd_options* options) } nsd->cookie_count = 1; nsd->cookie_secrets_source = COOKIE_SECRETS_GENERATED; - if((!options->cookie_secret_file || options->cookie_secret_file[0]) - && !cookie_secret_file_read(nsd, options->cookie_secret_file)) { - log_msg( LOG_ERR, "cookie secret file \"%s\" corrupt " - "or not readable" - , options->cookie_secret_file - ? options->cookie_secret_file - : COOKIESECRETSFILE); - } + if((!options->cookie_secret_file || options->cookie_secret_file[0])) + (void)cookie_secret_file_read(nsd, options->cookie_secret_file); } } From a8f5b46e2ab2e32cd109cd2fdb75c5be5e7b9452 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 28 Aug 2024 17:37:53 +0200 Subject: [PATCH 05/10] Updated documentation on cookie server secrets --- nsd.conf.5.in | 61 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/nsd.conf.5.in b/nsd.conf.5.in index b6661e02b..41fedf071 100644 --- a/nsd.conf.5.in +++ b/nsd.conf.5.in @@ -477,25 +477,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 @cookiesecretsfile@ -.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 From 6534967209cf08e4aeb5419526e2c2dc5a84e14f Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Thu, 29 Aug 2024 11:35:56 +0200 Subject: [PATCH 06/10] Tell were cookies came from even when they came from the old default location +some small code cleanup and reduced code repetition --- nsd.c | 1 + nsd.h | 11 +- options.h | 9 ++ region-allocator.c | 19 +++ region-allocator.h | 5 + remote.c | 164 ++++++++-------------- tpkg/dns-cookies.tdir/dns-cookies.dsc | 5 +- tpkg/dns-cookies.tdir/dns-cookies.test.C1 | 72 +++++----- util.c | 30 ++-- 9 files changed, 157 insertions(+), 159 deletions(-) diff --git a/nsd.c b/nsd.c index 66ff73ff5..c049675e2 100644 --- a/nsd.c +++ b/nsd.c @@ -946,6 +946,7 @@ main(int argc, char *argv[]) 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; diff --git a/nsd.h b/nsd.h index 742281863..2401865f5 100644 --- a/nsd.h +++ b/nsd.h @@ -368,20 +368,23 @@ 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_secrets_type cookie_secrets; - /* From where came the configured cookies - */ + /* 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; #ifdef HAVE_SSL diff --git a/options.h b/options.h index 7c785bb39..a7f860c2d 100644 --- a/options.h +++ b/options.h @@ -492,6 +492,15 @@ int nsd_options_insert_zone(struct nsd_options* opt, struct zone_options* zone); int nsd_options_insert_pattern(struct nsd_options* opt, struct pattern_options* pat); +/* return the configured cookie secrets filename or NULL if disabled */ +static inline const char* cookie_secret_file(struct nsd_options* opt) +{ + /* NULL means the default of COOKIESECRETSFILE, "" means disabled */ + return opt->cookie_secret_file + ? ( *opt->cookie_secret_file ? opt->cookie_secret_file : NULL ) + : COOKIESECRETSFILE; +} + /* parses options file. Returns false on failure. callback, if nonNULL, * gets called with error strings, default prints. */ int parse_options_file(struct nsd_options* opt, const char* file, 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 25a190644..e83a34b63 100644 --- a/remote.c +++ b/remote.c @@ -1985,24 +1985,6 @@ repat_cookie_options_changed(struct nsd_options* old, struct nsd_options* new) , new->cookie_secret_file); } -static void opt_strcpy(region_type* region, char** dst, const char* src) -{ - assert(dst); - - if(!*dst) { - if(src) - *dst = region_strdup(region, src); - - } else if(!src) { - region_recycle(region, *dst, strlen(*dst)+1); - *dst = NULL; - - } else if(strcasecmp(*dst, src)) { - region_recycle(region, *dst, strlen(*dst)+1); - *dst = region_strdup(region, src); - } -} - /** check if global options have changed */ static void repat_options(xfrd_state_type* xfrd, struct nsd_options* newopt) @@ -2023,15 +2005,15 @@ repat_options(xfrd_state_type* xfrd, struct nsd_options* newopt) if(repat_cookie_options_changed(oldopt, newopt)) { /* update our options */ oldopt->answer_cookie = newopt->answer_cookie; - opt_strcpy( oldopt->region - , &oldopt->cookie_secret - , newopt->cookie_secret); - opt_strcpy( oldopt->region - , &oldopt->cookie_staging_secret - , newopt->cookie_staging_secret); - opt_strcpy( oldopt->region - , &oldopt->cookie_secret_file - , newopt->cookie_secret_file); + region_str_replace( oldopt->region + , &oldopt->cookie_secret + , newopt->cookie_secret); + region_str_replace( oldopt->region + , &oldopt->cookie_staging_secret + , newopt->cookie_staging_secret); + 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; @@ -2399,21 +2381,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(!cookie_secret_file(nsd->options)) + (void)ssl_printf(ssl, "error: empty cookie-secret-file\n"); + + else if(nsd->cookie_secrets_source > COOKIE_SECRETS_FROM_FILE) + (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 - ? nsd->options->cookie_secret_file - : COOKIESECRETSFILE; +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\n", - secret_file, strerror(errno)); + if(!cookie_secret_file(nsd->options)) { + (void)ssl_printf(ssl, "cookie-secret-file empty\n"); + return 0; + } + else if((f = fopen(cookie_secret_file(nsd->options), "w")) == NULL ) { + (void)ssl_printf( ssl + , "unable to open cookie secret file %s: %s\n" + , cookie_secret_file(nsd->options) + , strerror(errno)); return 0; } for(i = 0; i < nsd->cookie_count; i++) { @@ -2427,6 +2430,13 @@ 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 + , cookie_secret_file(nsd->options)); + 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; } @@ -2437,41 +2447,22 @@ do_activate_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { cookie_secrets_type backup_cookie_secrets; (void)arg; - if(nsd->cookie_secrets_source == COOKIE_SECRETS_FROM_CONFIG) { - (void)ssl_printf(ssl, "error: cookie secrets already configured."); - (void)ssl_printf(ssl, " Remove \"cookie-secret:\" and " - "\"cookie-staging-secret:\" entries from configuration " - "first (and reconfig) before managing cookies with " - "nsd-control\n"); + 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; - } backup_cookie_count = nsd->cookie_count; memcpy( backup_cookie_secrets, nsd->cookie_secrets , sizeof(cookie_secrets_type)); activate_cookie_secret(nsd); - if(!cookie_secret_file_dump(ssl, nsd)) { - (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n" - , nsd->options->cookie_secret_file - ? nsd->options->cookie_secret_file - : COOKIESECRETSFILE); + 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; - return; } - nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_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); } static void @@ -2481,41 +2472,22 @@ do_drop_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { cookie_secrets_type backup_cookie_secrets; (void)arg; - if(nsd->cookie_secrets_source == COOKIE_SECRETS_FROM_CONFIG) { - (void)ssl_printf(ssl, "error: cookie secrets already configured."); - (void)ssl_printf(ssl, " Remove \"cookie-secret:\" and " - "\"cookie-staging-secret:\" entries from configuration " - "first (and reconfig) before managing cookies with " - "nsd-control\n"); + 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; - } backup_cookie_count = nsd->cookie_count; memcpy( backup_cookie_secrets, nsd->cookie_secrets , sizeof(cookie_secrets_type)); drop_cookie_secret(nsd); - if(!cookie_secret_file_dump(ssl, nsd)) { - (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n" - , nsd->options->cookie_secret_file - ? nsd->options->cookie_secret_file - : COOKIESECRETSFILE); + 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; - return; } - nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_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); } static void @@ -2525,14 +2497,9 @@ do_add_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { size_t backup_cookie_count; cookie_secrets_type backup_cookie_secrets; - if(nsd->cookie_secrets_source == COOKIE_SECRETS_FROM_CONFIG) { - (void)ssl_printf(ssl, "error: cookie secrets already configured."); - (void)ssl_printf(ssl, " Remove \"cookie-secret:\" and " - "\"cookie-staging-secret:\" entries from configuration " - "first (and reconfig) before managing cookies with " - "nsd-control\n"); + if(!can_dump_cookie_secrets(ssl, xfrd->nsd)) return; - } + if(*arg == '\0') { (void)ssl_printf(ssl, "error: missing argument (cookie_secret)\n"); return; @@ -2550,12 +2517,8 @@ 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; - } + explicit_bzero(arg, strlen(arg)); + backup_cookie_count = nsd->cookie_count; memcpy( backup_cookie_secrets, nsd->cookie_secrets , sizeof(cookie_secrets_type)); @@ -2564,23 +2527,14 @@ do_add_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { } add_cookie_secret(nsd, secret); explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE); - if(!cookie_secret_file_dump(ssl, nsd)) { + 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 - ? nsd->options->cookie_secret_file - : COOKIESECRETSFILE); + , cookie_secret_file(nsd->options)); memcpy( nsd->cookie_secrets, backup_cookie_secrets , sizeof(cookie_secrets_type)); nsd->cookie_count = backup_cookie_count; - return; } - nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; - task_new_cookies(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task, - nsd->do_answer_cookie, nsd->cookie_count, nsd->cookie_secrets); - explicit_bzero(arg, strlen(arg)); - xfrd_set_reload_now(xfrd); - send_ok(ssl); } static void @@ -2598,9 +2552,7 @@ do_print_cookie_secrets(RES* ssl, xfrd_state_type* xrfd, char* arg) { break; case COOKIE_SECRETS_FROM_FILE: ssl_printf( ssl, "source : \"%s\"\n" - , nsd->options->cookie_secret_file - ? nsd->options->cookie_secret_file - : COOKIESECRETSFILE); + , nsd->cookie_secrets_filename); break; case COOKIE_SECRETS_FROM_CONFIG: ssl_printf(ssl, "source : configuration\n"); 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/dns-cookies.test.C1 b/tpkg/dns-cookies.tdir/dns-cookies.test.C1 index 5b91879e3..06a4d9292 100644 --- a/tpkg/dns-cookies.tdir/dns-cookies.test.C1 +++ b/tpkg/dns-cookies.tdir/dns-cookies.test.C1 @@ -32,16 +32,18 @@ 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. Adding cookies by nsd-control should not be allowed with configured cookies" + 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.c2 +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.2 cat << EOF > cookiesecrets.conf server: @@ -54,7 +56,7 @@ $TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig sleep .1 -dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c3 +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.3 cat << EOF > cookiesecrets.conf server: @@ -69,7 +71,7 @@ $TPKG_NSD_CONTROL -c dns-cookies.by-nsd-control.conf add_cookie_secret dd3bdf934 sleep .1 -dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c4 +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.4 cat << EOF > cookiesecrets.conf server: @@ -82,7 +84,7 @@ $TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig sleep .1 -dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c5 +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.5 cat << EOF > cookiesecrets.conf server: @@ -93,14 +95,16 @@ server: EOF if $TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig 2>/dev/null then - echo "C.6. Cannot reconfigure with broken cookies" + 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.c7 +dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c1.7 cat << EOF > cookiesecrets.conf server: @@ -110,11 +114,13 @@ server: EOF if $TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig 2>/dev/null then - echo "C.8. Cannot reconfigure with only a staging secret" + 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 @@ -126,64 +132,64 @@ $TPKG_NSD_CONTROL -c dns-cookies.by-config.conf reconfig sleep .1 -dig @198.51.100.100 +cookie=2464c4abcf10c957 > dig.output.c9 +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.c2 +if grep -q "2464c4abcf10c9570100000066cca121c6adacef8ac2bcdd" dig.output.c1.2 then - echo "C.2. Active server cookie matched" + echo "C.1.2. Active server cookie matched" else - echo "C.2. Active server cookie failed to match" - cat dig.output.c2 + echo "C.1.2. Active server cookie failed to match" + cat dig.output.c1.2 ERRORS=1 fi -if grep -q "2464c4abcf10c9570100000066cca121b789adffadb783df" dig.output.c3 +if grep -q "2464c4abcf10c9570100000066cca121b789adffadb783df" dig.output.c1.3 then - echo "C.3. Active server cookie matched" + echo "C.1.3. Active server cookie matched" else - echo "C.3. Active server cookie failed to match" - cat dig.output.c3 + echo "C.1.3. Active server cookie failed to match" + cat dig.output.c1.3 ERRORS=1 fi -if grep -q "2464c4abcf10c9570100000066cca1218cf0b73f53bc2520" dig.output.c4 +if grep -q "2464c4abcf10c9570100000066cca1218cf0b73f53bc2520" dig.output.c1.4 then - echo "C.4. Active server cookie from file matched" + echo "C.1.4. Active server cookie from file matched" else - echo "C.4. Active server cookie from file failed to match" - cat dig.output.c4 + 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.c5 +if grep -q "2464c4abcf10c9570100000066cca121b789adffadb783df" dig.output.c1.5 then - echo "C.5. Configured secrets prefered over dynamic secrets" + echo "C.1.5. Configured secrets prefered over dynamic secrets" else - echo "C.5. Error on configured secrets over dynamic secrets preference" - cat dig.output.c5 + 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.c7 +if grep -q "2464c4abcf10c9570100000066cca121b789adffadb783df" dig.output.c1.7 then - echo "C.7. Cookie secrets remain after broken reconfig" + echo "C.1.7. Cookie secrets remain after broken reconfig" else - echo "C.7. Cookie secrets did not remain after broken reconfig" - cat dig.output.c7 + 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.c9 +if grep -q "2464c4abcf10c9570100000066cca1218cf0b73f53bc2520" dig.output.c1.9 then - echo "C.9. Active server cookie from file matched again" + echo "C.1.9. Active server cookie from file matched again" else - echo "C.9. Active server cookie from file failed to match again" - cat dig.output.c9 + echo "C.1.9. Active server cookie from file failed to match again" + cat dig.output.c1.9 ERRORS=1 fi diff --git a/util.c b/util.c index 6bd449d3b..eb0b87232 100644 --- a/util.c +++ b/util.c @@ -1151,27 +1151,28 @@ void drop_cookie_secret(struct nsd* nsd) } static -int cookie_secret_file_read(nsd_type* nsd, const char* cookie_secret_file) { +int cookie_secret_file_read(nsd_type* nsd) { cookie_secret_type cookie_secrets[NSD_COOKIE_HISTORY_SIZE]; char secret[NSD_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/]; - char const* file = cookie_secret_file - ? cookie_secret_file : COOKIESECRETSFILE; FILE* f; size_t count = 0; + const char* fn; - f = fopen(file, "r"); - /* a non-existing cookie file is not an error */ - if(f != NULL) + if(!(fn = cookie_secret_file(nsd->options))) + return 1; /* Explicitely disabled with empty filename */ + + else if((f = fopen(fn, "r")) != NULL) ; /* pass */ + /* a non-existing cookie file is not an error */ else if(errno != ENOENT) { log_msg( LOG_ERR , "error reading cookie secret file \"%s\": \"%s\"" - , file, strerror(errno)); + , fn, strerror(errno)); return 0; } - else if(cookie_secret_file != NULL - ||!(f = fopen((file = CONFIGDIR"/nsd_cookiesecrets.txt"), "r"))) + else if(nsd->options->cookie_secret_file != NULL /* explicit name */ + ||!(f = fopen((fn = CONFIGDIR"/nsd_cookiesecrets.txt"), "r"))) return 1; /* cookie secret file exists and is readable */ @@ -1187,7 +1188,7 @@ int cookie_secret_file_read(nsd_type* nsd, const char* cookie_secret_file) { fclose(f); log_msg( LOG_ERR , "error parsing cookie secret file \"%s\"" - , file); + , fn); return 0; } /* needed for `hex_pton`; stripping potential `\n` */ @@ -1198,7 +1199,7 @@ int cookie_secret_file_read(nsd_type* nsd, const char* cookie_secret_file) { fclose(f); log_msg( LOG_ERR , "error parsing cookie secret file \"%s\"" - , file); + , fn); return 0; } } @@ -1207,7 +1208,8 @@ int cookie_secret_file_read(nsd_type* nsd, const char* cookie_secret_file) { nsd->cookie_count = count; memcpy(nsd->cookie_secrets, cookie_secrets, sizeof(cookie_secrets)); nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; - + region_str_replace( nsd->region + , &nsd->cookie_secrets_filename, fn ); } return 1; } @@ -1249,8 +1251,8 @@ void reconfig_cookies(struct nsd* nsd, struct nsd_options* options) } nsd->cookie_count = 1; nsd->cookie_secrets_source = COOKIE_SECRETS_GENERATED; - if((!options->cookie_secret_file || options->cookie_secret_file[0])) - (void)cookie_secret_file_read(nsd, options->cookie_secret_file); + if(cookie_secret_file(nsd->options)) + cookie_secret_file_read(nsd); } } From 14d0a437ea83815caad457fa394b7d93b4e8bcd9 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 4 Sep 2024 10:28:20 +0200 Subject: [PATCH 07/10] Apply suggestions from code review Thanks @wcawijngaards ! Yes, cleanup secrets on the stack after use is definitely more secure. I think you even found some occurrences that weren't covered before. Co-authored-by: Wouter Wijngaards --- difffile.c | 1 + remote.c | 3 +++ util.c | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/difffile.c b/difffile.c index c5fcb107e..501d6e697 100644 --- a/difffile.c +++ b/difffile.c @@ -1976,6 +1976,7 @@ task_process_cookies(struct nsd* nsd, struct task_list_d* task) { 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 diff --git a/remote.c b/remote.c index e83a34b63..6913cb2ee 100644 --- a/remote.c +++ b/remote.c @@ -2463,6 +2463,7 @@ do_activate_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { , sizeof(cookie_secrets_type)); nsd->cookie_count = backup_cookie_count; } + explicit_bzero(backup_cookie_secrets, sizeof(cookie_secrets_type)); } static void @@ -2488,6 +2489,7 @@ do_drop_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { , sizeof(cookie_secrets_type)); nsd->cookie_count = backup_cookie_count; } + explicit_bzero(backup_cookie_secrets, sizeof(cookie_secrets_type)); } static void @@ -2535,6 +2537,7 @@ do_add_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { , sizeof(cookie_secrets_type)); nsd->cookie_count = backup_cookie_count; } + explicit_bzero(backup_cookie_secrets, sizeof(cookie_secrets_type)); } static void diff --git a/util.c b/util.c index eb0b87232..ba7db188f 100644 --- a/util.c +++ b/util.c @@ -1189,6 +1189,8 @@ int cookie_secret_file_read(nsd_type* nsd) { log_msg( LOG_ERR , "error parsing cookie secret file \"%s\"" , fn); + explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); + explicit_bzero(secret, sizeof(secret)); return 0; } /* needed for `hex_pton`; stripping potential `\n` */ @@ -1200,7 +1202,10 @@ int cookie_secret_file_read(nsd_type* nsd) { log_msg( LOG_ERR , "error parsing cookie secret file \"%s\"" , fn); + explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); + explicit_bzero(secret, sizeof(secret)); return 0; + explicit_bzero(secret, sizeof(secret)); } } fclose(f); @@ -1211,6 +1216,7 @@ int cookie_secret_file_read(nsd_type* nsd) { region_str_replace( nsd->region , &nsd->cookie_secrets_filename, fn ); } + explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); return 1; } From 5674228f1cd200bbf1e5a645d55e8bbf0b90dd3d Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 22 Oct 2024 16:24:42 +0200 Subject: [PATCH 08/10] Update util.c --- util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.c b/util.c index ba7db188f..8dda686dc 100644 --- a/util.c +++ b/util.c @@ -1209,7 +1209,7 @@ int cookie_secret_file_read(nsd_type* nsd) { } } fclose(f); - if(count && nsd->cookie_secrets_source <= COOKIE_SECRETS_FROM_FILE) { + if(count && nsd->cookie_secrets_source != COOKIE_SECRETS_FROM_FILE) { nsd->cookie_count = count; memcpy(nsd->cookie_secrets, cookie_secrets, sizeof(cookie_secrets)); nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; From b090af72c203436e8c65b46444b7e4d06f8b358e Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 22 Oct 2024 16:45:26 +0200 Subject: [PATCH 09/10] Remove order dependent comparisons with the cookie_secrets_source enum --- remote.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/remote.c b/remote.c index 6913cb2ee..af5fa8f55 100644 --- a/remote.c +++ b/remote.c @@ -2388,7 +2388,7 @@ can_dump_cookie_secrets(RES* ssl, nsd_type* const nsd) if(!cookie_secret_file(nsd->options)) (void)ssl_printf(ssl, "error: empty cookie-secret-file\n"); - else if(nsd->cookie_secrets_source > COOKIE_SECRETS_FROM_FILE) + 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 " @@ -2524,7 +2524,8 @@ do_add_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* 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) { + 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); From c743742a6ebdc899bb6b65b0f8ff2171662517c1 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 23 Oct 2024 14:17:39 +0200 Subject: [PATCH 10/10] Pro-actively set cookie_secret_file value Right after config is read, so no wrapper is needed anymore to determine the value. --- configparser.y | 12 ++++- nsd-checkconf.c | 26 +++++----- options.c | 6 +++ options.h | 11 +---- remote.c | 16 ++++--- util.c | 125 +++++++++++++++++++++++++++--------------------- 6 files changed, 111 insertions(+), 85 deletions(-) diff --git a/configparser.y b/configparser.y index edee66393..9204bb7fd 100644 --- a/configparser.y +++ b/configparser.y @@ -541,7 +541,17 @@ server_option: } } | 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/nsd-checkconf.c b/nsd-checkconf.c index 76c00ccb0..f4a2801d4 100644 --- a/nsd-checkconf.c +++ b/nsd-checkconf.c @@ -166,23 +166,13 @@ usage(void) } static void -print_string_var_default(const char* varname, const char* value, - const char* default_value) +print_string_var(const char* varname, const char* value) { - if (value) { - printf("\t%s \"%s\"\n", varname, value); - } else if (default_value) { - printf("\t#%s \"%s\"\n", varname, default_value); - } else { + if (!value) { printf("\t#%s\n", varname); + } else { + printf("\t%s \"%s\"\n", varname, value); } - -} - -static void -print_string_var(const char* varname, const char* value) -{ - print_string_var_default(varname, value, NULL); } static void @@ -731,7 +721,13 @@ config_test_print_server(nsd_options_type* opt) printf("\tanswer-cookie: %s\n", opt->answer_cookie?"yes":"no"); print_string_var("cookie-secret:", opt->cookie_secret); print_string_var("cookie-staging-secret:", opt->cookie_staging_secret); - print_string_var_default("cookie-secret-file:", opt->cookie_secret_file, COOKIESECRETSFILE); + 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/options.c b/options.c index 53190f823..dcae09344 100644 --- a/options.c +++ b/options.c @@ -154,6 +154,7 @@ nsd_options_create(region_type* region) opt->cookie_secret = NULL; 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; @@ -258,6 +259,11 @@ 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 " diff --git a/options.h b/options.h index 78ba7a5c5..2567a62d8 100644 --- a/options.h +++ b/options.h @@ -213,6 +213,8 @@ struct nsd_options { char *cookie_staging_secret; /** path to cookie secret store */ 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 */ @@ -493,15 +495,6 @@ int nsd_options_insert_zone(struct nsd_options* opt, struct zone_options* zone); int nsd_options_insert_pattern(struct nsd_options* opt, struct pattern_options* pat); -/* return the configured cookie secrets filename or NULL if disabled */ -static inline const char* cookie_secret_file(struct nsd_options* opt) -{ - /* NULL means the default of COOKIESECRETSFILE, "" means disabled */ - return opt->cookie_secret_file - ? ( *opt->cookie_secret_file ? opt->cookie_secret_file : NULL ) - : COOKIESECRETSFILE; -} - /* parses options file. Returns false on failure. callback, if nonNULL, * gets called with error strings, default prints. */ int parse_options_file(struct nsd_options* opt, const char* file, diff --git a/remote.c b/remote.c index af5fa8f55..83d791954 100644 --- a/remote.c +++ b/remote.c @@ -1981,6 +1981,8 @@ repat_cookie_options_changed(struct nsd_options* old, struct nsd_options* new) , 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); } @@ -2011,6 +2013,8 @@ repat_options(xfrd_state_type* xfrd, struct nsd_options* newopt) 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); @@ -2385,7 +2389,7 @@ do_del_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg) { static int can_dump_cookie_secrets(RES* ssl, nsd_type* const nsd) { - if(!cookie_secret_file(nsd->options)) + 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) @@ -2408,14 +2412,14 @@ cookie_secret_file_dump_and_reload(RES* ssl, nsd_type* const nsd) { size_t i; /* open write only and truncate */ - if(!cookie_secret_file(nsd->options)) { + if(!nsd->options->cookie_secret_file) { (void)ssl_printf(ssl, "cookie-secret-file empty\n"); return 0; } - else if((f = fopen(cookie_secret_file(nsd->options), "w")) == NULL ) { + else if((f = fopen(nsd->options->cookie_secret_file, "w")) == NULL ) { (void)ssl_printf( ssl , "unable to open cookie secret file %s: %s\n" - , cookie_secret_file(nsd->options) + , nsd->options->cookie_secret_file , strerror(errno)); return 0; } @@ -2432,7 +2436,7 @@ cookie_secret_file_dump_and_reload(RES* ssl, nsd_type* const nsd) { fclose(f); nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; region_str_replace(nsd->region, &nsd->cookie_secrets_filename - , cookie_secret_file(nsd->options)); + , 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); @@ -2533,7 +2537,7 @@ do_add_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { 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" - , cookie_secret_file(nsd->options)); + , nsd->options->cookie_secret_file); memcpy( nsd->cookie_secrets, backup_cookie_secrets , sizeof(cookie_secrets_type)); nsd->cookie_count = backup_cookie_count; diff --git a/util.c b/util.c index 6ad6c1e87..e598695b7 100644 --- a/util.c +++ b/util.c @@ -1175,30 +1175,69 @@ void drop_cookie_secret(struct nsd* nsd) nsd->cookie_count -= 1; } -static -int cookie_secret_file_read(nsd_type* nsd) { +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; + FILE* f = NULL; size_t count = 0; const char* fn; + size_t i, j; - if(!(fn = cookie_secret_file(nsd->options))) - return 1; /* Explicitely disabled with empty filename */ + 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 an error */ + /* 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)); - return 0; - } - else if(nsd->options->cookie_secret_file != NULL /* explicit name */ - ||!(f = fopen((fn = CONFIGDIR"/nsd_cookiesecrets.txt"), "r"))) - return 1; + 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++ ) { @@ -1216,7 +1255,7 @@ int cookie_secret_file_read(nsd_type* nsd) { , fn); explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); explicit_bzero(secret, sizeof(secret)); - return 0; + goto generate_cookie_secrets; } /* needed for `hex_pton`; stripping potential `\n` */ secret[secret_len] = '\0'; @@ -1229,61 +1268,39 @@ int cookie_secret_file_read(nsd_type* nsd) { , fn); explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); explicit_bzero(secret, sizeof(secret)); - return 0; - explicit_bzero(secret, sizeof(secret)); + goto generate_cookie_secrets; } + explicit_bzero(secret, sizeof(secret)); } fclose(f); - if(count && nsd->cookie_secrets_source != COOKIE_SECRETS_FROM_FILE) { + if(count) { nsd->cookie_count = count; memcpy(nsd->cookie_secrets, cookie_secrets, sizeof(cookie_secrets)); - nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; 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)); - return 1; -} - - -void reconfig_cookies(struct nsd* nsd, struct nsd_options* options) -{ - nsd->do_answer_cookie = options->answer_cookie; - 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; - nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_CONFIG; - 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; - } - } else { - size_t i, j; - - /* Calculate a new random secret */ - srandom(getpid() ^ time(NULL)); +generate_cookie_secrets: + /* Calculate a new random secret */ + srandom(getpid() ^ time(NULL)); - for( j = 0; j < NSD_COOKIE_HISTORY_SIZE; j++) { + 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)) + 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; - if(cookie_secret_file(nsd->options)) - cookie_secret_file_read(nsd); + 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; + /*********************************************************************/ }