From 7ced319c66672605e15bd3cee27e64e26d638e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Thu, 2 Jul 2026 15:35:56 +0200 Subject: [PATCH 1/2] Restrict the alias-chain deadlock check to chaining CNAMEs check_deadlock() aborted any fetch whose name equals the owner of a chaining rdataset, assuming nothing the validator needs can live at an alias. That is true for a CNAME, but a DNAME aliases only the names below its owner: with a DNAME at a zone apex, the DNSKEY signing the DNAME lives at the owner name itself, so every answer synthesized from a signed apex DNAME failed validation whenever that key was not already validated in the cache. Chaining CNAMEs, including those synthesized from a DNAME, still cover the self-join case the check was added for. (cherry picked from commit 057ae0adb836cbd664ad77cc5fd9ea0e1b482041) --- lib/dns/validator.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 10108d7501..84fbfd5789 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -993,12 +993,18 @@ check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, } /* - * Validating a CNAME/DNAME ("chaining" rdataset): a fetch at - * the alias's own name cannot advance the chain (the type we - * need, e.g. DS/DNSKEY for an insecurity proof, cannot live at - * an alias) and would only self-join the in-flight fetch. + * Validating a chaining CNAME: a fetch at the alias's own + * name cannot advance the chain (no other type can live at + * a CNAME owner, so e.g. the DS/DNSKEY needed for an + * insecurity proof cannot be there) and would only + * self-join the in-flight fetch. A chaining DNAME is + * different: it aliases only the names below its owner, so + * the owner itself may legitimately hold the DNSKEY or DS + * this validation needs (e.g. a DNAME at a zone apex). */ - if (cur->rdataset != NULL && CHAINING(cur->rdataset)) { + if (cur->rdataset != NULL && CHAINING(cur->rdataset) && + cur->rdataset->type == dns_rdatatype_cname) + { validator_log( val, ISC_LOG_DEBUG(3), "fetch would not advance the alias chain: " From b7178ff652ad128de246e8cfb8346b4685c5c7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Thu, 2 Jul 2026 15:35:56 +0200 Subject: [PATCH 2/2] Add a system test resolving through a signed apex DNAME The dnssec system test signs a DNAME-at-apex zone but only ever queried the apex directly; nothing resolved a name under the DNAME through the validating resolver, so a validator regression on that path went unnoticed. Assisted-by: Claude:claude-fable-5 (cherry picked from commit 38992e02bb02fbc0dd23634e80ad9f1c22a23c83) --- bin/tests/system/dnssec/tests_validation.py | 59 +++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 bin/tests/system/dnssec/tests_validation.py diff --git a/bin/tests/system/dnssec/tests_validation.py b/bin/tests/system/dnssec/tests_validation.py new file mode 100644 index 0000000000..c7f1ddca0d --- /dev/null +++ b/bin/tests/system/dnssec/tests_validation.py @@ -0,0 +1,59 @@ +# 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. + + +from dns import rdatatype + +import pytest + +import isctest +import isctest.mark + +pytestmark = pytest.mark.extra_artifacts( + [ + "*/K*", + "*/NSEC*", + "*/dsset-*", + "*/*.bk", + "*/*.conf", + "*/*.db", + "*/*.id", + "*/*.jnl", + "*/*.jbk", + "*/*.key", + "*/*.signed", + "*/settime.out.*", + "ans*/ans.run", + "*/trusted.keys", + "*/*.bad", + "*/*.next", + "*/*.stripped", + "*/*.tmp", + "*/*.stage?", + "*/*.patched", + "*/*.lower", + "*/*.upper", + "*/*.unsplit", + ] +) + + +def test_positive_validation_dname_at_apex(): + # an apex DNAME is signed by the DNSKEY living at the DNAME owner + # name itself; fetching that key must not be mistaken for a + # non-advancing alias chain (GL #6176) + msg = isctest.query.create("a.dname-at-apex-nsec3.example", "A") + res = isctest.query.tcp(msg, "10.53.0.4") + isctest.check.noerror(res) + isctest.check.adflag(res) + answers = {(str(rr.name), rr.rdtype) for rr in res.answer} + assert ("dname-at-apex-nsec3.example.", rdatatype.DNAME) in answers + assert ("a.example.", rdatatype.A) in answers