]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix that for NSEC3 proofs the NSEC3 zone, as the b32.name is
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 23 Jul 2026 13:54:59 +0000 (15:54 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 23 Jul 2026 13:54:59 +0000 (15:54 +0200)
  checked to be the same as the signer name. Also RRSIGs are
  not considered valid when an NSEC3 is not b32.signerzone.
  Thanks to Qifan Zhang, Palo Alto Networks, for the report.

doc/Changelog
validator/val_nsec3.c
validator/val_sigcrypt.c

index 07093b1ca1d6e16c77c2a6fc13acf4c8834d2b5a..112fc2465ae5d69318aaf41214d2f0cc811f1f4d 100644 (file)
@@ -2,6 +2,10 @@
        - Updated credits for Xuanchao Xie in 22 july changelog.
        - Merge #1478 from petrvaganoff: pythonmod: add check return
          value after ftell().
+       - Fix that for NSEC3 proofs the NSEC3 zone, as the b32.name is
+         checked to be the same as the signer name. Also RRSIGs are
+         not considered valid when an NSEC3 is not b32.signerzone.
+         Thanks to Qifan Zhang, Palo Alto Networks, for the report.
 
 22 July 2026: Wouter
        - Release tag for 1.25.2, with the security commits:
index 62effde2093fc3dcda7b1ec440008acc419cce4c..d0385be68e0c610cbc5e3627dfb725d6b2656215 100644 (file)
@@ -1248,6 +1248,10 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
        filter_init(&flt, list, num, qinfo); /* init RR iterator */
        if(!flt.zone)
                return sec_status_bogus; /* no RRs */
+       if(query_dname_compare(flt.zone, kkey->name) != 0) {
+               verbose(VERB_ALGO, "NSEC3 name is not b32.signer name");
+               return sec_status_bogus;
+       }
        if(!param_set_same(&flt, NULL))
                return sec_status_bogus; /* nsec3 params from distinct chains*/
        if(nsec3_iteration_count_high(ve, &flt, kkey))
@@ -1436,6 +1440,10 @@ nsec3_prove_nodata(struct module_env* env, struct val_env* ve,
        filter_init(&flt, list, num, qinfo); /* init RR iterator */
        if(!flt.zone)
                return sec_status_bogus; /* no RRs */
+       if(query_dname_compare(flt.zone, kkey->name) != 0) {
+               verbose(VERB_ALGO, "NSEC3 name is not b32.signer name");
+               return sec_status_bogus;
+       }
        if(!param_set_same(&flt, NULL))
                return sec_status_bogus; /* nsec3 params from distinct chains*/
        if(nsec3_iteration_count_high(ve, &flt, kkey))
@@ -1461,6 +1469,10 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
        filter_init(&flt, list, num, qinfo); /* init RR iterator */
        if(!flt.zone)
                return sec_status_bogus; /* no RRs */
+       if(query_dname_compare(flt.zone, kkey->name) != 0) {
+               verbose(VERB_ALGO, "NSEC3 name is not b32.signer name");
+               return sec_status_bogus;
+       }
        if(!param_set_same(&flt, NULL))
                return sec_status_bogus; /* nsec3 params from distinct chains*/
        if(nsec3_iteration_count_high(ve, &flt, kkey))
@@ -1565,6 +1577,11 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
                *reason = "no NSEC3 records";
                return sec_status_bogus; /* no RRs */
        }
+       if(query_dname_compare(flt.zone, kkey->name) != 0) {
+               verbose(VERB_ALGO, "NSEC3 name is not b32.signer name");
+               *reason = "NSEC3 name is not b32.signer name";
+               return sec_status_bogus;
+       }
        if(!param_set_same(&flt, reason))
                return sec_status_bogus; /* nsec3 params from distinct chains*/
        if(nsec3_iteration_count_high(ve, &flt, kkey))
@@ -1660,6 +1677,10 @@ nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve,
        filter_init(&flt, list, num, qinfo); /* init RR iterator */
        if(!flt.zone)
                return sec_status_bogus; /* no RRs */
+       if(query_dname_compare(flt.zone, kkey->name) != 0) {
+               verbose(VERB_ALGO, "NSEC3 name is not b32.signer name");
+               return sec_status_bogus;
+       }
        if(!param_set_same(&flt, NULL))
                return sec_status_bogus; /* nsec3 params from distinct chains*/
        if(nsec3_iteration_count_high(ve, &flt, kkey))
index 8cfbb146611f86d208ee6d8fe5dff9d4e86df62a..55e7f1266599a0f9711ace39602b111dc74350e5 100644 (file)
@@ -1627,6 +1627,20 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
                        *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus; /* signer name offtree */
        }
+       /* NSEC3, the owner name must be the <base32hash>.signername */
+       if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NSEC3 &&
+               rrset->rk.dname_len > 0) {
+               uint8_t* dnameless = rrset->rk.dname;
+               size_t dnamelesslen = rrset->rk.dname_len;
+               dname_remove_label(&dnameless, &dnamelesslen);
+               if(query_dname_compare(dnameless, signer) != 0) {
+                       verbose(VERB_QUERY, "verify: NSEC3 owner name is not b32.signer name");
+                       *reason = "NSEC3 owner name is not b32.signer name";
+                       if(reason_bogus)
+                               *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
+                       return sec_status_bogus; /* NSEC3 owner not b32.signer */
+               }
+       }
        sigblock = (unsigned char*)signer+signer_len;
        if(siglen < 2+18+signer_len+1) {
                verbose(VERB_QUERY, "verify: too short, no signature data");