import UBI bind-9.18.33-15.el10_2.2

This commit is contained in:
AlmaLinux RelEng Bot 2026-06-07 23:53:41 -04:00
parent f75de6fcf4
commit 131c6587b0
3 changed files with 1063 additions and 1 deletions

View File

@ -0,0 +1,386 @@
From 775156d85236e8cc032191f16304af36f30f30c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org>
Date: Wed, 18 Mar 2026 00:10:35 +0100
Subject: [PATCH] Fix GSS-API context leak in TKEY negotiation
Reject multi-round GSS-API negotiation (GSS_S_CONTINUE_NEEDED) in
dst_gssapi_acceptctx(). Each call to gss_accept_sec_context()
allocates a context inside the GSS library; without this fix, the
context handle was passed back to process_gsstkey() which did not
store it persistently, leaking it on every incomplete negotiation.
An unauthenticated attacker could exhaust server memory by sending
repeated TKEY queries with GSSAPI tokens, each leaking one GSS
context. The leaked memory is allocated by the GSS library via
malloc(), bypassing BIND's memory accounting.
In practice, Kerberos/SPNEGO (the only mechanism used with BIND)
completes in a single round, so rejecting continuation does not
affect real-world deployments. See RFC 3645 Section 4.1.3.
(cherry picked from commit 3d8e0d068f08694282c5ecd3bd6c332de6c75485)
(cherry picked from commit c420039fee2f428032a2f72aa82a4ed2fcc92f31)
Fix output token and GSS context leaks in TKEY/GSS-API error paths
In dst_gssapi_acceptctx(), rename outtoken to outtokenp (matching BIND
convention for output pointer parameters) and free the allocated output
token buffer on error in the cleanup path.
In process_gsstkey(), route the empty-principal error path through
cleanup via CLEANUP() instead of returning early, so that the output
token, GSS context, and TSIG key are all freed consistently by the
existing cleanup block.
(cherry picked from commit 6c46c85d02849fb659584275313529794039f433)
(cherry picked from commit d273e3def097fbd186f23bcbb146c3ffb4f47301)
%RH:
Some cleanup changes compared to original commits.
---
lib/dns/gssapictx.c | 129 +++++++++++++++++++----------------
lib/dns/include/dst/gssapi.h | 17 ++---
lib/dns/tkey.c | 49 ++++++-------
3 files changed, 99 insertions(+), 96 deletions(-)
diff --git a/lib/dns/gssapictx.c b/lib/dns/gssapictx.c
index 06580c2352..abed2384c5 100644
--- a/lib/dns/gssapictx.c
+++ b/lib/dns/gssapictx.c
@@ -100,6 +100,13 @@ static gss_OID_desc __gss_spnego_mechanism_oid_desc = {
goto out; \
} while (0)
+#define CHECK(x) \
+ do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
static void
name_to_gbuffer(const dns_name_t *name, isc_buffer_t *buffer,
gss_buffer_desc *gbuffer) {
@@ -612,7 +619,14 @@ dst_gssapi_initctx(const dns_name_t *name, isc_buffer_t *intoken,
GSS_SPNEGO_MECHANISM, flags, 0, NULL, gintokenp, NULL,
&gouttoken, &ret_flags, NULL);
- if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
+ switch (gret) {
+ case GSS_S_COMPLETE:
+ result = ISC_R_SUCCESS;
+ break;
+ case GSS_S_CONTINUE_NEEDED:
+ result = DNS_R_CONTINUE;
+ break;
+ default:
gss_err_message(mctx, gret, minor, err_message);
if (err_message != NULL && *err_message != NULL) {
gss_log(3, "Failure initiating security context: %s",
@@ -638,12 +652,6 @@ dst_gssapi_initctx(const dns_name_t *name, isc_buffer_t *intoken,
RETERR(isc_buffer_copyregion(outtoken, &r));
}
- if (gret == GSS_S_COMPLETE) {
- result = ISC_R_SUCCESS;
- } else {
- result = DNS_R_CONTINUE;
- }
-
out:
if (gouttoken.length != 0U) {
(void)gss_release_buffer(&minor, &gouttoken);
@@ -654,7 +662,7 @@ out:
isc_result_t
dst_gssapi_acceptctx(dns_gss_cred_id_t cred, const char *gssapi_keytab,
- isc_region_t *intoken, isc_buffer_t **outtoken,
+ isc_region_t *intoken, isc_buffer_t **outtokenp,
dns_gss_ctx_id_t *ctxout, dns_name_t *principal,
isc_mem_t *mctx) {
isc_region_t r;
@@ -667,16 +675,11 @@ dst_gssapi_acceptctx(dns_gss_cred_id_t cred, const char *gssapi_keytab,
isc_result_t result;
char buf[1024];
- REQUIRE(outtoken != NULL && *outtoken == NULL);
+ REQUIRE(outtokenp != NULL && *outtokenp == NULL);
+ REQUIRE(*ctxout == NULL);
REGION_TO_GBUFFER(*intoken, gintoken);
- if (*ctxout == NULL) {
- context = GSS_C_NO_CONTEXT;
- } else {
- context = *ctxout;
- }
-
if (gssapi_keytab != NULL) {
#if HAVE_GSSAPI_GSSAPI_KRB5_H || HAVE_GSSAPI_KRB5_H
gret = gsskrb5_register_acceptor_identity(gssapi_keytab);
@@ -721,8 +724,15 @@ dst_gssapi_acceptctx(dns_gss_cred_id_t cred, const char *gssapi_keytab,
switch (gret) {
case GSS_S_COMPLETE:
- case GSS_S_CONTINUE_NEEDED:
break;
+ /*
+ * RFC 3645 4.1.3: we don't handle GSS_S_CONTINUE_NEEDED
+ * Multi-round GSS-API negotiation is not supported.
+ */
+ case GSS_S_CONTINUE_NEEDED:
+ gss_log(3, "multi-round GSS-API negotiation not supported");
+ (void)gss_delete_sec_context(&minor, &context, NULL);
+ FALLTHROUGH;
case GSS_S_DEFECTIVE_TOKEN:
case GSS_S_DEFECTIVE_CREDENTIAL:
case GSS_S_BAD_SIG:
@@ -735,7 +745,7 @@ dst_gssapi_acceptctx(dns_gss_cred_id_t cred, const char *gssapi_keytab,
case GSS_S_BAD_MECH:
case GSS_S_FAILURE:
result = DNS_R_INVALIDTKEY;
- /* fall through */
+ FALLTHROUGH;
default:
gss_log(3, "failed gss_accept_sec_context: %s",
gss_error_tostring(gret, minor, buf, sizeof(buf)));
@@ -746,59 +756,64 @@ dst_gssapi_acceptctx(dns_gss_cred_id_t cred, const char *gssapi_keytab,
}
if (gouttoken.length > 0U) {
- isc_buffer_allocate(mctx, outtoken,
+ isc_buffer_allocate(mctx, outtokenp,
(unsigned int)gouttoken.length);
GBUFFER_TO_REGION(gouttoken, r);
- RETERR(isc_buffer_copyregion(*outtoken, &r));
+ CHECK(isc_buffer_copyregion(*outtokenp, &r));
(void)gss_release_buffer(&minor, &gouttoken);
}
- if (gret == GSS_S_COMPLETE) {
- gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
- if (gret != GSS_S_COMPLETE) {
- gss_log(3, "failed gss_display_name: %s",
- gss_error_tostring(gret, minor, buf,
- sizeof(buf)));
- RETERR(ISC_R_FAILURE);
- }
+ INSIST(gret == GSS_S_COMPLETE);
- /*
- * Compensate for a bug in Solaris8's implementation
- * of gss_display_name(). Should be harmless in any
- * case, since principal names really should not
- * contain null characters.
- */
- if (gnamebuf.length > 0U &&
- ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
- {
- gnamebuf.length--;
- }
+ gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "failed gss_display_name: %s",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
- gss_log(3, "gss-api source name (accept) is %.*s",
- (int)gnamebuf.length, (char *)gnamebuf.value);
+ /*
+ * Compensate for a bug in Solaris8's implementation
+ * of gss_display_name(). Should be harmless in any
+ * case, since principal names really should not
+ * contain null characters.
+ */
+ if (gnamebuf.length > 0U &&
+ ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
+ {
+ gnamebuf.length--;
+ }
- GBUFFER_TO_REGION(gnamebuf, r);
- isc_buffer_init(&namebuf, r.base, r.length);
- isc_buffer_add(&namebuf, r.length);
+ gss_log(3, "gss-api source name (accept) is %.*s", (int)gnamebuf.length,
+ (char *)gnamebuf.value);
- RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname, 0,
- NULL));
+ GBUFFER_TO_REGION(gnamebuf, r);
+ isc_buffer_init(&namebuf, r.base, r.length);
+ isc_buffer_add(&namebuf, r.length);
- if (gnamebuf.length != 0U) {
- gret = gss_release_buffer(&minor, &gnamebuf);
- if (gret != GSS_S_COMPLETE) {
- gss_log(3, "failed gss_release_buffer: %s",
- gss_error_tostring(gret, minor, buf,
- sizeof(buf)));
- }
- }
- } else {
- result = DNS_R_CONTINUE;
- }
+ CHECK(dns_name_fromtext(principal, &namebuf, dns_rootname, 0, NULL));
*ctxout = context;
-out:
+cleanup:
+ if (result != ISC_R_SUCCESS && *outtokenp != NULL) {
+ isc_buffer_free(outtokenp);
+ }
+
+ if (result != ISC_R_SUCCESS && context != GSS_C_NO_CONTEXT) {
+ (void)gss_delete_sec_context(&minor, &context, NULL);
+ }
+
+ if (gnamebuf.length != 0U) {
+ gret = gss_release_buffer(&minor, &gnamebuf);
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "failed gss_release_buffer: %s",
+ gss_error_tostring(gret, minor, buf,
+ sizeof(buf)));
+ }
+ }
+
if (gname != NULL) {
gret = gss_release_name(&minor, &gname);
if (gret != GSS_S_COMPLETE) {
diff --git a/lib/dns/include/dst/gssapi.h b/lib/dns/include/dst/gssapi.h
index 494b4b0762..5945bf7637 100644
--- a/lib/dns/include/dst/gssapi.h
+++ b/lib/dns/include/dst/gssapi.h
@@ -113,20 +113,17 @@ dst_gssapi_acceptctx(dns_gss_cred_id_t cred, const char *gssapi_keytab,
* generated by gss_accept_sec_context() to be sent to the
* initiator
* 'context' is a valid pointer to receive the generated context handle.
- * On the initial call, it should be a pointer to NULL, which
- * will be allocated as a dns_gss_ctx_id_t. Subsequent calls
- * should pass in the handle generated on the first call.
- * Call dst_gssapi_releasecred to delete the context and free
- * the memory.
*
* Requires:
- * 'outtoken' to != NULL && *outtoken == NULL.
+ * 'outtoken' != NULL && *outtoken == NULL.
+ * 'context' != NULL && *context == NULL.
*
* Returns:
- * ISC_R_SUCCESS msg was successfully updated to include the
- * query to be sent
- * DNS_R_CONTINUE transaction still in progress
- * other an error occurred while building the message
+ * ISC_R_SUCCESS msg was successfully updated to include
+ * the query to be sent
+ * DNS_R_INVALIDTKEY an error occurred while accepting the
+ * context
+ * ISC_R_FAILURE other error occurred
*/
isc_result_t
diff --git a/lib/dns/tkey.c b/lib/dns/tkey.c
index 66f4c5de52..679280dd14 100644
--- a/lib/dns/tkey.c
+++ b/lib/dns/tkey.c
@@ -535,19 +535,9 @@ process_gsstkey(dns_message_t *msg, dns_name_t *name, dns_rdata_tkey_t *tkeyin,
return ISC_R_SUCCESS;
}
- /*
- * XXXDCL need to check for key expiry per 4.1.1
- * XXXDCL need a way to check fully established, perhaps w/key_flags
- */
-
intoken.base = tkeyin->key;
intoken.length = tkeyin->keylen;
- result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring);
- if (result == ISC_R_SUCCESS) {
- gss_ctx = dst_key_getgssctx(tsigkey->key);
- }
-
principal = dns_fixedname_initname(&fixed);
/*
@@ -556,28 +546,27 @@ process_gsstkey(dns_message_t *msg, dns_name_t *name, dns_rdata_tkey_t *tkeyin,
result = dst_gssapi_acceptctx(tctx->gsscred, tctx->gssapi_keytab,
&intoken, &outtoken, &gss_ctx, principal,
tctx->mctx);
- if (result == DNS_R_INVALIDTKEY) {
- if (tsigkey != NULL) {
- dns_tsigkey_detach(&tsigkey);
- }
+ if (result != ISC_R_SUCCESS) {
tkeyout->error = dns_tsigerror_badkey;
- tkey_log("process_gsstkey(): dns_tsigerror_badkey"); /* XXXSRA
- */
- return ISC_R_SUCCESS;
- }
- if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) {
+ tkey_log("process_gsstkey(): dns_tsigerror_badkey");
+ result = ISC_R_SUCCESS;
goto failure;
}
+
/*
- * XXXDCL Section 4.1.3: Limit GSS_S_CONTINUE_NEEDED to 10 times.
+ * Multi-round GSS-API negotiation (GSS_S_CONTINUE_NEEDED) is
+ * rejected in dst_gssapi_acceptctx(), so if we reach here the
+ * negotiation is complete and the principal must be set.
*/
isc_stdtime_get(&now);
if (dns_name_countlabels(principal) == 0U) {
- if (tsigkey != NULL) {
- dns_tsigkey_detach(&tsigkey);
- }
+ tkeyout->error = dns_tsigerror_badkey;
+ tkey_log("process_gsstkey(): "
+ "completed context with empty principal");
+ result = ISC_R_SUCCESS;
+ goto failure;
} else if (tsigkey == NULL) {
#if HAVE_GSSAPI
OM_uint32 gret, minor, lifetime;
@@ -640,6 +629,9 @@ process_gsstkey(dns_message_t *msg, dns_name_t *name, dns_rdata_tkey_t *tkeyin,
return ISC_R_SUCCESS;
failure:
+ if (dstkey == NULL && gss_ctx != NULL) {
+ dst_gssapi_deletectx(tctx->mctx, &gss_ctx);
+ }
if (tsigkey != NULL) {
dns_tsigkey_detach(&tsigkey);
}
@@ -652,9 +644,9 @@ failure:
isc_buffer_free(&outtoken);
}
- tkey_log("process_gsstkey(): %s", isc_result_totext(result)); /* XXXSRA
- */
-
+ if (result != ISC_R_SUCCESS) {
+ tkey_log("process_gsstkey(): %s", isc_result_totext(result));
+ }
return result;
}
@@ -1577,9 +1569,8 @@ dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg,
NULL));
/*
- * XXXSRA This seems confused. If we got CONTINUE from initctx,
- * the GSS negotiation hasn't completed yet, so we can't sign
- * anything yet.
+ * GSS negotiation is complete (CONTINUE returned earlier).
+ * Create the TSIG key from the established context.
*/
RETERR(dns_tsigkey_createfromkey(
--
2.54.0

View 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

View File

@ -80,7 +80,7 @@ License: MPL-2.0 AND ISC AND MIT AND BSD-3-Clause AND BSD-2-Clause
# Before rebasing bind, ensure bind-dyndb-ldap is ready to be rebuild and use side-tag with it.
# Updating just bind will cause freeipa-dns-server package to be uninstallable.
Version: 9.18.33
Release: 15%{?dist}.1
Release: 15%{?dist}.2
Epoch: 32
Url: https://www.isc.org/downloads/bind/
#
@ -160,6 +160,10 @@ Patch227: bind-9.20-CVE-2025-8677-dual-signing-test.patch
# https://gitlab.isc.org/isc-projects/bind9/-/commit/5ef459eeaa92222ad28d2186f5eae9a586dece70
Patch228: bind-9.18-CVE-2026-1519.patch
Patch229: bind-9.18-CVE-2026-1519-test.patch
# https://gitlab.isc.org/isc-projects/bind9/-/commit/03ce21cf3099bd461f95ac1f4f22cce80ae1ba65
Patch230: bind-9.18-CVE-2026-3039.patch
# https://gitlab.isc.org/isc-projects/bind9/-/commit/7ce6ce37b1b04af0953ed2d3211587465085600e
Patch231: bind-9.18-CVE-2026-5946.patch
%{?systemd_ordering}
# https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers
@ -961,6 +965,10 @@ fi;
%endif
%changelog
* Mon May 25 2026 Petr Menšík <pemensik@redhat.com> - 32:9.18.33-15.2
- 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-15.1
- Prevent Denial of Service via maliciously crafted DNSSEC-validated zone
(CVE-2026-1519)