[9.18] [CVE-2026-5946] sec: usr: Disable recursion, UPDATE, and NOTIFY for non-IN views
Recursion, dynamic updates (UPDATE), and zone change notifications (NOTIFY) are now disabled for views with a class other than IN (such as CHAOS or HESIOD); authoritative service for non-IN zones (e.g. version.bind in class CHAOS) continues to work as before. Servers configured with recursion yes in a non-IN view will log a warning at startup, and named-checkconf flags the same condition. UPDATE and NOTIFY messages that specify the meta-classes ANY or NONE in the question section are now rejected with FORMERR. This addresses a set of closely related security issues collectively identified as CVE-2026-5946. ISC would like to thank Mcsky23 for bringing these issues to our attention. Resolves-Vulnerability: CVE-2026-5946 Resolves: RHEL-177671
This commit is contained in:
parent
84ee08768a
commit
0d395adab1
668
bind-9.18-CVE-2026-5946.patch
Normal file
668
bind-9.18-CVE-2026-5946.patch
Normal file
@ -0,0 +1,668 @@
|
||||
From 549972c7aab076fd4002aaa95eaebc81609c902d Mon Sep 17 00:00:00 2001
|
||||
From: Evan Hunt <each@isc.org>
|
||||
Date: Tue, 3 Mar 2026 14:00:38 -0800
|
||||
Subject: [PATCH] Disable recursion for non-IN classes
|
||||
|
||||
Force recursion off, and set allow-recursion/allow-recursion-on ACLs
|
||||
to none, for views with a class other than IN. Log a configuration
|
||||
warning if recursion is explicitly enabled for a non-IN view.
|
||||
|
||||
This addresses YWH-PGM40640-74 and YWH-PGM40640-75 by preventing any
|
||||
attempt at recursive processing in a class-CHAOS view, ensuring that
|
||||
server addresses used for recursive queries and received in recursive
|
||||
responses are of the expected format.
|
||||
|
||||
Fixes: isc-projects/bind9#5780
|
||||
Fixes: isc-projects/bind9#5781
|
||||
|
||||
(cherry picked from commit 7becff1a14684a68208c92b3b0315c045c05ad75)
|
||||
(cherry picked from commit 401a6374b026afb76a7b22acc4a1402d21a7e77b)
|
||||
|
||||
Disable UPDATE and NOTIFY for non-IN classes
|
||||
|
||||
Return NOTIMP for UPDATE and NOTIFY requests received for views with a
|
||||
class other than IN. Only QUERY is now supported for non-IN views such
|
||||
as CHAOS.
|
||||
|
||||
When running dns dns_rdata_tostruct() with types that are only defined
|
||||
for class IN, ensure that the class is correct before proceeding.
|
||||
|
||||
Add an assertion that any zone being updated is of class IN. (Note
|
||||
that previously, a DLZ zone could have its class value set incorrectly
|
||||
to NONE; this has been fixed.)
|
||||
|
||||
This addresses YWH-PGM40640-70 and YWH-PGM40640-73 (as well as any
|
||||
similar problems that might have occurred in the future) by minimizing
|
||||
the code paths that can be reached by rdata classes other than IN, so it
|
||||
is safe for the implementation to assume that rdatatypes that are only
|
||||
defined for class IN, such as SVCB or WKS, have been parsed and
|
||||
validated, and not accepted as unknown/opaque data.
|
||||
|
||||
Fixes: isc-projects/bind9#5777
|
||||
Fixes: isc-projects/bind9#5779
|
||||
|
||||
(cherry picked from commit a6d8e330ed6cf0021bff3f00aa1dc7a296f5aec0)
|
||||
(cherry picked from commit 04092ed136c8a6db2b1059dcd32693d57a7bdc24)
|
||||
|
||||
Validate DNS message CLASS early in request processing
|
||||
|
||||
Reject requests with unsupported or misused CLASS values before
|
||||
further processing. Only IN, CH, HS, RESERVED0 (for DNS Cookies),
|
||||
ANY (for TKEY negotiation), and NONE (for DNS UPDATE) are accepted;
|
||||
all other classes return NOTIMP. Misuse of NONE or ANY outside
|
||||
their allowed contexts returns FORMERR.
|
||||
|
||||
This adds further protection against bugs of the same general class
|
||||
as YWH-PGM40640-70 and YWH-PGM40640-73.
|
||||
|
||||
(cherry picked from commit 0a687451505037e9f9a850c9cb113aed4995b03f)
|
||||
(cherry picked from commit b247dbb3506ef628a683d184fbc6a99fad45ed94)
|
||||
|
||||
Reject meta-classes in UPDATE and NOTIFY messages
|
||||
|
||||
NOTIFY and UPDATE messages must specify a data class in the
|
||||
QUESTION/ZONE section. NONE and ANY are meta-classes and not
|
||||
appropriate here. Return FORMERR if either is used.
|
||||
|
||||
Rejecting messages with a query class of NONE addresses YWH-PGM40640-72,
|
||||
YWH-PGM40640-82, and YWH-PGM40640-83. Rejecting messages with a query
|
||||
class of ANY addresses YWH-PGM40640-87, YWH-PGM40640-88, and
|
||||
YWH-PGM40640-117.
|
||||
|
||||
Fixes: isc-projects/bind9#5778
|
||||
Fixes: isc-projects/bind9#5782
|
||||
Fixes: isc-projects/bind9#5783
|
||||
Fixes: isc-projects/bind9#5797
|
||||
Fixes: isc-projects/bind9#5798
|
||||
Fixes: isc-projects/bind9#5853
|
||||
|
||||
(cherry picked from commit c66a1b1e1bfd6c79d7b9bc8d4a59e69f4faa1563)
|
||||
(cherry picked from commit 185c10981b941bfa5b753b3624b6e11ccca8737f)
|
||||
|
||||
Skip "deny-answer-address" for non-IN addresses
|
||||
|
||||
Ensure that we don't attempt an ACL match for answer addresses
|
||||
when handling a class-CHAOS zone. This is an additional line of
|
||||
defense for YWH-PGM40640-74.
|
||||
|
||||
(cherry picked from commit e62673c765b52307c800e86f0185fe52b573c145)
|
||||
(cherry picked from commit 772d1d5f905c819d7155e76a08c33218bfcc973e)
|
||||
|
||||
Test UPDATE behavior in CHAOS and other non-IN classes
|
||||
|
||||
Send various UPDATE requests that are known to have caused
|
||||
crashes previously with deliberately misconfigured non-IN
|
||||
zones; confirm that UPDATE is not processed.
|
||||
|
||||
(cherry picked from commit e2f7ba2a4b6e7e5dba2fb1a2c9b2f0323e9a88be)
|
||||
(cherry picked from commit e5b3149b70ed24f7590b6bc08aad4492a00c2022)
|
||||
---
|
||||
bin/named/server.c | 42 ++++++-------------
|
||||
bin/tests/system/allow-query/tests.sh | 2 +-
|
||||
bin/tests/system/checkconf/tests.sh | 1 +
|
||||
bin/tests/system/resolver/tests.sh | 8 ++--
|
||||
bin/tests/system/unknown/tests.sh | 17 +++++---
|
||||
lib/bind9/check.c | 22 ++++++++--
|
||||
lib/dns/adb.c | 2 +-
|
||||
lib/dns/message.c | 11 +++++
|
||||
lib/dns/resolver.c | 7 ++++
|
||||
lib/ns/client.c | 59 +++++++++++++++++++++++----
|
||||
lib/ns/update.c | 41 ++++++++++---------
|
||||
11 files changed, 143 insertions(+), 69 deletions(-)
|
||||
|
||||
diff --git a/bin/named/server.c b/bin/named/server.c
|
||||
index a0a28c04a3..d95748305e 100644
|
||||
--- a/bin/named/server.c
|
||||
+++ b/bin/named/server.c
|
||||
@@ -1901,10 +1901,12 @@ dlzconfigure_callback(dns_view_t *view, dns_dlzdb_t *dlzdb, dns_zone_t *zone) {
|
||||
dns_rdataclass_t zclass = view->rdclass;
|
||||
isc_result_t result;
|
||||
|
||||
+ dns_zone_setclass(zone, zclass);
|
||||
result = dns_zonemgr_managezone(named_g_server->zonemgr, zone);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
+
|
||||
dns_zone_setstats(zone, named_g_server->zonestats);
|
||||
|
||||
return named_zone_configure_writeable_dlz(dlzdb, zone, zclass, origin);
|
||||
@@ -4429,6 +4431,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
|
||||
obj = NULL;
|
||||
result = named_config_get(maps, "max-cache-size", &obj);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
+
|
||||
/*
|
||||
* If "-T maxcachesize=..." is in effect, it overrides any other
|
||||
* "max-cache-size" setting found in configuration, either implicit or
|
||||
@@ -5139,34 +5142,15 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
|
||||
}
|
||||
|
||||
/*
|
||||
- * We have default hints for class IN if we need them.
|
||||
+ * We have default root hints for class IN if we need them.
|
||||
+ * Each view gets its own rootdb so a priming response only
|
||||
+ * writes into that view's copy. Other classes don't support
|
||||
+ * recursion and don't need hints.
|
||||
*/
|
||||
if (view->rdclass == dns_rdataclass_in && view->hints == NULL) {
|
||||
dns_view_sethints(view, named_g_server->in_roothints);
|
||||
}
|
||||
|
||||
- /*
|
||||
- * If we still have no hints, this is a non-IN view with no
|
||||
- * "hints zone" configured. Issue a warning, except if this
|
||||
- * is a root server. Root servers never need to consult
|
||||
- * their hints, so it's no point requiring users to configure
|
||||
- * them.
|
||||
- */
|
||||
- if (view->hints == NULL) {
|
||||
- dns_zone_t *rootzone = NULL;
|
||||
- (void)dns_view_findzone(view, dns_rootname, &rootzone);
|
||||
- if (rootzone != NULL) {
|
||||
- dns_zone_detach(&rootzone);
|
||||
- need_hints = false;
|
||||
- }
|
||||
- if (need_hints) {
|
||||
- isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
|
||||
- NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
|
||||
- "no root hints for view '%s'",
|
||||
- view->name);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
/*
|
||||
* Configure the view's transports (DoT/DoH)
|
||||
*/
|
||||
@@ -5294,7 +5278,8 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
|
||||
obj = NULL;
|
||||
result = named_config_get(maps, "recursion", &obj);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
- view->recursion = cfg_obj_asboolean(obj);
|
||||
+ view->recursion = (view->rdclass == dns_rdataclass_in &&
|
||||
+ cfg_obj_asboolean(obj));
|
||||
|
||||
obj = NULL;
|
||||
result = named_config_get(maps, "qname-minimization", &obj);
|
||||
@@ -5394,14 +5379,13 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
|
||||
CHECK(configure_view_acl(vconfig, config, NULL, "allow-query-cache-on",
|
||||
NULL, actx, named_g_mctx, &view->cacheonacl));
|
||||
|
||||
- if (strcmp(view->name, "_bind") != 0 &&
|
||||
- view->rdclass != dns_rdataclass_chaos)
|
||||
- {
|
||||
- /* named.conf only */
|
||||
+ if (view->rdclass != dns_rdataclass_in) {
|
||||
+ dns_acl_none(named_g_mctx, &view->recursionacl);
|
||||
+ dns_acl_none(named_g_mctx, &view->recursiononacl);
|
||||
+ } else {
|
||||
CHECK(configure_view_acl(vconfig, config, NULL,
|
||||
"allow-recursion", NULL, actx,
|
||||
named_g_mctx, &view->recursionacl));
|
||||
- /* named.conf only */
|
||||
CHECK(configure_view_acl(vconfig, config, NULL,
|
||||
"allow-recursion-on", NULL, actx,
|
||||
named_g_mctx, &view->recursiononacl));
|
||||
diff --git a/bin/tests/system/allow-query/tests.sh b/bin/tests/system/allow-query/tests.sh
|
||||
index e59a1abe6b..46d2a78077 100644
|
||||
--- a/bin/tests/system/allow-query/tests.sh
|
||||
+++ b/bin/tests/system/allow-query/tests.sh
|
||||
@@ -703,7 +703,7 @@ $DIG -p ${PORT} @10.53.1.2 d.normal.example a >dig.out.ns3.4.$n || ret=1
|
||||
grep 'recursion requested but not available' dig.out.ns3.4.$n >/dev/null || ret=1
|
||||
grep 'status: REFUSED' dig.out.ns3.4.$n >/dev/null || ret=1
|
||||
grep 'EDE: 18 (Prohibited)' dig.out.ns3.4.$n >/dev/null || ret=1
|
||||
-nextpart ns3/named.run | grep 'allow-recursion-on did not match' >/dev/null || ret=1
|
||||
+nextpart ns3/named.run | grep 'allow-query-cache-on did not match' >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
diff --git a/bin/tests/system/checkconf/tests.sh b/bin/tests/system/checkconf/tests.sh
|
||||
index 7d222ed863..b2c955d312 100644
|
||||
--- a/bin/tests/system/checkconf/tests.sh
|
||||
+++ b/bin/tests/system/checkconf/tests.sh
|
||||
@@ -543,6 +543,7 @@ $CHECKCONF -l good.conf \
|
||||
| grep -v "is not implemented" \
|
||||
| grep -v "is not recommended" \
|
||||
| grep -v "no longer exists" \
|
||||
+ | grep -v "recursion will be disabled" \
|
||||
| grep -v "is obsolete" >checkconf.out$n || ret=1
|
||||
diff good.zonelist checkconf.out$n >diff.out$n || ret=1
|
||||
if [ $ret -ne 0 ]; then
|
||||
diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh
|
||||
index 23b42f728c..b5d2cf8b46 100755
|
||||
--- a/bin/tests/system/resolver/tests.sh
|
||||
+++ b/bin/tests/system/resolver/tests.sh
|
||||
@@ -948,10 +948,12 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
-echo_i "checking NXDOMAIN is returned when querying non existing domain in CH class ($n)"
|
||||
+echo_i "checking REFUSED is returned when querying non existing domain in CH class ($n)"
|
||||
ret=0
|
||||
-dig_with_opts @10.53.0.1 id.hostname txt ch >dig.ns1.out.${n} || ret=1
|
||||
-grep "status: NXDOMAIN" dig.ns1.out.${n} >/dev/null || ret=1
|
||||
+dig_with_opts @10.53.0.1 hostname.chaostest txt ch >dig.ns1.out.1.${n} || ret=1
|
||||
+grep "status: NOERROR" dig.ns1.out.1.${n} >/dev/null || ret=1
|
||||
+dig_with_opts @10.53.0.1 id.hostname txt ch >dig.ns1.out.2.${n} || ret=1
|
||||
+grep "status: REFUSED" dig.ns1.out.2.${n} >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
diff --git a/bin/tests/system/unknown/tests.sh b/bin/tests/system/unknown/tests.sh
|
||||
index eb61f21f28..cbc2943f17 100644
|
||||
--- a/bin/tests/system/unknown/tests.sh
|
||||
+++ b/bin/tests/system/unknown/tests.sh
|
||||
@@ -25,6 +25,11 @@ dig_cmd() {
|
||||
"$DIG" $DIGOPTS "$@" | grep -v '^;'
|
||||
}
|
||||
|
||||
+dig_full() {
|
||||
+ # shellcheck disable=SC2086
|
||||
+ "$DIG" $DIGOPTS "$@"
|
||||
+}
|
||||
+
|
||||
n=$((n + 1))
|
||||
echo_i "querying for various representations of an IN A record ($n)"
|
||||
for i in 1 2 3 4 5 6 7 8 9 10 11 12; do
|
||||
@@ -81,8 +86,8 @@ n=$((n + 1))
|
||||
echo_i "querying for various representations of a CLASS10 TYPE1 record ($n)"
|
||||
for i in 1 2; do
|
||||
ret=0
|
||||
- dig_cmd +short @10.53.0.1 a$i.example a class10 >dig.out.$i.test$n
|
||||
- echo '\# 4 0A000001' | diff - dig.out.$i.test$n || ret=1
|
||||
+ dig_full @10.53.0.1 a$i.example a class10 >dig.out.$i.test$n
|
||||
+ grep -q "NOTIMP" dig.out.$i.test$n || ret=1
|
||||
if [ $ret != 0 ]; then
|
||||
echo_i "#$i failed"
|
||||
fi
|
||||
@@ -93,8 +98,8 @@ n=$((n + 1))
|
||||
echo_i "querying for various representations of a CLASS10 TXT record ($n)"
|
||||
for i in 1 2 3 4; do
|
||||
ret=0
|
||||
- dig_cmd +short @10.53.0.1 txt$i.example txt class10 >dig.out.$i.test$n
|
||||
- echo '"hello"' | diff - dig.out.$i.test$n || ret=1
|
||||
+ dig_full @10.53.0.1 txt$i.example txt class10 >dig.out.$i.test$n
|
||||
+ grep -q "NOTIMP" dig.out.$i.test$n || ret=1
|
||||
if [ $ret != 0 ]; then
|
||||
echo_i "#$i failed"
|
||||
fi
|
||||
@@ -105,8 +110,8 @@ n=$((n + 1))
|
||||
echo_i "querying for various representations of a CLASS10 TYPE123 record ($n)"
|
||||
for i in 1 2; do
|
||||
ret=0
|
||||
- dig_cmd +short @10.53.0.1 unk$i.example type123 class10 >dig.out.$i.test$n
|
||||
- echo '\# 1 00' | diff - dig.out.$i.test$n || ret=1
|
||||
+ dig_full @10.53.0.1 unk$i.example type123 class10 >dig.out.$i.test$n
|
||||
+ grep -q "NOTIMP" dig.out.$i.test$n || ret=1
|
||||
if [ $ret != 0 ]; then
|
||||
echo_i "#$i failed"
|
||||
fi
|
||||
diff --git a/lib/bind9/check.c b/lib/bind9/check.c
|
||||
index cefc3eb3ac..13f3212d08 100644
|
||||
--- a/lib/bind9/check.c
|
||||
+++ b/lib/bind9/check.c
|
||||
@@ -2789,13 +2789,17 @@ check_mirror_zone_notify(const cfg_obj_t *zoptions, const char *znamestr,
|
||||
*/
|
||||
static bool
|
||||
check_recursion(const cfg_obj_t *config, const cfg_obj_t *voptions,
|
||||
- const cfg_obj_t *goptions, isc_log_t *logctx,
|
||||
- cfg_aclconfctx_t *actx, isc_mem_t *mctx) {
|
||||
+ dns_rdataclass_t vclass, const cfg_obj_t *goptions,
|
||||
+ isc_log_t *logctx, cfg_aclconfctx_t *actx, isc_mem_t *mctx) {
|
||||
dns_acl_t *acl = NULL;
|
||||
const cfg_obj_t *obj;
|
||||
isc_result_t result;
|
||||
bool retval = true;
|
||||
|
||||
+ if (vclass != dns_rdataclass_in) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* Check the "recursion" option first.
|
||||
*/
|
||||
@@ -3380,7 +3384,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
|
||||
* contradicts the purpose of the former.
|
||||
*/
|
||||
if (ztype == CFG_ZONE_MIRROR &&
|
||||
- !check_recursion(config, voptions, goptions, logctx, actx, mctx))
|
||||
+ !check_recursion(config, voptions, zclass, goptions, logctx, actx,
|
||||
+ mctx))
|
||||
{
|
||||
cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
|
||||
"zone '%s': mirror zones cannot be used if "
|
||||
@@ -5215,6 +5220,17 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
|
||||
|
||||
cfg_aclconfctx_create(mctx, &actx);
|
||||
|
||||
+ if (vclass != dns_rdataclass_in) {
|
||||
+ if (check_recursion(config, voptions, dns_rdataclass_in,
|
||||
+ options, logctx, actx, mctx))
|
||||
+ {
|
||||
+ cfg_obj_log(opts, logctx, ISC_LOG_WARNING,
|
||||
+ "recursion will be disabled for "
|
||||
+ "non-IN view '%s'",
|
||||
+ viewname);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if (voptions != NULL) {
|
||||
(void)cfg_map_get(voptions, "zone", &zones);
|
||||
} else {
|
||||
diff --git a/lib/dns/adb.c b/lib/dns/adb.c
|
||||
index 8b6e153566..3da839cc12 100644
|
||||
--- a/lib/dns/adb.c
|
||||
+++ b/lib/dns/adb.c
|
||||
@@ -938,7 +938,7 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
|
||||
INSIST(DNS_ADB_VALID(adb));
|
||||
|
||||
rdtype = rdataset->type;
|
||||
- INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa));
|
||||
+ REQUIRE(rdtype == dns_rdatatype_a || rdtype == dns_rdatatype_aaaa);
|
||||
|
||||
addr_bucket = DNS_ADB_INVALIDBUCKET;
|
||||
new_addresses_added = false;
|
||||
diff --git a/lib/dns/message.c b/lib/dns/message.c
|
||||
index f939fa94a7..1dc50c142a 100644
|
||||
--- a/lib/dns/message.c
|
||||
+++ b/lib/dns/message.c
|
||||
@@ -1080,6 +1080,17 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
|
||||
rdtype = isc_buffer_getuint16(source);
|
||||
rdclass = isc_buffer_getuint16(source);
|
||||
|
||||
+ /*
|
||||
+ * Notify and update messages need to specify the data class.
|
||||
+ */
|
||||
+ if ((msg->opcode == dns_opcode_update ||
|
||||
+ msg->opcode == dns_opcode_notify) &&
|
||||
+ (rdclass == dns_rdataclass_none ||
|
||||
+ rdclass == dns_rdataclass_any))
|
||||
+ {
|
||||
+ DO_ERROR(DNS_R_FORMERR);
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* If this class is different than the one we already read,
|
||||
* this is an error.
|
||||
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
|
||||
index 5c2294a0f2..1326cb3428 100644
|
||||
--- a/lib/dns/resolver.c
|
||||
+++ b/lib/dns/resolver.c
|
||||
@@ -7249,6 +7249,13 @@ is_answeraddress_allowed(dns_view_t *view, dns_name_t *name,
|
||||
}
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * deny-answer-address doesn't apply to non-IN classes.
|
||||
+ */
|
||||
+ if (rdataset->rdclass != dns_rdataclass_in) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* Otherwise, search the filter list for a match for each
|
||||
* address record. If a match is found, the address should be
|
||||
diff --git a/lib/ns/client.c b/lib/ns/client.c
|
||||
index d8cd84bc5f..df4c570de2 100644
|
||||
--- a/lib/ns/client.c
|
||||
+++ b/lib/ns/client.c
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <dns/dispatch.h>
|
||||
#include <dns/dnstap.h>
|
||||
#include <dns/edns.h>
|
||||
+#include <dns/enumclass.h>
|
||||
#include <dns/events.h>
|
||||
#include <dns/message.h>
|
||||
#include <dns/peer.h>
|
||||
@@ -2068,7 +2069,9 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
|
||||
}
|
||||
}
|
||||
|
||||
- if (client->message->rdclass == 0) {
|
||||
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
|
||||
+ switch (client->message->rdclass) {
|
||||
+ case dns_rdataclass_reserved0:
|
||||
if ((client->attributes & NS_CLIENTATTR_WANTCOOKIE) != 0 &&
|
||||
client->message->opcode == dns_opcode_query &&
|
||||
client->message->counts[DNS_SECTION_QUESTION] == 0U)
|
||||
@@ -2087,12 +2090,46 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
|
||||
return;
|
||||
}
|
||||
|
||||
+ ns_client_dumpmessage(client,
|
||||
+ "message class could not be determined");
|
||||
+ ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
|
||||
+ return;
|
||||
+ case dns_rdataclass_in:
|
||||
+ break;
|
||||
+ case dns_rdataclass_chaos:
|
||||
+ break;
|
||||
+ case dns_rdataclass_hs:
|
||||
+ break;
|
||||
+ case dns_rdataclass_none:
|
||||
+ if (client->message->opcode != dns_opcode_update) {
|
||||
+ ns_client_dumpmessage(client,
|
||||
+ "message class NONE can be only "
|
||||
+ "used in DNS updates");
|
||||
+ ns_client_error(client, DNS_R_FORMERR);
|
||||
+ return;
|
||||
+ }
|
||||
+ break;
|
||||
+ case dns_rdataclass_any:
|
||||
+ /*
|
||||
+ * Required for TKEY negotiation.
|
||||
+ */
|
||||
+ if (client->message->tkey == 0) {
|
||||
+ ns_client_dumpmessage(client,
|
||||
+ "message class ANY can be only "
|
||||
+ "used for TKEY negotiation");
|
||||
+ ns_client_error(client, DNS_R_FORMERR);
|
||||
+ return;
|
||||
+ }
|
||||
+ break;
|
||||
+ default:
|
||||
+ dns_rdataclass_format(client->message->rdclass, classbuf,
|
||||
+ sizeof(classbuf));
|
||||
+ ns_client_dumpmessage(client, NULL);
|
||||
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
|
||||
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
|
||||
- "message class could not be determined");
|
||||
- ns_client_dumpmessage(client, "message class could not be "
|
||||
- "determined");
|
||||
- ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
|
||||
+ "invalid message class: %s", classbuf);
|
||||
+
|
||||
+ ns_client_error(client, DNS_R_NOTIMP);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2125,7 +2162,7 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
|
||||
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
|
||||
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
|
||||
"no matching view in class '%s'", classname);
|
||||
- ns_client_dumpmessage(client, "no matching view in class");
|
||||
+ ns_client_dumpmessage(client, NULL);
|
||||
ns_client_extendederror(client, DNS_EDE_PROHIBITED, NULL);
|
||||
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
|
||||
return;
|
||||
@@ -2322,6 +2359,10 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
|
||||
break;
|
||||
case dns_opcode_update:
|
||||
CTRACE("update");
|
||||
+ if (client->view->rdclass != dns_rdataclass_in) {
|
||||
+ ns_client_error(client, DNS_R_NOTIMP);
|
||||
+ break;
|
||||
+ }
|
||||
#ifdef HAVE_DNSTAP
|
||||
dns_dt_send(client->view, DNS_DTTYPE_UQ, &client->peeraddr,
|
||||
&client->destsockaddr, TCP_CLIENT(client), NULL,
|
||||
@@ -2332,6 +2373,10 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
|
||||
break;
|
||||
case dns_opcode_notify:
|
||||
CTRACE("notify");
|
||||
+ if (client->view->rdclass != dns_rdataclass_in) {
|
||||
+ ns_client_error(client, DNS_R_NOTIMP);
|
||||
+ break;
|
||||
+ }
|
||||
ns_client_settimeout(client, 60);
|
||||
ns_notify_start(client, handle);
|
||||
break;
|
||||
@@ -2758,7 +2803,7 @@ ns_client_dumpmessage(ns_client_t *client, const char *reason) {
|
||||
int len = 1024;
|
||||
isc_result_t result;
|
||||
|
||||
- if (!isc_log_wouldlog(ns_lctx, ISC_LOG_DEBUG(1))) {
|
||||
+ if (!isc_log_wouldlog(ns_lctx, ISC_LOG_DEBUG(1)) || reason == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
diff --git a/lib/ns/update.c b/lib/ns/update.c
|
||||
index 248f6ed53b..84ba9c48d1 100644
|
||||
--- a/lib/ns/update.c
|
||||
+++ b/lib/ns/update.c
|
||||
@@ -1026,7 +1026,9 @@ ssu_checkrr(void *data, rr_t *rr) {
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
target = &ptr.ptr;
|
||||
}
|
||||
- if (rr->rdata.type == dns_rdatatype_srv) {
|
||||
+ if (rr->rdata.rdclass == dns_rdataclass_in &&
|
||||
+ rr->rdata.type == dns_rdatatype_srv)
|
||||
+ {
|
||||
result = dns_rdata_tostruct(&rr->rdata, &srv, NULL);
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
target = &srv.target;
|
||||
@@ -1390,7 +1392,10 @@ replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
- if (db_rr->type == dns_rdatatype_wks) {
|
||||
+
|
||||
+ if (db_rr->rdclass == dns_rdataclass_in &&
|
||||
+ db_rr->type == dns_rdatatype_wks)
|
||||
+ {
|
||||
/*
|
||||
* Compare the address and protocol fields only. These
|
||||
* form the first five bytes of the RR data. Do a
|
||||
@@ -1533,8 +1538,7 @@ failure:
|
||||
* 'rdata', and 'ttl', respectively.
|
||||
*/
|
||||
static void
|
||||
-get_current_rr(dns_message_t *msg, dns_section_t section,
|
||||
- dns_rdataclass_t zoneclass, dns_name_t **name,
|
||||
+get_current_rr(dns_message_t *msg, dns_section_t section, dns_name_t **name,
|
||||
dns_rdata_t *rdata, dns_rdatatype_t *covers, dns_ttl_t *ttl,
|
||||
dns_rdataclass_t *update_class) {
|
||||
dns_rdataset_t *rdataset;
|
||||
@@ -1550,7 +1554,7 @@ get_current_rr(dns_message_t *msg, dns_section_t section,
|
||||
dns_rdataset_current(rdataset, rdata);
|
||||
INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
|
||||
*update_class = rdata->rdclass;
|
||||
- rdata->rdclass = zoneclass;
|
||||
+ rdata->rdclass = dns_rdataclass_in;
|
||||
}
|
||||
|
||||
/*%
|
||||
@@ -1652,7 +1656,6 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
|
||||
dns_message_t *request = client->message;
|
||||
isc_mem_t *mctx = client->manager->mctx;
|
||||
dns_aclenv_t *env = client->manager->aclenv;
|
||||
- dns_rdataclass_t zoneclass;
|
||||
dns_rdatatype_t covers;
|
||||
dns_name_t *zonename = NULL;
|
||||
unsigned int *maxbytype = NULL;
|
||||
@@ -1662,10 +1665,12 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
|
||||
|
||||
CHECK(dns_zone_getdb(zone, &db));
|
||||
zonename = dns_db_origin(db);
|
||||
- zoneclass = dns_db_class(db);
|
||||
dns_zone_getssutable(zone, &ssutable);
|
||||
dns_db_currentversion(db, &ver);
|
||||
|
||||
+ /* Updates are only supported for class IN. */
|
||||
+ INSIST(dns_zone_getclass(zone) == dns_rdataclass_in);
|
||||
+
|
||||
/*
|
||||
* Update message processing can leak record existence information
|
||||
* so check that we are allowed to query this zone. Additionally,
|
||||
@@ -1716,13 +1721,13 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
|
||||
|
||||
INSIST(ssutable == NULL || update < maxbytypelen);
|
||||
|
||||
- get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, &name,
|
||||
- &rdata, &covers, &ttl, &update_class);
|
||||
+ get_current_rr(request, DNS_SECTION_UPDATE, &name, &rdata,
|
||||
+ &covers, &ttl, &update_class);
|
||||
|
||||
if (!dns_name_issubdomain(name, zonename)) {
|
||||
FAILC(DNS_R_NOTZONE, "update RR is outside zone");
|
||||
}
|
||||
- if (update_class == zoneclass) {
|
||||
+ if (update_class == dns_rdataclass_in) {
|
||||
/*
|
||||
* Check for meta-RRs. The RFC2136 pseudocode says
|
||||
* check for ANY|AXFR|MAILA|MAILB, but the text adds
|
||||
@@ -1806,7 +1811,6 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
|
||||
}
|
||||
|
||||
if (update_class == dns_rdataclass_any &&
|
||||
- zoneclass == dns_rdataclass_in &&
|
||||
(rdata.type == dns_rdatatype_ptr ||
|
||||
rdata.type == dns_rdatatype_srv))
|
||||
{
|
||||
@@ -2897,7 +2901,6 @@ update_action(isc_task_t *task, isc_event_t *event) {
|
||||
isc_mem_t *mctx = client->mctx;
|
||||
dns_rdatatype_t covers;
|
||||
dns_message_t *request = client->message;
|
||||
- dns_rdataclass_t zoneclass;
|
||||
dns_name_t *zonename = NULL;
|
||||
dns_ssutable_t *ssutable = NULL;
|
||||
dns_fixedname_t tmpnamefixed;
|
||||
@@ -2918,10 +2921,10 @@ update_action(isc_task_t *task, isc_event_t *event) {
|
||||
|
||||
CHECK(dns_zone_getdb(zone, &db));
|
||||
zonename = dns_db_origin(db);
|
||||
- zoneclass = dns_db_class(db);
|
||||
dns_zone_getssutable(zone, &ssutable);
|
||||
options = dns_zone_getoptions(zone);
|
||||
|
||||
+ INSIST(dns_zone_getclass(zone) == dns_rdataclass_in);
|
||||
/*
|
||||
* Get old and new versions now that queryacl has been checked.
|
||||
*/
|
||||
@@ -2942,8 +2945,8 @@ update_action(isc_task_t *task, isc_event_t *event) {
|
||||
dns_rdataclass_t update_class;
|
||||
bool flag;
|
||||
|
||||
- get_current_rr(request, DNS_SECTION_PREREQUISITE, zoneclass,
|
||||
- &name, &rdata, &covers, &ttl, &update_class);
|
||||
+ get_current_rr(request, DNS_SECTION_PREREQUISITE, &name, &rdata,
|
||||
+ &covers, &ttl, &update_class);
|
||||
|
||||
if (ttl != 0) {
|
||||
PREREQFAILC(DNS_R_FORMERR,
|
||||
@@ -3006,7 +3009,7 @@ update_action(isc_task_t *task, isc_event_t *event) {
|
||||
"prerequisite not satisfied");
|
||||
}
|
||||
}
|
||||
- } else if (update_class == zoneclass) {
|
||||
+ } else if (update_class == dns_rdataclass_in) {
|
||||
/* "temp<rr.name, rr.type> += rr;" */
|
||||
result = temp_append(&temp, name, &rdata);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
@@ -3068,10 +3071,10 @@ update_action(isc_task_t *task, isc_event_t *event) {
|
||||
|
||||
INSIST(ssutable == NULL || update < maxbytypelen);
|
||||
|
||||
- get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, &name,
|
||||
- &rdata, &covers, &ttl, &update_class);
|
||||
+ get_current_rr(request, DNS_SECTION_UPDATE, &name, &rdata,
|
||||
+ &covers, &ttl, &update_class);
|
||||
|
||||
- if (update_class == zoneclass) {
|
||||
+ if (update_class == dns_rdataclass_in) {
|
||||
/*
|
||||
* RFC1123 doesn't allow MF and MD in master files.
|
||||
*/
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -168,6 +168,8 @@ Patch232: bind-9.18-CVE-2026-1519.patch
|
||||
Patch233: bind-9.18-CVE-2026-1519-test.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/commit/03ce21cf3099bd461f95ac1f4f22cce80ae1ba65
|
||||
Patch234: bind-9.18-CVE-2026-3039.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/commit/7ce6ce37b1b04af0953ed2d3211587465085600e
|
||||
Patch235: bind-9.18-CVE-2026-5946.patch
|
||||
|
||||
%{?systemd_ordering}
|
||||
# https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers
|
||||
@ -971,6 +973,7 @@ fi;
|
||||
%changelog
|
||||
* Mon May 25 2026 Petr Menšík <pemensik@redhat.com> - 32:9.18.33-19
|
||||
- Fix GSS-API resource leak (CVE-2026-3039)
|
||||
- Invalid handling of CLASS != IN (CVE-2026-5946)
|
||||
|
||||
* Fri Mar 27 2026 Petr Menšík <pemensik@redhat.com> - 32:9.18.33-18
|
||||
- Prevent Denial of Service via maliciously crafted DNSSEC-validated zone
|
||||
|
||||
Loading…
Reference in New Issue
Block a user