]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Check wildcard signer and NOQNAME signer match
authorEvan Hunt <each@isc.org>
Tue, 16 Jun 2026 19:06:21 +0000 (12:06 -0700)
committerEvan Hunt <each@isc.org>
Thu, 18 Jun 2026 17:01:29 +0000 (10:01 -0700)
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
bin/dnssec/dnssec-cds.c
bin/dnssec/dnssec-signzone.c
lib/dns/dnssec.c
lib/dns/include/dns/dnssec.h
lib/dns/include/dns/validator.h
lib/dns/validator.c
lib/dns/zone.c
lib/dns/zoneverify.c

index 50239e6178841e0165f4b16751492d79c669605e..d57d410fa345c6646786b7e03460984be994cf98 100644 (file)
@@ -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)
index 71a89991e086f18e9a8618f0a2b53d64e6d56419..0a526a5494216ce5c2f055c9ff6fb42371fe2318 100644 (file)
@@ -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;
index f7a6a9ca7f4b1f14ee5f9b705a33d0fcda89c97d..991a5e9fff5fb3a0f2bde8e6ea62b8094f37dec7 100644 (file)
@@ -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;
index 14f9e468ae4e3af49a9dc706eaac43c2a38b63e5..11d2759c7d016d0876e7a58dbf999054b97dc565 100644 (file)
@@ -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
index 7676fe534b66713b0c2decd51121283c873ee5f3..b7d0d8e712f381b356c5c84ce74b9d7f2e36b846 100644 (file)
@@ -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;
index 38ccb5611ca2831f9a4e0de4e7f1fd124079ea78..576c02acb29d49aab891ffa2e8bf0ffcd51e43a1 100644 (file)
@@ -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;
index 0909127c8ff27e365cd0d4ad25979340cedcbc76..d6a597abfd635181524cfdf14e7b6e3db0ff099c 100644 (file)
@@ -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),
index 70e21b4270d31038d6df1b3c67e8a4d062e518f6..aeb9b1e5b3e379fbc22fe4e246fb39ba787cb6a9 100644 (file)
@@ -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;
                }