+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]
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,
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);
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.
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);
}
}
/*
- * Add key zone for managed-keys.
+ * Add key zone for managed keys.
*/
obj = NULL;
(void)named_config_get(maps, "managed-keys-directory", &obj);
goto cleanup;
}
}
+
CHECK(add_keydata_zone(view, directory, named_g_mctx));
cleanup:
}
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
*/
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,
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;
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.
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
controls { /* empty */ };
+acl allowed {
+ ! 10.53.0.5;
+ any;
+};
+
options {
query-source address 10.53.0.1;
notify-source 10.53.0.1;
notify no;
dnssec-enable yes;
dnssec-validation yes;
+ allow-query { allowed; };
};
key rndc_key {
controls { /* empty */ };
+acl allowed {
+ ! 10.53.0.5;
+ any;
+};
+
options {
query-source address 10.53.0.1;
notify-source 10.53.0.1;
notify no;
dnssec-enable yes;
dnssec-validation yes;
+ allow-query { allowed; };
};
key rndc_key {
--- /dev/null
+/*
+ * 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";
+};
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 '
--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
--- /dev/null
+/*
+ * 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";
+};
--- /dev/null
+/*
+ * 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";
+};
--- /dev/null
+-m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g
--- /dev/null
+-m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -T mkeytimers=2/20/40
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/*
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 <<EOF
+managed-keys {
+ $originalkey
+ "$dn" initial-key $flags $proto $alg "$key";
+};
+EOF
+' > 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`
$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 /'
$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
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`
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
are not writable by the effective user ID. [RT #46077]
</para>
</listitem>
+ <listitem>
+ <para>
+ Initializing keys specified in a <command>managed-keys</command>
+ statement or by <command>dnssec-validation auto;</command> 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 <command>rndc secroots</command>.
+ [RT #46267]
+ </para>
+ </listitem>
<listitem>
<para>
Previously, <command>update-policy local;</command> accepted
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)
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:
*
*
*\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:
* 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);
/*%<
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,
isc_refcount_t refcount;
dst_key_t * key;
isc_boolean_t managed;
+ isc_boolean_t initial;
struct dns_keynode * next;
};
*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);
+ }
- knode->managed = managed;
+ /*
+ * If a key was supplied, transfer its ownership to the keytable.
+ */
+ if (keyp) {
+ knode->key = *keyp;
+ *keyp = NULL;
+ }
- RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
+ knode->managed = managed;
+ knode->initial = initial;
+ knode->next = node->data;
+ node->data = knode;
- node = NULL;
- result = dns_rbt_addnode(keytable->table, keyname, &node);
+ return (ISC_R_SUCCESS);
+}
- 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;
- }
+/*%
+ * 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;
- if (k == NULL)
- result = ISC_R_SUCCESS;
- else if (*keyp != NULL)
- dst_key_free(keyp);
- }
+ REQUIRE(VALID_KEYTABLE(keytable));
- if (result == ISC_R_SUCCESS) {
- knode->key = *keyp;
- knode->next = node->data;
- *keyp = NULL;
- }
- }
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
+ result = dns_rbt_addnode(keytable->table, keyname, &node);
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
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;
dst_key_t *
dns_keynode_key(dns_keynode_t *keynode) {
-
- /*
- * Get the DST key associated with keynode.
- */
-
REQUIRE(VALID_KEYNODE(keynode));
return (keynode->key);
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;
knode->magic = KEYNODE_MAGIC;
knode->managed = ISC_FALSE;
+ knode->initial = ISC_FALSE;
knode->key = NULL;
knode->next = NULL;
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);
}
}
/* 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 */
* 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);
/* 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);
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);
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
*/
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;
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:
/* 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) {
case dns_zone_key:
result = sync_keyzone(zone, db);
- if (result != ISC_R_SUCCESS)
+ if (result != ISC_R_SUCCESS) {
goto cleanup;
+ }
break;
default:
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));
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);
*/
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);
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,
dns_trust_secure;
kfetch->dnskeysigset.trust =
dns_trust_secure;
+ secure = ISC_TRUE;
+ initial = dns_keynode_initial(keynode);
+ dns_keynode_trust(keynode);
break;
}
}
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;
}
}
* 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 "
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) {
/*
/* 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) {
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,
}
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);
}
/* 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? */