From a71b61756650b0e1849d9e074c5f370a3067780b Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Fri, 20 Dec 2024 20:24:05 +1100 Subject: [PATCH 1/4] Add stripped DNSKEY RRSIG senario --- bin/tests/system/dnssec/ns1/root.db.in | 2 ++ bin/tests/system/dnssec/ns1/sign.sh | 1 + .../dnssec/ns2/dnskey-rrsigs-stripped.db.in | 27 +++++++++++++++++++ bin/tests/system/dnssec/ns2/named.conf.in | 5 ++++ bin/tests/system/dnssec/ns2/sign.sh | 17 ++++++++++++ bin/tests/system/dnssec/tests.sh | 23 ++++++++++++++++ bin/tests/system/dnssec/tests_sh_dnssec.py | 3 +++ 7 files changed, 78 insertions(+) create mode 100644 bin/tests/system/dnssec/ns2/dnskey-rrsigs-stripped.db.in diff --git a/bin/tests/system/dnssec/ns1/root.db.in b/bin/tests/system/dnssec/ns1/root.db.in index 4cb21c1826..e0a54b4317 100644 --- a/bin/tests/system/dnssec/ns1/root.db.in +++ b/bin/tests/system/dnssec/ns1/root.db.in @@ -39,3 +39,5 @@ too-many-iterations. NS ns2.too-many-iterations. ns2.too-many-iterations. A 10.53.0.2 peer-ns-spoof NS ns2.peer-ns-spoof. ns2.peer-ns-spoof. A 10.53.0.2 +dnskey-rrsigs-stripped. NS ns2.dnskey-rrsigs-stripped. +ns2.dnskey-rrsigs-stripped. A 10.53.0.2 diff --git a/bin/tests/system/dnssec/ns1/sign.sh b/bin/tests/system/dnssec/ns1/sign.sh index b649cd4a09..ba35576c40 100644 --- a/bin/tests/system/dnssec/ns1/sign.sh +++ b/bin/tests/system/dnssec/ns1/sign.sh @@ -31,6 +31,7 @@ cp "../ns2/dsset-in-addr.arpa." . cp "../ns2/dsset-too-many-iterations." . cp "../ns2/dsset-lazy-ksk." . cp "../ns2/dsset-peer-ns-spoof." . +cp "../ns2/dsset-dnskey-rrsigs-stripped." . grep "$DEFAULT_ALGORITHM_NUMBER [12] " "../ns2/dsset-algroll." >"dsset-algroll." cp "../ns6/dsset-optout-tld." . diff --git a/bin/tests/system/dnssec/ns2/dnskey-rrsigs-stripped.db.in b/bin/tests/system/dnssec/ns2/dnskey-rrsigs-stripped.db.in new file mode 100644 index 0000000000..5fcc74dc94 --- /dev/null +++ b/bin/tests/system/dnssec/ns2/dnskey-rrsigs-stripped.db.in @@ -0,0 +1,27 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 2000042407 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns2 + NS ns3 +ns2 A 10.53.0.2 +ns3 A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 diff --git a/bin/tests/system/dnssec/ns2/named.conf.in b/bin/tests/system/dnssec/ns2/named.conf.in index 310b46b322..001731b012 100644 --- a/bin/tests/system/dnssec/ns2/named.conf.in +++ b/bin/tests/system/dnssec/ns2/named.conf.in @@ -224,4 +224,9 @@ zone "peer.peer-ns-spoof" { file "peer.peer-ns-spoof.db.signed"; }; +zone "dnskey-rrsigs-stripped" { + type primary; + file "dnskey-rrsigs-stripped.db.signed"; +}; + include "trusted.conf"; diff --git a/bin/tests/system/dnssec/ns2/sign.sh b/bin/tests/system/dnssec/ns2/sign.sh index 106f67cbeb..19fbbf9634 100644 --- a/bin/tests/system/dnssec/ns2/sign.sh +++ b/bin/tests/system/dnssec/ns2/sign.sh @@ -387,3 +387,20 @@ ksk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone -f KSK "$z zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone") cat "$infile" "$ksk.key" "$zsk.key" >"$zonefile" "$SIGNER" -g -o "$zone" "$zonefile" >/dev/null 2>&1 + +# +# A zone with the DNSKEY RRSIGS stripped +# +zone=dnskey-rrsigs-stripped +infile=dnskey-rrsigs-stripped.db.in +zonefile=dnskey-rrsigs-stripped.db +ksk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone -f KSK "$zone") +zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone") +cat "$infile" "$ksk.key" "$zsk.key" >"$zonefile" +"$SIGNER" -g -o "$zone" "$zonefile" >/dev/null 2>&1 +"$CHECKZONE" -D -q -i local "$zone" "$zonefile.signed" \ + | awk '$4 == "RRSIG" && $5 == "DNSKEY" { next } { print }' >"$zonefile.stripped" +"$CHECKZONE" -D -q -i local "$zone" "$zonefile.signed" \ + | awk '$4 == "SOA" { $7 = $7 + 1; print; next } { print }' >"$zonefile.next" +"$SIGNER" -g -o "$zone" -f "$zonefile.next" "$zonefile.next" >/dev/null 2>&1 +cp "$zonefile.stripped" "$zonefile.signed" diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 254ba2a750..2518cfda1c 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -204,6 +204,29 @@ n=$((n + 1)) test "$ret" -eq 0 || echo_i "failed" status=$((status + ret)) +echo_i "checking recovery from stripped DNSKEY RRSIG ($n)" +ret=0 +# prime cache with DNSKEY without RRSIGs +dig_with_opts +noauth +cd dnskey-rrsigs-stripped. @10.53.0.4 dnskey >dig.out.prime.ns4.test$n || ret=1 +grep ";; flags: qr rd ra cd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1" dig.out.prime.ns4.test$n >/dev/null || ret=1 +grep "status: NOERROR" dig.out.prime.ns4.test$n >/dev/null || ret=1 +grep "RRSIG.DNSKEY" dig.out.prime.ns4.test$n >/dev/null && ret=1 +# reload server with properly signed zone +cp ns2/dnskey-rrsigs-stripped.db.next ns2/dnskey-rrsigs-stripped.db.signed +nextpart ns2/named.run >/dev/null +rndccmd 10.53.0.2 reload dnskey-rrsigs-stripped | sed 's/^/ns2 /' | cat_i +wait_for_log 5 "zone dnskey-rrsigs-stripped/IN: loaded serial 2000042408" ns2/named.run || ret=1 +# make a query that flushes the unsigned DNSKEY RRset +dig_with_opts +noauth a.dnskey-rrsigs-stripped. @10.53.0.4 a >dig.out.ns4.test$n || ret=1 +# make a second query that should now validate +dig_with_opts +noauth b.dnskey-rrsigs-stripped. @10.53.0.2 a >dig.out.ns2.test$n || ret=1 +dig_with_opts +noauth b.dnskey-rrsigs-stripped. @10.53.0.4 a >dig.out.ns4.test$n || ret=1 +digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1 +grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1 +n=$((n + 1)) +test "$ret" -eq 0 || echo_i "failed" +status=$((status + ret)) + echo_i "checking that 'example/DS' from the referral was used in previous validation ($n)" ret=0 grep "query 'example/DS/IN' approved" ns1/named.run >/dev/null && ret=1 diff --git a/bin/tests/system/dnssec/tests_sh_dnssec.py b/bin/tests/system/dnssec/tests_sh_dnssec.py index e00d160cd7..d5811b1dbe 100644 --- a/bin/tests/system/dnssec/tests_sh_dnssec.py +++ b/bin/tests/system/dnssec/tests_sh_dnssec.py @@ -50,6 +50,9 @@ pytestmark = pytest.mark.extra_artifacts( "ns2/cds-update.secure.id", "ns2/cds-x.secure.db", "ns2/cds.secure.db", + "ns2/dnskey-rrsigs-stripped.db", + "ns2/dnskey-rrsigs-stripped.db.next", + "ns2/dnskey-rrsigs-stripped.db.stripped", "ns2/example.db", "ns2/in-addr.arpa.db", "ns2/lazy-ksk.db", From 66f293a9529dc4b80fd89126e36abd9dccef7649 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Mon, 23 Dec 2024 11:12:56 +1100 Subject: [PATCH 2/4] Add stripped DS RRSIG senario --- bin/tests/system/dnssec/ns1/root.db.in | 2 ++ bin/tests/system/dnssec/ns1/sign.sh | 1 + .../dnssec/ns2/child.ds-rrsigs-stripped.db.in | 27 +++++++++++++++++ .../dnssec/ns2/ds-rrsigs-stripped.db.in | 29 +++++++++++++++++++ bin/tests/system/dnssec/ns2/named.conf.in | 10 +++++++ bin/tests/system/dnssec/ns2/sign.sh | 28 ++++++++++++++++++ bin/tests/system/dnssec/tests.sh | 23 +++++++++++++++ bin/tests/system/dnssec/tests_sh_dnssec.py | 4 +++ 8 files changed, 124 insertions(+) create mode 100644 bin/tests/system/dnssec/ns2/child.ds-rrsigs-stripped.db.in create mode 100644 bin/tests/system/dnssec/ns2/ds-rrsigs-stripped.db.in diff --git a/bin/tests/system/dnssec/ns1/root.db.in b/bin/tests/system/dnssec/ns1/root.db.in index e0a54b4317..419e613c90 100644 --- a/bin/tests/system/dnssec/ns1/root.db.in +++ b/bin/tests/system/dnssec/ns1/root.db.in @@ -41,3 +41,5 @@ peer-ns-spoof NS ns2.peer-ns-spoof. ns2.peer-ns-spoof. A 10.53.0.2 dnskey-rrsigs-stripped. NS ns2.dnskey-rrsigs-stripped. ns2.dnskey-rrsigs-stripped. A 10.53.0.2 +ds-rrsigs-stripped. NS ns2.ds-rrsigs-stripped. +ns2.ds-rrsigs-stripped. A 10.53.0.2 diff --git a/bin/tests/system/dnssec/ns1/sign.sh b/bin/tests/system/dnssec/ns1/sign.sh index ba35576c40..2e9f3c23e4 100644 --- a/bin/tests/system/dnssec/ns1/sign.sh +++ b/bin/tests/system/dnssec/ns1/sign.sh @@ -32,6 +32,7 @@ cp "../ns2/dsset-too-many-iterations." . cp "../ns2/dsset-lazy-ksk." . cp "../ns2/dsset-peer-ns-spoof." . cp "../ns2/dsset-dnskey-rrsigs-stripped." . +cp "../ns2/dsset-ds-rrsigs-stripped." . grep "$DEFAULT_ALGORITHM_NUMBER [12] " "../ns2/dsset-algroll." >"dsset-algroll." cp "../ns6/dsset-optout-tld." . diff --git a/bin/tests/system/dnssec/ns2/child.ds-rrsigs-stripped.db.in b/bin/tests/system/dnssec/ns2/child.ds-rrsigs-stripped.db.in new file mode 100644 index 0000000000..5fcc74dc94 --- /dev/null +++ b/bin/tests/system/dnssec/ns2/child.ds-rrsigs-stripped.db.in @@ -0,0 +1,27 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 2000042407 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns2 + NS ns3 +ns2 A 10.53.0.2 +ns3 A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 diff --git a/bin/tests/system/dnssec/ns2/ds-rrsigs-stripped.db.in b/bin/tests/system/dnssec/ns2/ds-rrsigs-stripped.db.in new file mode 100644 index 0000000000..7b1a7a53e8 --- /dev/null +++ b/bin/tests/system/dnssec/ns2/ds-rrsigs-stripped.db.in @@ -0,0 +1,29 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 2000042407 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns2 + NS ns3 +ns2 A 10.53.0.2 +ns3 A 10.53.0.3 + +child NS ns2.child +ns2.child A 10.53.0.2 +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 diff --git a/bin/tests/system/dnssec/ns2/named.conf.in b/bin/tests/system/dnssec/ns2/named.conf.in index 001731b012..454472d33e 100644 --- a/bin/tests/system/dnssec/ns2/named.conf.in +++ b/bin/tests/system/dnssec/ns2/named.conf.in @@ -229,4 +229,14 @@ zone "dnskey-rrsigs-stripped" { file "dnskey-rrsigs-stripped.db.signed"; }; +zone "ds-rrsigs-stripped" { + type primary; + file "ds-rrsigs-stripped.db.signed"; +}; + +zone "child.ds-rrsigs-stripped" { + type primary; + file "child.ds-rrsigs-stripped.db.signed"; +}; + include "trusted.conf"; diff --git a/bin/tests/system/dnssec/ns2/sign.sh b/bin/tests/system/dnssec/ns2/sign.sh index 19fbbf9634..1f4b47c4a9 100644 --- a/bin/tests/system/dnssec/ns2/sign.sh +++ b/bin/tests/system/dnssec/ns2/sign.sh @@ -404,3 +404,31 @@ cat "$infile" "$ksk.key" "$zsk.key" >"$zonefile" | awk '$4 == "SOA" { $7 = $7 + 1; print; next } { print }' >"$zonefile.next" "$SIGNER" -g -o "$zone" -f "$zonefile.next" "$zonefile.next" >/dev/null 2>&1 cp "$zonefile.stripped" "$zonefile.signed" + +# +# A child zone for the stripped DS RRSIGs test +# +zone=child.ds-rrsigs-stripped +infile=child.ds-rrsigs-stripped.db.in +zonefile=child.ds-rrsigs-stripped.db +ksk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone -f KSK "$zone") +zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone") +cat "$infile" "$ksk.key" "$zsk.key" >"$zonefile" +"$SIGNER" -g -o "$zone" "$zonefile" >/dev/null 2>&1 + +# +# A zone with the DNSKEY RRSIGS stripped +# +zone=ds-rrsigs-stripped +infile=ds-rrsigs-stripped.db.in +zonefile=ds-rrsigs-stripped.db +ksk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone -f KSK "$zone") +zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone") +cat "$infile" "$ksk.key" "$zsk.key" >"$zonefile" +"$SIGNER" -g -o "$zone" "$zonefile" >/dev/null 2>&1 +"$CHECKZONE" -D -q -i local "$zone" "$zonefile.signed" \ + | awk '$4 == "RRSIG" && $5 == "DS" { next } { print }' >"$zonefile.stripped" +"$CHECKZONE" -D -q -i local "$zone" "$zonefile.signed" \ + | awk '$4 == "SOA" { $7 = $7 + 1; print; next } { print }' >"$zonefile.next" +"$SIGNER" -g -o "$zone" -f "$zonefile.next" "$zonefile.next" >/dev/null 2>&1 +cp "$zonefile.stripped" "$zonefile.signed" diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 2518cfda1c..86cb7bfac6 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -227,6 +227,29 @@ n=$((n + 1)) test "$ret" -eq 0 || echo_i "failed" status=$((status + ret)) +echo_i "checking recovery from stripped DS RRSIG ($n)" +ret=0 +# prime cache with DS without RRSIGs +dig_with_opts +noauth +cd child.ds-rrsigs-stripped. @10.53.0.4 ds >dig.out.prime.ns4.test$n || ret=1 +grep ";; flags: qr rd ra cd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1" dig.out.prime.ns4.test$n >/dev/null || ret=1 +grep "status: NOERROR" dig.out.prime.ns4.test$n >/dev/null || ret=1 +grep "RRSIG.DS" dig.out.prime.ns4.test$n >/dev/null && ret=1 +# reload server with properly signed zone +cp ns2/ds-rrsigs-stripped.db.next ns2/ds-rrsigs-stripped.db.signed +nextpart ns2/named.run >/dev/null +rndccmd 10.53.0.2 reload ds-rrsigs-stripped | sed 's/^/ns2 /' | cat_i +wait_for_log 5 "zone ds-rrsigs-stripped/IN: loaded serial 2000042408" ns2/named.run || ret=1 +# make a query that flushes the unsigned DS RRset +dig_with_opts +noauth a.child.ds-rrsigs-stripped. @10.53.0.4 a >dig.out.ns4.test$n || ret=1 +# make a second query that should now validate +dig_with_opts +noauth b.child.ds-rrsigs-stripped. @10.53.0.2 a >dig.out.ns2.test$n || ret=1 +dig_with_opts +noauth b.child.ds-rrsigs-stripped. @10.53.0.4 a >dig.out.ns4.test$n || ret=1 +digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1 +grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1 +n=$((n + 1)) +test "$ret" -eq 0 || echo_i "failed" +status=$((status + ret)) + echo_i "checking that 'example/DS' from the referral was used in previous validation ($n)" ret=0 grep "query 'example/DS/IN' approved" ns1/named.run >/dev/null && ret=1 diff --git a/bin/tests/system/dnssec/tests_sh_dnssec.py b/bin/tests/system/dnssec/tests_sh_dnssec.py index d5811b1dbe..f6909dd8ad 100644 --- a/bin/tests/system/dnssec/tests_sh_dnssec.py +++ b/bin/tests/system/dnssec/tests_sh_dnssec.py @@ -53,6 +53,10 @@ pytestmark = pytest.mark.extra_artifacts( "ns2/dnskey-rrsigs-stripped.db", "ns2/dnskey-rrsigs-stripped.db.next", "ns2/dnskey-rrsigs-stripped.db.stripped", + "ns2/child.ds-rrsigs-stripped.db", + "ns2/ds-rrsigs-stripped.db", + "ns2/ds-rrsigs-stripped.db.next", + "ns2/ds-rrsigs-stripped.db.stripped", "ns2/example.db", "ns2/in-addr.arpa.db", "ns2/lazy-ksk.db", From 8b900d180886ca333d94c87c782619dbedc775b5 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Fri, 20 Dec 2024 20:24:05 +1100 Subject: [PATCH 3/4] Complete the deferred validation if there are no RRSIGs When a query is made with CD=1, we store the result in the cache marked pending so that it can be validated later, at which time it will either be accepted as an answer or removed from the cache as invalid. Deferred validation was not attempted when there were no cached RRSIGs for DNSKEY and DS. We now complete the deferred validation in this scenario. --- lib/dns/validator.c | 71 ++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 50 deletions(-) diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 3dd9ad719e..9661afd7c9 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -1126,14 +1126,13 @@ seek_dnskey(dns_validator_t *val) { * We have an rrset for the given keyname. */ val->keyset = &val->frdataset; - if ((DNS_TRUST_PENDING(val->frdataset.trust) || - DNS_TRUST_ANSWER(val->frdataset.trust)) && - dns_rdataset_isassociated(&val->fsigrdataset)) + if (DNS_TRUST_PENDING(val->frdataset.trust) || + DNS_TRUST_ANSWER(val->frdataset.trust)) { /* - * We know the key but haven't validated it yet or - * we have a key of trust answer but a DS - * record for the zone may have been added. + * We know the key but haven't validated it yet, or + * we had a key with trust level "answer" and + * a DS record for the zone has now been added. */ result = create_validator( val, &siginfo->signer, dns_rdatatype_dnskey, @@ -1143,12 +1142,6 @@ seek_dnskey(dns_validator_t *val) { return result; } return DNS_R_WAIT; - } else if (DNS_TRUST_PENDING(val->frdataset.trust)) { - /* - * Having a pending key with no signature means that - * something is broken. - */ - result = DNS_R_CONTINUE; } else if (val->frdataset.trust < dns_trust_secure) { /* * The key is legitimately insecure. There's no @@ -1906,9 +1899,8 @@ get_dsset(dns_validator_t *val, dns_name_t *tname, isc_result_t *resp) { * We have a DS RRset. */ val->dsset = &val->frdataset; - if ((DNS_TRUST_PENDING(val->frdataset.trust) || - DNS_TRUST_ANSWER(val->frdataset.trust)) && - dns_rdataset_isassociated(&val->fsigrdataset)) + if (DNS_TRUST_PENDING(val->frdataset.trust) || + DNS_TRUST_ANSWER(val->frdataset.trust)) { /* * ... which is signed but not yet validated. @@ -1916,21 +1908,12 @@ get_dsset(dns_validator_t *val, dns_name_t *tname, isc_result_t *resp) { result = create_validator( val, tname, dns_rdatatype_ds, &val->frdataset, &val->fsigrdataset, validator_callback_ds, - "validate_dnskey"); + "get_dsset"); *resp = DNS_R_WAIT; if (result != ISC_R_SUCCESS) { *resp = result; } return ISC_R_COMPLETE; - } else if (DNS_TRUST_PENDING(val->frdataset.trust)) { - /* - * There should never be an unsigned DS. - */ - disassociate_rdatasets(val); - validator_log(val, ISC_LOG_DEBUG(2), - "unsigned DS record"); - *resp = DNS_R_NOVALIDSIG; - return ISC_R_COMPLETE; } break; @@ -3006,7 +2989,7 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { val, ISC_LOG_DEBUG(3), "no supported algorithm/digest (%s/DS)", namebuf); - *resp = markanswer(val, "proveunsecure (5)"); + *resp = markanswer(val, "seek_ds (1)"); return ISC_R_COMPLETE; } @@ -3016,22 +2999,12 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { /* * Otherwise, try to validate it now. */ - if (dns_rdataset_isassociated(&val->fsigrdataset)) { - result = create_validator( - val, tname, dns_rdatatype_ds, &val->frdataset, - &val->fsigrdataset, validator_callback_ds, - "proveunsecure"); - *resp = DNS_R_WAIT; - if (result != ISC_R_SUCCESS) { - *resp = result; - } - } else { - /* - * There should never be an unsigned DS. - */ - validator_log(val, ISC_LOG_DEBUG(3), - "unsigned DS record"); - *resp = DNS_R_NOVALIDSIG; + result = create_validator(val, tname, dns_rdatatype_ds, + &val->frdataset, &val->fsigrdataset, + validator_callback_ds, "seek_ds"); + *resp = DNS_R_WAIT; + if (result != ISC_R_SUCCESS) { + *resp = result; } return ISC_R_COMPLETE; @@ -3042,7 +3015,7 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { */ *resp = DNS_R_WAIT; result = create_fetch(val, tname, dns_rdatatype_ds, - fetch_callback_ds, "proveunsecure"); + fetch_callback_ds, "seek_ds"); if (result != ISC_R_SUCCESS) { *resp = result; } @@ -3063,7 +3036,7 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { result = create_validator( val, tname, dns_rdatatype_ds, &val->frdataset, &val->fsigrdataset, validator_callback_ds, - "proveunsecure"); + "seek_ds"); *resp = DNS_R_WAIT; if (result != ISC_R_SUCCESS) { *resp = result; @@ -3083,7 +3056,7 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { NULL) == ISC_R_SUCCESS && dns_name_equal(tname, found)) { - *resp = markanswer(val, "proveunsecure (3)"); + *resp = markanswer(val, "seek_ds (2)"); return ISC_R_COMPLETE; } @@ -3102,7 +3075,7 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { } if (isdelegation(tname, &val->frdataset, result)) { - *resp = markanswer(val, "proveunsecure (4)"); + *resp = markanswer(val, "seek_ds (3)"); return ISC_R_COMPLETE; } @@ -3133,7 +3106,7 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { result = create_validator( val, tname, dns_rdatatype_ds, &val->frdataset, &val->fsigrdataset, validator_callback_ds, - "proveunsecure"); + "seek_ds"); if (result != ISC_R_SUCCESS) { *resp = result; } @@ -3162,9 +3135,7 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { result = create_validator( val, tname, dns_rdatatype_cname, &val->frdataset, &val->fsigrdataset, - validator_callback_cname, - "proveunsecure " - "(cname)"); + validator_callback_cname, "seek_ds (cname)"); *resp = DNS_R_WAIT; if (result != ISC_R_SUCCESS) { *resp = result; From 04b1484ed8308baede372e642d1ed7c05c523a94 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Fri, 20 Dec 2024 20:24:05 +1100 Subject: [PATCH 4/4] Re-fetch pending records that failed validation If a deferred validation on data that was originally queried with CD=1 fails, we now repeat the query, since the zone data may have changed in the meantime. --- bin/tests/system/dnssec/tests.sh | 6 ----- lib/dns/validator.c | 38 ++++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 86cb7bfac6..5c5651aac0 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -216,9 +216,6 @@ cp ns2/dnskey-rrsigs-stripped.db.next ns2/dnskey-rrsigs-stripped.db.signed nextpart ns2/named.run >/dev/null rndccmd 10.53.0.2 reload dnskey-rrsigs-stripped | sed 's/^/ns2 /' | cat_i wait_for_log 5 "zone dnskey-rrsigs-stripped/IN: loaded serial 2000042408" ns2/named.run || ret=1 -# make a query that flushes the unsigned DNSKEY RRset -dig_with_opts +noauth a.dnskey-rrsigs-stripped. @10.53.0.4 a >dig.out.ns4.test$n || ret=1 -# make a second query that should now validate dig_with_opts +noauth b.dnskey-rrsigs-stripped. @10.53.0.2 a >dig.out.ns2.test$n || ret=1 dig_with_opts +noauth b.dnskey-rrsigs-stripped. @10.53.0.4 a >dig.out.ns4.test$n || ret=1 digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1 @@ -239,9 +236,6 @@ cp ns2/ds-rrsigs-stripped.db.next ns2/ds-rrsigs-stripped.db.signed nextpart ns2/named.run >/dev/null rndccmd 10.53.0.2 reload ds-rrsigs-stripped | sed 's/^/ns2 /' | cat_i wait_for_log 5 "zone ds-rrsigs-stripped/IN: loaded serial 2000042408" ns2/named.run || ret=1 -# make a query that flushes the unsigned DS RRset -dig_with_opts +noauth a.child.ds-rrsigs-stripped. @10.53.0.4 a >dig.out.ns4.test$n || ret=1 -# make a second query that should now validate dig_with_opts +noauth b.child.ds-rrsigs-stripped. @10.53.0.2 a >dig.out.ns2.test$n || ret=1 dig_with_opts +noauth b.child.ds-rrsigs-stripped. @10.53.0.4 a >dig.out.ns4.test$n || ret=1 digcomp dig.out.ns2.test$n dig.out.ns4.test$n || ret=1 diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 9661afd7c9..b97210921e 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -162,6 +162,10 @@ validator_logcreate(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, const char *caller, const char *operation); +static isc_result_t +create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, + isc_job_cb callback, const char *caller); + /*% * Ensure the validator's rdatasets are marked as expired. */ @@ -611,13 +615,19 @@ validator_callback_dnskey(void *arg) { result = validate_async_run(val, resume_answer); } } else { - if (result != DNS_R_BROKENCHAIN) { - expire_rdatasets(val); - } validator_log(val, ISC_LOG_DEBUG(3), "validator_callback_dnskey: got %s", isc_result_totext(result)); - result = DNS_R_BROKENCHAIN; + if (result != DNS_R_BROKENCHAIN) { + expire_rdatasets(val); + result = create_fetch(val, &val->siginfo->signer, + dns_rdatatype_dnskey, + fetch_callback_dnskey, + "validator_callback_dnskey"); + if (result == ISC_R_SUCCESS) { + result = DNS_R_WAIT; + } + } } cleanup: @@ -636,8 +646,7 @@ static void validator_callback_ds(void *arg) { dns_validator_t *subvalidator = (dns_validator_t *)arg; dns_validator_t *val = subvalidator->parent; - isc_result_t result; - isc_result_t eresult = subvalidator->result; + isc_result_t result = subvalidator->result; val->subvalidator = NULL; @@ -647,7 +656,7 @@ validator_callback_ds(void *arg) { } validator_log(val, ISC_LOG_DEBUG(3), "in validator_callback_ds"); - if (eresult == ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { bool have_dsset; dns_name_t *name; validator_log(val, ISC_LOG_DEBUG(3), "%s with trust %s", @@ -669,13 +678,18 @@ validator_callback_ds(void *arg) { result = validate_async_run(val, validate_dnskey); } } else { - if (eresult != DNS_R_BROKENCHAIN) { - expire_rdatasets(val); - } validator_log(val, ISC_LOG_DEBUG(3), "validator_callback_ds: got %s", - isc_result_totext(eresult)); - result = DNS_R_BROKENCHAIN; + isc_result_totext(result)); + if (result != DNS_R_BROKENCHAIN) { + expire_rdatasets(val); + result = create_fetch(val, val->name, dns_rdatatype_ds, + fetch_callback_ds, + "validator_callback_ds"); + if (result == ISC_R_SUCCESS) { + result = DNS_R_WAIT; + } + } } cleanup: