From 71df06e2bf3da31c5d542fb33dbda67b21537322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= 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= (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= (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