From: Evan Hunt Date: Thu, 21 May 2026 21:41:55 +0000 (-0700) Subject: Check NSEC3 signer matches the owning zone X-Git-Tag: v9.21.24~5^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6e5066bb1f0f12d090e8707adb7d6ccf74f8012b;p=thirdparty%2Fbind9.git Check NSEC3 signer matches the owning zone When validating NSEC3 records, reject any signature whose signer field does not match the zone owning the NSEC3. This ensures that a child zone cannot impersonate its parent and forge NXDOMAIN responses for sibling domains. Fixes: isc-projects/bind9#5874 --- diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index d71d1e68b97..f7aae5126c3 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -409,10 +409,25 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, } /* - * NS, SOA and DNSKEY records are signed by their owner. - * DS records are signed by the parent. + * NS, SOA and DNSKEY records are signed by their owners. + * NSEC3 records are signed by the apex, exactly one level up + * from their owner names. + * DS records are signed by the parent zone. */ switch (set->type) { + case dns_rdatatype_nsec3: { + dns_name_t apex = DNS_NAME_INITEMPTY; + labels = dns_name_countlabels(name); + if (labels <= 1) { + inc_stat(dns_dnssecstats_fail); + return DNS_R_INVALIDNSEC3; + } + dns_name_split(name, labels - 1, NULL, &apex); + if (!dns_name_equal(&apex, &sig.signer)) { + inc_stat(dns_dnssecstats_fail); + return DNS_R_SIGINVALID; + } + } break; case dns_rdatatype_ns: case dns_rdatatype_soa: case dns_rdatatype_dnskey: diff --git a/lib/isc/result.c b/lib/isc/result.c index 5b2fbb4ac84..14308233dad 100644 --- a/lib/isc/result.c +++ b/lib/isc/result.c @@ -184,7 +184,7 @@ static const char *description[ISC_R_NRESULTS] = { [DNS_R_COVERINGNSEC] = "covering NSEC record returned", [DNS_R_MXISADDRESS] = "MX is an address", [DNS_R_DUPLICATE] = "duplicate query", - [DNS_R_INVALIDNSEC3] = "invalid NSEC3 owner name (wildcard)", + [DNS_R_INVALIDNSEC3] = "invalid NSEC3 owner name", [DNS_R_NOTPRIMARY] = "not primary", [DNS_R_BROKENCHAIN] = "broken trust chain", [DNS_R_EXPIRED] = "expired",