File bind-9.16-CVE-2024-11187.patch of Package bind.37284
commit 0ae8ee3b9c6e7d60eaacd957929aec7a508fb777
Author: Nicki Křížek <nicki@isc.org>
Date: Mon Jan 20 16:17:08 2025 +0000
[9.16] [CVE-2024-11187] sec: usr: Limit the additional processing for large RDATA sets
When answering queries, don't add data to the additional section if the answer has more than 13 names in the RDATA. This limits the number of lookups into the database(s) during a single client query, reducing query processing load.
Backport of MR !750
See isc-projects/bind9#5034
Merge branch '5034-security-limit-additional-9.16' into 'bind-9.16-release'
See merge request isc-private/bind9!760
diff --git a/bin/tests/system/additional/tests.sh b/bin/tests/system/additional/tests.sh
index 3701790678..bf7b082f9a 100644
--- a/bin/tests/system/additional/tests.sh
+++ b/bin/tests/system/additional/tests.sh
@@ -278,7 +278,7 @@ n=$(expr $n + 1)
echo_i "testing with 'minimal-any no;' ($n)"
ret=0
$DIG $DIGOPTS -t ANY www.rt.example @10.53.0.1 >dig.out.$n || ret=1
-grep "ANSWER: 3, AUTHORITY: 2, ADDITIONAL: 2" dig.out.$n >/dev/null || ret=1
+grep "ANSWER: 3, AUTHORITY: 2, ADDITIONAL: 1" dig.out.$n >/dev/null || ret=1
if [ $ret -eq 1 ]; then
echo_i "failed"
status=$((status + 1))
diff --git a/bin/tests/system/resolver/ns4/named.noaa b/bin/tests/system/resolver/ns4/named.noaa
deleted file mode 100644
index be78cc2c94..0000000000
--- a/bin/tests/system/resolver/ns4/named.noaa
+++ /dev/null
@@ -1,12 +0,0 @@
-Copyright (C) Internet Systems Consortium, Inc. ("ISC")
-
-SPDX-License-Identifier: MPL-2.0
-
-This Source Code Form is subject to the terms of the Mozilla Public
-License, v. 2.0. If a copy of the MPL was not distributed with this
-file, you can obtain one at https://mozilla.org/MPL/2.0/.
-
-See the COPYRIGHT file distributed with this work for additional
-information regarding copyright ownership.
-
-Add -T noaa.
diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh
index 2e089fa371..761a728eba 100755
--- a/bin/tests/system/resolver/tests.sh
+++ b/bin/tests/system/resolver/tests.sh
@@ -309,6 +309,10 @@ done
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
+stop_server ns4
+touch ns4/named.noaa
+start_server --noclean --restart --port ${PORT} ns4 || ret=1
+
n=$((n + 1))
echo_i "RT21594 regression test check setup ($n)"
ret=0
@@ -345,6 +349,10 @@ grep "status: NXDOMAIN" dig.ns5.out.${n} >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
+stop_server ns4
+rm ns4/named.noaa
+start_server --noclean --restart --port ${PORT} ns4 || ret=1
+
n=$((n + 1))
echo_i "check that replacement of additional data by a negative cache no data entry clears the additional RRSIGs ($n)"
ret=0
diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h
index de3f638f89..b7f0f8f055 100644
--- a/lib/dns/include/dns/rdataset.h
+++ b/lib/dns/include/dns/rdataset.h
@@ -55,6 +55,8 @@
#include <dns/rdatastruct.h>
#include <dns/types.h>
+#define DNS_RDATASET_MAXADDITIONAL 13
+
ISC_LANG_BEGINDECLS
typedef enum {
@@ -446,7 +448,8 @@ dns_rdataset_towirepartial(dns_rdataset_t *rdataset,
isc_result_t
dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
- dns_additionaldatafunc_t add, void *arg);
+ dns_additionaldatafunc_t add, void *arg,
+ size_t limit);
/*%<
* For each rdata in rdataset, call 'add' for each name and type in the
* rdata which is subject to additional section processing.
@@ -465,10 +468,15 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
*\li If a call to dns_rdata_additionaldata() is not successful, the
* result returned will be the result of dns_rdataset_additionaldata().
*
+ *\li If 'limit' is non-zero and the number of the rdatasets is larger
+ * than 'limit', no additional data will be processed.
+ *
* Returns:
*
*\li #ISC_R_SUCCESS
*
+ *\li #DNS_R_TOOMANYRECORDS in case rdataset count is larger than 'limit'
+ *
*\li Any error that dns_rdata_additionaldata() can return.
*/
diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
index 5179f942fb..24de589137 100644
--- a/lib/dns/rbtdb.c
+++ b/lib/dns/rbtdb.c
@@ -10632,7 +10632,7 @@ no_glue:
maybe_rehash_gluetable(rbtversion);
idx = hash_32(hash, rbtversion->glue_table_bits);
- (void)dns_rdataset_additionaldata(rdataset, glue_nsdname_cb, &ctx);
+ (void)dns_rdataset_additionaldata(rdataset, glue_nsdname_cb, &ctx, 0);
cur = isc_mem_get(rbtdb->common.mctx, sizeof(*cur));
diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c
index 221d7f84a7..122b197612 100644
--- a/lib/dns/rdataset.c
+++ b/lib/dns/rdataset.c
@@ -29,6 +29,7 @@
#include <dns/ncache.h>
#include <dns/rdata.h>
#include <dns/rdataset.h>
+#include <dns/result.h>
static const char *trustnames[] = {
"none", "pending-additional",
@@ -578,7 +579,8 @@ dns_rdataset_towire(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
isc_result_t
dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
- dns_additionaldatafunc_t add, void *arg) {
+ dns_additionaldatafunc_t add, void *arg,
+ size_t limit) {
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_result_t result;
@@ -590,6 +592,10 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
REQUIRE(DNS_RDATASET_VALID(rdataset));
REQUIRE((rdataset->attributes & DNS_RDATASETATTR_QUESTION) == 0);
+ if (limit != 0 && dns_rdataset_count(rdataset) > limit) {
+ return (DNS_R_TOOMANYRECORDS);
+ }
+
result = dns_rdataset_first(rdataset);
if (result != ISC_R_SUCCESS) {
return (result);
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
index 0952624689..dcd2208af0 100644
--- a/lib/dns/resolver.c
+++ b/lib/dns/resolver.c
@@ -9014,8 +9014,8 @@ rctx_answer_any(respctx_t *rctx) {
rdataset->attributes |= DNS_RDATASETATTR_CACHE;
rdataset->trust = rctx->trust;
- (void)dns_rdataset_additionaldata(rdataset, check_related,
- rctx);
+ (void)dns_rdataset_additionaldata(rdataset, check_related, rctx,
+ 0);
}
return (ISC_R_SUCCESS);
@@ -9062,7 +9062,8 @@ rctx_answer_match(respctx_t *rctx) {
rctx->ardataset->attributes |= DNS_RDATASETATTR_ANSWER;
rctx->ardataset->attributes |= DNS_RDATASETATTR_CACHE;
rctx->ardataset->trust = rctx->trust;
- (void)dns_rdataset_additionaldata(rctx->ardataset, check_related, rctx);
+ (void)dns_rdataset_additionaldata(rctx->ardataset, check_related, rctx,
+ 0);
for (sigrdataset = ISC_LIST_HEAD(rctx->aname->list);
sigrdataset != NULL;
@@ -9268,7 +9269,8 @@ rctx_authority_positive(respctx_t *rctx) {
* to this rdataset.
*/
(void)dns_rdataset_additionaldata(
- rdataset, check_related, rctx);
+ rdataset, check_related, rctx,
+ 0);
done = true;
}
}
@@ -9775,8 +9777,11 @@ rctx_referral(respctx_t *rctx) {
*/
INSIST(rctx->ns_rdataset != NULL);
FCTX_ATTR_SET(fctx, FCTX_ATTR_GLUING);
+ /*
+ * Mark the glue records in the additional section to be cached.
+ */
(void)dns_rdataset_additionaldata(rctx->ns_rdataset, check_related,
- rctx);
+ rctx, 0);
#if CHECK_FOR_GLUE_IN_ANSWER
/*
* Look in the answer section for "glue" that is incorrectly
@@ -9789,7 +9794,7 @@ rctx_referral(respctx_t *rctx) {
(fctx->type == dns_rdatatype_aaaa || fctx->type == dns_rdatatype_a))
{
(void)dns_rdataset_additionaldata(rctx->ns_rdataset,
- check_answer, fctx);
+ check_answer, fctx, 0);
}
#endif /* if CHECK_FOR_GLUE_IN_ANSWER */
FCTX_ATTR_CLR(fctx, FCTX_ATTR_GLUING);
@@ -9899,7 +9904,7 @@ again:
if (CHASE(rdataset)) {
rdataset->attributes &= ~DNS_RDATASETATTR_CHASE;
(void)dns_rdataset_additionaldata(
- rdataset, check_related, rctx);
+ rdataset, check_related, rctx, 0);
rescan = true;
}
}
diff --git a/lib/ns/query.c b/lib/ns/query.c
index ec9bf5b769..b1d8387a8b 100644
--- a/lib/ns/query.c
+++ b/lib/ns/query.c
@@ -2048,7 +2048,8 @@ addname:
*/
if (trdataset != NULL && dns_rdatatype_followadditional(type)) {
eresult = dns_rdataset_additionaldata(
- trdataset, query_additional_cb, qctx);
+ trdataset, query_additional_cb, qctx,
+ DNS_RDATASET_MAXADDITIONAL);
}
cleanup:
@@ -2139,7 +2140,8 @@ regular:
* Add other additional data if needed.
* We don't care if dns_rdataset_additionaldata() fails.
*/
- (void)dns_rdataset_additionaldata(rdataset, query_additional_cb, qctx);
+ (void)dns_rdataset_additionaldata(rdataset, query_additional_cb, qctx,
+ DNS_RDATASET_MAXADDITIONAL);
CTRACE(ISC_LOG_DEBUG(3), "query_additional: done");
}
@@ -2165,7 +2167,8 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep,
* To the current response for 'client', add the answer RRset
* '*rdatasetp' and an optional signature set '*sigrdatasetp', with
* owner name '*namep', to section 'section', unless they are
- * already there. Also add any pertinent additional data.
+ * already there. Also add any pertinent additional data, unless
+ * the query was for type ANY.
*
* If 'dbuf' is not NULL, then '*namep' is the name whose data is
* stored in 'dbuf'. In this case, query_addrrset() guarantees that
@@ -2220,7 +2223,9 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep,
*/
query_addtoname(mname, rdataset);
query_setorder(qctx, mname, rdataset);
- query_additional(qctx, rdataset);
+ if (qctx->qtype != dns_rdatatype_any) {
+ query_additional(qctx, rdataset);
+ }
/*
* Note: we only add SIGs if we've added the type they cover, so