From 1b814f1ee905ee434432c9b8ecc3bf77fba361bf 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 (cherry picked from commit a71b61756650b0e1849d9e074c5f370a3067780b) --- 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 ca72f0ee32..d3750ea70a 100644 --- a/bin/tests/system/dnssec/ns1/root.db.in +++ b/bin/tests/system/dnssec/ns1/root.db.in @@ -37,3 +37,5 @@ inprogress. NS ns10.inprogress. ns10.inprogress. A 10.53.0.10 too-many-iterations. NS ns2.too-many-iterations. ns2.too-many-iterations. 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 286b27883d..1d6bc2c825 100644 --- a/bin/tests/system/dnssec/ns1/sign.sh +++ b/bin/tests/system/dnssec/ns1/sign.sh @@ -30,6 +30,7 @@ cp "../ns2/dsset-example." . cp "../ns2/dsset-in-addr.arpa." . cp "../ns2/dsset-too-many-iterations." . cp "../ns2/dsset-lazy-ksk." . +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 84a14ffdc5..1cbb382e0d 100644 --- a/bin/tests/system/dnssec/ns2/named.conf.in +++ b/bin/tests/system/dnssec/ns2/named.conf.in @@ -216,4 +216,9 @@ zone "lazy-ksk" { allow-update { any; }; }; +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 b60e82a631..9aa7d22a37 100644 --- a/bin/tests/system/dnssec/ns2/sign.sh +++ b/bin/tests/system/dnssec/ns2/sign.sh @@ -369,3 +369,20 @@ rm "$rm1.key" rm "$rm1.private" rm "$rm2.key" rm "$rm2.private" + +# +# 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 b58b11a5c7..b589e282f8 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -184,6 +184,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 529a4a0e10..e529a72a7a 100644 --- a/bin/tests/system/dnssec/tests_sh_dnssec.py +++ b/bin/tests/system/dnssec/tests_sh_dnssec.py @@ -56,6 +56,9 @@ pytestmark = pytest.mark.extra_artifacts( "ns2/cds-x.secure.db", "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 39bfa739fbf351e4992a88e4a3b101fb104e8d74 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 (cherry picked from commit 66f293a9529dc4b80fd89126e36abd9dccef7649) --- 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 d3750ea70a..d76c89f455 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 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 1d6bc2c825..d5e4e5ff53 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-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 1cbb382e0d..fef3f47c41 100644 --- a/bin/tests/system/dnssec/ns2/named.conf.in +++ b/bin/tests/system/dnssec/ns2/named.conf.in @@ -221,4 +221,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 9aa7d22a37..1e7779e14d 100644 --- a/bin/tests/system/dnssec/ns2/sign.sh +++ b/bin/tests/system/dnssec/ns2/sign.sh @@ -386,3 +386,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 b589e282f8..9f29018c06 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -207,6 +207,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 e529a72a7a..60176f9555 100644 --- a/bin/tests/system/dnssec/tests_sh_dnssec.py +++ b/bin/tests/system/dnssec/tests_sh_dnssec.py @@ -59,6 +59,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 48b32e64c4a14f9817a277b12cc325a162bffde6 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. (cherry picked from commit 8b900d180886ca333d94c87c782619dbedc775b5) --- 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 712fc0755a..1313c9f854 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -1247,14 +1247,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, @@ -1264,12 +1263,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 @@ -1723,9 +1716,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. @@ -1733,21 +1725,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; @@ -2726,7 +2709,7 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { "no supported algorithm/" "digest (%s/DS)", namebuf); - *resp = markanswer(val, "proveunsecure (5)", + *resp = markanswer(val, "seek_ds (1)", "no supported " "algorithm/digest (DS)"); return ISC_R_COMPLETE; @@ -2738,22 +2721,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; @@ -2764,7 +2737,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; } @@ -2785,7 +2758,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; @@ -2805,7 +2778,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)", "no DS at zone cut"); return ISC_R_COMPLETE; } @@ -2825,7 +2798,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)", "this is a delegation"); return ISC_R_COMPLETE; } @@ -2857,7 +2830,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; } @@ -2886,9 +2859,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 86e65f317a608a397057b50d9f27003590764aeb 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. (cherry picked from commit 04b1484ed8308baede372e642d1ed7c05c523a94) --- bin/tests/system/dnssec/tests.sh | 6 ---- lib/dns/validator.c | 61 +++++++++++++++++++------------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 9f29018c06..061a27262a 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -196,9 +196,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 @@ -219,9 +216,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 1313c9f854..b76f1c53a3 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -137,6 +137,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_taskaction_t callback, const char *caller); + /*% * Ensure the validator's rdatasets are marked as expired. */ @@ -641,7 +645,6 @@ validator_callback_dnskey(isc_task_t *task, isc_event_t *event) { dns_validator_t *val; bool want_destroy; isc_result_t result; - isc_result_t eresult; isc_result_t saved_result; UNUSED(task); @@ -649,7 +652,7 @@ validator_callback_dnskey(isc_task_t *task, isc_event_t *event) { devent = (dns_validatorevent_t *)event; val = devent->ev_arg; - eresult = devent->result; + result = devent->result; isc_event_free(&event); dns_validator_destroy(&val->subvalidator); @@ -659,8 +662,8 @@ validator_callback_dnskey(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "in validator_callback_dnskey"); LOCK(&val->lock); if (CANCELED(val)) { - validator_done(val, ISC_R_CANCELED); - } else if (eresult == ISC_R_SUCCESS) { + result = ISC_R_CANCELED; + } else if (result == ISC_R_SUCCESS) { validator_log(val, ISC_LOG_DEBUG(3), "keyset with trust %s", dns_trust_totext(val->frdataset.trust)); /* @@ -681,17 +684,23 @@ validator_callback_dnskey(isc_task_t *task, isc_event_t *event) { result = saved_result; } } - if (result != DNS_R_WAIT) { - validator_done(val, result); - } } else { - if (eresult != DNS_R_BROKENCHAIN) { - expire_rdatasets(val); - } validator_log(val, ISC_LOG_DEBUG(3), "validator_callback_dnskey: got %s", - isc_result_totext(eresult)); - validator_done(val, DNS_R_BROKENCHAIN); + isc_result_totext(result)); + 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; + } + } + } + if (result != DNS_R_WAIT) { + validator_done(val, result); } want_destroy = exit_check(val); @@ -712,14 +721,13 @@ validator_callback_ds(isc_task_t *task, isc_event_t *event) { dns_validator_t *val; bool want_destroy; isc_result_t result; - isc_result_t eresult; UNUSED(task); INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE); devent = (dns_validatorevent_t *)event; val = devent->ev_arg; - eresult = devent->result; + result = devent->result; isc_event_free(&event); dns_validator_destroy(&val->subvalidator); @@ -729,8 +737,8 @@ validator_callback_ds(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "in validator_callback_ds"); LOCK(&val->lock); if (CANCELED(val)) { - validator_done(val, ISC_R_CANCELED); - } else if (eresult == ISC_R_SUCCESS) { + result = ISC_R_CANCELED; + } else if (result == ISC_R_SUCCESS) { bool have_dsset; dns_name_t *name; validator_log(val, ISC_LOG_DEBUG(3), "%s with trust %s", @@ -754,17 +762,22 @@ validator_callback_ds(isc_task_t *task, isc_event_t *event) { } else { result = validate_dnskey(val); } - if (result != DNS_R_WAIT) { - validator_done(val, result); - } } 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)); - validator_done(val, DNS_R_BROKENCHAIN); + isc_result_totext(result)); + if (result != DNS_R_BROKENCHAIN) { + expire_rdatasets(val); + result = create_fetch( + val, val->event->name, dns_rdatatype_ds, + fetch_callback_ds, "validator_callback_ds"); + if (result == ISC_R_SUCCESS) { + result = DNS_R_WAIT; + } + } + } + if (result != DNS_R_WAIT) { + validator_done(val, result); } want_destroy = exit_check(val);