From c9f8165a067512e7bafb5bbefa1b3046382edd47 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Fri, 27 Oct 2017 15:45:18 -0700 Subject: [PATCH] [master] tag initializing keys 4798. [func] Keys specified in "managed-keys" statements are tagged as "initializing" until they have been updated by a key refresh query. If initialization fails it will be visible from "rndc secroots". [RT #46267] --- CHANGES | 6 + bin/named/server.c | 58 +++++-- bin/tests/system/mkeys/README | 16 +- bin/tests/system/mkeys/clean.sh | 4 +- bin/tests/system/mkeys/ns1/named1.conf | 6 + bin/tests/system/mkeys/ns1/named2.conf | 6 + bin/tests/system/mkeys/ns1/named3.conf | 39 +++++ bin/tests/system/mkeys/ns1/sign.sh | 2 + bin/tests/system/mkeys/ns2/named.args | 2 +- bin/tests/system/mkeys/ns4/named.conf | 41 +++++ bin/tests/system/mkeys/ns5/named.conf | 38 +++++ bin/tests/system/mkeys/ns5/named1.args | 1 + bin/tests/system/mkeys/ns5/named2.args | 1 + bin/tests/system/mkeys/setup.sh | 11 +- bin/tests/system/mkeys/tests.sh | 112 +++++++++++++- doc/arm/notes.xml | 10 ++ lib/dns/client.c | 2 +- lib/dns/include/dns/keytable.h | 27 +++- lib/dns/include/dns/resolver.h | 6 + lib/dns/keytable.c | 205 ++++++++++++++++++------- lib/dns/resolver.c | 4 +- lib/dns/tests/keytable_test.c | 123 ++++++++++++++- lib/dns/win32/libdns.def.in | 3 + lib/dns/zone.c | 62 +++++--- 24 files changed, 664 insertions(+), 121 deletions(-) create mode 100644 bin/tests/system/mkeys/ns1/named3.conf create mode 100644 bin/tests/system/mkeys/ns4/named.conf create mode 100644 bin/tests/system/mkeys/ns5/named.conf create mode 100644 bin/tests/system/mkeys/ns5/named1.args create mode 100644 bin/tests/system/mkeys/ns5/named2.args diff --git a/CHANGES b/CHANGES index 8e0575b08e..0f4513d0b6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +4798. [func] Keys specified in "managed-keys" statements + are tagged as "initializing" until they have been + updated by a key refresh query. If initialization + fails it will be visible from "rndc secroots". + [RT #46267] + 4797. [func] Removed "isc-hmac-fixup", as the versions of BIND that had the bug it worked around are long past end of life. [RT #46411] diff --git a/bin/named/server.c b/bin/named/server.c index a8ce5e01e5..0a4ade0e7c 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -805,6 +805,11 @@ dstkey_fromconfig(const cfg_obj_t *vconfig, const cfg_obj_t *key, return (result); } +/* + * Load keys from configuration into key table. If 'keyname' is specified, + * only load keys matching that name. If 'managed' is true, load the key as + * an initializing key. + */ static isc_result_t load_view_keys(const cfg_obj_t *keys, const cfg_obj_t *vconfig, dns_view_t *view, isc_boolean_t managed, @@ -820,12 +825,14 @@ load_view_keys(const cfg_obj_t *keys, const cfg_obj_t *vconfig, for (elt = cfg_list_first(keys); elt != NULL; - elt = cfg_list_next(elt)) { + elt = cfg_list_next(elt)) + { keylist = cfg_listelt_value(elt); for (elt2 = cfg_list_first(keylist); elt2 != NULL; - elt2 = cfg_list_next(elt2)) { + elt2 = cfg_list_next(elt2)) + { key = cfg_listelt_value(elt2); result = dstkey_fromconfig(vconfig, key, managed, &dstkey, mctx); @@ -833,8 +840,9 @@ load_view_keys(const cfg_obj_t *keys, const cfg_obj_t *vconfig, result = ISC_R_SUCCESS; continue; } - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { goto cleanup; + } /* * If keyname was specified, we only add that key. @@ -846,17 +854,27 @@ load_view_keys(const cfg_obj_t *keys, const cfg_obj_t *vconfig, continue; } - CHECK(dns_keytable_add(secroots, managed, &dstkey)); + /* + * This key is taken from the configuration, so + * if it's a managed key then it's an + * initializing key; that's why 'managed' + * is duplicated below. + */ + CHECK(dns_keytable_add2(secroots, managed, + managed, &dstkey)); } } cleanup: - if (dstkey != NULL) + if (dstkey != NULL) { dst_key_free(&dstkey); - if (secroots != NULL) + } + if (secroots != NULL) { dns_keytable_detach(&secroots); - if (result == DST_R_NOCRYPTO) + } + if (result == DST_R_NOCRYPTO) { result = ISC_R_SUCCESS; + } return (result); } @@ -1026,7 +1044,7 @@ configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig, } /* - * Add key zone for managed-keys. + * Add key zone for managed keys. */ obj = NULL; (void)named_config_get(maps, "managed-keys-directory", &obj); @@ -1050,6 +1068,7 @@ configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig, goto cleanup; } } + CHECK(add_keydata_zone(view, directory, named_g_mctx)); cleanup: @@ -6443,16 +6462,19 @@ dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) { } nextnode = NULL; (void)dns_keytable_nextkeynode(keytable, keynode, &nextnode); - if (keynode != firstnode) + if (keynode != firstnode) { dns_keytable_detachkeynode(keytable, &keynode); + } keynode = nextnode; } while (keynode != NULL); - if (n == 0) + if (n == 0) { return; + } - if (n > 1) + if (n > 1) { qsort(ids, n, sizeof(ids[0]), cid); + } /* * Encoded as "_ta-xxxx\(-xxxx\)*" where xxxx is the hex version of @@ -6460,22 +6482,25 @@ dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) { */ label[0] = 0; r.base = label; - r.length = sizeof(label);; + r.length = sizeof(label); m = snprintf(r.base, r.length, "_ta"); - if (m < 0 || (unsigned)m > r.length) + if (m < 0 || (unsigned)m > r.length) { return; + } isc_textregion_consume(&r, m); for (i = 0; i < n; i++) { m = snprintf(r.base, r.length, "-%04x", ids[i]); - if (m < 0 || (unsigned)m > r.length) + if (m < 0 || (unsigned)m > r.length) { return; + } isc_textregion_consume(&r, m); } dns_fixedname_init(&fixed); tatname = dns_fixedname_name(&fixed); result = dns_name_fromstring2(tatname, label, name, 0, NULL); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return; + } dns_name_format(tatname, namebuf, sizeof(namebuf)); isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, @@ -6484,8 +6509,9 @@ dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) { view->name, namebuf); tat = isc_mem_get(dotat_arg->view->mctx, sizeof(*tat)); - if (tat == NULL) + if (tat == NULL) { return; + } tat->mctx = NULL; tat->task = NULL; diff --git a/bin/tests/system/mkeys/README b/bin/tests/system/mkeys/README index 8682940f8c..40310a2d70 100644 --- a/bin/tests/system/mkeys/README +++ b/bin/tests/system/mkeys/README @@ -16,16 +16,8 @@ is used so it will send TAT queries once per second. ns3 is a validator with a broken key in managed-keys. -Tests TODO: +ns4 is a validator with a deliberately broken managed-keys.bind and +managed-keys.jnl, causing RFC 5011 initialization to fail. -- initial working KSK - -TODO: test using delv with new trusted key too - -- introduce a REVOKE bit - -- later remove a signature - -- corrupt a signature - -TODO: also same things with dlv auto updates of trust anchor +ns5 is a validator which is prevented from getting a response from the +root server, causing key refresh queries to fail. diff --git a/bin/tests/system/mkeys/clean.sh b/bin/tests/system/mkeys/clean.sh index a02f05b777..609da5e49b 100644 --- a/bin/tests/system/mkeys/clean.sh +++ b/bin/tests/system/mkeys/clean.sh @@ -10,8 +10,10 @@ rm -f */K* */*.signed */trusted.conf */*.jnl */*.bk rm -f dsset-. ns1/dsset-. rm -f ns*/named.lock rm -f */managed-keys.bind* */named.secroots -rm -f */managed.conf ns1/managed.key ns1/managed.key.id +rm -f */managed*.conf ns1/managed.key ns1/managed.key.id rm -f */named.memstats */named.run rm -f dig.out* delv.out* rndc.out* signer.out* rm -f ns1/named.secroots ns1/root.db.signed* ns1/root.db.tmp rm -f ns1/named.conf +rm -rf ns4/nope +rm -f ns5/named.args diff --git a/bin/tests/system/mkeys/ns1/named1.conf b/bin/tests/system/mkeys/ns1/named1.conf index 0f17bdc16b..5f9eeaf91e 100644 --- a/bin/tests/system/mkeys/ns1/named1.conf +++ b/bin/tests/system/mkeys/ns1/named1.conf @@ -10,6 +10,11 @@ controls { /* empty */ }; +acl allowed { + ! 10.53.0.5; + any; +}; + options { query-source address 10.53.0.1; notify-source 10.53.0.1; @@ -22,6 +27,7 @@ options { notify no; dnssec-enable yes; dnssec-validation yes; + allow-query { allowed; }; }; key rndc_key { diff --git a/bin/tests/system/mkeys/ns1/named2.conf b/bin/tests/system/mkeys/ns1/named2.conf index a033e10d81..42f6712859 100644 --- a/bin/tests/system/mkeys/ns1/named2.conf +++ b/bin/tests/system/mkeys/ns1/named2.conf @@ -10,6 +10,11 @@ controls { /* empty */ }; +acl allowed { + ! 10.53.0.5; + any; +}; + options { query-source address 10.53.0.1; notify-source 10.53.0.1; @@ -22,6 +27,7 @@ options { notify no; dnssec-enable yes; dnssec-validation yes; + allow-query { allowed; }; }; key rndc_key { diff --git a/bin/tests/system/mkeys/ns1/named3.conf b/bin/tests/system/mkeys/ns1/named3.conf new file mode 100644 index 0000000000..614c49c348 --- /dev/null +++ b/bin/tests/system/mkeys/ns1/named3.conf @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015-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/. + */ + +// NS1 + +controls { /* empty */ }; + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify no; + dnssec-enable yes; + dnssec-validation yes; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.1 port 9953 allow { any; } keys { rndc_key; }; +}; + +zone "." { + type master; + file "root.db.signed"; +}; diff --git a/bin/tests/system/mkeys/ns1/sign.sh b/bin/tests/system/mkeys/ns1/sign.sh index fb134742d2..054422de55 100644 --- a/bin/tests/system/mkeys/ns1/sign.sh +++ b/bin/tests/system/mkeys/ns1/sign.sh @@ -28,6 +28,8 @@ managed-keys { EOF ' > managed.conf cp managed.conf ../ns2/managed.conf +cp managed.conf ../ns4/managed.conf +cp managed.conf ../ns5/managed.conf # Configure a trusted key statement (used by delve) cat $keyname.key | grep -v '^; ' | $PERL -n -e ' diff --git a/bin/tests/system/mkeys/ns2/named.args b/bin/tests/system/mkeys/ns2/named.args index d222b7faea..71e466df40 100644 --- a/bin/tests/system/mkeys/ns2/named.args +++ b/bin/tests/system/mkeys/ns2/named.args @@ -1 +1 @@ --m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -T mkeytimers=2/20/40 +-m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -T mkeytimers=2/20/40 -T tat=1 diff --git a/bin/tests/system/mkeys/ns4/named.conf b/bin/tests/system/mkeys/ns4/named.conf new file mode 100644 index 0000000000..ad3979d7a7 --- /dev/null +++ b/bin/tests/system/mkeys/ns4/named.conf @@ -0,0 +1,41 @@ +/* + * Copyright (C) 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/. + */ + +// NS4 + +controls { /* empty */ }; + +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; + notify no; + dnssec-enable yes; + dnssec-validation auto; + bindkeys-file "managed.conf"; + managed-keys-directory "nope"; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.4 port 9953 allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; diff --git a/bin/tests/system/mkeys/ns5/named.conf b/bin/tests/system/mkeys/ns5/named.conf new file mode 100644 index 0000000000..98204929ad --- /dev/null +++ b/bin/tests/system/mkeys/ns5/named.conf @@ -0,0 +1,38 @@ +/* + * Copyright (C) 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/. + */ + +// NS5 + +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; + notify no; + dnssec-enable yes; + dnssec-validation auto; + bindkeys-file "managed.conf"; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.5 port 9953 allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; diff --git a/bin/tests/system/mkeys/ns5/named1.args b/bin/tests/system/mkeys/ns5/named1.args new file mode 100644 index 0000000000..efb102a4ba --- /dev/null +++ b/bin/tests/system/mkeys/ns5/named1.args @@ -0,0 +1 @@ +-m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g diff --git a/bin/tests/system/mkeys/ns5/named2.args b/bin/tests/system/mkeys/ns5/named2.args new file mode 100644 index 0000000000..d222b7faea --- /dev/null +++ b/bin/tests/system/mkeys/ns5/named2.args @@ -0,0 +1 @@ +-m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -T mkeytimers=2/20/40 diff --git a/bin/tests/system/mkeys/setup.sh b/bin/tests/system/mkeys/setup.sh index d555c4e93e..5636491072 100644 --- a/bin/tests/system/mkeys/setup.sh +++ b/bin/tests/system/mkeys/setup.sh @@ -14,5 +14,14 @@ $SHELL clean.sh test -r $RANDFILE || $GENRANDOM 800 $RANDFILE cp ns1/named1.conf ns1/named.conf +cp ns5/named1.args ns5/named.args -cd ns1 && $SHELL sign.sh +( cd ns1 && $SHELL sign.sh ) + +cp ns2/managed.conf ns2/managed1.conf + +cd ns4 +mkdir nope +touch nope/managed-keys.bind +touch nope/managed.keys.bind.jnl +chmod 444 nope/* diff --git a/bin/tests/system/mkeys/tests.sh b/bin/tests/system/mkeys/tests.sh index b9806f3431..5a3d7a2e96 100644 --- a/bin/tests/system/mkeys/tests.sh +++ b/bin/tests/system/mkeys/tests.sh @@ -215,9 +215,36 @@ t2=`grep "trust pending" ns2/managed-keys.bind` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` -echo "I: reinitialize trust anchors" +echo "I: reinitialize trust anchors, add second key to bind.keys" $PERL $SYSTEMTESTTOP/stop.pl --use-rndc . ns2 rm -f ns2/managed-keys.bind* +cat ns1/$standby1.key | grep -v '^; ' | $PERL -n -e ' +local ($dn, $class, $type, $flags, $proto, $alg, @rest) = split; +local $key = join("", @rest); +local $originalkey = `grep initial-key ns2/managed1.conf`; +print < ns2/managed.conf +$PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns2 + +n=`expr $n + 1` +echo "I: check that no key from bind.keys is marked as an initializing key ($n)" +ret=0 +sleep 3 +$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 secroots | sed 's/^/I: ns2 /' +sleep 1 +grep '; initializing' ns2/named.secroots > /dev/null 2>&1 && ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I: reinitialize trust anchors, revert to one key in bind.keys" +$PERL $SYSTEMTESTTOP/stop.pl --use-rndc . ns2 +rm -f ns2/managed-keys.bind* +mv ns2/managed1.conf ns2/managed.conf $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns2 n=`expr $n + 1` @@ -446,7 +473,6 @@ rm -f ${revoked}.key ${revoked}.private $SETTIME -D none -R none -K ns1 `cat ns1/managed.key` > /dev/null $SETTIME -D now -K ns1 $standby1 > /dev/null $SETTIME -D now -K ns1 $standby2 > /dev/null -$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 flush | sed 's/^/I: ns1 /' sleep 1 $SIGNER -Sg -K ns1 -N unixtime -r $RANDFILE -o . ns1/root.db > /dev/null 2>&- $RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953 reload . | sed 's/^/I: ns1 /' @@ -454,6 +480,7 @@ sleep 3 $RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 managed-keys refresh | sed 's/^/I: ns2 /' sleep 1 $RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 managed-keys status > rndc.out.$n 2>&1 +$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 flush | sed 's/^/I: ns1 /' $DIG $DIGOPTS +noauth example. @10.53.0.2 txt > dig.out.ns2.test$n || ret=1 grep "flags:.*ad.*QUERY" dig.out.ns2.test$n > /dev/null || ret=1 grep "example..*.RRSIG..*TXT" dig.out.ns2.test$n > /dev/null || ret=1 @@ -537,14 +564,14 @@ status=`expr $status + $ret` n=`expr $n + 1` echo "I: check that trust-anchor-telemetry queries are logged ($n)" ret=0 -grep "sending trust-anchor-telemetry query '_ta-[0-9a-f]*/NULL" ns3/named.run > /dev/null || ret=1 +grep "sending trust-anchor-telemetry query '_ta-[0-9a-f]*/NULL" ns2/named.run > /dev/null || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` n=`expr $n + 1` echo "I: check that trust-anchor-telemetry queries are received ($n)" ret=0 -grep "query '_ta-[0-9a-f]*/NULL/IN' approved" ns1/named.run > /dev/null || ret=1 +grep "query '_ta-[0-9a-f][0-9a-f]*/NULL/IN' approved" ns1/named.run > /dev/null || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` @@ -562,5 +589,82 @@ grep "name: \." rndc.out.$n > /dev/null || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +n=`expr $n + 1` +echo "I: check that trust-anchor-telemetry queries contain the correct key ($n)" +ret=0 +# convert the hexadecimal key from the TAT query into decimal and +# compare against the known key. +tathex=`grep "query '_ta-[0-9a-f][0-9a-f]*/NULL/IN' approved" ns1/named.run | awk '{print $6; exit 0}' | sed -e 's/(_ta-\([0-9a-f][0-9a-f]*\)):/\1/'` +tatkey=`$PERL -e 'printf("%d\n", hex(@ARGV[0]));' $tathex` +realkey=`$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 secroots - | grep '; managed' | sed 's#.*SHA256/\([0-9][0-9]*\) ; managed.*#\1#'` +[ "$tatkey" -eq "$realkey" ] || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: check initialization fails if managed-keys can't be created ($n)" +ret=0 +$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 secroots | sed 's/^/I: ns4 /' +grep '; initializing managed' ns4/named.secroots > /dev/null 2>&1 || ret=1 +grep '; managed' ns4/named.secroots > /dev/null 2>&1 && ret=1 +grep '; trusted' ns4/named.secroots > /dev/null 2>&1 && ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: check failure to contact root servers does not prevent key refreshes after restart ($n)" +ret=0 +# By the time we get here, ns5 should have attempted refreshing its managed +# keys. These attempts should fail as ns1 is configured to REFUSE all queries +# from ns5. Note that named1.args does not contain "-T mkeytimers"; this is to +# ensure key refresh retry will be scheduled to one actual hour after the first +# key refresh failure instead of just a few seconds, in order to prevent races +# between the next scheduled key refresh time and startup time of restarted ns5. +$PERL $SYSTEMTESTTOP/stop.pl --use-rndc . ns5 +$PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns5 +sleep 2 +# ns5/named.run will contain logs from both the old instance and the new +# instance. In order for the test to pass, both must attempt a fetch. +count=`grep -c "Creating key fetch" ns5/named.run` +[ $count -lt 2 ] && ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: check key refreshes are resumed after root servers become available ($n)" +ret=0 +$PERL $SYSTEMTESTTOP/stop.pl --use-rndc . ns5 +# Prevent previous check from affecting this one +rm -f ns2/managed-keys.bind* +# named2.args adds "-T mkeytimers=2/20/40" to named1.args as we need to wait for +# an "hour" until keys are refreshed again after initial failure +cp ns5/named2.args ns5/named.args +$PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns5 +sleep 2 +$RNDC -c ../common/rndc.conf -s 10.53.0.5 -p 9953 secroots | sed 's/^/I: ns4 /' +sleep 1 +grep '; initializing managed' ns5/named.secroots > /dev/null 2>&1 || ret=1 +# ns1 should still REFUSE queries from ns5, so resolving should be impossible +$DIG $DIGOPTS +noauth example. @10.53.0.5 txt > dig.out.ns5.a.test$n || ret=1 +grep "flags:.*ad.*QUERY" dig.out.ns5.a.test$n > /dev/null && ret=1 +grep "example..*.RRSIG..*TXT" dig.out.ns5.a.test$n > /dev/null && ret=1 +grep "status: SERVFAIL" dig.out.ns5.a.test$n > /dev/null || ret=1 +# Allow queries from ns5 to ns1 +cp ns1/named3.conf ns1/named.conf +rm -f ns1/root.db.signed.jnl +$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953 reconfig +sleep 3 +$RNDC -c ../common/rndc.conf -s 10.53.0.5 -p 9953 secroots | sed 's/^/I: ns4 /' +sleep 1 +grep '; managed' ns5/named.secroots > /dev/null 2>&1 || ret=1 +# ns1 should not longer REFUSE queries from ns5, so managed keys should be +# correctly refreshed and resolving should succeed +$DIG $DIGOPTS +noauth example. @10.53.0.5 txt > dig.out.ns5.b.test$n || ret=1 +grep "flags:.*ad.*QUERY" dig.out.ns5.b.test$n > /dev/null || ret=1 +grep "example..*.RRSIG..*TXT" dig.out.ns5.b.test$n > /dev/null || ret=1 +grep "status: NOERROR" dig.out.ns5.b.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/notes.xml b/doc/arm/notes.xml index a7ce6ed018..296a0f574b 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -566,6 +566,16 @@ are not writable by the effective user ID. [RT #46077] + + + Initializing keys specified in a managed-keys + statement or by dnssec-validation auto; are + now tagged as "initializing", until they have been updated by a + key refresh query. If key maintenance fails to initialize, + this will be visible when running rndc secroots. + [RT #46267] + + Previously, update-policy local; accepted diff --git a/lib/dns/client.c b/lib/dns/client.c index 1d8269912e..9a8d9b3819 100644 --- a/lib/dns/client.c +++ b/lib/dns/client.c @@ -1546,7 +1546,7 @@ dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass, if (result != ISC_R_SUCCESS) goto cleanup; - result = dns_keytable_add(secroots, ISC_FALSE, &dstkey); + result = dns_keytable_add2(secroots, ISC_FALSE, ISC_FALSE, &dstkey); cleanup: if (dstkey != NULL) diff --git a/lib/dns/include/dns/keytable.h b/lib/dns/include/dns/keytable.h index 62c55a9788..fd089b6a54 100644 --- a/lib/dns/include/dns/keytable.h +++ b/lib/dns/include/dns/keytable.h @@ -102,10 +102,18 @@ dns_keytable_detach(dns_keytable_t **keytablep); isc_result_t dns_keytable_add(dns_keytable_t *keytable, isc_boolean_t managed, - dst_key_t **keyp); + dst_key_t **keyp) ISC_DEPRECATED; +isc_result_t +dns_keytable_add2(dns_keytable_t *keytable, isc_boolean_t managed, + isc_boolean_t initial, dst_key_t **keyp); /*%< * Add '*keyp' to 'keytable' (using the name in '*keyp'). - * The value of keynode->managed is set to 'managed' + * The value of keynode->managed is set to 'managed', and the + * value of keynode->initial is set to 'initial'. (Note: 'initial' + * should only be used when adding managed-keys from configuration. + * This indicates the key is in "initializing" state, and has not yet + * been confirmed with a key refresh query. Once a key refresh query + * has validated, we update the keynode with inital == ISC_FALSE.) * * Notes: * @@ -117,6 +125,8 @@ dns_keytable_add(dns_keytable_t *keytable, isc_boolean_t managed, * *\li 'keytable' points to a valid keytable. * + *\li if 'initial' is true then 'managed' must also be true. + * *\li keyp != NULL && *keyp is a valid dst_key_t *. * * Ensures: @@ -402,6 +412,19 @@ dns_keynode_managed(dns_keynode_t *keynode); * Is this flagged as a managed key? */ +isc_boolean_t +dns_keynode_initial(dns_keynode_t *keynode); +/*%< + * Is this flagged as an initializing key? + */ + +void +dns_keynode_trust(dns_keynode_t *keynode); +/*%< + * Sets keynode->initial to ISC_FALSE in order to mark the key as + * trusted: no longer an initializing key. + */ + isc_result_t dns_keynode_create(isc_mem_t *mctx, dns_keynode_t **target); /*%< diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index 1e6aba528e..8544dbe810 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -614,6 +614,12 @@ dns_resolver_setnonbackofftries(dns_resolver_t *resolver, unsigned int tries); unsigned int dns_resolver_getoptions(dns_resolver_t *resolver); +/*%< + * Get the resolver options. + * + * Requires: + * \li resolver to be valid. + */ void dns_resolver_addbadcache(dns_resolver_t *resolver, const dns_name_t *name, diff --git a/lib/dns/keytable.c b/lib/dns/keytable.c index 86324cf18b..c6093a8a1c 100644 --- a/lib/dns/keytable.c +++ b/lib/dns/keytable.c @@ -47,6 +47,7 @@ struct dns_keynode { isc_refcount_t refcount; dst_key_t * key; isc_boolean_t managed; + isc_boolean_t initial; struct dns_keynode * next; }; @@ -164,83 +165,169 @@ dns_keytable_detach(dns_keytable_t **keytablep) { *keytablep = NULL; } +/*% + * Search "node" for either a null key node or a key node for the exact same + * key as the one supplied in "keyp" and, if found, update it accordingly. + */ static isc_result_t -insert(dns_keytable_t *keytable, isc_boolean_t managed, - const dns_name_t *keyname, dst_key_t **keyp) +update_keynode(dst_key_t **keyp, dns_rbtnode_t *node, isc_boolean_t initial) { + dns_keynode_t *knode; + + REQUIRE(keyp != NULL && *keyp != NULL); + REQUIRE(node != NULL); + + for (knode = node->data; knode != NULL; knode = knode->next) { + if (knode->key == NULL) { + /* + * Null key node found. Attach the supplied key to it, + * making it a non-null key node and transferring key + * ownership to the keytable. + */ + knode->key = *keyp; + *keyp = NULL; + return (ISC_R_SUCCESS); + } else if (dst_key_compare(knode->key, *keyp)) { + /* + * Key node found for the supplied key. Free the + * supplied copy of the key and update the found key + * node's flags if necessary. + */ + dst_key_free(keyp); + if (!initial) { + dns_keynode_trust(knode); + } + return (ISC_R_SUCCESS); + } + } + + return (ISC_R_NOTFOUND); +} + +/*% + * Create a key node for "keyp" (or a null key node if "keyp" is NULL), set + * "managed" and "initial" as requested and make the created key node the first + * one attached to "node" in "keytable". + */ +static isc_result_t +prepend_keynode(dst_key_t **keyp, dns_rbtnode_t *node, + dns_keytable_t *keytable, isc_boolean_t managed, + isc_boolean_t initial) { - isc_result_t result; dns_keynode_t *knode = NULL; - dns_rbtnode_t *node; + isc_result_t result; REQUIRE(keyp == NULL || *keyp != NULL); REQUIRE(VALID_KEYTABLE(keytable)); + REQUIRE(!initial || managed); result = dns_keynode_create(keytable->mctx, &knode); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (result); + } + + /* + * If a key was supplied, transfer its ownership to the keytable. + */ + if (keyp) { + knode->key = *keyp; + *keyp = NULL; + } knode->managed = managed; + knode->initial = initial; + knode->next = node->data; + node->data = knode; + + return (ISC_R_SUCCESS); +} + +/*% + * Add key "keyp" at "keyname" in "keytable". If the key already exists at the + * requested name, update its flags. If "keyp" is NULL, add a null key to + * indicate that "keyname" should be treated as a secure domain without + * supplying key data which would allow the domain to be validated. + */ +static isc_result_t +insert(dns_keytable_t *keytable, isc_boolean_t managed, isc_boolean_t initial, + const dns_name_t *keyname, dst_key_t **keyp) +{ + dns_rbtnode_t *node = NULL; + isc_result_t result; + + REQUIRE(VALID_KEYTABLE(keytable)); RWLOCK(&keytable->rwlock, isc_rwlocktype_write); - node = NULL; result = dns_rbt_addnode(keytable->table, keyname, &node); - - if (keyp != NULL) { - if (result == ISC_R_EXISTS) { - /* Key already in table? */ - dns_keynode_t *k; - for (k = node->data; k != NULL; k = k->next) { - if (k->key == NULL) { - k->key = *keyp; - *keyp = NULL; /* transfer ownership */ - break; - } - if (dst_key_compare(k->key, *keyp) == ISC_TRUE) - break; - } - - if (k == NULL) - result = ISC_R_SUCCESS; - else if (*keyp != NULL) - dst_key_free(keyp); - } - - if (result == ISC_R_SUCCESS) { - knode->key = *keyp; - knode->next = node->data; - *keyp = NULL; - } - } - if (result == ISC_R_SUCCESS) { - node->data = knode; - knode = NULL; + /* + * There was no node for "keyname" in "keytable" yet, so one + * was created. Create a new key node for the supplied key (or + * a null key node if "keyp" is NULL) and attach it to the + * created node. + */ + result = prepend_keynode(keyp, node, keytable, managed, + initial); + } else if (result == ISC_R_EXISTS) { + /* + * A node already exists for "keyname" in "keytable". + */ + if (keyp == NULL) { + /* + * We were told to add a null key at "keyname", which + * means there is nothing left to do as there is either + * a null key at this node already or there is a + * non-null key node which would not be affected. + * Reset result to reflect the fact that the node for + * "keyname" is already marked as secure. + */ + result = ISC_R_SUCCESS; + } else { + /* + * We were told to add the key supplied in "keyp" at + * "keyname". Try to find an already existing key node + * we could reuse for the supplied key (i.e. a null key + * node or a key node for the exact same key) and, if + * found, update it accordingly. + */ + result = update_keynode(keyp, node, initial); + if (result == ISC_R_NOTFOUND) { + /* + * The node for "keyname" only contains key + * nodes for keys different than the supplied + * one. Create a new key node for the supplied + * key and prepend it before the others. + */ + result = prepend_keynode(keyp, node, keytable, + managed, initial); + } + } } - /* Key was already there? That's the same as a success */ - if (result == ISC_R_EXISTS) - result = ISC_R_SUCCESS; - RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write); - if (knode != NULL) - dns_keynode_detach(keytable->mctx, &knode); - return (result); } isc_result_t dns_keytable_add(dns_keytable_t *keytable, isc_boolean_t managed, dst_key_t **keyp) +{ + return (dns_keytable_add2(keytable, managed, ISC_FALSE, keyp)); +} + +isc_result_t +dns_keytable_add2(dns_keytable_t *keytable, isc_boolean_t managed, + isc_boolean_t initial, dst_key_t **keyp) { REQUIRE(keyp != NULL && *keyp != NULL); - return (insert(keytable, managed, dst_key_name(*keyp), keyp)); + REQUIRE(!initial || managed); + return (insert(keytable, managed, initial, dst_key_name(*keyp), keyp)); } isc_result_t dns_keytable_marksecure(dns_keytable_t *keytable, const dns_name_t *name) { - return (insert(keytable, ISC_TRUE, name, NULL)); + return (insert(keytable, ISC_TRUE, ISC_FALSE, name, NULL)); } isc_result_t @@ -644,8 +731,9 @@ dns_keytable_totext(dns_keytable_t *keytable, isc_buffer_t **text) { if (knode->key == NULL) continue; dst_key_format(knode->key, pbuf, sizeof(pbuf)); - snprintf(obuf, sizeof(obuf), "%s ; %s\n", pbuf, - knode->managed ? "managed" : "trusted"); + snprintf(obuf, sizeof(obuf), "%s ; %s%s\n", pbuf, + knode->initial ? "initializing " : "", + knode->managed ? "managed" : "trusted"); result = putstr(text, obuf); if (result != ISC_R_SUCCESS) break; @@ -703,11 +791,6 @@ dns_keytable_forall(dns_keytable_t *keytable, dst_key_t * dns_keynode_key(dns_keynode_t *keynode) { - - /* - * Get the DST key associated with keynode. - */ - REQUIRE(VALID_KEYNODE(keynode)); return (keynode->key); @@ -715,14 +798,25 @@ dns_keynode_key(dns_keynode_t *keynode) { isc_boolean_t dns_keynode_managed(dns_keynode_t *keynode) { - /* - * Is this a managed key? - */ REQUIRE(VALID_KEYNODE(keynode)); return (keynode->managed); } +isc_boolean_t +dns_keynode_initial(dns_keynode_t *keynode) { + REQUIRE(VALID_KEYNODE(keynode)); + + return (keynode->initial); +} + +void +dns_keynode_trust(dns_keynode_t *keynode) { + REQUIRE(VALID_KEYNODE(keynode)); + + keynode->initial = ISC_FALSE; +} + isc_result_t dns_keynode_create(isc_mem_t *mctx, dns_keynode_t **target) { isc_result_t result; @@ -736,6 +830,7 @@ dns_keynode_create(isc_mem_t *mctx, dns_keynode_t **target) { knode->magic = KEYNODE_MAGIC; knode->managed = ISC_FALSE; + knode->initial = ISC_FALSE; knode->key = NULL; knode->next = NULL; diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index cf71b054a4..1f620dd9a9 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -10595,8 +10595,8 @@ dns_resolver_addbadcache(dns_resolver_t *resolver, const dns_name_t *name, if (!fuzzing_resolver) #endif { - (void) dns_badcache_add(resolver->badcache, name, type, - ISC_FALSE, 0, expire); + dns_badcache_add(resolver->badcache, name, type, + ISC_FALSE, 0, expire); } } diff --git a/lib/dns/tests/keytable_test.c b/lib/dns/tests/keytable_test.c index 52219b699c..6057340219 100644 --- a/lib/dns/tests/keytable_test.c +++ b/lib/dns/tests/keytable_test.c @@ -126,7 +126,12 @@ create_tables() { /* Add a normal key */ create_key(257, 3, 5, "example.com", keystr1, &key); - ATF_REQUIRE_EQ(dns_keytable_add(keytable, ISC_FALSE, &key), + ATF_REQUIRE_EQ(dns_keytable_add2(keytable, ISC_FALSE, ISC_FALSE, &key), + ISC_R_SUCCESS); + + /* Add an initializing managed key */ + create_key(257, 3, 5, "managed.com", keystr1, &key); + ATF_REQUIRE_EQ(dns_keytable_add2(keytable, ISC_TRUE, ISC_TRUE, &key), ISC_R_SUCCESS); /* Add a null key */ @@ -185,7 +190,7 @@ ATF_TC_BODY(add, tc) { * nextkeynode() should still return NOTFOUND. */ create_key(257, 3, 5, "example.com", keystr1, &key); - ATF_REQUIRE_EQ(dns_keytable_add(keytable, ISC_FALSE, &key), + ATF_REQUIRE_EQ(dns_keytable_add2(keytable, ISC_FALSE, ISC_FALSE, &key), ISC_R_SUCCESS); ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode, &next_keynode), ISC_R_NOTFOUND); @@ -193,23 +198,129 @@ ATF_TC_BODY(add, tc) { /* Add another key (different keydata) */ dns_keytable_detachkeynode(keytable, &keynode); create_key(257, 3, 5, "example.com", keystr2, &key); - ATF_REQUIRE_EQ(dns_keytable_add(keytable, ISC_FALSE, &key), + ATF_REQUIRE_EQ(dns_keytable_add2(keytable, ISC_FALSE, ISC_FALSE, &key), ISC_R_SUCCESS); ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("example.com"), &keynode), ISC_R_SUCCESS); ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode, &next_keynode), ISC_R_SUCCESS); dns_keytable_detachkeynode(keytable, &next_keynode); + dns_keytable_detachkeynode(keytable, &keynode); + + /* + * Get the keynode for the managed.com key. There's no other key for + * the name, so nextkeynode() should return NOTFOUND. Ensure the + * retrieved key is an initializing key, then mark it as trusted using + * dns_keynode_trust() and ensure the latter works as expected. + */ + ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("managed.com"), + &keynode), ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode, + &next_keynode), ISC_R_NOTFOUND); + ATF_REQUIRE_EQ(dns_keynode_initial(keynode), ISC_TRUE); + dns_keynode_trust(keynode); + ATF_REQUIRE_EQ(dns_keynode_initial(keynode), ISC_FALSE); + dns_keytable_detachkeynode(keytable, &keynode); + + /* + * Add a different managed key for managed.com, marking it as an + * initializing key. Ensure nextkeynode() no longer returns + * ISC_R_NOTFOUND and that the added key is an initializing key. + */ + create_key(257, 3, 5, "managed.com", keystr2, &key); + ATF_REQUIRE_EQ(dns_keytable_add2(keytable, ISC_TRUE, ISC_TRUE, &key), + ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("managed.com"), + &keynode), ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode, + &next_keynode), ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keynode_initial(keynode), ISC_TRUE); + dns_keytable_detachkeynode(keytable, &next_keynode); + dns_keytable_detachkeynode(keytable, &keynode); + + /* + * Add the same managed key again, but this time mark it as a + * non-initializing key. Ensure the previously added key is upgraded + * to a non-initializing key and make sure there are still two key + * nodes for managed.com, both containing non-initializing keys. + */ + create_key(257, 3, 5, "managed.com", keystr2, &key); + ATF_REQUIRE_EQ(dns_keytable_add2(keytable, ISC_TRUE, ISC_FALSE, &key), + ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("managed.com"), + &keynode), ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keynode_initial(keynode), ISC_FALSE); + ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode, + &next_keynode), ISC_R_SUCCESS); + dns_keytable_detachkeynode(keytable, &keynode); + keynode = next_keynode; + next_keynode = NULL; + ATF_REQUIRE_EQ(dns_keynode_initial(keynode), ISC_FALSE); + ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode, + &next_keynode), ISC_R_NOTFOUND); + dns_keytable_detachkeynode(keytable, &keynode); + + /* + * Add a managed key at a new node, two.com, marking it as an + * initializing key. Ensure nextkeynode() returns ISC_R_NOTFOUND and + * that the added key is an initializing key. + */ + create_key(257, 3, 5, "two.com", keystr1, &key); + ATF_REQUIRE_EQ(dns_keytable_add2(keytable, ISC_TRUE, ISC_TRUE, &key), + ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("two.com"), + &keynode), ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode, + &next_keynode), ISC_R_NOTFOUND); + ATF_REQUIRE_EQ(dns_keynode_initial(keynode), ISC_TRUE); + dns_keytable_detachkeynode(keytable, &keynode); + + /* + * Add a different managed key for two.com, marking it as a + * non-initializing key. Ensure nextkeynode() no longer returns + * ISC_R_NOTFOUND and that the added key is not an initializing key. + */ + create_key(257, 3, 5, "two.com", keystr2, &key); + ATF_REQUIRE_EQ(dns_keytable_add2(keytable, ISC_TRUE, ISC_FALSE, &key), + ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("two.com"), + &keynode), ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode, + &next_keynode), ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keynode_initial(keynode), ISC_FALSE); + dns_keytable_detachkeynode(keytable, &next_keynode); + dns_keytable_detachkeynode(keytable, &keynode); + + /* + * Add the first managed key again, but this time mark it as a + * non-initializing key. Ensure the previously added key is upgraded + * to a non-initializing key and make sure there are still two key + * nodes for two.com, both containing non-initializing keys. + */ + create_key(257, 3, 5, "two.com", keystr1, &key); + ATF_REQUIRE_EQ(dns_keytable_add2(keytable, ISC_TRUE, ISC_FALSE, &key), + ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("two.com"), + &keynode), ISC_R_SUCCESS); + ATF_REQUIRE_EQ(dns_keynode_initial(keynode), ISC_FALSE); + ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode, + &next_keynode), ISC_R_SUCCESS); + dns_keytable_detachkeynode(keytable, &keynode); + keynode = next_keynode; + next_keynode = NULL; + ATF_REQUIRE_EQ(dns_keynode_initial(keynode), ISC_FALSE); + ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode, + &next_keynode), ISC_R_NOTFOUND); + dns_keytable_detachkeynode(keytable, &keynode); /* * Add a normal key to a name that has a null key. The null key node * will be updated with the normal key. */ - dns_keytable_detachkeynode(keytable, &keynode); ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("null.example"), &null_keynode), ISC_R_SUCCESS); create_key(257, 3, 5, "null.example", keystr2, &key); - ATF_REQUIRE_EQ(dns_keytable_add(keytable, ISC_FALSE, &key), + ATF_REQUIRE_EQ(dns_keytable_add2(keytable, ISC_FALSE, ISC_FALSE, &key), ISC_R_SUCCESS); ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("null.example"), &keynode), ISC_R_SUCCESS); @@ -523,7 +634,7 @@ ATF_TC_BODY(nta, tc) { ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); create_key(257, 3, 5, "example", keystr1, &key); - result = dns_keytable_add(keytable, ISC_FALSE, &key); + result = dns_keytable_add2(keytable, ISC_FALSE, ISC_FALSE, &key); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); isc_stdtime_get(&now); diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index 6747ade8ee..cd5e85ed8c 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -431,10 +431,13 @@ dns_keynode_attach dns_keynode_create dns_keynode_detach dns_keynode_detachall +dns_keynode_initial dns_keynode_key dns_keynode_managed +dns_keynode_trust dns_keyring_restore dns_keytable_add +dns_keytable_add2 dns_keytable_attach dns_keytable_attachkeynode dns_keytable_create diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 4e608d0cf7..c0a9597917 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -3891,7 +3891,8 @@ compute_tag(dns_name_t *name, dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx, */ static void trust_key(dns_zone_t *zone, dns_name_t *keyname, - dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx) + dns_rdata_dnskey_t *dnskey, isc_boolean_t initial, + isc_mem_t *mctx) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; @@ -3910,7 +3911,7 @@ trust_key(dns_zone_t *zone, dns_name_t *keyname, goto failure; CHECK(dns_dnssec_keyfromrdata(keyname, &rdata, mctx, &dstkey)); - CHECK(dns_keytable_add(sr, ISC_TRUE, &dstkey)); + CHECK(dns_keytable_add2(sr, ISC_TRUE, initial, &dstkey)); dns_keytable_detach(&sr); failure: @@ -3996,7 +3997,8 @@ load_secroots(dns_zone_t *zone, dns_name_t *name, dns_rdataset_t *rdataset) { /* Add to keytables. */ trusted++; - trust_key(zone, name, &dnskey, mctx); + trust_key(zone, name, &dnskey, + ISC_TF(keydata.addhd == 0), mctx); } if (trusted == 0 && pending != 0) { @@ -4731,8 +4733,9 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, case dns_zone_key: result = sync_keyzone(zone, db); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { goto cleanup; + } break; default: @@ -4882,9 +4885,17 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, return (result); cleanup: + if (zone->type == dns_zone_key && result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "failed to initialize managed-keys (%s): " + "DNSSEC validation is at risk", + isc_result_totext(result)); + } + for (inc = ISC_LIST_HEAD(zone->newincludes); inc != NULL; - inc = ISC_LIST_HEAD(zone->newincludes)) { + inc = ISC_LIST_HEAD(zone->newincludes)) + { ISC_LIST_UNLINK(zone->newincludes, inc, link); isc_mem_free(zone->mctx, inc->name); isc_mem_put(zone->mctx, inc, sizeof(*inc)); @@ -9045,7 +9056,7 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { dst_key_t *dstkey; isc_stdtime_t now; int pending = 0; - isc_boolean_t secure = ISC_FALSE; + isc_boolean_t secure = ISC_FALSE, initial = ISC_FALSE; isc_boolean_t free_needed; UNUSED(task); @@ -9122,7 +9133,8 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { */ for (result = dns_rdataset_first(&kfetch->dnskeysigset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(&kfetch->dnskeysigset)) { + result = dns_rdataset_next(&kfetch->dnskeysigset)) + { dns_keynode_t *keynode = NULL; dns_rdata_reset(&sigrr); @@ -9141,7 +9153,8 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { break; if (dst_key_alg(dstkey) == sig.algorithm && - dst_key_id(dstkey) == sig.keyid) { + dst_key_id(dstkey) == sig.keyid) + { result = dns_dnssec_verify2(keyname, &kfetch->dnskeyset, dstkey, ISC_FALSE, @@ -9159,6 +9172,9 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { dns_trust_secure; kfetch->dnskeysigset.trust = dns_trust_secure; + secure = ISC_TRUE; + initial = dns_keynode_initial(keynode); + dns_keynode_trust(keynode); break; } } @@ -9169,11 +9185,11 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { keynode = nextnode; } - if (keynode != NULL) + if (keynode != NULL) { dns_keytable_detachkeynode(secroots, &keynode); + } - if (kfetch->dnskeyset.trust == dns_trust_secure) { - secure = ISC_TRUE; + if (secure) { break; } } @@ -9182,7 +9198,6 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { * If we were not able to verify the answer using the current * trusted keys then all we can do is look at any revoked keys. */ - if (!secure) { dns_zone_log(zone, ISC_LOG_DEBUG(3), "DNSKEY set for zone '%s' could not be verified " @@ -9422,10 +9437,13 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { trustkey = ISC_TRUE; dns_zone_log(zone, ISC_LOG_INFO, "Key %d for zone %s " - "acceptance timer " - "complete: " - "key now trusted", - keytag, namebuf); + "is now trusted (%s)", + keytag, namebuf, + initial + ? "initializing key " + "verified" + : "acceptance timer " + "complete"); } } else if (keydata.addhd > now) { /* @@ -9524,7 +9542,7 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { /* Trust this key. */ result = dns_rdata_tostruct(&dnskeyrr, &dnskey, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - trust_key(zone, keyname, &dnskey, mctx); + trust_key(zone, keyname, &dnskey, ISC_FALSE, mctx); } if (secure && !deletekey) { @@ -9546,7 +9564,6 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { fail_secure(zone, keyname); done: - if (!ISC_LIST_EMPTY(diff.tuples)) { /* Write changes to journal file. */ CHECK(update_soa_serial(kfetch->db, ver, &diff, mctx, @@ -9559,7 +9576,12 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { } failure: - + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "error during managed-keys processing (%s): " + "DNSSEC validation may be at risk", + isc_result_totext(result)); + } dns_diff_clear(&diff); if (ver != NULL) dns_db_closeversion(kfetch->db, &ver, commit); @@ -9669,7 +9691,7 @@ zone_refreshkeys(dns_zone_t *zone) { } /* Acceptance timer expired? */ - if (kd.addhd != 0 && kd.addhd < now) + if (kd.addhd < now) timer = kd.addhd; /* Or do we just need to refresh the keyset? */