]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Enforce NSEC3 record consistency
authorMark Andrews <marka@isc.org>
Wed, 18 Feb 2026 01:30:22 +0000 (12:30 +1100)
committerOndřej Surý <ondrej@sury.org>
Tue, 24 Feb 2026 13:57:22 +0000 (14:57 +0100)
NSEC3 hashes are required to fit within a single DNS label.  Since there
are 5 bits per label byte without pad characters, the maximum hash size
is floor(63*5/8) (39 bytes).

This patch enforces this maximum length for unknown algorithms, while
strictly enforcing the exact expected digest length for known algorithms
like SHA-1.

bin/tests/system/checkzone/zones/crashzone.db
lib/dns/include/dns/nsec3.h
lib/dns/rdata/generic/nsec3_50.c
lib/isc/include/isc/iterated_hash.h
tests/bench/iterated_hash.c

index 2a62e2a09d7bb6e10bef84d7e8313f7016863fc6..169cbe331e9e56d727bec1cade4ac8ceafb7bdec 100644 (file)
@@ -47,7 +47,6 @@ FQ7RBG86KRMACA1NAAKP2KQRQALBA0C7.dyn.example.net.  7200       RRSIG   NSEC3 7 4 7200 201
                                        577WZnTQemStx+diON9rEGXAGnU7C0KLjrFL
                                        VyhocnBnNtxJS8eRMSWvb9XuYCMNhYKOurtt
                                        Ar4qh4VW1+unmA== )
-I7A7A184GGMI35K1E3IR650LKO7NOB5R.dyn.example.net. 7200 IN NSEC3        1 0 10 76931F IMQ912BREQP1POLAH3RMONG;UED541AS A RRSIG
 IMQ912BREQP1POLAH3RMONG3UED541AS.dyn.example.net. 7200 IN NSEC3        1 0 10 76931F S3USV4M1HLVJ8F88EDSG8N9PVQRQ20N7 A RRSIG
                        7200    RRSIG   NSEC3 7 4 7200 20100227180048 (
                                        20100221180048 30323 dyn.example.net.
index c1a7bd8c7fcc7d9a28763b71154899339761c3ab..e5dddf78a9bad88d068e376960024c17c25d87ea 100644 (file)
 #define DNS_NSEC3_SALTSIZE     255
 #define DNS_NSEC3_MAXITERATIONS 50U
 
+/*
+ * The maximum hash that can be encoded in a single label using
+ * base32hexnp.  floor(63*5/8)
+ */
+#define NSEC3_MAX_HASH_LENGTH 39
+
 /*
  * hash = 1, flags =1, iterations = 2, salt length = 1, salt = 255 (max)
  * hash length = 1, hash = 255 (max), bitmap = 8192 + 512 (max)
index 9f4d4e5a998edfb4e6d116a4eb79a696ffc6040a..eb06507d8d9dfaf3c951de325ec3272256dfd38e 100644 (file)
@@ -35,6 +35,8 @@
 #include <isc/base32.h>
 #include <isc/iterated_hash.h>
 
+#include <dns/nsec3.h>
+
 #define RRTYPE_NSEC3_ATTRIBUTES DNS_RDATATYPEATTR_DNSSEC
 
 static isc_result_t
@@ -96,8 +98,17 @@ fromtext_nsec3(ARGS_FROMTEXT) {
                                      false));
        isc_buffer_init(&b, buf, sizeof(buf));
        RETTOK(isc_base32hexnp_decodestring(DNS_AS_STR(token), &b));
-       if (isc_buffer_usedlength(&b) > 0xffU) {
-               RETTOK(ISC_R_RANGE);
+       switch (hashalg) {
+       case dns_hash_sha1:
+               if (isc_buffer_usedlength(&b) != ISC_SHA1_DIGESTLENGTH) {
+                       RETTOK(ISC_R_RANGE);
+               }
+               break;
+       default:
+               if (isc_buffer_usedlength(&b) > NSEC3_MAX_HASH_LENGTH) {
+                       RETTOK(ISC_R_RANGE);
+               }
+               break;
        }
        RETERR(uint8_tobuffer(isc_buffer_usedlength(&b), target));
        RETERR(mem_tobuffer(target, &buf, isc_buffer_usedlength(&b)));
@@ -184,7 +195,7 @@ totext_nsec3(ARGS_TOTEXT) {
 static isc_result_t
 fromwire_nsec3(ARGS_FROMWIRE) {
        isc_region_t sr, rr;
-       unsigned int saltlen, hashlen;
+       unsigned int hash, saltlen, hashlen;
 
        REQUIRE(type == dns_rdatatype_nsec3);
 
@@ -199,6 +210,7 @@ fromwire_nsec3(ARGS_FROMWIRE) {
        if (sr.length < 5U) {
                RETERR(DNS_R_FORMERR);
        }
+       hash = sr.base[0];
        saltlen = sr.base[4];
        isc_region_consume(&sr, 5);
 
@@ -213,8 +225,19 @@ fromwire_nsec3(ARGS_FROMWIRE) {
        hashlen = sr.base[0];
        isc_region_consume(&sr, 1);
 
-       if (hashlen < 1 || sr.length < hashlen) {
-               RETERR(DNS_R_FORMERR);
+       switch (hash) {
+       case dns_hash_sha1:
+               if (hashlen != ISC_SHA1_DIGESTLENGTH || sr.length < hashlen) {
+                       RETERR(DNS_R_FORMERR);
+               }
+               break;
+       default:
+               if (hashlen < 1 || hashlen > NSEC3_MAX_HASH_LENGTH ||
+                   sr.length < hashlen)
+               {
+                       RETERR(DNS_R_FORMERR);
+               }
+               break;
        }
        isc_region_consume(&sr, hashlen);
 
index c96753b632f3137ddfd7ad1870b6e54e7cff4298..100294d8c0855cc042cc79a8eac54f34d10370bd 100644 (file)
 
 #pragma once
 
-/*
- * The maximal hash length that can be encoded in a name
- * using base32hex.  floor(255/8)*5
- */
-#define NSEC3_MAX_HASH_LENGTH 155
-
-/*
- * The maximum has that can be encoded in a single label using
- * base32hex.  floor(63/8)*5
- */
-#define NSEC3_MAX_LABEL_HASH 35
-
 int
 isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
                  const int iterations, const unsigned char *salt,
index 1cc5755e8fb1b48448b94dc98430ffd6a26beb00..145cc0a7e33e8382ae6d0649492ee02550cf1e99 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <dns/lib.h>
 #include <dns/name.h>
+#include <dns/nsec3.h>
 
 static void
 time_it(const int count, const int iterations, const unsigned char *salt,