Skip to content

Commit

Permalink
- cookie-secret-file, unbound-control add_cookie_secret, drop_cookie_…
Browse files Browse the repository at this point in the history
…secret,

  activate_cookie_secret and print_cookie_secrets.
  • Loading branch information
wcawijngaards committed Jun 14, 2024
1 parent 6f7e8cc commit a29fc03
Show file tree
Hide file tree
Showing 9 changed files with 381 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -1297,7 +1297,7 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h
$(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \
$(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \
$(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \
$(srcdir)/sldns/wire2str.h
$(srcdir)/sldns/wire2str.h $(srcdir)/util/edns.h
stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
Expand Down
216 changes: 216 additions & 0 deletions daemon/remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
#include "sldns/wire2str.h"
#include "sldns/sbuffer.h"
#include "util/timeval_func.h"
#include "util/edns.h"
#ifdef USE_CACHEDB
#include "cachedb/cachedb.h"
#endif
Expand Down Expand Up @@ -3193,6 +3194,212 @@ do_rpz_disable(RES* ssl, struct worker* worker, char* arg)
do_rpz_enable_disable(ssl, worker, arg, 0);
}

/* write the cookie secrets to file, returns `0` on failure */
static int
cookie_secret_file_dump(RES* ssl, struct worker* worker) {
char const* secret_file = worker->env.cfg->cookie_secret_file;
struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets;
char secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2 + 1];
FILE* f;
size_t i;
if(secret_file == NULL || secret_file[0]==0) {
(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
return 0;
}
log_assert( secret_file != NULL );

/* open write only and truncate */
if((f = fopen(secret_file, "w")) == NULL ) {
(void)ssl_printf(ssl, "unable to open cookie secret file %s: %s",
secret_file, strerror(errno));
return 0;
}
if(cookie_secrets == NULL) {
/* nothing to write */
fclose(f);
return 1;
}
lock_basic_lock(&cookie_secrets->lock);

for(i = 0; i < cookie_secrets->cookie_count; i++) {
struct cookie_secret const* cs = &cookie_secrets->
cookie_secrets[i];
ssize_t const len = hex_ntop(cs->cookie_secret,
UNBOUND_COOKIE_SECRET_SIZE, secret_hex,
sizeof(secret_hex));
(void)len; /* silence unused variable warning with -DNDEBUG */
log_assert( len == UNBOUND_COOKIE_SECRET_SIZE * 2 );
secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2] = '\0';
fprintf(f, "%s\n", secret_hex);
}
lock_basic_unlock(&cookie_secrets->lock);
explicit_bzero(secret_hex, sizeof(secret_hex));
fclose(f);
return 1;
}

/** Activate cookie secret */
static void
do_activate_cookie_secret(RES* ssl, struct worker* worker) {
char const* secret_file = worker->env.cfg->cookie_secret_file;
struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets;

if(secret_file == NULL || secret_file[0] == 0) {
(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
return;
}
if(cookie_secrets == NULL) {
(void)ssl_printf(ssl, "error: there are no cookie_secrets.");
return;
}
lock_basic_lock(&cookie_secrets->lock);

if(cookie_secrets->cookie_count <= 1 ) {
lock_basic_unlock(&cookie_secrets->lock);
(void)ssl_printf(ssl, "error: no staging cookie secret to activate\n");
return;
}
/* Only the worker 0 writes to file, the others update state. */
if(worker->thread_num == 0 && !cookie_secret_file_dump(ssl, worker)) {
lock_basic_unlock(&cookie_secrets->lock);
(void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n",
secret_file);
return;
}
activate_cookie_secret(cookie_secrets);
if(worker->thread_num == 0)
(void)cookie_secret_file_dump(ssl, worker);
lock_basic_unlock(&cookie_secrets->lock);
send_ok(ssl);
}

/** Drop cookie secret */
static void
do_drop_cookie_secret(RES* ssl, struct worker* worker) {
char const* secret_file = worker->env.cfg->cookie_secret_file;
struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets;

if(secret_file == NULL || secret_file[0] == 0) {
(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
return;
}
if(cookie_secrets == NULL) {
(void)ssl_printf(ssl, "error: there are no cookie_secrets.");
return;
}
lock_basic_lock(&cookie_secrets->lock);

if(cookie_secrets->cookie_count <= 1 ) {
lock_basic_unlock(&cookie_secrets->lock);
(void)ssl_printf(ssl, "error: can not drop the currently active cookie secret\n");
return;
}
/* Only the worker 0 writes to file, the others update state. */
if(worker->thread_num == 0 && !cookie_secret_file_dump(ssl, worker)) {
lock_basic_unlock(&cookie_secrets->lock);
(void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n",
secret_file);
return;
}
drop_cookie_secret(cookie_secrets);
if(worker->thread_num == 0)
(void)cookie_secret_file_dump(ssl, worker);
lock_basic_unlock(&cookie_secrets->lock);
send_ok(ssl);
}

/** Add cookie secret */
static void
do_add_cookie_secret(RES* ssl, struct worker* worker, char* arg) {
uint8_t secret[UNBOUND_COOKIE_SECRET_SIZE];
char const* secret_file = worker->env.cfg->cookie_secret_file;
struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets;

if(secret_file == NULL || secret_file[0] == 0) {
(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
return;
}
if(cookie_secrets == NULL) {
worker->daemon->cookie_secrets = cookie_secrets_create();
if(!worker->daemon->cookie_secrets) {
(void)ssl_printf(ssl, "error: out of memory");
return;
}
cookie_secrets = worker->daemon->cookie_secrets;
}
lock_basic_lock(&cookie_secrets->lock);

if(*arg == '\0') {
lock_basic_unlock(&cookie_secrets->lock);
(void)ssl_printf(ssl, "error: missing argument (cookie_secret)\n");
return;
}
if(strlen(arg) != 32) {
lock_basic_unlock(&cookie_secrets->lock);
explicit_bzero(arg, strlen(arg));
(void)ssl_printf(ssl, "invalid cookie secret: invalid argument length\n");
(void)ssl_printf(ssl, "please provide a 128bit hex encoded secret\n");
return;
}
if(hex_pton(arg, secret, UNBOUND_COOKIE_SECRET_SIZE) !=
UNBOUND_COOKIE_SECRET_SIZE ) {
lock_basic_unlock(&cookie_secrets->lock);
explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
explicit_bzero(arg, strlen(arg));
(void)ssl_printf(ssl, "invalid cookie secret: parse error\n");
(void)ssl_printf(ssl, "please provide a 128bit hex encoded secret\n");
return;
}
/* Only the worker 0 writes to file, the others update state. */
if(worker->thread_num == 0 && !cookie_secret_file_dump(ssl, worker)) {
lock_basic_unlock(&cookie_secrets->lock);
explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
explicit_bzero(arg, strlen(arg));
(void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n",
secret_file);
return;
}
add_cookie_secret(cookie_secrets, secret, UNBOUND_COOKIE_SECRET_SIZE);
explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
if(worker->thread_num == 0)
(void)cookie_secret_file_dump(ssl, worker);
lock_basic_unlock(&cookie_secrets->lock);
explicit_bzero(arg, strlen(arg));
send_ok(ssl);
}

/** Print cookie secrets */
static void
do_print_cookie_secrets(RES* ssl, struct worker* worker) {
struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets;
char secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2 + 1];
int i;

if(!cookie_secrets)
return; /* Output is empty. */
lock_basic_lock(&cookie_secrets->lock);
/* (void)ssl_printf(ssl, "cookie_secret_count=%zu\n", cookie_secrets->cookie_count); */
for(i = 0; (size_t)i < cookie_secrets->cookie_count; i++) {
struct cookie_secret const* cs = &cookie_secrets->
cookie_secrets[i];
ssize_t const len = hex_ntop(cs->cookie_secret,
UNBOUND_COOKIE_SECRET_SIZE, secret_hex,
sizeof(secret_hex));
(void)len; /* silence unused variable warning with -DNDEBUG */
log_assert( len == UNBOUND_COOKIE_SECRET_SIZE * 2 );
secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2] = '\0';
if (i == 0)
(void)ssl_printf(ssl, "active : %s\n", secret_hex);
else if (cookie_secrets->cookie_count == 2)
(void)ssl_printf(ssl, "staging: %s\n", secret_hex);
else
(void)ssl_printf(ssl, "staging[%d]: %s\n", i,
secret_hex);
}
lock_basic_unlock(&cookie_secrets->lock);
explicit_bzero(secret_hex, sizeof(secret_hex));
}

/** check for name with end-of-string, space or tab after it */
static int
cmdcmp(char* p, const char* cmd, size_t len)
Expand Down Expand Up @@ -3325,6 +3532,9 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd,
} else if(cmdcmp(p, "view_local_datas", 16)) {
do_view_datas_add(rc, ssl, worker, skipwhite(p+16));
return;
} else if(cmdcmp(p, "print_cookie_secrets", 20)) {
do_print_cookie_secrets(ssl, worker);
return;
}

#ifdef THREADS_DISABLED
Expand Down Expand Up @@ -3389,6 +3599,12 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd,
do_rpz_enable(ssl, worker, skipwhite(p+10));
} else if(cmdcmp(p, "rpz_disable", 11)) {
do_rpz_disable(ssl, worker, skipwhite(p+11));
} else if(cmdcmp(p, "add_cookie_secret", 17)) {
do_add_cookie_secret(ssl, worker, skipwhite(p+17));
} else if(cmdcmp(p, "drop_cookie_secret", 18)) {
do_drop_cookie_secret(ssl, worker);
} else if(cmdcmp(p, "activate_cookie_secret", 22)) {
do_activate_cookie_secret(ssl, worker);
} else {
(void)ssl_printf(ssl, "error unknown command '%s'\n", p);
}
Expand Down
35 changes: 35 additions & 0 deletions doc/unbound-control.8.in
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,41 @@ Remove a list of \fIlocal_data\fR for given view from stdin. Like local_datas_re
.TP
.B view_local_datas \fIview\fR
Add a list of \fIlocal_data\fR for given view from stdin. Like local_datas.
.TP
.B add_cookie_secret <secret>
Add or replace a cookie secret persistently. <secret> needs to be an 128 bit
hex string.
.IP
Cookie secrets can be either \fIactive\fR or \fIstaging\fR. \fIActive\fR cookie
secrets are used to create DNS Cookies, but verification of a DNS Cookie
succeeds with any of the \fIactive\fR or \fIstaging\fR cookie secrets. The
state of the current cookie secrets can be printed with the
\fBprint_cookie_secrets\fR command.
.IP
When there are no cookie secrets configured yet, the <secret> is added as
\fIactive\fR. If there is already an \fIactive\fR cookie secret, the <secret>
is added as \fIstaging\fR or replacing an existing \fIstaging\fR secret.
.IP
To "roll" a cookie secret used in an anycast set. The new secret has to be
added as staging secret to \fBall\fR nodes in the anycast set. When \fBall\fR
nodes can verify DNS Cookies with the new secret, the new secret can be
activated with the \fBactivate_cookie_secret\fR command. After \fBall\fR nodes
have the new secret \fIactive\fR for at least one hour, the previous secret can
be dropped with the \fBdrop_cookie_secret\fR command.
.IP
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.
This is default disabled, "".
.TP
.B drop_cookie_secret
Drop the \fIstaging\fR cookie secret.
.TP
.B activate_cookie_secret
Make the current \fIstaging\fR cookie secret \fIactive\fR, and the current
\fIactive\fR cookie secret \fIstaging\fR.
.TP
.B print_cookie_secrets
Show the current configured cookie secrets with their status.
.SH "EXIT CODE"
The unbound\-control program exits with status code 1 on error, 0 on success.
.SH "SET UP"
Expand Down
4 changes: 4 additions & 0 deletions smallapp/unbound-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ usage(void)
printf(" rpz_enable zone Enable the RPZ zone if it had previously\n");
printf(" been disabled\n");
printf(" rpz_disable zone Disable the RPZ zone\n");
printf(" add_cookie_secret <secret> add (or replace) a new cookie secret <secret>\n");
printf(" drop_cookie_secret drop a staging cookie secret\n");
printf(" activate_cookie_secret make a staging cookie secret active\n");
printf(" print_cookie_secrets show all cookie secrets with their status\n");
printf("Version %s\n", PACKAGE_VERSION);
printf("BSD licensed, see LICENSE in source package for details.\n");
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
Expand Down
2 changes: 1 addition & 1 deletion testcode/unitmain.c
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,7 @@ static void edns_ede_encode_encodedecode(struct query_info* qinfo,
sldns_buffer_skip(pkt, 2 + 2);
/* decode */
unit_assert(parse_edns_from_query_pkt(pkt, edns, NULL, NULL, NULL, 0,
region) == 0);
region, NULL) == 0);
}

static void edns_ede_encode_check(struct edns_data* edns, int* found_ede,
Expand Down
Loading

0 comments on commit a29fc03

Please sign in to comment.