]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
change dns_nsec_requiredtypespresent to dns_nsec_is_legal
authorAydın Mercan <aydin@isc.org>
Tue, 12 May 2026 11:54:09 +0000 (14:54 +0300)
committerMichał Kępień <michal@isc.org>
Fri, 10 Jul 2026 07:26:46 +0000 (09:26 +0200)
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.

lib/dns/include/dns/nsec.h
lib/dns/nsec.c
lib/dns/resolver.c
lib/ns/query.c

index ebb1c65a43853153f89ca804d9aa95d6bebcf221..be387c6e3cddaea3d81ed7b747d38383f516aa16 100644 (file)
@@ -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
  */
index eda153cbe25d8607037c402c12f4de14da62849f..26783658eb247120166140742f0ef25b39493767 100644 (file)
@@ -21,6 +21,7 @@
 #include <isc/util.h>
 
 #include <dns/db.h>
+#include <dns/name.h>
 #include <dns/nsec.h>
 #include <dns/rdata.h>
 #include <dns/rdatalist.h>
@@ -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);
index f27a8a63c1ec0c501ccd5935007e965d7c75b231..b3bc301017f23fdf4c2168b0e7c28a0d160be687 100644 (file)
 #include <dns/rootns.h>
 #include <dns/stats.h>
 #include <dns/tsig.h>
+#include <dns/types.h>
 #include <dns/validator.h>
+#include <dns/view.h>
 #include <dns/zone.h>
 #include <dns/zoneproperties.h>
 
-#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;
                        }
index 4f6410b2bed585e2fd920fccd2d6b6fcc424743b..519d164f1f8651d5238a91c62f88f8728476b8dc 100644 (file)
@@ -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;
        }