From f381cb86daa34730edee87554f40367e587ab1fa Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 27 May 2015 15:25:45 +1000 Subject: [PATCH] 4127. [protocol] CDS and CDNSKEY need to be signed by the key signing key as per RFC 7344, Section 4.1. [RT #37215] (cherry picked from commit 598b502695802c3d4e23316b85368e54f39f5cab) --- CHANGES | 3 + bin/dnssec/dnssec-dsfromkey.c | 40 +++-- bin/dnssec/dnssec-dsfromkey.docbook | 15 +- bin/dnssec/dnssec-signzone.c | 4 +- bin/named/update.c | 13 ++ bin/tests/system/dnssec/clean.sh | 123 +++++++------ .../dnssec/ns2/cdnskey-auto.secure.db.in | 17 ++ .../dnssec/ns2/cdnskey-update.secure.db.in | 17 ++ .../system/dnssec/ns2/cdnskey.secure.db.in | 17 ++ .../system/dnssec/ns2/cds-auto.secure.db.in | 17 ++ .../system/dnssec/ns2/cds-update.secure.db.in | 17 ++ bin/tests/system/dnssec/ns2/cds.secure.db.in | 17 ++ bin/tests/system/dnssec/ns2/named.conf | 36 ++++ bin/tests/system/dnssec/ns2/sign.sh | 50 ++++++ bin/tests/system/dnssec/tests.sh | 163 ++++++++++++++++++ doc/design/cds-child | 51 ++++++ lib/dns/include/dns/result.h | 5 +- lib/dns/include/dns/zone.h | 17 ++ lib/dns/result.c | 6 +- lib/dns/update.c | 11 +- lib/dns/zone.c | 153 +++++++++++++++- 21 files changed, 717 insertions(+), 75 deletions(-) create mode 100644 bin/tests/system/dnssec/ns2/cdnskey-auto.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cdnskey-update.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cdnskey.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cds-auto.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cds-update.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cds.secure.db.in create mode 100644 doc/design/cds-child diff --git a/CHANGES b/CHANGES index 90ec9de159..07f0f5da78 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +4127. [protocol] CDS and CDNSKEY need to be signed by the key signing + key as per RFC 7344, Section 4.1. [RT #37215] + 4123. [port] Added %z (size_t) format options to the portable internal printf/sprintf implementation. [RT #39586] diff --git a/bin/dnssec/dnssec-dsfromkey.c b/bin/dnssec/dnssec-dsfromkey.c index 8c1bd86f16..72310f84c5 100644 --- a/bin/dnssec/dnssec-dsfromkey.c +++ b/bin/dnssec/dnssec-dsfromkey.c @@ -238,7 +238,7 @@ logkey(dns_rdata_t *rdata) static void emit(unsigned int dtype, isc_boolean_t showall, char *lookaside, - dns_rdata_t *rdata) + isc_boolean_t cds, dns_rdata_t *rdata) { isc_result_t result; unsigned char buf[DNS_DS_BUFFERSIZE]; @@ -302,9 +302,12 @@ emit(unsigned int dtype, isc_boolean_t showall, char *lookaside, isc_buffer_usedregion(&classb, &r); printf("%.*s", (int)r.length, r.base); - if (lookaside == NULL) - printf(" DS "); - else + if (lookaside == NULL) { + if (cds) + printf(" CDS "); + else + printf(" DS "); + } else printf(" DLV "); isc_buffer_usedregion(&textb, &r); @@ -332,6 +335,7 @@ usage(void) { "(SHA-1, SHA-256, GOST or SHA-384)\n"); fprintf(stderr, " -1: use SHA-1\n"); fprintf(stderr, " -2: use SHA-256\n"); + fprintf(stderr, " -C: print CDS record\n"); fprintf(stderr, " -l: add lookaside zone and print DLV records\n"); fprintf(stderr, " -s: read keyset from keyset- file\n"); fprintf(stderr, " -c class: rdata class for DS set (default: IN)\n"); @@ -352,6 +356,7 @@ main(int argc, char **argv) { char *endp; int ch; unsigned int dtype = DNS_DSDIGEST_SHA1; + isc_boolean_t cds = ISC_FALSE; isc_boolean_t both = ISC_TRUE; isc_boolean_t usekeyset = ISC_FALSE; isc_boolean_t showall = ISC_FALSE; @@ -374,8 +379,8 @@ main(int argc, char **argv) { isc_commandline_errprint = ISC_FALSE; - while ((ch = isc_commandline_parse(argc, argv, - "12Aa:c:d:Ff:K:l:sT:v:hV")) != -1) { +#define OPTIONS "12Aa:Cc:d:Ff:K:l:sT:v:hV" + while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) { switch (ch) { case '1': dtype = DNS_DSDIGEST_SHA1; @@ -392,6 +397,12 @@ main(int argc, char **argv) { algname = isc_commandline_argument; both = ISC_FALSE; break; + case 'C': + if (lookaside != NULL) + fatal("lookaside and CDS are mutually" + " exclusive"); + cds = ISC_TRUE; + break; case 'c': classname = isc_commandline_argument; break; @@ -408,6 +419,9 @@ main(int argc, char **argv) { filename = isc_commandline_argument; break; case 'l': + if (cds) + fatal("lookaside and CDS are mutually" + " exclusive"); lookaside = isc_commandline_argument; if (strlen(lookaside) == 0U) fatal("lookaside must be a non-empty string"); @@ -526,11 +540,11 @@ main(int argc, char **argv) { if (both) { emit(DNS_DSDIGEST_SHA1, showall, lookaside, - &rdata); + cds, &rdata); emit(DNS_DSDIGEST_SHA256, showall, lookaside, - &rdata); + cds, &rdata); } else - emit(dtype, showall, lookaside, &rdata); + emit(dtype, showall, lookaside, cds, &rdata); } } else { unsigned char key_buf[DST_KEY_MAXSIZE]; @@ -539,10 +553,12 @@ main(int argc, char **argv) { DST_KEY_MAXSIZE, &rdata); if (both) { - emit(DNS_DSDIGEST_SHA1, showall, lookaside, &rdata); - emit(DNS_DSDIGEST_SHA256, showall, lookaside, &rdata); + emit(DNS_DSDIGEST_SHA1, showall, lookaside, cds, + &rdata); + emit(DNS_DSDIGEST_SHA256, showall, lookaside, cds, + &rdata); } else - emit(dtype, showall, lookaside, &rdata); + emit(dtype, showall, lookaside, cds, &rdata); } if (dns_rdataset_isassociated(&rdataset)) diff --git a/bin/dnssec/dnssec-dsfromkey.docbook b/bin/dnssec/dnssec-dsfromkey.docbook index 7245a83f4d..2711a782de 100644 --- a/bin/dnssec/dnssec-dsfromkey.docbook +++ b/bin/dnssec/dnssec-dsfromkey.docbook @@ -41,6 +41,7 @@ 2011 2012 2014 + 2015 Internet Systems Consortium, Inc. ("ISC") @@ -52,6 +53,7 @@ + keyfile @@ -122,6 +124,16 @@ + + -C + + + Generate CDS records rather than DS records. This is mutually + exclusive with generating lookaside records. + + + + -T TTL @@ -182,7 +194,8 @@ is appended to the name for each record in the set. The DNSSEC Lookaside Validation (DLV) RR is described - in RFC 4431. + in RFC 4431. This is mutually exclusive with generating + CDS records. diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index fc00b73ecd..694b7c457d 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -681,7 +681,9 @@ signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name, (iszsk(key) && !keyset_kskonly)) signwithkey(name, set, key->key, ttl, add, "signing with dnskey"); - } else if (iszsk(key)) { + } else if (set->type == dns_rdatatype_cds || + set->type == dns_rdatatype_cdnskey || + iszsk(key)) { signwithkey(name, set, key->key, ttl, add, "signing with dnskey"); } diff --git a/bin/named/update.c b/bin/named/update.c index a526b02a10..c220a6cd83 100644 --- a/bin/named/update.c +++ b/bin/named/update.c @@ -3017,6 +3017,19 @@ update_action(isc_task_t *task, isc_event_t *event) { goto failure; } } + if (! ISC_LIST_EMPTY(diff.tuples)) { + result = dns_zone_cdscheck(zone, db, ver); + if (result == DNS_R_BADCDS || result == DNS_R_BADCDNSKEY) { + update_log(client, zone, LOGLEVEL_PROTOCOL, + "update rejected: bad %s RRset", + result == DNS_R_BADCDS ? "CDS" : "CDNSKEY"); + result = DNS_R_REFUSED; + goto failure; + } + if (result != ISC_R_SUCCESS) + goto failure; + + } /* * If any changes were made, increment the SOA serial number, diff --git a/bin/tests/system/dnssec/clean.sh b/bin/tests/system/dnssec/clean.sh index 0a8ce1de70..b97ed41535 100644 --- a/bin/tests/system/dnssec/clean.sh +++ b/bin/tests/system/dnssec/clean.sh @@ -16,68 +16,81 @@ # PERFORMANCE OF THIS SOFTWARE. rm -f */K* */keyset-* */dsset-* */dlvset-* */signedkey-* */*.signed -rm -f */trusted.conf */managed.conf */revoked.conf -rm -f */tmp* */*.jnl */*.bk */*.jbk -rm -f ns1/root.db ns2/example.db ns3/secure.example.db -rm -f ns3/unsecure.example.db ns3/bogus.example.db ns3/keyless.example.db -rm -f ns3/dynamic.example.db ns3/dynamic.example.db.signed.jnl -rm -f ns3/rsasha256.example.db ns3/rsasha512.example.db -rm -f ns3/split-dnssec.example.db -rm -f ns3/expiring.example.db ns3/nosign.example.db -rm -f ns2/private.secure.example.db -rm -f ns2/badparam.db ns2/badparam.db.bad -rm -f ns2/single-nsec3.db -rm -f ns2/nsec3chain-test.db -rm -f ns2/in-addr.arpa.db rm -f */example.bk -rm -f dig.out.* -rm -f sample.out* -rm -f ns2/dlv.db -rm -f ns3/multiple.example.db ns3/nsec3-unknown.example.db ns3/nsec3.example.db -rm -f ns3/optout-unknown.example.db ns3/optout.example.db -rm -f ns3/expired.example.db ns3/update-nsec3.example.db -rm -f ns7/multiple.example.bk ns7/nsec3.example.bk ns7/optout.example.bk rm -f */named.memstats rm -f */named.run -rm -f ns3/nsec3.nsec3.example.db -rm -f ns3/nsec3.optout.example.db -rm -f ns3/optout.nsec3.example.db -rm -f ns3/optout.optout.example.db -rm -f ns3/secure.nsec3.example.db -rm -f ns3/secure.optout.example.db rm -f */named.secroots -rm -f ns1/managed.key.id -rm -f signer/*.db -rm -f signer/signer.out.* -rm -f ns2/algroll.db -rm -f ns3/kskonly.example.db -rm -f ns4/named.conf ns5/named.conf -rm -f ns4/managed-keys.bind* -rm -f ns3/auto-nsec.example.db ns3/auto-nsec3.example.db -rm -f ns3/secure.below-cname.example.db -rm -f ns3/publish-inactive.example.db -rm -f signer/example.db.after signer/example.db.before -rm -f signer/example.db.changed -rm -f signer/nsec3param.out -rm -f ns3/ttlpatch.example.db ns3/ttlpatch.example.db.signed -rm -f ns3/ttlpatch.example.db.patched -rm -f ns3/split-smart.example.db -rm -f ns3/siginterval.example.db -rm -f ns3/inline.example.db.signed -rm -f ns3/lower.example.db ns3/upper.example.db ns3/upper.example.db.lower -rm -f ns6/optout-tld.db -rm -f nosign.before -rm -f signing.out* -rm -f canonical?.* -rm -f ns1/resolve.key -rm -f ns3/siginterval.conf -rm -f ns4/named_dump.db -rm -f ns7/split-rrsig.db ns7/split-rrsig.db.unsplit +rm -f */tmp* */*.jnl */*.bk */*.jbk +rm -f */trusted.conf */managed.conf */revoked.conf rm -f Kexample.* +rm -f canonical?.* +rm -f delv.out* +rm -f delve.out* +rm -f dig.out.* rm -f keygen.err -rm -f ns3/future.example.db ns3/trusted-future.key +rm -f named.secroots.test* +rm -f nosign.before +rm -f ns*/*.nta +rm -f ns*/named.lock +rm -f ns1/managed.key.id +rm -f ns1/resolve.key +rm -f ns1/root.db ns2/example.db ns3/secure.example.db +rm -f ns2/algroll.db +rm -f ns2/badparam.db ns2/badparam.db.bad +rm -f ns2/cdnskey-update.secure.db +rm -f ns2/cdnskey.secure.db +rm -f ns2/cds-auto.secure.db ns2/cds-auto.secure.db.jnl +rm -f ns2/cds-update.secure.db ns2/cds-update.secure.db.jnl +rm -f ns2/cds.secure.db +rm -f ns2/dlv.db +rm -f ns2/in-addr.arpa.db +rm -f ns2/nsec3chain-test.db +rm -f ns2/private.secure.example.db +rm -f ns2/single-nsec3.db +rm -f ns3/auto-nsec.example.db ns3/auto-nsec3.example.db +rm -f ns3/badds.example.db rm -f ns3/dnskey-nsec3-unknown.example.db rm -f ns3/dnskey-nsec3-unknown.example.db.tmp rm -f ns3/dnskey-unknown.example.db rm -f ns3/dnskey-unknown.example.db.tmp -rm -f named.secroots.test* +rm -f ns3/dynamic.example.db ns3/dynamic.example.db.signed.jnl +rm -f ns3/expired.example.db ns3/update-nsec3.example.db +rm -f ns3/expiring.example.db ns3/nosign.example.db +rm -f ns3/future.example.db ns3/trusted-future.key +rm -f ns3/inline.example.db.signed +rm -f ns3/kskonly.example.db +rm -f ns3/lower.example.db ns3/upper.example.db ns3/upper.example.db.lower +rm -f ns3/multiple.example.db ns3/nsec3-unknown.example.db ns3/nsec3.example.db +rm -f ns3/nsec3.nsec3.example.db +rm -f ns3/nsec3.optout.example.db +rm -f ns3/optout-unknown.example.db ns3/optout.example.db +rm -f ns3/optout.nsec3.example.db +rm -f ns3/optout.optout.example.db +rm -f ns3/publish-inactive.example.db +rm -f ns3/rsasha256.example.db ns3/rsasha512.example.db +rm -f ns3/secure.below-cname.example.db +rm -f ns3/secure.nsec3.example.db +rm -f ns3/secure.optout.example.db +rm -f ns3/siginterval.conf +rm -f ns3/siginterval.example.db +rm -f ns3/split-dnssec.example.db +rm -f ns3/split-smart.example.db +rm -f ns3/ttlpatch.example.db ns3/ttlpatch.example.db.signed +rm -f ns3/ttlpatch.example.db.patched +rm -f ns3/unsecure.example.db ns3/bogus.example.db ns3/keyless.example.db +rm -f ns4/managed-keys.bind* +rm -f ns4/named.conf +rm -f ns4/named.conf ns5/named.conf +rm -f ns4/named_dump.db +rm -f ns6/optout-tld.db +rm -f ns7/multiple.example.bk ns7/nsec3.example.bk ns7/optout.example.bk +rm -f ns7/split-rrsig.db ns7/split-rrsig.db.unsplit +rm -f nsupdate.out* +rm -f rndc.out.* +rm -f sample.out* +rm -f signer/*.db +rm -f signer/example.db.after signer/example.db.before +rm -f signer/example.db.changed +rm -f signer/nsec3param.out +rm -f signer/signer.out.* +rm -f signing.out* diff --git a/bin/tests/system/dnssec/ns2/cdnskey-auto.secure.db.in b/bin/tests/system/dnssec/ns2/cdnskey-auto.secure.db.in new file mode 100644 index 0000000000..69dd9de02b --- /dev/null +++ b/bin/tests/system/dnssec/ns2/cdnskey-auto.secure.db.in @@ -0,0 +1,17 @@ +; Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$TTL 3600 +@ SOA ns2.example. . 1 3600 1200 86400 1200 +@ NS ns2.example. diff --git a/bin/tests/system/dnssec/ns2/cdnskey-update.secure.db.in b/bin/tests/system/dnssec/ns2/cdnskey-update.secure.db.in new file mode 100644 index 0000000000..69dd9de02b --- /dev/null +++ b/bin/tests/system/dnssec/ns2/cdnskey-update.secure.db.in @@ -0,0 +1,17 @@ +; Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$TTL 3600 +@ SOA ns2.example. . 1 3600 1200 86400 1200 +@ NS ns2.example. diff --git a/bin/tests/system/dnssec/ns2/cdnskey.secure.db.in b/bin/tests/system/dnssec/ns2/cdnskey.secure.db.in new file mode 100644 index 0000000000..69dd9de02b --- /dev/null +++ b/bin/tests/system/dnssec/ns2/cdnskey.secure.db.in @@ -0,0 +1,17 @@ +; Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$TTL 3600 +@ SOA ns2.example. . 1 3600 1200 86400 1200 +@ NS ns2.example. diff --git a/bin/tests/system/dnssec/ns2/cds-auto.secure.db.in b/bin/tests/system/dnssec/ns2/cds-auto.secure.db.in new file mode 100644 index 0000000000..69dd9de02b --- /dev/null +++ b/bin/tests/system/dnssec/ns2/cds-auto.secure.db.in @@ -0,0 +1,17 @@ +; Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$TTL 3600 +@ SOA ns2.example. . 1 3600 1200 86400 1200 +@ NS ns2.example. diff --git a/bin/tests/system/dnssec/ns2/cds-update.secure.db.in b/bin/tests/system/dnssec/ns2/cds-update.secure.db.in new file mode 100644 index 0000000000..69dd9de02b --- /dev/null +++ b/bin/tests/system/dnssec/ns2/cds-update.secure.db.in @@ -0,0 +1,17 @@ +; Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$TTL 3600 +@ SOA ns2.example. . 1 3600 1200 86400 1200 +@ NS ns2.example. diff --git a/bin/tests/system/dnssec/ns2/cds.secure.db.in b/bin/tests/system/dnssec/ns2/cds.secure.db.in new file mode 100644 index 0000000000..69dd9de02b --- /dev/null +++ b/bin/tests/system/dnssec/ns2/cds.secure.db.in @@ -0,0 +1,17 @@ +; Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$TTL 3600 +@ SOA ns2.example. . 1 3600 1200 86400 1200 +@ NS ns2.example. diff --git a/bin/tests/system/dnssec/ns2/named.conf b/bin/tests/system/dnssec/ns2/named.conf index baeba5997d..40fe25984a 100644 --- a/bin/tests/system/dnssec/ns2/named.conf +++ b/bin/tests/system/dnssec/ns2/named.conf @@ -107,4 +107,40 @@ zone "in-addr.arpa" { file "in-addr.arpa.db.signed"; }; +zone "cds.secure" { + type master; + file "cds.secure.db.signed"; +}; + +zone "cds-update.secure" { + type master; + file "cds-update.secure.db.signed"; + allow-update { any; }; +}; + +zone "cds-auto.secure" { + type master; + file "cds-auto.secure.db.signed"; + auto-dnssec maintain; + allow-update { any; }; +}; + +zone "cdnskey.secure" { + type master; + file "cdnskey.secure.db.signed"; +}; + +zone "cdnskey-update.secure" { + type master; + file "cdnskey-update.secure.db.signed"; + allow-update { any; }; +}; + +zone "cdnskey-auto.secure" { + type master; + file "cdnskey-auto.secure.db.signed"; + auto-dnssec maintain; + allow-update { any; }; +}; + include "trusted.conf"; diff --git a/bin/tests/system/dnssec/ns2/sign.sh b/bin/tests/system/dnssec/ns2/sign.sh index fb056f6785..d37d3805fe 100644 --- a/bin/tests/system/dnssec/ns2/sign.sh +++ b/bin/tests/system/dnssec/ns2/sign.sh @@ -193,3 +193,53 @@ key1=`$KEYGEN -q -r $RANDFILE -a RSASHA256 -b 1024 -n zone -fk $zone` key2=`$KEYGEN -q -r $RANDFILE -a RSASHA256 -b 1024 -n zone $zone` cat $key1.key $key2.key >> $zonefile $SIGNER -P -3 - -A -H 1 -g -r $RANDFILE -o $zone -k $key1 $zonefile $key2 > /dev/null + +zone=cds.secure +infile=cds.secure.db.in +zonefile=cds.secure.db +key1=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -fk $zone` +key2=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone` +$DSFROMKEY -C $key1.key > $key1.cds +cat $infile $key1.key $key2.key $key1.cds >$zonefile +$SIGNER -P -g -r $RANDFILE -o $zone $zonefile > /dev/null + +zone=cds-update.secure +infile=cds-update.secure.db.in +zonefile=cds-update.secure.db +key1=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -fk $zone` +key2=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone` +cat $infile $key1.key $key2.key > $zonefile +$SIGNER -P -g -r $RANDFILE -o $zone $zonefile > /dev/null + +zone=cds-auto.secure +infile=cds-auto.secure.db.in +zonefile=cds-auto.secure.db +key1=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -fk $zone` +key2=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone` +$DSFROMKEY -C $key1.key > $key1.cds +cat $infile $key1.cds > $zonefile.signed + +zone=cdnskey.secure +infile=cdnskey.secure.db.in +zonefile=cdnskey.secure.db +key1=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -fk $zone` +key2=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone` +sed 's/DNSKEY/CDNSKEY/' $key1.key > $key1.cds +cat $infile $key1.key $key2.key $key1.cds >$zonefile +$SIGNER -P -g -r $RANDFILE -o $zone $zonefile > /dev/null + +zone=cdnskey-update.secure +infile=cdnskey-update.secure.db.in +zonefile=cdnskey-update.secure.db +key1=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -fk $zone` +key2=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone` +cat $infile $key1.key $key2.key > $zonefile +$SIGNER -P -g -r $RANDFILE -o $zone $zonefile > /dev/null + +zone=cdnskey-auto.secure +infile=cdnskey-auto.secure.db.in +zonefile=cdnskey-auto.secure.db +key1=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -fk $zone` +key2=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone` +sed 's/DNSKEY/CDNSKEY/' $key1.key > $key1.cds +cat $infile $key1.cds > $zonefile.signed diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 0187bc6718..769a23dc8b 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -2415,6 +2415,15 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check that CDS records are signed using KSK by dnssec-signzone ($n)" +ret=0 +$DIG $DIGOPTS +noall +answer @10.53.0.2 cds cds.secure > dig.out.test$n +lines=`awk '$4 == "RRSIG" && $5 == "CDS" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 2 || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + # # Test for +sigchase with a null set of trusted keys. # @@ -2450,6 +2459,57 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check that CDS records are signed using KSK by with dnssec-auto ($n)" +ret=0 +$DIG $DIGOPTS +noall +answer @10.53.0.2 cds cds-auto.secure > dig.out.test$n +lines=`awk '$4 == "RRSIG" && $5 == "CDS" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 2 || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:check that a lone non matching CDS record is rejected ($n)" +ret=0 +( +echo zone cds-update.secure +echo server 10.53.0.2 5300 +echo update delete cds-update.secure CDS +$DIG $DIGOPTS +noall +answer @10.53.0.2 dnskey cds-update.secure | +grep "DNSKEY.257" | sed 's/DNSKEY.257/DNSKEY 258/' | +$DSFROMKEY -C -A -f - -T 1 cds-update.secure | +sed "s/^/update add /" +echo send +) | $NSUPDATE > nsupdate.out.test$n 2>&1 +grep "update failed: REFUSED" nsupdate.out.test$n > /dev/null || ret=1 +$DIG $DIGOPTS +noall +answer @10.53.0.2 cds cds-update.secure > dig.out.test$n +lines=`awk '$4 == "CDS" {print}' dig.out.test$n | wc -l` +test ${lines:-10} -eq 0 || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:check that CDS records are signed using KSK when added by nsupdate ($n)" +ret=0 +( +echo zone cds-update.secure +echo server 10.53.0.2 5300 +echo update delete cds-update.secure CDS +echo send +$DIG $DIGOPTS +noall +answer @10.53.0.2 dnskey cds-update.secure | +grep "DNSKEY.257" | +$DSFROMKEY -C -f - -T 1 cds-update.secure | +sed "s/^/update add /" +echo send +) | $NSUPDATE +$DIG $DIGOPTS +noall +answer @10.53.0.2 cds cds-update.secure > dig.out.test$n +lines=`awk '$4 == "RRSIG" && $5 == "CDS" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 2 || ret=1 +lines=`awk '$4 == "CDS" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 2 || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:checking that positive unknown NSEC3 hash algorithm with OPTOUT does validate ($n)" ret=0 $DIG $DIGOPTS +noauth +noadd +nodnssec +adflag -p 5300 @10.53.0.3 optout-unknown.example SOA > dig.out.ns3.test$n @@ -2462,6 +2522,32 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check that a non matching CDS record is accepted with a matching CDS record ($n)" +ret=0 +( +echo zone cds-update.secure +echo server 10.53.0.2 5300 +echo update delete cds-update.secure CDS +echo send +$DIG $DIGOPTS +noall +answer @10.53.0.2 dnskey cds-update.secure | +grep "DNSKEY.257" | +$DSFROMKEY -C -f - -T 1 cds-update.secure | +sed "s/^/update add /" +$DIG $DIGOPTS +noall +answer @10.53.0.2 dnskey cds-update.secure | +grep "DNSKEY.257" | sed 's/DNSKEY.257/DNSKEY 258/' | +$DSFROMKEY -C -A -f - -T 1 cds-update.secure | +sed "s/^/update add /" +echo send +) | $NSUPDATE +$DIG $DIGOPTS +noall +answer @10.53.0.2 cds cds-update.secure > dig.out.test$n +lines=`awk '$4 == "RRSIG" && $5 == "CDS" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 2 || ret=1 +lines=`awk '$4 == "CDS" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 4 || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:checking that negative unknown NSEC3 hash algorithm does not validate ($n)" ret=0 $DIG $DIGOPTS +noauth +noadd +nodnssec +adflag -p 5300 @10.53.0.3 nsec3-unknown.example A > dig.out.ns3.test$n @@ -2472,6 +2558,15 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check that CDNSKEY records are signed using KSK by dnssec-signzone ($n)" +ret=0 +$DIG $DIGOPTS +noall +answer @10.53.0.2 cdnskey cdnskey.secure > dig.out.test$n +lines=`awk '$4 == "RRSIG" && $5 == "CDNSKEY" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 2 || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:checking that negative unknown NSEC3 hash algorithm with OPTOUT does not validate ($n)" ret=0 $DIG $DIGOPTS +noauth +noadd +nodnssec +adflag -p 5300 @10.53.0.3 optout-unknown.example A > dig.out.ns3.test$n @@ -2482,6 +2577,15 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check that CDNSKEY records are signed using KSK by with dnssec-auto ($n)" +ret=0 +$DIG $DIGOPTS +noall +answer @10.53.0.2 cdnskey cdnskey-auto.secure > dig.out.test$n +lines=`awk '$4 == "RRSIG" && $5 == "CDNSKEY" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 2 || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:checking that unknown DNSKEY algorithm validates as insecure ($n)" ret=0 $DIG $DIGOPTS +noauth +noadd +nodnssec +adflag -p 5300 @10.53.0.3 dnskey-unknown.example A > dig.out.ns3.test$n @@ -2493,6 +2597,25 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check that a lone non matching CDNSKEY record is rejected ($n)" +ret=0 +( +echo zone cdnskey-update.secure +echo server 10.53.0.2 5300 +echo update delete cdnskey-update.secure CDNSKEY +echo send +$DIG $DIGOPTS +noall +answer @10.53.0.2 dnskey cdnskey-update.secure | +sed -n -e "s/^/update add /" -e 's/DNSKEY.257/CDNSKEY 258/p' +echo send +) | $NSUPDATE > nsupdate.out.test$n 2>&1 +grep "update failed: REFUSED" nsupdate.out.test$n > /dev/null || ret=1 +$DIG $DIGOPTS +noall +answer @10.53.0.2 cdnskey cdnskey-update.secure > dig.out.test$n +lines=`awk '$4 == "CDNSKEY" {print}' dig.out.test$n | wc -l` +test ${lines:-10} -eq 0 || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:checking that unknown DNSKEY algorithm + unknown NSEC3 has algorithm validates as insecure ($n)" ret=0 $DIG $DIGOPTS +noauth +noadd +nodnssec +adflag -p 5300 @10.53.0.3 dnskey-nsec3-unknown.example A > dig.out.ns3.test$n @@ -2504,6 +2627,25 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check that CDNSKEY records are signed using KSK when added by nsupdate ($n)" +ret=0 +( +echo zone cdnskey-update.secure +echo server 10.53.0.2 5300 +echo update delete cdnskey-update.secure CDNSKEY +$DIG $DIGOPTS +noall +answer @10.53.0.2 dnskey cdnskey-update.secure | +sed -n -e "s/^/update add /" -e 's/DNSKEY.257/CDNSKEY 257/p' +echo send +) | $NSUPDATE +$DIG $DIGOPTS +noall +answer @10.53.0.2 cdnskey cdnskey-update.secure > dig.out.test$n +lines=`awk '$4 == "RRSIG" && $5 == "CDNSKEY" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 2 || ret=1 +lines=`awk '$4 == "CDNSKEY" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 1 || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:checking initialization with a revoked managed key ($n)" ret=0 cp ns5/named2.conf ns5/named.conf @@ -2515,5 +2657,26 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check that a non matching CDNSKEY record is accepted with a matching CDNSKEY record ($n)" +ret=0 +( +echo zone cdnskey-update.secure +echo server 10.53.0.2 5300 +echo update delete cdnskey-update.secure CDNSKEY +$DIG $DIGOPTS +noall +answer @10.53.0.2 dnskey cdnskey-update.secure | +sed -n -e "s/^/update add /" -e 's/DNSKEY.257/CDNSKEY 257/p' +$DIG $DIGOPTS +noall +answer @10.53.0.2 dnskey cdnskey-update.secure | +sed -n -e "s/^/update add /" -e 's/DNSKEY.257/CDNSKEY 258/p' +echo send +) | $NSUPDATE +$DIG $DIGOPTS +noall +answer @10.53.0.2 cdnskey cdnskey-update.secure > dig.out.test$n +lines=`awk '$4 == "RRSIG" && $5 == "CDNSKEY" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 2 || ret=1 +lines=`awk '$4 == "CDNSKEY" {print}' dig.out.test$n | wc -l` +test ${lines:-0} -eq 2 || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:exit status: $status" exit $status diff --git a/doc/design/cds-child b/doc/design/cds-child new file mode 100644 index 0000000000..d95afbc3da --- /dev/null +++ b/doc/design/cds-child @@ -0,0 +1,51 @@ + + CDS / CDNSKEY Child side processing. + +* We need a mechanism to say that key should have a cds publish + start/end dates. + +* We need a mechanism to say that key should have a cdnskey publish + start/end dates + + - update dnssec-settime, dnssec-keygen, dnssec-keyfromlabel + - update K* files + +* dnssec-signzone should add cds and/or cdnskey to zone apex iff the + DNSKEY is published and is signing the DNSKEY RRset. CDS and CDNSKEY + records are only removed if there is a deletion date set (implicit on + matching DNSKEY going inactive / unpublished or explict). + + Non-matching CDS and CDNSKEY are removed. + +* auto-dnssec maintain should cds and/or cdnskey to zone apex iff the + DNSKEY is published and is signing the DNSKEY RRset. CDS and CDNSKEY + records are only removed if there is a deletion date set (implicit on + matching DNSKEY going inactive / unpublished or explict). + +* UPDATE should check that CDS and CDNSKEY match a active DNSKEY that + is signing the DNSKEY RRset and ignore otherwise. This should be + done after all the update section records have been processed. + + ? how will this tie in with CDS/CDNSKEY sanity checks? Only on fail? + +* UPDATE should remove CDS and CDNSKEY records that match a DNSKEY + that is being removed. This should be done after all the update + section records have been processed. + + ? how will this tie in with CDS/CDNSKEY sanity checks? Only on fail? + +* Zone loading should perform sanity checks on CDS and CDNSKEY + records against the DNSKEY records. This will flow through into + dnssec-checkzone and "dnssec-checkconf -z". ignore/warn/fail + +* rndc add the ability to say generate CDS / CDNSKEY along with a key list / + all / all SEP + +* rndc add the ability to say remove CDS / CDNSKEY. + +* inline zones need to check CDS and CDNSKEY records in the raw zone and + filter non matching. + +* CDS and CDNSKEY must be signed by a DNSKEY which matches parent DS record. + This is is different to how non DNSKEY RRsets are usually signed + RFC 7344, 4.1. diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h index 12aacf9ba7..a1f0c7c636 100644 --- a/lib/dns/include/dns/result.h +++ b/lib/dns/include/dns/result.h @@ -153,8 +153,11 @@ #define DNS_R_EXPIRED (ISC_RESULTCLASS_DNS + 107) #define DNS_R_NOTDYNAMIC (ISC_RESULTCLASS_DNS + 108) #define DNS_R_BADEUI (ISC_RESULTCLASS_DNS + 109) +#define DNS_R_NTACOVERED (ISC_RESULTCLASS_DNS + 110) +#define DNS_R_BADCDS (ISC_RESULTCLASS_DNS + 111) +#define DNS_R_BADCDNSKEY (ISC_RESULTCLASS_DNS + 112) -#define DNS_R_NRESULTS 110 /*%< Number of results */ +#define DNS_R_NRESULTS 113 /*%< Number of results */ /* * DNS wire format rcodes. diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 4c0420d015..9ecabdd47e 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -1961,6 +1961,23 @@ dns_zone_nscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, * ISC_R_SUCCESS if there were no errors examining the zone contents. */ +isc_result_t +dns_zone_cdscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version); +/*% + * Check if CSD, CDNSKEY and DNSKEY are consistent. + * + * Requires: + * \li 'zone' to be valid. + * \li 'db' to be valid. + * \li 'version' to be valid or NULL. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #DNS_R_BADCDS + *\li #DNS_R_BADCDNSKEY + * Others + */ + void dns_zone_setadded(dns_zone_t *zone, isc_boolean_t added); /*% diff --git a/lib/dns/result.c b/lib/dns/result.c index 39879532d4..e4c600669d 100644 --- a/lib/dns/result.c +++ b/lib/dns/result.c @@ -162,7 +162,11 @@ static const char *text[DNS_R_NRESULTS] = { "broken trust chain", /*%< 106 DNS_R_BROKENCHAIN */ "expired", /*%< 107 DNS_R_EXPIRED */ "not dynamic", /*%< 108 DNS_R_NOTDYNAMIC */ - "bad EUI" /*%< 109 DNS_R_BADEUI */ + "bad EUI", /*%< 109 DNS_R_BADEUI */ + + "covered by negative trust anchor", /*%< 110 DNS_R_NTACOVERED */ + "bad CDS", /*%< 111 DNS_R_BADCSD */ + "bad CDNSKEY" /*%< 112 DNS_R_BADCDNSKEY */ }; static const char *rcode_text[DNS_R_NRCODERESULTS] = { diff --git a/lib/dns/update.c b/lib/dns/update.c index 881d12e09a..30640b79cd 100644 --- a/lib/dns/update.c +++ b/lib/dns/update.c @@ -1139,8 +1139,15 @@ add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, if (type == dns_rdatatype_dnskey) { if (!KSK(keys[i]) && keyset_kskonly) continue; - } else if (KSK(keys[i])) - continue; + } else if (KSK(keys[i])) { + /* + * CDS and CDNSKEY are signed with KSK + * (RFC 7344, 4.1). + */ + if (type != dns_rdatatype_cds && + type != dns_rdatatype_cdnskey) + continue; + } } else if (REVOKE(keys[i]) && type != dns_rdatatype_dnskey) continue; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index f4aadf54f2..5f6ebf7344 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -6114,8 +6115,14 @@ sign_a_node(dns_db_t *db, dns_name_t *name, dns_dbnode_t *node, if (rdataset.type == dns_rdatatype_dnskey) { if (!is_ksk && keyset_kskonly) goto next_rdataset; - } else if (is_ksk) - goto next_rdataset; + } else if (is_ksk) { + /* + * CDS and CDNSKEY are signed with KSK (RFC 7344, 4.1). + */ + if (rdataset.type != dns_rdatatype_cds && + rdataset.type != dns_rdatatype_cdnskey) + goto next_rdataset; + } if (*delegation && rdataset.type != dns_rdatatype_ds && rdataset.type != dns_rdatatype_nsec) @@ -16950,6 +16957,148 @@ dns_zone_nscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, return (result); } +isc_result_t +dns_zone_cdscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version) { + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t dnskey, cds, cdnskey; + unsigned char buffer[DNS_DS_BUFFERSIZE]; + unsigned char algorithms[256]; + unsigned int i; + + REQUIRE(DNS_ZONE_VALID(zone)); + + result = dns_db_getoriginnode(db, &node); + if (result != ISC_R_SUCCESS) + return (result); + + dns_rdataset_init(&cds); + dns_rdataset_init(&dnskey); + dns_rdataset_init(&cdnskey); + + result = dns_db_findrdataset(db, node, version, dns_rdatatype_cds, + dns_rdatatype_none, 0, &cds, NULL); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto failure; + + result = dns_db_findrdataset(db, node, version, dns_rdatatype_cdnskey, + dns_rdatatype_none, 0, &cdnskey, NULL); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto failure; + + if (!dns_rdataset_isassociated(&cds) && + !dns_rdataset_isassociated(&cdnskey)) { + result = ISC_R_SUCCESS; + goto failure; + } + + result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey, + dns_rdatatype_none, 0, &dnskey, NULL); + if (result == ISC_R_NOTFOUND) { + if (dns_rdataset_isassociated(&cds)) + result = DNS_R_BADCDS; + else + result = DNS_R_BADCDNSKEY; + goto failure; + } + if (result != ISC_R_SUCCESS) + goto failure; + + /* + * For each DNSSEC algorithm in the CDS RRset there must be + * a matching DNSKEY record. + */ + if (dns_rdataset_isassociated(&cds)) { + memset(algorithms, 0, sizeof(algorithms)); + for (result = dns_rdataset_first(&cds); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&cds)) { + dns_rdata_t crdata = DNS_RDATA_INIT; + dns_rdata_cds_t structcds; + + dns_rdataset_current(&cds, &crdata); + CHECK(dns_rdata_tostruct(&crdata, &structcds, NULL)); + if (algorithms[structcds.algorithm] == 0) + algorithms[structcds.algorithm] = 1; + for (result = dns_rdataset_first(&dnskey); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&dnskey)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_t dsrdata = DNS_RDATA_INIT; + + dns_rdataset_current(&dnskey, &rdata); + CHECK(dns_ds_buildrdata(&zone->origin, &rdata, + structcds.digest_type, + buffer, &dsrdata)); + if (crdata.length == dsrdata.length && + memcmp(crdata.data, dsrdata.data, + dsrdata.length) == 0) { + algorithms[structcds.algorithm] = 2; + } + } + if (result != ISC_R_NOMORE) + goto failure; + } + for (i = 0; i < sizeof(algorithms); i++) { + if (algorithms[i] == 1) { + result = DNS_R_BADCDNSKEY; + goto failure; + } + } + } + + /* + * For each DNSSEC algorithm in the CDNSKEY RRset there must be + * a matching DNSKEY record. + */ + if (dns_rdataset_isassociated(&cdnskey)) { + memset(algorithms, 0, sizeof(algorithms)); + for (result = dns_rdataset_first(&cdnskey); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&cdnskey)) { + dns_rdata_t crdata = DNS_RDATA_INIT; + dns_rdata_cdnskey_t structcdnskey; + + dns_rdataset_current(&cdnskey, &crdata); + CHECK(dns_rdata_tostruct(&crdata, &structcdnskey, + NULL)); + if (algorithms[structcdnskey.algorithm] == 0) + algorithms[structcdnskey.algorithm] = 1; + for (result = dns_rdataset_first(&dnskey); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&dnskey)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(&dnskey, &rdata); + if (crdata.length == rdata.length && + memcmp(crdata.data, rdata.data, + rdata.length) == 0) { + algorithms[structcdnskey.algorithm] = 2; + } + } + if (result != ISC_R_NOMORE) + goto failure; + } + for (i = 0; i < sizeof(algorithms); i++) { + if (algorithms[i] == 1) { + result = DNS_R_BADCDS; + goto failure; + } + } + } + result = ISC_R_SUCCESS; + + failure: + if (dns_rdataset_isassociated(&cds)) + dns_rdataset_disassociate(&cds); + if (dns_rdataset_isassociated(&dnskey)) + dns_rdataset_disassociate(&dnskey); + if (dns_rdataset_isassociated(&cdnskey)) + dns_rdataset_disassociate(&cdnskey); + dns_db_detachnode(db, &node); + return (result); +} + void dns_zone_setadded(dns_zone_t *zone, isc_boolean_t added) { REQUIRE(DNS_ZONE_VALID(zone));