Commit 5aa13045c2 for bind
commit 5aa13045c27c25eebd78ce8cc243be28b1d45983
Author: OndÅ™ej Surý <ondrej@isc.org>
Date: Thu Sep 24 16:49:50 2026 +0200
Allow DS lookups when validating CNAMEs
check_deadlock() rejected any fetch at the owner of a CNAME being
validated. This also blocked the DS lookup needed to prove an apex
CNAME insecure, causing SERVFAIL for unsigned child zones beneath a
signed parent. DS records belong to the parent zone, so the child's
CNAME must not prevent that lookup.
Remove the blanket restriction on fetches at a CNAME owner. The
resolver already rejects attempts to join an ancestor fetch, which
handles the circular dependency when a DS query itself receives a
CNAME and validation tries to fetch the same DS again.
diff --git a/bin/tests/system/dnssec_cname_response/tests_cname_rejection.py b/bin/tests/system/dnssec_cname_response/tests_cname_rejection.py
index f395956658..e4bdc3aab9 100644
--- a/bin/tests/system/dnssec_cname_response/tests_cname_rejection.py
+++ b/bin/tests/system/dnssec_cname_response/tests_cname_rejection.py
@@ -175,15 +175,8 @@ def test_cname_for_validator_dnskey_fetch(ns3):
def test_ds_cname_does_not_deadlock():
"""
- A DS query answered with an unsigned CNAME must not send the validator
- into a self-join deadlock (GL#5878). While proving the CNAME insecure
- the validator would fetch the DS for the same name, re-entering the
- in-flight DS fetch it is blocked on and stalling for ~12 seconds until a
- backstop timer fires. The validator now detects that such a fetch cannot
- advance the alias chain and aborts, so the client gets SERVFAIL promptly.
-
- 'secure.' is a properly signed zone (so validation reaches the DS query),
- but its authoritative server answers DS queries with an unsigned CNAME.
+ An unsigned CNAME answer to a DS query makes validation fetch the same DS.
+ Reject the fetch loop promptly instead of waiting for a timeout (GL#5878).
"""
msg = isctest.query.create("insecure.secure.", "DS")
diff --git a/lib/dns/validator.c b/lib/dns/validator.c
index 5f92d40d6c..a17c27d743 100644
--- a/lib/dns/validator.c
+++ b/lib/dns/validator.c
@@ -1270,9 +1270,10 @@ notfound:
}
/*%
- * Returns true if proceeding would stall the SHARED fetch: either the
- * fetch cannot advance an alias chain, or an ancestor is already
- * resolving the same (name, type).
+ * Reject validation of the same (name, type) as an ancestor.
+ *
+ * DS at a CNAME owner may be needed to prove insecurity. Fetch loops are
+ * checked by dns_resolver_createfetch().
*/
static bool
check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
@@ -1282,27 +1283,6 @@ check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
continue;
}
- /*
- * 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 &&
- cur->rdataset->attributes.chaining &&
- cur->rdataset->type == dns_rdatatype_cname)
- {
- validator_log(
- val, ISC_LOG_DEBUG(3),
- "fetch would not advance the alias chain: "
- "aborting validation");
- return true;
- }
-
/*
* Not a loop: NSEC3 is meta data, so proving a name's
* nonexistence can need the NSEC3 RRset that proves it
@@ -1366,6 +1346,11 @@ create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
0, fopts, 0, val->qc, val->gqc, val->parent_fetch, val->loop,
callback, val, &val->edectx, &val->frdataset,
&val->fsigrdataset, &val->fetch);
+ if (result == DNS_R_LOOPDETECTED) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "fetch would join a fetch waiting on this "
+ "validation: aborting validation");
+ }
if (result != ISC_R_SUCCESS) {
dns_validator_detach(&val);
}
@@ -1374,16 +1359,7 @@ create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
}
/*%
- * Start a fetch for the DS RRset at 'name', which lives in the parent zone.
- *
- * If the delegation database already has a usable delegation for that parent,
- * pass it to the resolver as a hint so the fetch is anchored at the parent
- * zone cut. DS is an at-parent type, so the resolver derives the same cut on
- * its own for a hintless query; supplying it explicitly additionally gives the
- * resolver's fetch loop detection a zone cut to match on, which it does not
- * have for a fetch started without a hint. The lookup fails harmlessly if the
- * parent delegation is expired or does not line up with the labels in 'name',
- * leaving a hintless DS fetch.
+ * Fetch DS from the parent, using a cached delegation hint if available.
*/
static isc_result_t
create_ds_fetch(dns_validator_t *val, dns_name_t *name, isc_job_cb callback,