diff --git a/CHANGES b/CHANGES index de6ff35539..3d04d415f8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +4699. [func] Multiple cookie-secret clauses can now be specified. + The first one specified is used to generate new + server cookies. [RT #45672] + 4698. [port] Add --with-python-install-dir configure option to allow specifying a nonstandard installation directory for Python modules. [RT #45407] diff --git a/bin/named/client.c b/bin/named/client.c index 624eeac3b5..ecc13cb241 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -248,7 +248,8 @@ static inline isc_boolean_t allowed(isc_netaddr_t *addr, dns_name_t *signer, isc_netaddr_t *ecs_addr, isc_uint8_t ecs_addrlen, isc_uint8_t *ecs_scope, dns_acl_t *acl); static void compute_cookie(ns_client_t *client, isc_uint32_t when, - isc_uint32_t nonce, isc_buffer_t *buf); + isc_uint32_t nonce, const unsigned char *secret, + isc_buffer_t *buf); void ns_client_recursing(ns_client_t *client) { @@ -1623,7 +1624,7 @@ ns_client_addopt(ns_client_t *client, dns_message_t *message, isc_stdtime_get(&now); isc_random_get(&nonce); - compute_cookie(client, now, nonce, &buf); + compute_cookie(client, now, nonce, ns_g_server->secret, &buf); INSIST(count < DNS_EDNSOPTIONS); ednsopts[count].code = DNS_OPT_COOKIE; @@ -1830,7 +1831,7 @@ ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey, static void compute_cookie(ns_client_t *client, isc_uint32_t when, isc_uint32_t nonce, - isc_buffer_t *buf) + const unsigned char *secret, isc_buffer_t *buf) { switch (ns_g_server->cookiealg) { #if defined(HAVE_OPENSSL_AES) || defined(HAVE_OPENSSL_EVP_AES) @@ -1847,7 +1848,7 @@ compute_cookie(ns_client_t *client, isc_uint32_t when, isc_uint32_t nonce, isc_buffer_putuint32(buf, nonce); isc_buffer_putuint32(buf, when); memmove(input, cp, 16); - isc_aes128_crypt(ns_g_server->secret, input, digest); + isc_aes128_crypt(secret, input, digest); for (i = 0; i < 8; i++) input[i] = digest[i] ^ digest[i + 8]; isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); @@ -1856,12 +1857,12 @@ compute_cookie(ns_client_t *client, isc_uint32_t when, isc_uint32_t nonce, cp = (unsigned char *)&netaddr.type.in; memmove(input + 8, cp, 4); memset(input + 12, 0, 4); - isc_aes128_crypt(ns_g_server->secret, input, digest); + isc_aes128_crypt(secret, input, digest); break; case AF_INET6: cp = (unsigned char *)&netaddr.type.in6; memmove(input + 8, cp, 16); - isc_aes128_crypt(ns_g_server->secret, input, digest); + isc_aes128_crypt(secret, input, digest); for (i = 0; i < 8; i++) input[i + 8] = digest[i] ^ digest[i + 8]; isc_aes128_crypt(ns_g_server->secret, input + 8, @@ -1887,9 +1888,7 @@ compute_cookie(ns_client_t *client, isc_uint32_t when, isc_uint32_t nonce, isc_buffer_putuint32(buf, nonce); isc_buffer_putuint32(buf, when); - isc_hmacsha1_init(&hmacsha1, - ns_g_server->secret, - ISC_SHA1_DIGESTLENGTH); + isc_hmacsha1_init(&hmacsha1, secret, ISC_SHA1_DIGESTLENGTH); isc_hmacsha1_update(&hmacsha1, cp, 16); isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); switch (netaddr.family) { @@ -1925,8 +1924,7 @@ compute_cookie(ns_client_t *client, isc_uint32_t when, isc_uint32_t nonce, isc_buffer_putuint32(buf, nonce); isc_buffer_putuint32(buf, when); - isc_hmacsha256_init(&hmacsha256, - ns_g_server->secret, + isc_hmacsha256_init(&hmacsha256, secret, ISC_SHA256_DIGESTLENGTH); isc_hmacsha256_update(&hmacsha256, cp, 16); isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); @@ -1957,6 +1955,7 @@ compute_cookie(ns_client_t *client, isc_uint32_t when, isc_uint32_t nonce, static void process_cookie(ns_client_t *client, isc_buffer_t *buf, size_t optlen) { + ns_altsecret_t *altsecret; unsigned char dbuf[COOKIE_SIZE]; unsigned char *old; isc_stdtime_t now; @@ -2016,17 +2015,31 @@ process_cookie(ns_client_t *client, isc_buffer_t *buf, size_t optlen) { } isc_buffer_init(&db, dbuf, sizeof(dbuf)); - compute_cookie(client, when, nonce, &db); + compute_cookie(client, when, nonce, ns_g_server->secret, &db); - if (!isc_safe_memequal(old, dbuf, COOKIE_SIZE)) { + if (isc_safe_memequal(old, dbuf, COOKIE_SIZE)) { isc_stats_increment(ns_g_server->nsstats, - dns_nsstatscounter_cookienomatch); + dns_nsstatscounter_cookiematch); + client->attributes |= NS_CLIENTATTR_HAVECOOKIE; return; } + for (altsecret = ISC_LIST_HEAD(ns_g_server->altsecrets); + altsecret != NULL; + altsecret = ISC_LIST_NEXT(altsecret, link)) + { + isc_buffer_init(&db, dbuf, sizeof(dbuf)); + compute_cookie(client, when, nonce, altsecret->secret, &db); + if (isc_safe_memequal(old, dbuf, COOKIE_SIZE)) { + isc_stats_increment(ns_g_server->nsstats, + dns_nsstatscounter_cookiematch); + client->attributes |= NS_CLIENTATTR_HAVECOOKIE; + return; + } + } + isc_stats_increment(ns_g_server->nsstats, - dns_nsstatscounter_cookiematch); - client->attributes |= NS_CLIENTATTR_HAVECOOKIE; + dns_nsstatscounter_cookienomatch); } static isc_result_t diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index d45cc8a25a..60d1ce6dd4 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -117,6 +117,7 @@ struct ns_server { isc_uint16_t session_keybits; isc_boolean_t interface_auto; unsigned char secret[32]; /*%< Server Cookie Secret */ + ns_altsecretlist_t altsecrets; ns_cookiealg_t cookiealg; dns_dtenv_t *dtenv; /*%< Dnstap environment */ @@ -126,6 +127,11 @@ struct ns_server { isc_uint16_t transfer_tcp_message_size; }; +struct ns_altsecret { + ISC_LINK(ns_altsecret_t) link; + unsigned char secret[32]; +}; + #define NS_SERVER_MAGIC ISC_MAGIC('S','V','E','R') #define NS_SERVER_VALID(s) ISC_MAGIC_VALID(s, NS_SERVER_MAGIC) diff --git a/bin/named/include/named/types.h b/bin/named/include/named/types.h index 5a91ad0a41..336472abdf 100644 --- a/bin/named/include/named/types.h +++ b/bin/named/include/named/types.h @@ -36,6 +36,8 @@ typedef struct ns_dispatch ns_dispatch_t; typedef ISC_LIST(ns_dispatch_t) ns_dispatchlist_t; typedef struct ns_statschannel ns_statschannel_t; typedef ISC_LIST(ns_statschannel_t) ns_statschannellist_t; +typedef struct ns_altsecret ns_altsecret_t; +typedef ISC_LIST(ns_altsecret_t) ns_altsecretlist_t; typedef enum { ns_cookiealg_aes, diff --git a/bin/named/server.c b/bin/named/server.c index 63f056121a..c6d607229c 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -7147,12 +7147,15 @@ load_configuration(const char *filename, ns_server_t *server, isc_uint32_t transfer_message_size; ns_cache_t *nsc; ns_cachelist_t cachelist, tmpcachelist; + ns_altsecret_t *altsecret; + ns_altsecretlist_t altsecrets, tmpaltsecrets; unsigned int maxsocks; isc_uint32_t softquota = 0; ISC_LIST_INIT(viewlist); ISC_LIST_INIT(builtin_viewlist); ISC_LIST_INIT(cachelist); + ISC_LIST_INIT(altsecrets); /* Create the ACL configuration context */ if (ns_g_aclconfctx != NULL) @@ -8103,7 +8106,6 @@ load_configuration(const char *filename, ns_server_t *server, } } - obj = NULL; if (options != NULL && cfg_map_get(options, "memstatistics", &obj) == ISC_R_SUCCESS) @@ -8205,32 +8207,69 @@ load_configuration(const char *filename, ns_server_t *server, obj = NULL; result = ns_config_get(maps, "cookie-secret", &obj); if (result == ISC_R_SUCCESS) { + const char *str; + isc_boolean_t first = ISC_TRUE; isc_buffer_t b; unsigned int usedlength; - memset(server->secret, 0, sizeof(server->secret)); - isc_buffer_init(&b, server->secret, sizeof(server->secret)); - result = isc_hex_decodestring(cfg_obj_asstring(obj), &b); - if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE) - goto cleanup; + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + str = cfg_obj_asstring(obj); - usedlength = isc_buffer_usedlength(&b); - switch (server->cookiealg) { - case ns_cookiealg_aes: - if (usedlength != ISC_AES128_KEYLENGTH) - CHECKM(ISC_R_RANGE, - "AES cookie-secret must be 128 bits"); - break; - case ns_cookiealg_sha1: - if (usedlength != ISC_SHA1_DIGESTLENGTH) - CHECKM(ISC_R_RANGE, - "SHA1 cookie-secret must be 160 bits"); - break; - case ns_cookiealg_sha256: - if (usedlength != ISC_SHA256_DIGESTLENGTH) - CHECKM(ISC_R_RANGE, - "SHA256 cookie-secret must be 256 bits"); - break; + if (first) { + memset(server->secret, 0, + sizeof(server->secret)); + isc_buffer_init(&b, server->secret, + sizeof(server->secret)); + result = isc_hex_decodestring(str, &b); + if (result != ISC_R_SUCCESS && + result != ISC_R_NOSPACE) + goto cleanup; + first = ISC_FALSE; + } else { + altsecret = isc_mem_get(server->mctx, + sizeof(*altsecret)); + if (altsecret == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + isc_buffer_init(&b, altsecret->secret, + sizeof(altsecret->secret)); + result = isc_hex_decodestring(str, &b); + if (result != ISC_R_SUCCESS && + result != ISC_R_NOSPACE) { + isc_mem_put(server->mctx, altsecret, + sizeof(*altsecret)); + goto cleanup; + } + ISC_LIST_INITANDAPPEND(altsecrets, + altsecret, link); + } + + usedlength = isc_buffer_usedlength(&b); + switch (server->cookiealg) { + case ns_cookiealg_aes: + if (usedlength != ISC_AES128_KEYLENGTH) + CHECKM(ISC_R_RANGE, + "AES cookie-secret must be " + "128 bits"); + break; + case ns_cookiealg_sha1: + if (usedlength != ISC_SHA1_DIGESTLENGTH) + CHECKM(ISC_R_RANGE, + "SHA1 cookie-secret must be " + "160 bits"); + break; + case ns_cookiealg_sha256: + if (usedlength != ISC_SHA256_DIGESTLENGTH) + CHECKM(ISC_R_RANGE, + "SHA256 cookie-secret must be " + "256 bits"); + break; + } } } else { result = isc_entropy_getdata(ns_g_entropy, @@ -8242,6 +8281,13 @@ load_configuration(const char *filename, ns_server_t *server, goto cleanup; } + /* + * Swap altsecrets lists. + */ + tmpaltsecrets = server->altsecrets; + server->altsecrets = altsecrets; + altsecrets = tmpaltsecrets; + (void) ns_server_loadnta(server); result = ISC_R_SUCCESS; @@ -8297,6 +8343,12 @@ load_configuration(const char *filename, ns_server_t *server, isc_mem_put(server->mctx, nsc, sizeof(*nsc)); } + /* Same cleanup for altsecrets list. */ + while ((altsecret = ISC_LIST_HEAD(altsecrets)) != NULL) { + ISC_LIST_UNLINK(altsecrets, altsecret, link); + isc_mem_put(server->mctx, altsecret, sizeof(*altsecret)); + } + /* * Adjust the listening interfaces in accordance with the source * addresses specified in views and zones. @@ -8520,6 +8572,7 @@ shutdown_server(isc_task_t *task, isc_event_t *event) { ns_server_t *server = (ns_server_t *)event->ev_arg; isc_boolean_t flush = server->flushonshutdown; ns_cache_t *nsc; + ns_altsecret_t *altsecret; UNUSED(task); INSIST(task == server->task); @@ -8564,6 +8617,11 @@ shutdown_server(isc_task_t *task, isc_event_t *event) { isc_mem_put(server->mctx, nsc, sizeof(*nsc)); } + while ((altsecret = ISC_LIST_HEAD(server->altsecrets)) != NULL) { + ISC_LIST_UNLINK(server->altsecrets, altsecret, link); + isc_mem_put(server->mctx, altsecret, sizeof(*altsecret)); + } + isc_timer_detach(&server->interface_timer); isc_timer_detach(&server->heartbeat_timer); isc_timer_detach(&server->pps_timer); @@ -8808,6 +8866,8 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) { ISC_LIST_INIT(server->cachelist); + ISC_LIST_INIT(server->altsecrets); + server->sessionkey = NULL; server->session_keyfile = NULL; server->session_keyname = NULL; diff --git a/bin/tests/system/cookie/ns4/named.conf b/bin/tests/system/cookie/ns4/named.conf new file mode 100644 index 0000000000..bd3230a332 --- /dev/null +++ b/bin/tests/system/cookie/ns4/named.conf @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.4 port 9953 allow { any; } keys { rndc_key; }; +}; + +options { + query-source address 10.53.0.4; + notify-source 10.53.0.4; + transfer-source 10.53.0.4; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.4; }; + listen-on-v6 { none; }; + recursion yes; + cookie-algorithm sha1; + cookie-secret "569d36a6cc27d6bf55502183302ba352745255a2"; + require-server-cookie yes; +}; + +zone "." { + type hint; + file "root.hint"; +}; diff --git a/bin/tests/system/cookie/ns4/root.hint b/bin/tests/system/cookie/ns4/root.hint new file mode 100644 index 0000000000..2d75a29cf3 --- /dev/null +++ b/bin/tests/system/cookie/ns4/root.hint @@ -0,0 +1,11 @@ +; Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +; $Id: root.hint,v 1.7 2007/06/19 23:47:05 tbox Exp $ + +$TTL 999999 +. IN NS a.root-servers.nil. +a.root-servers.nil. IN A 10.53.0.2 diff --git a/bin/tests/system/cookie/ns5/named.conf b/bin/tests/system/cookie/ns5/named.conf new file mode 100644 index 0000000000..480052ecfb --- /dev/null +++ b/bin/tests/system/cookie/ns5/named.conf @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.5 port 9953 allow { any; } keys { rndc_key; }; +}; + +options { + query-source address 10.53.0.5; + notify-source 10.53.0.5; + transfer-source 10.53.0.5; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.5; }; + listen-on-v6 { none; }; + recursion yes; + cookie-algorithm sha1; + cookie-secret "569d36a6cc27d6bf55502183302ba352745255a2"; + cookie-secret "6b300e27a0db46d4b046e4189790fa7db3c1ffb3"; + require-server-cookie yes; +}; + +zone "." { + type hint; + file "root.hint"; +}; diff --git a/bin/tests/system/cookie/ns5/root.hint b/bin/tests/system/cookie/ns5/root.hint new file mode 100644 index 0000000000..2d75a29cf3 --- /dev/null +++ b/bin/tests/system/cookie/ns5/root.hint @@ -0,0 +1,11 @@ +; Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +; $Id: root.hint,v 1.7 2007/06/19 23:47:05 tbox Exp $ + +$TTL 999999 +. IN NS a.root-servers.nil. +a.root-servers.nil. IN A 10.53.0.2 diff --git a/bin/tests/system/cookie/ns6/named.conf b/bin/tests/system/cookie/ns6/named.conf new file mode 100644 index 0000000000..59d4ae1e9c --- /dev/null +++ b/bin/tests/system/cookie/ns6/named.conf @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.6 port 9953 allow { any; } keys { rndc_key; }; +}; + +options { + query-source address 10.53.0.6; + notify-source 10.53.0.6; + transfer-source 10.53.0.6; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.6; }; + listen-on-v6 { none; }; + recursion yes; + cookie-algorithm sha1; + cookie-secret "6b300e27a0db46d4b046e4189790fa7db3c1ffb3"; + require-server-cookie yes; +}; + +zone "." { + type hint; + file "root.hint"; +}; diff --git a/bin/tests/system/cookie/ns6/root.hint b/bin/tests/system/cookie/ns6/root.hint new file mode 100644 index 0000000000..2d75a29cf3 --- /dev/null +++ b/bin/tests/system/cookie/ns6/root.hint @@ -0,0 +1,11 @@ +; Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +; $Id: root.hint,v 1.7 2007/06/19 23:47:05 tbox Exp $ + +$TTL 999999 +. IN NS a.root-servers.nil. +a.root-servers.nil. IN A 10.53.0.2 diff --git a/bin/tests/system/cookie/tests.sh b/bin/tests/system/cookie/tests.sh index d720de0d20..833539dd3a 100755 --- a/bin/tests/system/cookie/tests.sh +++ b/bin/tests/system/cookie/tests.sh @@ -138,6 +138,105 @@ if [ $linecount != 2 ]; then ret=1; fi if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +# +# Test shared cookie-secret support. +# +# NS4 has cookie-secret "569d36a6cc27d6bf55502183302ba352745255a2"; +# +# NS5 has cookie-secret "569d36a6cc27d6bf55502183302ba352745255a2"; +# NS5 has cookie-secret "6b300e27a0db46d4b046e4189790fa7db3c1ffb3"; (alternate) +# +# NS6 has cookie-secret "6b300e27a0db46d4b046e4189790fa7db3c1ffb3"; +# +# Server cookies from NS4 are accepted by NS5 and not NS6 +# Server cookies from NS5 are accepted by NS4 and not NS6 +# Server cookies from NS6 are accepted by NS5 and not NS4 +# +# Force local address so that the client's address is the same to all servers. +# + +n=`expr $n + 1` +echo "I:get NS4 cookie for cross server checking ($n)" +ret=0 +$DIG +cookie -b 10.53.0.4 soa . @10.53.0.4 -p 5300 > dig.out.test$n +grep "; COOKIE:.*(good)" dig.out.test$n > /dev/null || ret=1 +ns4cookie=`getcookie dig.out.test$n` +test -n "$ns4cookie" || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:get NS5 cookie for cross server checking ($n)" +ret=0 +$DIG +cookie -b 10.53.0.4 soa . @10.53.0.5 -p 5300 > dig.out.test$n +grep "; COOKIE:.*(good)" dig.out.test$n > /dev/null || ret=1 +ns5cookie=`getcookie dig.out.test$n` +test -n "$ns5cookie" || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:get NS6 cookie for cross server checking ($n)" +ret=0 +$DIG +cookie -b 10.53.0.4 soa . @10.53.0.6 -p 5300 > dig.out.test$n +grep "; COOKIE:.*(good)" dig.out.test$n > /dev/null || ret=1 +ns6cookie=`getcookie dig.out.test$n` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:test NS4 cookie on NS5 (expect success) ($n)" +ret=0 +$DIG +cookie=$ns4cookie -b 10.53.0.4 +nobadcookie soa . @10.53.0.5 -p 5300 > dig.out.test$n +grep "; COOKIE:.*(good)" dig.out.test$n > /dev/null || ret=1 +grep "status: NOERROR," dig.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:test NS4 cookie on NS6 (expect badcookie) ($n)" +ret=0 +$DIG +cookie=$ns4cookie -b 10.53.0.4 +nobadcookie soa . @10.53.0.6 -p 5300 > dig.out.test$n +grep "; COOKIE:.*(good)" dig.out.test$n > /dev/null || ret=1 +grep "status: BADCOOKIE," dig.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:test NS5 cookie on NS4 (expect success) ($n)" +ret=0 +$DIG +cookie=$ns5cookie -b 10.53.0.4 +nobadcookie soa . @10.53.0.4 -p 5300 > dig.out.test$n +grep "; COOKIE:.*(good)" dig.out.test$n > /dev/null || ret=1 +grep "status: NOERROR," dig.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:test NS5 cookie on NS6 (expect badcookie) ($n)" +ret=0 +$DIG +cookie=$ns5cookie -b 10.53.0.4 +nobadcookie soa . @10.53.0.6 -p 5300 > dig.out.test$n +grep "; COOKIE:.*(good)" dig.out.test$n > /dev/null || ret=1 +grep "status: BADCOOKIE," dig.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:test NS6 cookie on NS4 (expect badcookie) ($n)" +ret=0 +$DIG +cookie=$ns6cookie -b 10.53.0.4 +nobadcookie soa . @10.53.0.4 -p 5300 > dig.out.test$n +grep "; COOKIE:.*(good)" dig.out.test$n > /dev/null || ret=1 +grep "status: BADCOOKIE," dig.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:test NS6 cookie on NS5 (expect success) ($n)" +ret=0 +$DIG +cookie=$ns6cookie -b 10.53.0.4 +nobadcookie soa . @10.53.0.5 -p 5300 > dig.out.test$n +grep "; COOKIE:.*(good)" dig.out.test$n > /dev/null || ret=1 +grep "status: NOERROR," dig.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` echo "I:exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 0e63c3b2aa..eb4b600a7d 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -6637,6 +6637,12 @@ options { to be 128 bits for AES128, 160 bits for SHA1 and 256 bits for SHA256. + + If there are multiple secrets specified, the first + one listed in named.conf is + used to generate new server cookies. The others + will only be used to verify returned cookies. + diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index ed0516ce9b..cc25d1f08b 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -516,6 +516,16 @@ validator messages. + + + Multiple cookie-secret clause are now + supported. The first cookie-secret in + named.conf is used to generate new + server cookies. Any others are used to accept old server + cookies or those generated by other servers using the + matching cookie-secret. + + diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 228654d008..b4dbdbde27 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -1364,39 +1364,56 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, if (obj != NULL) { unsigned char secret[32]; - memset(secret, 0, sizeof(secret)); - isc_buffer_init(&b, secret, sizeof(secret)); - tresult = isc_hex_decodestring(cfg_obj_asstring(obj), &b); - if (tresult == ISC_R_NOSPACE) { - cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "cookie-secret: too long"); - } else if (tresult != ISC_R_SUCCESS) { - cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "cookie-secret: invalid hex string"); - } - if (tresult != ISC_R_SUCCESS) - result = tresult; + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) { + unsigned int usedlength; - if (tresult == ISC_R_SUCCESS && - strcasecmp(ccalg, "aes") == 0 && - isc_buffer_usedlength(&b) != ISC_AES128_KEYLENGTH) { - cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "AES cookie-secret must be 128 bits"); - result = ISC_R_RANGE; - } - if (tresult == ISC_R_SUCCESS && - strcasecmp(ccalg, "sha1") == 0 && - isc_buffer_usedlength(&b) != ISC_SHA1_DIGESTLENGTH) { - cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "SHA1 cookie-secret must be 160 bits"); - result = ISC_R_RANGE; - } - if (tresult == ISC_R_SUCCESS && - strcasecmp(ccalg, "sha256") == 0 && - isc_buffer_usedlength(&b) != ISC_SHA256_DIGESTLENGTH) { - cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "SHA256 cookie-secret must be 256 bits"); - result = ISC_R_RANGE; + obj = cfg_listelt_value(element); + str = cfg_obj_asstring(obj); + + memset(secret, 0, sizeof(secret)); + isc_buffer_init(&b, secret, sizeof(secret)); + tresult = isc_hex_decodestring(str, &b); + if (tresult == ISC_R_NOSPACE) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "cookie-secret: too long"); + } else if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "cookie-secret: invalid hex " + "string"); + } + if (tresult != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) + result = tresult; + continue; + } + + usedlength = isc_buffer_usedlength(&b); + if (strcasecmp(ccalg, "aes") == 0 && + usedlength != ISC_AES128_KEYLENGTH) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "AES cookie-secret must be " + "128 bits"); + if (result == ISC_R_SUCCESS) + result = ISC_R_RANGE; + } + if (strcasecmp(ccalg, "sha1") == 0 && + usedlength != ISC_SHA1_DIGESTLENGTH) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "SHA1 cookie-secret must be " + "160 bits"); + if (result == ISC_R_SUCCESS) + result = ISC_R_RANGE; + } + if (strcasecmp(ccalg, "sha256") == 0 && + usedlength != ISC_SHA256_DIGESTLENGTH) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "SHA256 cookie-secret must be " + "256 bits"); + if (result == ISC_R_SUCCESS) + result = ISC_R_RANGE; + } } } diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 832275249e..8813c4a070 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1029,7 +1029,7 @@ options_clauses[] = { { "bindkeys-file", &cfg_type_qstring, 0 }, { "blackhole", &cfg_type_bracketed_aml, 0 }, { "cookie-algorithm", &cfg_type_cookiealg, 0 }, - { "cookie-secret", &cfg_type_sstring, 0 }, + { "cookie-secret", &cfg_type_sstring, CFG_CLAUSEFLAG_MULTI }, { "coresize", &cfg_type_size, 0 }, { "datasize", &cfg_type_size, 0 }, { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },