diff --git a/SOURCES/bind-9.11-CVE-2026-3039.patch b/SOURCES/bind-9.11-CVE-2026-3039.patch new file mode 100644 index 0000000..e758c40 --- /dev/null +++ b/SOURCES/bind-9.11-CVE-2026-3039.patch @@ -0,0 +1,375 @@ +From 7c2afb490391a308733da1c37730328477bd80d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= +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 3883058bf284de2889e4e3676767e58ad91a0ae3) +(cherry picked from commit e3c74f19c6b3b29ec649062f95bf1df5688a0799) + +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 f2240d2d06a1a68b622bd6b00a52c6fe4274426d) +(cherry picked from commit f2f1c0cb244a6d9e2fd8550a38bd2465856abce3) +--- + lib/dns/gssapictx.c | 127 ++++++++++++++++++++--------------- + lib/dns/include/dst/gssapi.h | 17 ++--- + lib/dns/tkey.c | 50 +++++++------- + 3 files changed, 104 insertions(+), 90 deletions(-) + +diff --git a/lib/dns/gssapictx.c b/lib/dns/gssapictx.c +index 482c25e1cc..a58c3071d4 100644 +--- a/lib/dns/gssapictx.c ++++ b/lib/dns/gssapictx.c +@@ -594,7 +594,14 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, + 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", +@@ -619,14 +626,10 @@ dst_gssapi_initctx(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) ++out: ++ if (gouttoken.length != 0U) { + (void)gss_release_buffer(&minor, &gouttoken); ++ } + (void)gss_release_name(&minor, &gname); + return (result); + } +@@ -634,7 +637,7 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, + isc_result_t + dst_gssapi_acceptctx(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, + gss_ctx_id_t *ctxout, dns_name_t *principal, + isc_mem_t *mctx) { + isc_region_t r; +@@ -647,15 +650,11 @@ dst_gssapi_acceptctx(gss_cred_id_t cred, + 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 defined(ISC_PLATFORM_GSSAPI_KRB5_HEADER) || defined(WIN32) + gret = gsskrb5_register_acceptor_identity(gssapi_keytab); +@@ -697,8 +696,15 @@ dst_gssapi_acceptctx(gss_cred_id_t cred, + + 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: +@@ -711,7 +717,7 @@ dst_gssapi_acceptctx(gss_cred_id_t cred, + 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))); +@@ -722,55 +728,70 @@ dst_gssapi_acceptctx(gss_cred_id_t cred, + } + + if (gouttoken.length > 0U) { +- RETERR(isc_buffer_allocate(mctx, outtoken, ++ RETERR(isc_buffer_allocate(mctx, outtokenp, + (unsigned int)gouttoken.length)); + GBUFFER_TO_REGION(gouttoken, r); +- RETERR(isc_buffer_copyregion(*outtoken, &r)); ++ result = isc_buffer_copyregion(*outtokenp, &r); ++ if (result != ISC_R_SUCCESS) { ++ goto out; ++ } + (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 out; ++ } + +- 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; ++ result = dns_name_fromtext(principal, &namebuf, dns_rootname, 0, NULL); ++ if (result != ISC_R_SUCCESS) { ++ goto out; ++ } + + *ctxout = context; + +- out: ++out: ++ 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 8e31587871..bf1c4c959a 100644 +--- a/lib/dns/include/dst/gssapi.h ++++ b/lib/dns/include/dst/gssapi.h +@@ -126,20 +126,17 @@ dst_gssapi_acceptctx(gss_cred_id_t cred, + * 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 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 ae7a76cd3d..03bf521ee6 100644 +--- a/lib/dns/tkey.c ++++ b/lib/dns/tkey.c +@@ -503,18 +503,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); + + /* +@@ -523,25 +514,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) { + #ifdef GSSAPI + OM_uint32 gret, minor, lifetime; +@@ -612,6 +605,10 @@ 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); + +@@ -621,10 +618,10 @@ failure: + if (outtoken != NULL) + isc_buffer_free(&outtoken); + +- tkey_log("process_gsstkey(): %s", +- isc_result_totext(result)); /* XXXSRA */ +- +- return (result); ++ if (result != ISC_R_SUCCESS) { ++ tkey_log("process_gsstkey(): %s", isc_result_totext(result)); ++ } ++ return result; + } + + static isc_result_t +@@ -1521,9 +1518,8 @@ dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg, + &dstkey, 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(tkeyname, +-- +2.54.0 + diff --git a/SOURCES/bind-9.11-CVE-2026-5946.patch b/SOURCES/bind-9.11-CVE-2026-5946.patch new file mode 100644 index 0000000..2dc4069 --- /dev/null +++ b/SOURCES/bind-9.11-CVE-2026-5946.patch @@ -0,0 +1,512 @@ +From de237c66c901b451e4957a28e0d953c57648eab9 Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Tue, 3 Mar 2026 14:00:38 -0800 +Subject: [PATCH] Disable recursion for non-IN classes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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 70532a37a1aec761e8a12444852866ce9d9d5fcc) +(cherry picked from commit cf0d5a4e385525e21f2ae39098b1ab90c1137a2a) +(cherry picked from commit c6b05d8449d94c74e85be70b822b7ad439abca40) + +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 9ae24c32bec16c0f64225ef04f34670018bf0765) +(cherry picked from commit a2ca2408b3ff031c426c5dc785f550c9d30bf4aa) +(cherry picked from commit f107b702c4ff4a68042d0dad8384544dd99954a8) + +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 d41865a458b9ecd76be4097ac1bea1005cad72db) +(cherry picked from commit 1c8016c91c3674929f87cbe7ad09f3670e05ad4e) +(cherry picked from commit 6c50d4812c35fe982614cd53355c4048ab09267a) + +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 7de5160517ae69196d1c323b8627b267cdd10761) +(cherry picked from commit 3c44de9e6252ec1c7742ef02ecc0d6cbf1cde5e9) +(cherry picked from commit f7d5fe628c220245a14397207893424f0b27ff53) + +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 4cd3d8e6d866143ddc62df821a1007bf3ee7f083) +(cherry picked from commit fa60101e910346e64fa2a684b903fbcb84d8243b) +(cherry picked from commit c01d298f53b0ffbf38bf19867f368f60a9ea4ecf) + +Pass empty string instead of NULL to ns_client_dumpmessage() + +The two new call sites added by the CLASS-validation work passed NULL +as the reason, but ns_client_dumpmessage() bails out early on a NULL +reason — so the message dump never happened. The intent was to dump +the message and let the follow-up ns_client_log() carry the reason +text, so pass "" to suppress the prefix without short-circuiting the +dump. + +(cherry picked from commit 8af39c360407a92ef31bf233b9df760d1bb9fb5f) +(cherry picked from commit 4d7e442c9aee72851f2b2d973273ebc92da42cf0) + +%RH +Backport fixes. +--- + bin/named/client.c | 56 +++++++++++++++++++++++++++++-- + bin/named/server.c | 38 ++++++--------------- + bin/named/update.c | 31 +++++++++-------- + bin/tests/system/unknown/tests.sh | 12 +++---- + lib/dns/adb.c | 2 +- + lib/dns/message.c | 11 ++++++ + lib/dns/resolver.c | 13 +++++-- + 7 files changed, 109 insertions(+), 54 deletions(-) + +diff --git a/bin/named/client.c b/bin/named/client.c +index ea121b301e..6990e02b03 100644 +--- a/bin/named/client.c ++++ b/bin/named/client.c +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2829,7 +2830,9 @@ client_request(isc_task_t *task, isc_event_t *event) { + goto cleanup; + } + +- 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) +@@ -2851,6 +2854,44 @@ client_request(isc_task_t *task, isc_event_t *event) { + "message class could not be determined"); + ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR); + goto cleanup; ++ 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, ""); ++ 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, DNS_R_NOTIMP); ++ goto cleanup; + } + + /* +@@ -2969,7 +3010,7 @@ client_request(isc_task_t *task, isc_event_t *event) { + 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, ""); + ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED); + goto cleanup; + } +@@ -3149,11 +3190,19 @@ client_request(isc_task_t *task, isc_event_t *event) { + break; + case dns_opcode_update: + CTRACE("update"); ++ if (client->view->rdclass != dns_rdataclass_in) { ++ ns_client_error(client, DNS_R_NOTIMP); ++ break; ++ } + ns_client_settimeout(client, 60); + ns_update_start(client, sigresult); + 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); + break; +@@ -4241,8 +4290,9 @@ ns_client_dumpmessage(ns_client_t *client, const char *reason) { + int len = 1024; + isc_result_t result; + +- if (!isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(1))) ++ if (!isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(1)) || reason == NULL) { + return; ++ } + + /* + * Note that these are multiline debug messages. We want a newline +diff --git a/bin/named/server.c b/bin/named/server.c +index 2086e41ca9..be521f6cf2 100644 +--- a/bin/named/server.c ++++ b/bin/named/server.c +@@ -1756,6 +1756,7 @@ 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(ns_g_server->zonemgr, zone); + if (result != ISC_R_SUCCESS) + return (result); +@@ -4170,32 +4171,14 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, + CHECK(configure_alternates(config, view, alternates)); + + /* +- * 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, ns_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(ns_g_lctx, NS_LOGCATEGORY_GENERAL, +- NS_LOGMODULE_SERVER, ISC_LOG_WARNING, +- "no root hints for view '%s'", +- view->name); +- } +- + /* + * Configure the view's TSIG keys. + */ +@@ -4302,7 +4285,8 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, + obj = NULL; + result = ns_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 = ns_config_get(maps, "auth-nxdomain", &obj); +@@ -4409,10 +4393,10 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, + "allow-query-cache", NULL, actx, + ns_g_mctx, &view->cacheacl)); + +- if (strcmp(view->name, "_bind") != 0 && +- view->rdclass != dns_rdataclass_chaos) +- { +- /* named.conf only */ ++ if (view->rdclass != dns_rdataclass_in) { ++ dns_acl_none(ns_g_mctx, &view->recursionacl); ++ dns_acl_none(ns_g_mctx, &view->recursiononacl); ++ } else { + CHECK(configure_view_acl(vconfig, config, NULL, + "allow-recursion", NULL, actx, + ns_g_mctx, &view->recursionacl)); +diff --git a/bin/named/update.c b/bin/named/update.c +index 407ac38272..be469f8e91 100644 +--- a/bin/named/update.c ++++ b/bin/named/update.c +@@ -1269,7 +1269,10 @@ replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { + dbsig.algorithm == updatesig.algorithm) + 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 +@@ -1412,8 +1415,7 @@ add_rr_prepare_action(void *data, rr_t *rr) { + * '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) + { +@@ -1430,7 +1432,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; + } + + /*% +@@ -1580,7 +1582,7 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_ttl_t ttl; + dns_rdataclass_t update_class; +- get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, ++ get_current_rr(request, DNS_SECTION_UPDATE, + &name, &rdata, &covers, &ttl, &update_class); + + if (! dns_name_issubdomain(name, zonename)) +@@ -1812,6 +1814,9 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) { + CHECK(DNS_R_DROP); + } + ++ /* Updates are only supported for class IN. */ ++ INSIST(dns_zone_getclass(zone) == dns_rdataclass_in); ++ + event = (update_event_t *) + isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE, + update_action, NULL, sizeof(*event)); +@@ -2798,7 +2803,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; +@@ -2819,10 +2823,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. + */ +@@ -2843,8 +2847,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, +@@ -2902,7 +2906,7 @@ update_action(isc_task_t *task, isc_event_t *event) { + "satisfied"); + } + } +- } else if (update_class == zoneclass) { ++ } else if (update_class == dns_rdataclass_in) { + /* "temp += rr;" */ + result = temp_append(&temp, name, &rdata); + if (result != ISC_R_SUCCESS) { +@@ -2960,11 +2964,10 @@ update_action(isc_task_t *task, isc_event_t *event) { + dns_rdataclass_t update_class; + bool flag; + +- get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, +- &name, &rdata, &covers, &ttl, &update_class); +- +- if (update_class == zoneclass) { ++ get_current_rr(request, DNS_SECTION_UPDATE, &name, ++ &rdata, &covers, &ttl, &update_class); + ++ if (update_class == dns_rdataclass_in) { + /* + * RFC1123 doesn't allow MF and MD in master zones. + */ +diff --git a/bin/tests/system/unknown/tests.sh b/bin/tests/system/unknown/tests.sh +index deb9a419ab..a6ceb7c69b 100644 +--- a/bin/tests/system/unknown/tests.sh ++++ b/bin/tests/system/unknown/tests.sh +@@ -73,8 +73,8 @@ echo_i "querying for various representations of a CLASS10 TYPE1 record" + for i in 1 2 + do + ret=0 +- $DIG +short $DIGOPTS @10.53.0.1 a$i.example a class10 > dig.out || ret=1 +- echo '\# 4 0A000001' | $DIFF - dig.out || ret=1 ++ $DIG $DIGOPTS @10.53.0.1 a$i.example a class10 > dig.out || ret=1 ++ grep -q "NOTIMP" dig.out || ret=1 + if [ $ret != 0 ] + then + echo_i "#$i failed" +@@ -86,8 +86,8 @@ echo_i "querying for various representations of a CLASS10 TXT record" + for i in 1 2 3 4 + do + ret=0 +- $DIG +short $DIGOPTS @10.53.0.1 txt$i.example txt class10 > dig.out || ret=1 +- echo '"hello"' | $DIFF - dig.out || ret=1 ++ $DIG $DIGOPTS @10.53.0.1 txt$i.example txt class10 > dig.out || ret=1 ++ grep -q "NOTIMP" dig.out || ret=1 + if [ $ret != 0 ] + then + echo_i "#$i failed" +@@ -99,8 +99,8 @@ echo_i "querying for various representations of a CLASS10 TYPE123 record" + for i in 1 2 + do + ret=0 +- $DIG +short $DIGOPTS @10.53.0.1 unk$i.example type123 class10 > dig.out || ret=1 +- echo '\# 1 00' | $DIFF - dig.out || ret=1 ++ $DIG $DIGOPTS @10.53.0.1 unk$i.example type123 class10 > dig.out || ret=1 ++ grep -q "NOTIMP" dig.out || ret=1 + if [ $ret != 0 ] + then + echo_i "#$i failed" +diff --git a/lib/dns/adb.c b/lib/dns/adb.c +index e12c804f2b..25cfa32873 100644 +--- a/lib/dns/adb.c ++++ b/lib/dns/adb.c +@@ -892,7 +892,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); + if (rdtype == dns_rdatatype_a) + findoptions = DNS_ADBFIND_INET; + else +diff --git a/lib/dns/message.c b/lib/dns/message.c +index 5603bcadf9..f9e3163d71 100644 +--- a/lib/dns/message.c ++++ b/lib/dns/message.c +@@ -1179,6 +1179,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 1c704551aa..5331ea9dd2 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -6514,9 +6514,16 @@ is_answeraddress_allowed(dns_view_t *view, dns_name_t *name, + } + + /* +- * Otherwise, search the filter list for a match for each address +- * record. If a match is found, the address should be filtered, +- * so should the entire answer. ++ * 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 ++ * filtered, so should the entire answer. + */ + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; +-- +2.54.0 + diff --git a/SPECS/bind.spec b/SPECS/bind.spec index 8b9bcf6..41efe2b 100644 --- a/SPECS/bind.spec +++ b/SPECS/bind.spec @@ -68,7 +68,7 @@ Summary: The Berkeley Internet Name Domain (BIND) DNS (Domain Name System) serv Name: bind License: MPLv2.0 Version: 9.11.36 -Release: 16%{?PATCHVER:.%{PATCHVER}}%{?PREVER:.%{PREVER}}%{?dist}.7 +Release: 16%{?PATCHVER:.%{PATCHVER}}%{?PREVER:.%{PREVER}}%{?dist}.8 Epoch: 32 Url: https://www.isc.org/downloads/bind/ # @@ -207,6 +207,11 @@ Patch213: bind-9.11-d-max-records-checkconf.patch Patch214: bind-9.11-CVE-2025-40778.patch # https://gitlab.isc.org/isc-projects/bind9/commit/e5357c1623da3842227d2c76468b76bc983584d6 Patch215: bind-9.11-CVE-2026-1519.patch +# https://gitlab.isc.org/isc-projects/bind9/commit/94a96c69193587b2c6df30055fbb0c1d00622a7d +Patch216: bind-9.11-CVE-2026-3039.patch +# https://gitlab.isc.org/isc-projects/bind9/commit/bba70f08b1818f697e8292b79270a464279adc23 +# https://gitlab.isc.org/isc-projects/bind9/commit/befe1903d0aebfd69cb362c429afc1f9c1c89610 +Patch217: bind-9.11-CVE-2026-5946.patch # SDB patches Patch11: bind-9.3.2b2-sdbsrc.patch @@ -637,6 +642,8 @@ are used for building ISC DHCP. %patch -P 213 -p1 -b .records-checkconf %patch -P 214 -p1 -b .CVE-2025-40778 %patch -P 215 -p1 -b .CVE-2026-1519 +%patch -P 216 -p1 -b .CVE-2026-3039 +%patch -P 217 -p1 -b .CVE-2026-5946 mkdir lib/dns/tests/testdata/dstrandom cp -a %{SOURCE50} lib/dns/tests/testdata/dstrandom/random.data @@ -1689,6 +1696,10 @@ rm -rf ${RPM_BUILD_ROOT} %endif %changelog +* Wed May 27 2026 Petr Menšík - 32:9.11.36-16.8 +- Fix GSS-API resource leak (CVE-2026-3039) +- Invalid handling of CLASS != IN (CVE-2026-5946) + * Fri Mar 27 2026 Petr Menšík - 32:9.11.36-16.7 - Denial of Service via maliciously crafted DNSSEC-validated zone (CVE-2026-1519)