From: Aydın Mercan Date: Tue, 12 May 2026 11:54:09 +0000 (+0300) Subject: change dns_nsec_requiredtypespresent to dns_nsec_is_legal X-Git-Tag: v9.21.24~4^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=be2a6a497312469890b552907d039d2de0b44ccc;p=thirdparty%2Fbind9.git change dns_nsec_requiredtypespresent to dns_nsec_is_legal Change `dns_nsec_requiredtypespresent` to `dns_nsec_is_legal` as a function for checking multiple NSEC validity rules. Currently we now additionally check for out-of-zone NSEC entries. --- diff --git a/lib/dns/include/dns/nsec.h b/lib/dns/include/dns/nsec.h index ebb1c65a438..be387c6e3cd 100644 --- a/lib/dns/include/dns/nsec.h +++ b/lib/dns/include/dns/nsec.h @@ -115,11 +115,19 @@ dns_nsec_noexistnodata(dns_rdatatype_t type, const dns_name_t *name, */ bool -dns_nsec_requiredtypespresent(dns_rdataset_t *rdataset); -/* - * Return true if all the NSEC records in rdataset have both - * NSEC and RRSIG present. +dns_nsec_is_legal(dns_rdataset_t *rdataset, const dns_name_t *name); +/**< + * \brief + * Validates a rdataset of type NSEC. * - * Requires: + * This functions checks for the following in the given rdataset: + * \li All NSEC records have both NSEC and RRSIG present + * \li All NSEC entries are under the `name` + * + * \par Requires: * \li rdataset to be a NSEC rdataset. + * \li `name` is a valid dns_name_t + * + * \retval true if all the checks pass + * \retval false otherwise */ diff --git a/lib/dns/nsec.c b/lib/dns/nsec.c index eda153cbe25..26783658eb2 100644 --- a/lib/dns/nsec.c +++ b/lib/dns/nsec.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -482,8 +483,10 @@ dns_nsec_noexistnodata(dns_rdatatype_t type, const dns_name_t *name, } bool -dns_nsec_requiredtypespresent(dns_rdataset_t *nsecset) { +dns_nsec_is_legal(dns_rdataset_t *nsecset, const dns_name_t *name) { dns_rdataset_t rdataset = DNS_RDATASET_INIT; + dns_rdata_nsec_t nsec; + isc_result_t result; bool found = false; REQUIRE(DNS_RDATASET_VALID(nsecset)); @@ -494,12 +497,19 @@ dns_nsec_requiredtypespresent(dns_rdataset_t *nsecset) { DNS_RDATASET_FOREACH(&rdataset) { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(&rdataset, &rdata); - if (!dns_nsec_typepresent(&rdata, dns_rdatatype_nsec) || - !dns_nsec_typepresent(&rdata, dns_rdatatype_rrsig)) + + /* must never fail */ + result = dns_rdata_tostruct(&rdata, &nsec, NULL); + INSIST(result == ISC_R_SUCCESS); + + if (!dns_name_issubdomain(&nsec.next, name) || + !dns_nsec_typepresent(&rdata, dns_rdatatype_rrsig) || + !dns_nsec_typepresent(&rdata, dns_rdatatype_nsec)) { dns_rdataset_disassociate(&rdataset); return false; } + found = true; } dns_rdataset_disassociate(&rdataset); diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index f27a8a63c1e..b3bc301017f 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -76,11 +76,12 @@ #include #include #include +#include #include +#include #include #include -#include "dns/view.h" #include "probes-dns.h" #ifdef WANT_QUERYTRACE @@ -5739,10 +5740,42 @@ cache_rrset(fetchctx_t *fctx, isc_stdtime_t now, dns_name_t *name, return result; } +static bool +get_and_check_signer_name(dns_name_t *signer, dns_rdataset_t *sigrdataset) { + dns_rdata_rrsig_t rrsig; + isc_result_t result; + dns_rdata_t rdata; + + if (dns_rdataset_first(sigrdataset) != ISC_R_SUCCESS) { + return false; + } + + rdata = (dns_rdata_t)DNS_RDATA_INIT; + dns_rdataset_current(sigrdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &rrsig, NULL); + INSIST(result == ISC_R_SUCCESS); + dns_name_copy(&rrsig.signer, signer); + + while (dns_rdataset_next(sigrdataset) == ISC_R_SUCCESS) { + rdata = (dns_rdata_t)DNS_RDATA_INIT; + dns_rdataset_current(sigrdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &rrsig, NULL); + INSIST(result == ISC_R_SUCCESS); + + if (!dns_name_equal(signer, &rrsig.signer)) { + return false; + } + } + + return true; +} + static void fctx_cacheauthority(fetchctx_t *fctx, dns_message_t *message, isc_stdtime_t now) { + dns_fixedname_t fsigner; isc_result_t result; + dns_name_t *signer; /* * Cache any SOA/NS/NSEC records that happened to be validated. @@ -5765,10 +5798,20 @@ fctx_cacheauthority(fetchctx_t *fctx, dns_message_t *message, } /* - * Don't cache NSEC if missing NSEC or RRSIG types. + * Don't cache if all the RRSIGs don't have the same + * signer. + */ + signer = dns_fixedname_initname(&fsigner); + if (!get_and_check_signer_name(signer, sigrdataset)) { + continue; + } + + /* + * Don't cache NSEC if missing NSEC or RRSIG + * types. */ if (rdataset->type == dns_rdatatype_nsec && - !dns_nsec_requiredtypespresent(rdataset)) + !dns_nsec_is_legal(rdataset, signer)) { continue; } diff --git a/lib/ns/query.c b/lib/ns/query.c index 4f6410b2bed..519d164f1f8 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -9486,10 +9486,10 @@ query_coveringnsec(query_ctx_t *qctx) { } /* - * If NSEC or RRSIG are missing from the type map - * reject the NSEC RRset. + * Check that the NSEC entry is legal. + * (NSEC + RRSIG present and the entry isn't out-of-zone) */ - if (!dns_nsec_requiredtypespresent(qctx->rdataset)) { + if (!dns_nsec_is_legal(qctx->rdataset, signer)) { goto cleanup; }