From 18c1295fdc25d9c70f989ac8214707cf6e4d7d75 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 6e9ccf68a9..3a3d676eb9 100644 --- a/bin/tests/system/dnssec/ns2/named.conf.in +++ b/bin/tests/system/dnssec/ns2/named.conf.in @@ -214,4 +214,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 9ac57f776c..c4e861c69a 100644 --- a/bin/tests/system/dnssec/ns2/sign.sh +++ b/bin/tests/system/dnssec/ns2/sign.sh @@ -354,3 +354,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 12cb3de13e..a407a1a400 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 c7e4d9df7e..21a3da2993 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 77417f8fc603237501c0cc0852883d74eb63fb59 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 3a3d676eb9..1d3274ad71 100644 --- a/bin/tests/system/dnssec/ns2/named.conf.in +++ b/bin/tests/system/dnssec/ns2/named.conf.in @@ -219,4 +219,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 c4e861c69a..3be827090f 100644 --- a/bin/tests/system/dnssec/ns2/sign.sh +++ b/bin/tests/system/dnssec/ns2/sign.sh @@ -371,3 +371,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 a407a1a400..eec5b823fa 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 21a3da2993..2bad6653a9 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 de8893733f470d42bd7fb2b2eed7d22aca2d4c5e 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 38da65f0f1..b98a4bee91 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -1137,14 +1137,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, @@ -1154,12 +1153,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 @@ -1917,9 +1910,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. @@ -1927,21 +1919,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; @@ -3019,7 +3002,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)", "no supported " "algorithm/digest (DS)"); return ISC_R_COMPLETE; @@ -3031,22 +3014,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; @@ -3057,7 +3030,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; } @@ -3078,7 +3051,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; @@ -3098,7 +3071,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; } @@ -3118,7 +3091,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; } @@ -3150,7 +3123,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; } @@ -3179,9 +3152,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 89122c3fde58a7e19c5dd34efd71a550e48ded96 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 | 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 eec5b823fa..da8e7fe36c 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 b98a4bee91..4083a069ec 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -161,6 +161,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. */ @@ -621,13 +625,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: @@ -646,8 +656,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; @@ -657,7 +666,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", @@ -680,13 +689,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: