From dc1e0ea269697ba11ec76de294a2dcd7e85ef7d7 Mon Sep 17 00:00:00 2001 From: liyunqing Date: Mon, 10 Nov 2025 16:11:09 +0800 Subject: [PATCH] backport fix CVE-2025-40778 --- backport-CVE-2025-40778.patch | 341 ++++++++++++++++++++++++++++++++++ bind.spec | 11 +- 2 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 backport-CVE-2025-40778.patch diff --git a/backport-CVE-2025-40778.patch b/backport-CVE-2025-40778.patch new file mode 100644 index 0000000..0ba8325 --- /dev/null +++ b/backport-CVE-2025-40778.patch @@ -0,0 +1,341 @@ +From f5bb40bd485d26486acea5b2b175a29acf5e53f3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Thu, 30 Oct 2025 19:27:56 +0100 +Subject: [PATCH] Address various spoofing attacks (CVE-2025-40778) + +https://kb.isc.org/docs/cve-2025-40778 + +Upstream 9.11 patch + +--- + lib/dns/include/dns/message.h | 8 ++++ + lib/dns/message.c | 16 +++++++- + lib/dns/resolver.c | 77 +++++++++++++++++++++++++++-------- + 3 files changed, 84 insertions(+), 17 deletions(-) + +diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h +index d5d0350..372ff17 100644 +--- a/lib/dns/include/dns/message.h ++++ b/lib/dns/include/dns/message.h +@@ -226,6 +226,7 @@ struct dns_message { + unsigned int cc_bad : 1; + unsigned int tkey : 1; + unsigned int rdclass_set : 1; ++ unsigned int has_dname : 1; + + unsigned int opt_reserved; + unsigned int sig_reserved; +@@ -1447,6 +1448,13 @@ dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass); + * \li msg be a valid message with parsing intent. + */ + ++bool ++dns_message_hasdname(dns_message_t *msg); ++/*%< ++ * Return whether a DNAME was detected in the ANSWER section of a QUERY ++ * message when it was parsed. ++ */ ++ + ISC_LANG_ENDDECLS + + #endif /* DNS_MESSAGE_H */ +diff --git a/lib/dns/message.c b/lib/dns/message.c +index 4780245..c1e0415 100644 +--- a/lib/dns/message.c ++++ b/lib/dns/message.c +@@ -454,6 +454,7 @@ msginit(dns_message_t *m) { + m->cc_bad = 0; + m->tkey = 0; + m->rdclass_set = 0; ++ m->has_dname = 0; + m->querytsig = NULL; + m->indent.string = dns_master_indentstr; + m->indent.count = dns_master_indent; +@@ -1668,10 +1669,15 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + * Windows doesn't like TSIG names to be compressed. + */ + msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; +- rdataset = NULL; + free_rdataset = false; + free_name = false; ++ } else if (rdtype == dns_rdatatype_dname && ++ sectionid == DNS_SECTION_ANSWER && ++ msg->opcode == dns_opcode_query) ++ { ++ msg->has_dname = 1; + } ++ rdataset = NULL; + + if (seen_problem) { + if (free_name) +@@ -4541,3 +4547,11 @@ dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass) { + msg->rdclass = rdclass; + msg->rdclass_set = 1; + } ++ ++bool ++dns_message_hasdname(dns_message_t *msg) { ++ ++ REQUIRE(DNS_MESSAGE_VALID(msg)); ++ ++ return (msg->has_dname); ++} +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c +index 6c0b804..b780abe 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -428,6 +428,7 @@ typedef struct { + typedef struct { + fetchctx_t * fctx; + dns_message_t * rmessage; ++ dns_name_t * ns_name; + } dns_chkarg_t; + + struct dns_fetch { +@@ -6252,7 +6253,9 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset, + * locally served zone. + */ + static inline bool +-name_external(dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) { ++name_external(dns_name_t *name, dns_name_t *ns_name, dns_rdatatype_t type, ++ fetchctx_t *fctx) ++{ + isc_result_t result; + dns_forwarders_t *forwarders = NULL; + dns_fixedname_t fixed, zfixed; +@@ -6270,7 +6273,9 @@ name_external(dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) { + int _orderp = 0; + unsigned int _nlabelsp = 0; + +- apex = ISFORWARDER(fctx->addrinfo) ? fctx->fwdname : &fctx->domain; ++ apex = ISFORWARDER(fctx->addrinfo) ++ ? fctx->fwdname ++ : (ns_name != NULL) ? ns_name : &fctx->domain; + + /* + * The name is outside the queried namespace. +@@ -6379,7 +6384,7 @@ check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type, + result = dns_message_findname(rmessage, section, addname, + dns_rdatatype_any, 0, &name, NULL); + if (result == ISC_R_SUCCESS) { +- external = name_external(name, type, fctx); ++ external = name_external(name, chkarg->ns_name, type, fctx); + if (type == dns_rdatatype_a) { + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; +@@ -6453,7 +6458,7 @@ chase_additional(fetchctx_t *fctx, dns_message_t *rmessage) { + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + if (CHASE(rdataset)) { +- dns_chkarg_t chkarg; ++ dns_chkarg_t chkarg = { 0 }; + chkarg.fctx = fctx; + chkarg.rmessage = rmessage; + rdataset->attributes &= ~DNS_RDATASETATTR_CHASE; +@@ -7061,7 +7066,7 @@ noanswer_response(fetchctx_t *fctx, dns_message_t *message, + * we're not following a chain.) + */ + if (!negative_response && ns_name != NULL && oqname == NULL) { +- dns_chkarg_t chkarg; ++ dns_chkarg_t chkarg = { 0 }; + /* + * We already know ns_name is a subdomain of fctx->domain. + * If ns_name is equal to fctx->domain, we're not making +@@ -7093,6 +7098,7 @@ noanswer_response(fetchctx_t *fctx, dns_message_t *message, + FCTX_ATTR_SET(fctx, FCTX_ATTR_GLUING); + chkarg.fctx = fctx; + chkarg.rmessage = message; ++ chkarg.ns_name = ns_name; + + /* + * Mark the glue records in the additional section to be cached. +@@ -7110,7 +7116,7 @@ noanswer_response(fetchctx_t *fctx, dns_message_t *message, + if ((look_in_options & LOOK_FOR_GLUE_IN_ANSWER) != 0 && + (fctx->type == dns_rdatatype_aaaa || + fctx->type == dns_rdatatype_a)) { +- dns_chkarg_t chkarg; ++ dns_chkarg_t chkarg = { 0 }; + chkarg.fcx = fctx; + chkarg.rmessage = message; + (void)dns_rdataset_additionaldata(ns_rdataset, +@@ -7190,8 +7196,9 @@ validinanswer(dns_rdataset_t *rdataset, fetchctx_t *fctx) { + } + + static isc_result_t +-answer_response(fetchctx_t *fctx, dns_message_t *message) { ++answer_response(resquery_t *query, dns_message_t *message) { + isc_result_t result; ++ fetchctx_t *fctx = NULL; + dns_name_t *name = NULL, *qname = NULL, *ns_name = NULL; + dns_name_t *aname = NULL, *cname = NULL, *dname = NULL; + dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; +@@ -7204,6 +7211,8 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) { + dns_view_t *view = NULL; + dns_trust_t trust; + ++ REQUIRE(VALID_QUERY(query)); ++ fctx = query->fctx; + REQUIRE(VALID_FCTX(fctx)); + + FCTXTRACE("answer_response"); +@@ -7268,7 +7277,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) { + /* + * Don't accept DNAME from parent namespace. + */ +- if (name_external(name, dns_rdatatype_dname, fctx)) { ++ if (name_external(name, NULL, dns_rdatatype_dname, fctx)) { + continue; + } + +@@ -7323,7 +7332,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) { + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { +- dns_chkarg_t chkarg; ++ dns_chkarg_t chkarg = { 0 }; + if (!validinanswer(rdataset, fctx)) { + return (DNS_R_FORMERR); + } +@@ -7354,12 +7363,13 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) { + rdataset->attributes &= ~DNS_RDATASETATTR_CHASE; + chkarg.fctx = fctx; + chkarg.rmessage = message; ++ chkarg.ns_name = ns_name; + (void)dns_rdataset_additionaldata(rdataset, + check_related, + &chkarg, 0); + } + } else if (aname != NULL) { +- dns_chkarg_t chkarg; ++ dns_chkarg_t chkarg = { 0 }; + if (!validinanswer(ardataset, fctx)) + return (DNS_R_FORMERR); + if ((ardataset->type == dns_rdatatype_a || +@@ -7383,6 +7393,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) { + ardataset->trust = trust; + chkarg.fctx = fctx; + chkarg.rmessage = message; ++ chkarg.ns_name = ns_name; + (void)dns_rdataset_additionaldata(ardataset, check_related, + &chkarg, 0); + for (sigrdataset = ISC_LIST_HEAD(aname->list); +@@ -7504,12 +7515,22 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) { + * + * We expect there to be only one owner name for all the rdatasets + * in this section, and we expect that it is not external. ++ * If the message was not sent over TCP or otherwise secured, ++ * skip this. + */ +- result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); ++ if (message->cc_ok || message->tsig != NULL || message->sig0 != NULL || ++ (query->options & DNS_FETCHOPT_TCP) != 0) ++ { ++ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); ++ } else { ++ done = true; ++ } + while (!done && result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); +- if (!name_external(name, dns_rdatatype_ns, fctx)) { ++ if (!name_external(name, ns_name, dns_rdatatype_ns, fctx) && ++ dns_name_issubdomain(&fctx->name, name)) ++ { + /* + * We expect to find NS or SIG NS rdatasets, and + * nothing else. +@@ -7520,7 +7541,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) { + if (rdataset->type == dns_rdatatype_ns || + (rdataset->type == dns_rdatatype_rrsig && + rdataset->covers == dns_rdatatype_ns)) { +- dns_chkarg_t chkarg; ++ dns_chkarg_t chkarg = { 0 }; + name->attributes |= + DNS_NAMEATTR_CACHE; + rdataset->attributes |= +@@ -7544,6 +7565,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) { + */ + chkarg.fctx = fctx; + chkarg.rmessage = message; ++ chkarg.ns_name = ns_name; + (void)dns_rdataset_additionaldata( + rdataset, + check_related, +@@ -7983,6 +8005,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + unsigned int bucketnum; + dns_resolver_t *res; + bool bucket_empty; ++ bool secured = false; + #ifdef HAVE_DNSTAP + isc_socket_t *sock = NULL; + isc_sockaddr_t localaddr, *la = NULL; +@@ -8289,6 +8312,15 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + goto done; + } + ++ /* ++ * Remember whether this message was signed or had a ++ * valid client cookie; if not, we may need to retry over ++ * TCP later. ++ */ ++ if (rmessage->cc_ok || rmessage->tsig != NULL || rmessage->sig0 != NULL) ++ { ++ secured = true; ++ } + /* + * The dispatcher should ensure we only get responses with QR set. + */ +@@ -8301,6 +8333,18 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + * ensured by the dispatch code). + */ + ++ /* ++ * Check whether we need to retry over TCP for some other ++ * reason. ++ */ ++ if (!secured && (options & DNS_FETCHOPT_TCP) == 0) { ++ if (dns_message_hasdname(rmessage)) { ++ options |= DNS_FETCHOPT_TCP; ++ resend = true; ++ goto done; ++ } ++ } ++ + /* + * We have an affirmative response to the query and we have + * previously got a response from this server which indicated +@@ -8655,7 +8699,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + if ((rmessage->flags & DNS_MESSAGEFLAG_AA) != 0 || + ISFORWARDER(query->addrinfo)) + { +- result = answer_response(fctx, rmessage); ++ result = answer_response(query, rmessage); + if (result != ISC_R_SUCCESS) + FCTXTRACE3("answer_response (AA/fwd)", result); + } else if (iscname(fctx, rmessage) && +@@ -8667,7 +8711,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + * answer when a CNAME is followed. We should treat + * it as a valid answer. + */ +- result = answer_response(fctx, rmessage); ++ result = answer_response(query, rmessage); + if (result != ISC_R_SUCCESS) + FCTXTRACE3("answer_response (!ANY/!CNAME)", + result); +@@ -8676,7 +8720,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + /* + * Lame response !!!. + */ +- result = answer_response(fctx, rmessage); ++ result = answer_response(query, rmessage); + if (result != ISC_R_SUCCESS) + FCTXTRACE3("answer_response (!NS)", result); + } else { +@@ -10678,3 +10722,4 @@ dns_resolver_setnonbackofftries(dns_resolver_t *resolver, unsigned int tries) { + + resolver->nonbackofftries = tries; + } ++ +-- +2.37.3.windows.1 + diff --git a/bind.spec b/bind.spec index 40b6961..325107d 100644 --- a/bind.spec +++ b/bind.spec @@ -19,7 +19,7 @@ Name: bind Summary: Domain Name System (DNS) Server (named) License: MPLv2.0 Version: 9.11.21 -Release: 21 +Release: 22 Epoch: 32 Url: http://www.isc.org/products/BIND/ Source0: https://ftp.isc.org/isc/bind9/9.11.21/bind-%{version}.tar.gz @@ -254,6 +254,7 @@ Patch6077:backport-CVE-2024-11187.patch Patch6078:backport-bind-9.11-CVE-2023-50387.patch Patch6079:backport-bind-9.11-CVE-2023-50387-fixup.patch +Patch6080:backport-CVE-2025-40778.patch %description Berkeley Internet Name Domain (BIND) is an implementation of the Domain Name @@ -550,6 +551,8 @@ cp -a %{SOURCE29} lib/dns/tests/testdata/dstrandom/random.data %patch199 -p1 +%patch6080 -p1 + %if %{with PKCS11} cp -r bin/named{,-pkcs11} cp -r bin/dnssec{,-pkcs11} @@ -1328,6 +1331,12 @@ rm -rf ${RPM_BUILD_ROOT} %changelog +* Mon Nov 10 2025 liyunqing - 32:9.11.21-22 +- Type:CVE +- CVE:CVE-2025-40778 +- SUG:NA +- DESC:fix CVE-2025-40778 + * Mon Oct 20 2025 lifeifei - 32:9.11.21-21 - Type:CVE - CVE:CVE-2023-50387,CVE-2023-50868 -- Gitee