From: W.C.A. Wijngaards Date: Thu, 23 Jul 2026 13:54:59 +0000 (+0200) Subject: - Fix that for NSEC3 proofs the NSEC3 zone, as the b32.name is X-Git-Tag: release-1.26.0rc1~20 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=0735cb28d151a56776e1b073c50d0c4775637df4;p=thirdparty%2Funbound.git - 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. --- diff --git a/doc/Changelog b/doc/Changelog index 07093b1ca..112fc2465 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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: diff --git a/validator/val_nsec3.c b/validator/val_nsec3.c index 62effde20..d0385be68 100644 --- a/validator/val_nsec3.c +++ b/validator/val_nsec3.c @@ -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)) diff --git a/validator/val_sigcrypt.c b/validator/val_sigcrypt.c index 8cfbb1466..55e7f1266 100644 --- a/validator/val_sigcrypt.c +++ b/validator/val_sigcrypt.c @@ -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 .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");