From: Evan Hunt Date: Tue, 16 Jun 2026 19:06:21 +0000 (-0700) Subject: Check wildcard signer and NOQNAME signer match X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=45c9bd26032c6bc93a1d04acc94f4db895b59b1b;p=thirdparty%2Fbind9.git Check wildcard signer and NOQNAME signer match A positive wildcard answer, and the NSEC3 proof that the requested name doesn't exist in the zone, must both be from the same zone. Otherwise, an NSEC3 from an ancestor zone could be used to interfere with validation. We now retrieve the signer name from a wildcard response's signature. An NSEC3 record cannot be used as a NOQNAME proof for the wildcard unless it exactly matches the name one level above the NSEC3. Fixes: isc-projects/bind9#5971 --- diff --git a/bin/dnssec/dnssec-cds.c b/bin/dnssec/dnssec-cds.c index 50239e61788..d57d410fa34 100644 --- a/bin/dnssec/dnssec-cds.c +++ b/bin/dnssec/dnssec-cds.c @@ -636,7 +636,7 @@ matching_sigs(keyinfo_t *keytbl, dns_rdataset_t *rdataset, result = dns_dnssec_verify(name, rdataset, ki->dst, false, isc_g_mctx, &sigrdata, - NULL); + NULL, NULL); if (result != ISC_R_SUCCESS && result != DNS_R_FROMWILDCARD) diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index 71a89991e08..0a526a54942 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -295,7 +295,7 @@ signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dst_key_t *key, if (tryverify) { result = dns_dnssec_verify(name, rdataset, key, true, - isc_g_mctx, &trdata, NULL); + isc_g_mctx, &trdata, NULL, NULL); if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { vbprintf(3, "\tsignature verified\n"); INCSTAT(nverified); @@ -456,7 +456,7 @@ setverifies(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, dns_rdata_t *rrsig) { isc_result_t result; result = dns_dnssec_verify(name, set, key, false, isc_g_mctx, rrsig, - NULL); + NULL, NULL); if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { INCSTAT(nverified); return true; diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index f7a6a9ca7f4..991a5e9fff5 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -342,7 +342,7 @@ cleanup_databuf: isc_result_t dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, bool ignoretime, isc_mem_t *mctx, dns_rdata_t *sigrdata, - dns_name_t *wild) { + dns_name_t *wild, dns_name_t *wildsigner) { dns_rdata_rrsig_t sig; dns_fixedname_t fnewname; isc_region_t r; @@ -550,6 +550,9 @@ cleanup_struct: dns_fixedname_name(&fnewname), wild) == ISC_R_SUCCESS); } + if (wildsigner != NULL) { + dns_name_copy(&sig.signer, wildsigner); + } inc_stat(dns_dnssecstats_wildcard); result = DNS_R_FROMWILDCARD; } @@ -1035,7 +1038,7 @@ dns_dnssec_signs(dns_rdata_t *rdata, const dns_name_t *name, if (sig.algorithm == key.algorithm && sig.keyid == keytag) { result = dns_dnssec_verify(name, rdataset, dstkey, ignoretime, mctx, &sigrdata, - NULL); + NULL, NULL); if (result == ISC_R_SUCCESS) { dst_key_free(&dstkey); return true; diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h index 14f9e468ae4..11d2759c7d0 100644 --- a/lib/dns/include/dns/dnssec.h +++ b/lib/dns/include/dns/dnssec.h @@ -141,7 +141,7 @@ dns_dnssec_sign(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, isc_result_t dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, bool ignoretime, isc_mem_t *mctx, dns_rdata_t *sigrdata, - dns_name_t *wild); + dns_name_t *wild, dns_name_t *wildsigner); /*%< * Verifies the RRSIG record covering this rdataset signed by a specific * key. This does not determine if the key's owner is authorized to sign @@ -156,13 +156,16 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, *\li 'key' is a valid key *\li 'mctx' is not NULL *\li 'sigrdata' is a valid rdata containing a SIG record - *\li 'wild' if non-NULL then is a valid and has a buffer. + *\li 'wild' if non-NULL then is a valid name and has a buffer. + *\li 'wildsigner' if non-NULL then is a valid name and has a buffer. * * Returns: *\li #ISC_R_SUCCESS *\li #DNS_R_FROMWILDCARD - the signature is valid and is from * a wildcard expansion. dns_dnssec_verify2() only. - * 'wild' contains the name of the wildcard if non-NULL. + * 'wild', if non-NULL, contains the name of the wildcard. + * 'wildsigner', if non-NULL, contains the 'signer' name + * from the RRSIG signing the wildcard. *\li #DNS_R_SIGINVALID - the signature fails to verify *\li #DNS_R_SIGEXPIRED - the signature has expired *\li #DNS_R_SIGFUTURE - the signature's validity period has not begun diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h index 7676fe534b6..b7d0d8e712f 100644 --- a/lib/dns/include/dns/validator.h +++ b/lib/dns/include/dns/validator.h @@ -140,6 +140,7 @@ struct dns_validator { dns_rdataset_t dsrdataset; dns_fixedname_t fname; dns_fixedname_t wild; + dns_fixedname_t wildsigner; dns_fixedname_t closest; ISC_LINK(dns_validator_t) link; unsigned int depth; diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 38ccb5611ca..576c02acb29 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -1491,9 +1491,9 @@ selfsigned_dnskey(dns_validator_t *val) { } consume_validation(val); - result = dns_dnssec_verify(name, rdataset, - dstkey, true, mctx, - &sigrdata, NULL); + result = dns_dnssec_verify( + name, rdataset, dstkey, true, mctx, + &sigrdata, NULL, NULL); switch (result) { case DNS_R_SIGFUTURE: case DNS_R_SIGEXPIRED: @@ -1550,9 +1550,10 @@ static isc_result_t verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata, uint16_t keyid) { isc_result_t result; - dns_fixedname_t fixed; + dns_fixedname_t fwild, fsigner; bool ignore = false; - dns_name_t *wild = dns_fixedname_initname(&fixed); + dns_name_t *wild = dns_fixedname_initname(&fwild); + dns_name_t *wildsigner = dns_fixedname_initname(&fsigner); if (DNS_TRUST_SECURE(val->rdataset->trust)) { /* @@ -1569,7 +1570,7 @@ verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata, again: result = dns_dnssec_verify(val->name, val->rdataset, key, ignore, - val->view->mctx, rdata, wild); + val->view->mctx, rdata, wild, wildsigner); if ((result == DNS_R_SIGEXPIRED || result == DNS_R_SIGFUTURE) && val->view->acceptexpired) { @@ -1595,17 +1596,18 @@ again: } if (result == DNS_R_FROMWILDCARD) { if (!dns_name_equal(val->name, wild)) { - dns_name_t *closest; - unsigned int labels; + dns_name_t *closest = dns_fixedname_name(&val->closest); /* * Compute the closest encloser in case we need it * for the NSEC3 NOQNAME proof. */ - closest = dns_fixedname_name(&val->closest); dns_name_copy(wild, closest); - labels = dns_name_countlabels(closest) - 1; - dns_name_getlabelsequence(closest, 1, labels, closest); + dns_name_getlabelsequence( + closest, 1, dns_name_countlabels(closest) - 1, + closest); + dns_name_copy(wildsigner, + dns_fixedname_name(&val->wildsigner)); val->attributes |= VALATTR_NEEDNOQNAME; } result = ISC_R_SUCCESS; @@ -2796,10 +2798,13 @@ findnsec3proofs(dns_validator_t *val) { * have a valid closest encloser. Otherwise we could still be looking * at proofs from the parent zone. */ + dns_name_t *wildsigner = dns_fixedname_name(&val->wildsigner); if (dns_name_countlabels(closest) > 0 && dns_name_countlabels(nearest) == dns_name_countlabels(closest) + 1 && - dns_name_issubdomain(nearest, closest)) + dns_name_issubdomain(nearest, closest) && + (dns_name_countlabels(wildsigner) == 0 || + dns_name_equal(zonename, wildsigner))) { val->attributes |= VALATTR_FOUNDCLOSEST; result = dns_name_concatenate(dns_wildcardname, closest, @@ -3811,6 +3816,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, dns_rdataset_init(&val->fsigrdataset); dns_rdataset_init(&val->dsrdataset); dns_fixedname_init(&val->wild); + dns_fixedname_init(&val->wildsigner); dns_fixedname_init(&val->closest); val->start = isc_stdtime_now(); val->magic = VALIDATOR_MAGIC; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 0909127c8ff..d6a597abfd6 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -8967,8 +8967,6 @@ revocable(dns_zonefetch_t *fetch, dns_rdata_keydata_t *keydata) { /* See if that key generated any of the signatures */ DNS_RDATASET_FOREACH(&fetch->sigset) { dns_rdata_t sigrr = DNS_RDATA_INIT; - dns_fixedname_t fixed; - dns_fixedname_init(&fixed); dns_rdataset_current(&fetch->sigset, &sigrr); result = dns_rdata_tostruct(&sigrr, &sig, NULL); @@ -8981,7 +8979,7 @@ revocable(dns_zonefetch_t *fetch, dns_rdata_keydata_t *keydata) { { result = dns_dnssec_verify(keyname, &fetch->rrset, dstkey, false, mctx, &sigrr, - dns_fixedname_name(&fixed)); + NULL, NULL); dnssec_log(fetch->zone, ISC_LOG_DEBUG(3), "Confirm revoked DNSKEY is self-signed: %s", @@ -9198,7 +9196,8 @@ keyfetch_done(dns_zonefetch_t *fetch, isc_result_t eresult) { } result = dns_dnssec_verify(keyname, dnskeys, dstkey, - false, mctx, &sigrr, NULL); + false, mctx, &sigrr, NULL, + NULL); dst_key_free(&dstkey); dnssec_log(zone, ISC_LOG_DEBUG(3), diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c index 70e21b4270d..aeb9b1e5b3e 100644 --- a/lib/dns/zoneverify.c +++ b/lib/dns/zoneverify.c @@ -185,7 +185,7 @@ goodsig(const vctx_t *vctx, dns_rdata_t *sigrdata, const dns_name_t *name, continue; } result = dns_dnssec_verify(name, rdataset, dstkeys[key], false, - vctx->mctx, sigrdata, NULL); + vctx->mctx, sigrdata, NULL, NULL); if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { return true; }