b311829d1b
6400. [security] Excessively large rdatasets can slow down database query processing, so a limit has been placed on the number of records that can be stored per rdataset in a cache or zone database. This is configured with the new "max-records-per-type" option, and defaults to 100. (CVE-2024-1737) [GL #497] [GL #3405] 6401. [security] An excessively large number of rrtypes per owner can slow down database query processing, so a limit has been placed on the number of rrtypes that can be stored per owner (node) in a cache or zone database. This is configured with the new "max-rrtypes-per-name" option, and defaults to 100. (CVE-2024-1737) [GL #3403] [GL #4548] Does not change db methods like 9.18 fix. It makes limits set at build time and fixed numbers, but does not need adjusting db interface to set new limits. Resolves: RHEL-49877
318 lines
11 KiB
Diff
318 lines
11 KiB
Diff
From 71df06e2bf3da31c5d542fb33dbda67b21537322 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org>
|
|
Date: Fri, 1 Mar 2024 08:26:07 +0100
|
|
Subject: [PATCH] [9.11][CVE-2024-1737] Add a limit to the number of RRs in
|
|
RRSets
|
|
|
|
Add a limit to the number of RRs in RRSets
|
|
|
|
Previously, the number of RRs in the RRSets were internally unlimited.
|
|
As the data structure that holds the RRs is just a linked list, and
|
|
there are places where we just walk through all of the RRs, adding an
|
|
RRSet with huge number of RRs inside would slow down processing of said
|
|
RRSets.
|
|
|
|
The fix for end-of-life branches make the limit compile-time only for
|
|
simplicity and the limit can be changed at the compile time by adding
|
|
following define to CFLAGS:
|
|
|
|
-DDNS_RDATASET_MAX_RECORDS=<limit>
|
|
|
|
(cherry picked from commit c5c4d00c38530390c9e1ae4c98b65fbbadfe9e5e)
|
|
(cherry picked from commit 7f705778af729ada7fec36ac4b456c73329bd996)
|
|
(cherry picked from commit b9b5485b22c364fb88c27aa04bad4c8f616da3fa)
|
|
|
|
Add a limit to the number of RR types for single name
|
|
|
|
Previously, the number of RR types for a single owner name was limited
|
|
only by the maximum number of the types (64k). As the data structure
|
|
that holds the RR types for the database node is just a linked list, and
|
|
there are places where we just walk through the whole list (again and
|
|
again), adding a large number of RR types for a single owner named with
|
|
would slow down processing of such name (database node).
|
|
|
|
Add a hard-coded limit (100) to cap the number of the RR types for a single
|
|
owner. The limit can be changed at the compile time by adding following
|
|
define to CFLAGS:
|
|
|
|
-DDNS_RBTDB_MAX_RTYPES=<limit>
|
|
|
|
(cherry picked from commit 538b843d84f49ba5125ff545e3d0cf1c8434a8f2)
|
|
(cherry picked from commit 3f10d6eff035702796ba82cd28b9f7cf9836e743)
|
|
|
|
Optimize the slabheader placement for certain RRTypes
|
|
|
|
Mark the infrastructure RRTypes as "priority" types and place them at
|
|
the beginning of the rdataslab header data graph. The non-priority
|
|
types either go right after the priority types (if any).
|
|
|
|
(cherry picked from commit 3ac482be7fd058d284e89873021339579fad0615)
|
|
(cherry picked from commit 23a4652346fb2877d6246b1eebaa967969dbde16)
|
|
|
|
[9.11][CVE-2024-1737 (part 2)] Be smarter about refusing to add many RR types to the database
|
|
|
|
Expand the list of the priority types
|
|
|
|
Add HTTPS, SVCB, SRV, PTR, NAPTR, DNSKEY and TXT records to the list of
|
|
the priority types that are put at the beginning of the slabheader list
|
|
for faster access and to avoid eviction when there are more types than
|
|
the max-types-per-name limit.
|
|
|
|
(cherry picked from commit b27c6bcce894786a8e082eafd59eccbf6f2731cb)
|
|
(cherry picked from commit 3e0a67e4bdb253dae3a03a45c1aa117239a3313d)
|
|
|
|
Be smarter about refusing to add many RR types to the database
|
|
|
|
Instead of outright refusing to add new RR types to the cache, be a bit
|
|
smarter:
|
|
|
|
1. If the new header type is in our priority list, we always add either
|
|
positive or negative entry at the beginning of the list.
|
|
|
|
2. If the new header type is negative entry, and we are over the limit,
|
|
we mark it as ancient immediately, so it gets evicted from the cache
|
|
as soon as possible.
|
|
|
|
3. Otherwise add the new header after the priority headers (or at the
|
|
head of the list).
|
|
|
|
4. If we are over the limit, evict the last entry on the normal header
|
|
list.
|
|
|
|
(cherry picked from commit 57cd34441a1b4ecc9874a4a106c2c95b8d7a3120)
|
|
(cherry picked from commit e4d7ce686bb38428eddc7e33b40057d68eca9a6e)
|
|
---
|
|
configure | 2 +-
|
|
configure.ac | 2 +-
|
|
lib/dns/rbtdb.c | 114 +++++++++++++++++++++++++++++++++++++++++++-
|
|
lib/dns/rdataslab.c | 12 +++++
|
|
4 files changed, 126 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/configure b/configure
|
|
index e060e9d..6421c9b 100755
|
|
--- a/configure
|
|
+++ b/configure
|
|
@@ -12189,7 +12189,7 @@ fi
|
|
XTARGETS=
|
|
case "$enable_developer" in
|
|
yes)
|
|
- STD_CDEFINES="$STD_CDEFINES -DISC_LIST_CHECKINIT=1"
|
|
+ STD_CDEFINES="$STD_CDEFINES -DISC_LIST_CHECKINIT=1 -DDNS_RDATASET_MAX_RECORDS=5000 -DDNS_RBTDB_MAX_RTYPES=5000"
|
|
test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes
|
|
test "${enable_querytrace+set}" = set || enable_querytrace=yes
|
|
test "${enable_filter_aaaa+set}" = set || enable_filter_aaaa=yes
|
|
diff --git a/configure.ac b/configure.ac
|
|
index 83cad4a..1c35ce9 100644
|
|
--- a/configure.ac
|
|
+++ b/configure.ac
|
|
@@ -100,7 +100,7 @@ AC_ARG_ENABLE(developer,
|
|
XTARGETS=
|
|
case "$enable_developer" in
|
|
yes)
|
|
- STD_CDEFINES="$STD_CDEFINES -DISC_LIST_CHECKINIT=1"
|
|
+ STD_CDEFINES="$STD_CDEFINES -DISC_LIST_CHECKINIT=1 -DDNS_RDATASET_MAX_RECORDS=5000 -DDNS_RBTDB_MAX_RTYPES=5000"
|
|
test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes
|
|
test "${enable_querytrace+set}" = set || enable_querytrace=yes
|
|
test "${enable_filter_aaaa+set}" = set || enable_filter_aaaa=yes
|
|
diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
|
|
index ee59c1b..a2b2df7 100644
|
|
--- a/lib/dns/rbtdb.c
|
|
+++ b/lib/dns/rbtdb.c
|
|
@@ -1183,6 +1183,44 @@ set_ttl(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, dns_ttl_t newttl) {
|
|
isc_heap_decreased(heap, header->heap_index);
|
|
}
|
|
|
|
+static bool
|
|
+prio_type(rbtdb_rdatatype_t type) {
|
|
+ switch (type) {
|
|
+ case dns_rdatatype_soa:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_soa):
|
|
+ case dns_rdatatype_a:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_a):
|
|
+ case dns_rdatatype_mx:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_mx):
|
|
+ case dns_rdatatype_aaaa:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_aaaa):
|
|
+ case dns_rdatatype_nsec:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec):
|
|
+ case dns_rdatatype_nsec3:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec3):
|
|
+ case dns_rdatatype_ns:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ns):
|
|
+ case dns_rdatatype_ds:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ds):
|
|
+ case dns_rdatatype_cname:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_cname):
|
|
+ case dns_rdatatype_dname:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_dname):
|
|
+ case dns_rdatatype_dnskey:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_dnskey):
|
|
+ case dns_rdatatype_srv:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_srv):
|
|
+ case dns_rdatatype_txt:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_txt):
|
|
+ case dns_rdatatype_ptr:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ptr):
|
|
+ case dns_rdatatype_naptr:
|
|
+ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_naptr):
|
|
+ return (true);
|
|
+ }
|
|
+ return (false);
|
|
+}
|
|
+
|
|
/*%
|
|
* These functions allow the heap code to rank the priority of each
|
|
* element. It returns true if v1 happens "sooner" than v2.
|
|
@@ -6278,6 +6316,30 @@ update_recordsandbytes(bool add, rbtdb_version_t *rbtversion,
|
|
RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
|
|
}
|
|
|
|
+#ifndef DNS_RBTDB_MAX_RTYPES
|
|
+#define DNS_RBTDB_MAX_RTYPES 100
|
|
+#endif /* DNS_RBTDB_MAX_RTYPES */
|
|
+
|
|
+static bool
|
|
+overmaxtype(dns_rbtdb_t *rbtdb, uint32_t ntypes) {
|
|
+ UNUSED(rbtdb);
|
|
+
|
|
+ if (DNS_RBTDB_MAX_RTYPES == 0) {
|
|
+ return (false);
|
|
+ }
|
|
+
|
|
+ return (ntypes >= DNS_RBTDB_MAX_RTYPES);
|
|
+}
|
|
+
|
|
+static bool
|
|
+prio_header(rdatasetheader_t *header) {
|
|
+ if (NEGATIVE(header) && prio_type(RBTDB_RDATATYPE_EXT(header->type))) {
|
|
+ return (true);
|
|
+ }
|
|
+
|
|
+ return (prio_type(header->type));
|
|
+}
|
|
+
|
|
/*
|
|
* write lock on rbtnode must be held.
|
|
*/
|
|
@@ -6288,6 +6350,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
|
|
{
|
|
rbtdb_changed_t *changed = NULL;
|
|
rdatasetheader_t *topheader, *topheader_prev, *header, *sigheader;
|
|
+ rdatasetheader_t *prioheader = NULL, *expireheader = NULL;
|
|
unsigned char *merged;
|
|
isc_result_t result;
|
|
bool header_nx;
|
|
@@ -6297,6 +6360,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
|
|
rbtdb_rdatatype_t negtype, sigtype;
|
|
dns_trust_t trust;
|
|
int idx;
|
|
+ uint32_t ntypes = 0;
|
|
|
|
/*
|
|
* Add an rdatasetheader_t to a node.
|
|
@@ -6429,6 +6493,15 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
|
|
for (topheader = rbtnode->data;
|
|
topheader != NULL;
|
|
topheader = topheader->next) {
|
|
+ if (IS_CACHE(rbtdb) && ACTIVE(topheader, now)) {
|
|
+ ++ntypes;
|
|
+ expireheader = topheader;
|
|
+ } else if (!IS_CACHE(rbtdb)) {
|
|
+ ++ntypes;
|
|
+ }
|
|
+ if (prio_header(topheader)) {
|
|
+ prioheader = topheader;
|
|
+ }
|
|
if (topheader->type == newheader->type ||
|
|
topheader->type == negtype)
|
|
break;
|
|
@@ -6792,9 +6865,46 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
|
|
/*
|
|
* No rdatasets of the given type exist at the node.
|
|
*/
|
|
- newheader->next = rbtnode->data;
|
|
+ if (!IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) {
|
|
+ free_rdataset(rbtdb, rbtdb->common.mctx,
|
|
+ newheader);
|
|
+ return (ISC_R_QUOTA);
|
|
+ }
|
|
+
|
|
newheader->down = NULL;
|
|
- rbtnode->data = newheader;
|
|
+
|
|
+ if (prio_header(newheader)) {
|
|
+ /* This is a priority type, prepend it */
|
|
+ newheader->next = rbtnode->data;
|
|
+ rbtnode->data = newheader;
|
|
+ } else if (prioheader != NULL) {
|
|
+ /* Append after the priority headers */
|
|
+ newheader->next = prioheader->next;
|
|
+ prioheader->next = newheader;
|
|
+ } else {
|
|
+ /* There were no priority headers */
|
|
+ newheader->next = rbtnode->data;
|
|
+ rbtnode->data = newheader;
|
|
+ }
|
|
+
|
|
+ if (IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) {
|
|
+ if (expireheader == NULL) {
|
|
+ expireheader = newheader;
|
|
+ }
|
|
+ if (NEGATIVE(newheader) &&
|
|
+ !prio_header(newheader))
|
|
+ {
|
|
+ /*
|
|
+ * Add the new non-priority negative
|
|
+ * header to the database only
|
|
+ * temporarily.
|
|
+ */
|
|
+ expireheader = newheader;
|
|
+ }
|
|
+
|
|
+ set_ttl(rbtdb, expireheader, 0);
|
|
+ mark_header_ancient(rbtdb, expireheader);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c
|
|
index b0f77b1..347b7d2 100644
|
|
--- a/lib/dns/rdataslab.c
|
|
+++ b/lib/dns/rdataslab.c
|
|
@@ -115,6 +115,10 @@ fillin_offsets(unsigned char *offsetbase, unsigned int *offsettable,
|
|
}
|
|
#endif
|
|
|
|
+#ifndef DNS_RDATASET_MAX_RECORDS
|
|
+#define DNS_RDATASET_MAX_RECORDS 100
|
|
+#endif /* DNS_RDATASET_MAX_RECORDS */
|
|
+
|
|
isc_result_t
|
|
dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx,
|
|
isc_region_t *region, unsigned int reservelen)
|
|
@@ -161,6 +165,10 @@ dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx,
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
+ if (nitems > DNS_RDATASET_MAX_RECORDS) {
|
|
+ return (DNS_R_TOOMANYRECORDS);
|
|
+ }
|
|
+
|
|
if (nitems > 0xffff)
|
|
return (ISC_R_NOSPACE);
|
|
|
|
@@ -654,6 +662,10 @@ dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab,
|
|
#endif
|
|
INSIST(ocount > 0 && ncount > 0);
|
|
|
|
+ if (ocount + ncount > DNS_RDATASET_MAX_RECORDS) {
|
|
+ return (DNS_R_TOOMANYRECORDS);
|
|
+ }
|
|
+
|
|
#if DNS_RDATASET_FIXED
|
|
oncount = ncount;
|
|
#endif
|
|
--
|
|
2.45.2
|
|
|