diff --git a/backport-0001-CVE-2025-40778.patch b/backport-0001-CVE-2025-40778.patch new file mode 100644 index 0000000000000000000000000000000000000000..815bbfca14966127f85e60f94e5e9762ba29ffc2 --- /dev/null +++ b/backport-0001-CVE-2025-40778.patch @@ -0,0 +1,36 @@ +From 41ab0709d1bde6fb8a2dde623d00e69bc48fab0d Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Mon, 29 Sep 2025 22:17:39 -0700 +Subject: [PATCH] Tighten restrictions on caching NS RRsets in authority + section + +To prevent certain spoofing attacks, a new check has been added +to the existing rules for whether NS data can be cached: the owner +name of the NS RRset must be an ancestor of the name being queried. + +(cherry picked from commit fa153f791f9324bf84abf8d259e11c0531fe6e25) + +Conflict:NA +Reference:https://gitlab.isc.org/isc-projects/bind9/-/commit/41ab0709d1bde6f +--- + bind/bind-9.11.14/lib/dns/resolver.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/bind/bind-9.11.14/lib/dns/resolver.c b/bind/bind-9.11.36/lib/dns/resolver.c +index cada655..8bcaede 100644 +--- a/bind/bind-9.11.14/lib/dns/resolver.c ++++ b/bind/bind-9.11.14/lib/dns/resolver.c +@@ -7507,7 +7507,9 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) { + 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, dns_rdatatype_ns, fctx) && ++ dns_name_issubdomain(&fctx->name, name)) ++ { + /* + * We expect to find NS or SIG NS rdatasets, and + * nothing else. +-- +2.33.0 + diff --git a/backport-0002-CVE-2025-40778.patch b/backport-0002-CVE-2025-40778.patch new file mode 100644 index 0000000000000000000000000000000000000000..3b76cbafafbc329a15deae4eac46b1e0f6ecaa67 --- /dev/null +++ b/backport-0002-CVE-2025-40778.patch @@ -0,0 +1,207 @@ +From eba8e3eb33f907a1a622c065138e19b087b6e4f1 Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Mon, 29 Sep 2025 22:24:33 -0700 +Subject: [PATCH] Further restrict addresses that are cached when +processing +referrals + +Use the owner name of the NS record as the bailwick apex name +when determining which additional records to cache, rather than +the name of the delegating zone (or a parent thereof). + +(cherry picked from commit a41054e9e606a61f1b3c8bc0c54e2f1059347165) + +Conflict:introduce dns_chkarg_t in commit d391a0b4c to adapt the patch. +Reference:https://gitlab.isc.org/isc-projects/bind9/-/commit/eba8e3eb33f9 + +--- + bind/bind-9.11.14/lib/dns/resolver.c | 55 +++++++++++++++++++++------- + 1 file changed, 42 insertions(+), 13 deletions(-) + +diff --git a/bind/bind-9.11.14/lib/dns/resolver.c b/bind/bind-9.11.14/lib/dns/resolver.c +index 3f352e6..efef946 100644 +--- a/bind/bind-9.11.14/lib/dns/resolver.c ++++ b/bind/bind-9.11.14/lib/dns/resolver.c +@@ -416,6 +416,11 @@ typedef struct { + fetchctx_t * fctx; + } dns_valarg_t; + ++typedef struct { ++ fetchctx_t * fctx; ++ dns_name_t * ns_name; ++} dns_chkarg_t; ++ + struct dns_fetch { + unsigned int magic; + isc_mem_t * mctx; +@@ -6204,7 +6209,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; +@@ -6222,7 +6229,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. +@@ -6307,7 +6316,8 @@ static isc_result_t + check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type, + dns_section_t section) + { +- fetchctx_t *fctx = arg; ++ dns_chkarg_t *chkarg = arg; ++ fetchctx_t *fctx = chkarg->fctx; + isc_result_t result; + dns_name_t *name = NULL; + dns_rdataset_t *rdataset = NULL; +@@ -6329,7 +6339,7 @@ check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type, + result = dns_message_findname(fctx->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; +@@ -6403,10 +6413,12 @@ chase_additional(fetchctx_t *fctx) { + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) { + if (CHASE(rdataset)) { ++ dns_chkarg_t chkarg = { 0 }; ++ chkarg.fctx = fctx; + rdataset->attributes &= ~DNS_RDATASETATTR_CHASE; + (void)dns_rdataset_additionaldata(rdataset, + check_related, +- fctx, 0); ++ &chkarg, 0); + rescan = true; + } + } +@@ -7004,6 +7016,7 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, + * we're not following a chain.) + */ + if (!negative_response && ns_name != NULL && oqname == NULL) { ++ 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 +@@ -7033,8 +7046,10 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, + */ + INSIST(ns_rdataset != NULL); + FCTX_ATTR_SET(fctx, FCTX_ATTR_GLUING); ++ chkarg.fctx = fctx; ++ chkarg.ns_name = ns_name; + (void)dns_rdataset_additionaldata(ns_rdataset, check_related, +- fctx, 0); ++ &chkarg, 0); + #if CHECK_FOR_GLUE_IN_ANSWER + /* + * Look in the answer section for "glue" that is incorrectly +@@ -7045,9 +7060,12 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, + */ + if ((look_in_options & LOOK_FOR_GLUE_IN_ANSWER) != 0 && + (fctx->type == dns_rdatatype_aaaa || +- fctx->type == dns_rdatatype_a)) ++ fctx->type == dns_rdatatype_a)) { ++ dns_chkarg_t chkarg = { 0 }; ++ chkarg.fcx = fctx; + (void)dns_rdataset_additionaldata(ns_rdataset, +- check_answer, fctx, 0); ++ check_answer, &chkarg, 0); ++ } + #endif + FCTX_ATTR_CLR(fctx, FCTX_ATTR_GLUING); + /* +@@ -7202,7 +7220,9 @@ answer_response(fetchctx_t *fctx) { + /* + * 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; + } + +@@ -7257,6 +7277,7 @@ answer_response(fetchctx_t *fctx) { + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { ++ dns_chkarg_t chkarg = { 0 }; + if (!validinanswer(rdataset, fctx)) { + return (DNS_R_FORMERR); + } +@@ -7284,11 +7305,14 @@ answer_response(fetchctx_t *fctx) { + rdataset->attributes |= DNS_RDATASETATTR_ANSWER; + rdataset->attributes |= DNS_RDATASETATTR_CACHE; + rdataset->trust = trust; ++ chkarg.fctx = fctx; ++ chkarg.ns_name = ns_name; + (void)dns_rdataset_additionaldata(rdataset, + check_related, +- fctx, 0); ++ &chkarg, 0); + } + } else if (aname != NULL) { ++ dns_chkarg_t chkarg = { 0 }; + if (!validinanswer(ardataset, fctx)) + return (DNS_R_FORMERR); + if ((ardataset->type == dns_rdatatype_a || +@@ -7310,8 +7334,10 @@ answer_response(fetchctx_t *fctx) { + ardataset->attributes |= DNS_RDATASETATTR_ANSWER; + ardataset->attributes |= DNS_RDATASETATTR_CACHE; + ardataset->trust = trust; ++ chkarg.fctx = fctx; ++ chkarg.ns_name = ns_name; + (void)dns_rdataset_additionaldata(ardataset, check_related, +- fctx, 0); ++ &chkarg, 0); + for (sigrdataset = ISC_LIST_HEAD(aname->list); + sigrdataset != NULL; + sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { +@@ -7436,7 +7462,7 @@ answer_response(fetchctx_t *fctx) { + 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)) + { + /* +@@ -7449,6 +7475,7 @@ answer_response(fetchctx_t *fctx) { + if (rdataset->type == dns_rdatatype_ns || + (rdataset->type == dns_rdatatype_rrsig && + rdataset->covers == dns_rdatatype_ns)) { ++ dns_chkarg_t chkarg = { 0 }; + name->attributes |= + DNS_NAMEATTR_CACHE; + rdataset->attributes |= +@@ -7470,10 +7497,12 @@ answer_response(fetchctx_t *fctx) { + * Mark any additional data related + * to this rdataset. + */ ++ chkarg.fctx = fctx; ++ chkarg.ns_name = ns_name; + (void)dns_rdataset_additionaldata( + rdataset, + check_related, +- fctx, 0); ++ &chkarg, 0); + done = true; + } + } +-- +2.33.0 + diff --git a/backport-0003-CVE-2025-40778.patch b/backport-0003-CVE-2025-40778.patch new file mode 100644 index 0000000000000000000000000000000000000000..dccb978501af55b531f1b7e641f57e537d0bfc29 --- /dev/null +++ b/backport-0003-CVE-2025-40778.patch @@ -0,0 +1,520 @@ +From 0e4cd87bed5efc61443337034a9d96287b4885dc Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Mon, 29 Sep 2025 22:42:44 -0700 +Subject: [PATCH] Retry lookups with unsigned DNAME over TCP + +To prevent spoofed unsigned DNAME responses being accepted retry +response with unsigned DNAMEs over TCP if the response is not TSIG +signed or there isn't a good DNS CLIENT COOKIE. + +To prevent test failures, this required adding TCP support to the +ans3 and ans4 servers in the chain system test. + +(cherry picked from commit 2e40705c06831988106335ed77db3cf924d431f6) + +Conflict: context adaption +Reference:https://gitlab.isc.org/isc-projects/bind9/-/commit/0e4cd87bed5ef + +--- + .../bin/tests/system/chain/ans3/ans.py | 216 ++++++++++++++++++ + .../bin/tests/system/chain/ans4/ans.py | 58 ++++- + .../lib/dns/include/dns/message.h | 8 + + bind/bind-9.11.14/lib/dns/message.c | 12 + + bind/bind-9.11.14/lib/dns/resolver.c | 46 +++- + 5 files changed, 325 insertions(+), 15 deletions(-) + create mode 100644 bind/bind-9.11.14/bin/tests/system/chain/ans3/ans.py + +diff --git a/bind/bind-9.11.14/bin/tests/system/chain/ans3/ans.py b/bind/bind-9.11.14/bin/tests/system/chain/ans3/ans.py +new file mode 100644 +index 0000000..d6d4eea +--- /dev/null ++++ b/bind/bind-9.11.14/bin/tests/system/chain/ans3/ans.py +@@ -0,0 +1,216 @@ ++# Copyright (C) Internet Systems Consortium, Inc. ("ISC") ++# ++# SPDX-License-Identifier: MPL-2.0 ++# ++# This Source Code Form is subject to the terms of the Mozilla Public ++# License, v. 2.0. If a copy of the MPL was not distributed with this ++# file, you can obtain one at https://mozilla.org/MPL/2.0/. ++# ++# See the COPYRIGHT file distributed with this work for additional ++# information regarding copyright ownership. ++ ++############################################################################ ++# ans.py: See README.anspy for details. ++############################################################################ ++ ++from __future__ import print_function ++import os ++import sys ++import signal ++import socket ++import select ++from datetime import datetime, timedelta ++import functools ++ ++import dns, dns.message, dns.query ++from dns.rdatatype import * ++from dns.rdataclass import * ++from dns.rcode import * ++from dns.name import * ++ ++############################################################################ ++# Respond to a DNS query. ++############################################################################ ++def create_response(msg): ++ ttl = 60 ++ zone = "example.broken." ++ nsname = "ns3." + zone ++ synth = "synth-then-dname." + zone ++ synth2 = "synth2-then-dname." + zone ++ ++ m = dns.message.from_wire(msg) ++ qname = m.question[0].name.to_text() ++ ++ # prepare the response and convert to wire format ++ r = dns.message.make_response(m) ++ ++ # get qtype ++ rrtype = m.question[0].rdtype ++ qtype = dns.rdatatype.to_text(rrtype) ++ print("request: " + qname + "/" + qtype) ++ ++ rcode = "NOERROR" ++ if qname == zone: ++ if qtype == "SOA": ++ r.answer.append(dns.rrset.from_text(qname, ttl, IN, SOA, ". . 0 0 0 0 0")) ++ elif qtype == "NS": ++ r.answer.append(dns.rrset.from_text(qname, ttl, IN, NS, nsname)) ++ r.additional.append(dns.rrset.from_text(nsname, ttl, IN, A, ip4)) ++ elif qname == "cname-to-" + synth2: ++ r.answer.append(dns.rrset.from_text(qname, ttl, IN, CNAME, "name." + synth2)) ++ r.answer.append(dns.rrset.from_text("name." + synth2, ttl, IN, CNAME, "name.")) ++ r.answer.append(dns.rrset.from_text(synth2, ttl, IN, DNAME, ".")) ++ elif qname == synth or qname == synth2: ++ if qtype == "DNAME": ++ r.answer.append(dns.rrset.from_text(qname, ttl, IN, DNAME, ".")) ++ elif qname == "name." + synth: ++ r.answer.append(dns.rrset.from_text(qname, ttl, IN, CNAME, "name.")) ++ r.answer.append(dns.rrset.from_text(synth, ttl, IN, DNAME, ".")) ++ elif qname == "name." + synth2: ++ r.answer.append(dns.rrset.from_text(qname, ttl, IN, CNAME, "name.")) ++ r.answer.append(dns.rrset.from_text(synth2, ttl, IN, DNAME, ".")) ++ elif qname == "ns3.example.dname.": ++ # This and the next two code branches referring to the "example.dname" ++ # zone are necessary for the resolver variant of the CVE-2021-25215 ++ # regression test to work. A named instance cannot be used for ++ # serving the DNAME records below as a version of BIND vulnerable to ++ # CVE-2021-25215 would crash while answering the queries asked by ++ # the tested resolver. ++ if qtype == "A": ++ r.answer.append(dns.rrset.from_text(qname, ttl, IN, A, ip4)) ++ elif qtype == "AAAA": ++ r.authority.append( ++ dns.rrset.from_text("example.dname.", ttl, IN, SOA, ". . 0 0 0 0 0") ++ ) ++ elif qname == "self.example.self..example.dname.": ++ r.answer.append( ++ dns.rrset.from_text("self.example.dname.", ttl, IN, DNAME, "dname.") ++ ) ++ r.answer.append( ++ dns.rrset.from_text(qname, ttl, IN, CNAME, "self.example.dname.") ++ ) ++ elif qname == "self.example.dname.": ++ if qtype == "DNAME": ++ r.answer.append(dns.rrset.from_text(qname, ttl, IN, DNAME, "dname.")) ++ else: ++ rcode = "REFUSED" ++ ++ r.flags |= dns.flags.AA ++ r.use_edns() ++ return r.to_wire() ++ ++ ++def sigterm(signum, frame): ++ print("Shutting down now...") ++ os.remove("ans.pid") ++ running = False ++ sys.exit(0) ++ ++ ++############################################################################ ++# Main ++# ++# Set up responder and control channel, open the pid file, and start ++# the main loop, listening for queries on the query channel or commands ++# on the control channel and acting on them. ++############################################################################ ++ip4 = "10.53.0.3" ++ip6 = "fd92:7065:b8e:ffff::3" ++ ++try: ++ port = int(os.environ["PORT"]) ++except: ++ port = 5300 ++ ++query4_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ++query4_udp.bind((ip4, port)) ++ ++query4_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ++query4_tcp.bind((ip4, port)) ++query4_tcp.listen(1) ++query4_tcp.settimeout(1) ++ ++havev6 = True ++try: ++ query6_udp = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) ++ try: ++ query6_udp.bind((ip6, port)) ++ except: ++ query6_udp.close() ++ havev6 = False ++ ++ query6_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ++ try: ++ query6_tcp.bind((ip4, port)) ++ query6_tcp.listen(1) ++ query6_tcp.settimeout(1) ++ except: ++ query6_tcp.close() ++ havev6 = False ++except: ++ havev6 = False ++ ++signal.signal(signal.SIGTERM, sigterm) ++ ++f = open("ans.pid", "w") ++pid = os.getpid() ++print(pid, file=f) ++f.close() ++ ++running = True ++ ++print("Listening on %s port %d" % (ip4, port)) ++if havev6: ++ print("Listening on %s port %d" % (ip6, port)) ++print("Ctrl-c to quit") ++ ++if havev6: ++ input = [query4_udp, query4_tcp, query6_udp, query6_tcp] ++else: ++ input = [query4_udp, query4_tcp] ++ ++while running: ++ try: ++ inputready, outputready, exceptready = select.select(input, [], []) ++ except select.error as e: ++ break ++ except socket.error as e: ++ break ++ except KeyboardInterrupt: ++ break ++ ++ for s in inputready: ++ if s == query4_udp or s == query6_udp: ++ print("Query received on %s" % (ip4 if s == query4_udp else ip6)) ++ # Handle incoming queries ++ msg = s.recvfrom(65535) ++ rsp = create_response(msg[0]) ++ if rsp: ++ s.sendto(rsp, msg[1]) ++ elif s == query4_tcp or s == query6_tcp: ++ try: ++ conn, _ = s.accept() ++ if s == query4_tcp or s == query6_tcp: ++ print( ++ "TCP Query received on %s" % (ip4 if s == query4_tcp else ip6), ++ end=" ", ++ ) ++ # get TCP message length ++ msg = conn.recv(2) ++ if len(msg) != 2: ++ print("couldn't read TCP message length") ++ continue ++ length = struct.unpack(">H", msg[:2])[0] ++ msg = conn.recv(length) ++ if len(msg) != length: ++ print("couldn't read TCP message") ++ continue ++ rsp = create_response(msg) ++ if rsp: ++ conn.send(struct.pack(">H", len(rsp))) ++ conn.send(rsp) ++ conn.close() ++ except socket.error as e: ++ print("error: %s" % str(e)) ++ if not running: ++ break +diff --git a/bind/bind-9.11.14/bin/tests/system/chain/ans4/ans.py b/bind/bind-9.11.14/bin/tests/system/chain/ans4/ans.py +index 2dd7def..5e9c7b1 100755 +--- a/bind/bind-9.11.14/bin/tests/system/chain/ans4/ans.py ++++ b/bind/bind-9.11.14/bin/tests/system/chain/ans4/ans.py +@@ -276,16 +276,30 @@ except: port=5300 + try: ctrlport=int(os.environ['EXTRAPORT1']) + except: ctrlport=5300 + +-query4_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +-query4_socket.bind((ip4, port)) ++query4_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ++query4_udp.bind((ip4, port)) ++ ++query4_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ++query4_tcp.bind((ip4, port)) ++query4_tcp.listen(1) ++query4_tcp.settimeout(1) + + havev6 = True + try: +- query6_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) ++ query6_udp = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) ++ try: ++ query6_udp.bind((ip6, port)) ++ except: ++ query6_udp.close() ++ havev6 = False ++ ++ query6_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: +- query6_socket.bind((ip6, port)) ++ query6_tcp.bind((ip4, port)) ++ query6_tcp.listen(1) ++ query6_tcp.settimeout(1) + except: +- query6_socket.close() ++ query6_tcp.close() + havev6 = False + except: + havev6 = False +@@ -310,9 +324,9 @@ print ("Control channel on %s port %d" % (ip4, ctrlport)) + print ("Ctrl-c to quit") + + if havev6: +- input = [query4_socket, query6_socket, ctrl_socket] ++ input = [query4_udp, query4_tcp, query6_udp, query6_tcp, ctrl_socket] + else: +- input = [query4_socket, ctrl_socket] ++ input = [query4_udp, query4_tcp, ctrl_socket] + + while running: + try: +@@ -335,13 +349,37 @@ while running: + break + ctl_channel(msg) + conn.close() +- if s == query4_socket or s == query6_socket: +- print ("Query received on %s" % +- (ip4 if s == query4_socket else ip6)) ++ elif s == query4_udp or s == query6_udp: ++ print("Query received on %s" % (ip4 if s == query4_udp else ip6)) + # Handle incoming queries + msg = s.recvfrom(65535) + rsp = create_response(msg[0]) + if rsp: + s.sendto(rsp, msg[1]) ++ elif s == query4_tcp or s == query6_tcp: ++ try: ++ conn, _ = s.accept() ++ if s == query4_tcp or s == query6_tcp: ++ print( ++ "TCP Query received on %s" % (ip4 if s == query4_tcp else ip6), ++ end=" ", ++ ) ++ # get TCP message length ++ msg = conn.recv(2) ++ if len(msg) != 2: ++ print("couldn't read TCP message length") ++ continue ++ length = struct.unpack(">H", msg[:2])[0] ++ msg = conn.recv(length) ++ if len(msg) != length: ++ print("couldn't read TCP message") ++ continue ++ rsp = create_response(msg) ++ if rsp: ++ conn.send(struct.pack(">H", len(rsp))) ++ conn.send(rsp) ++ conn.close() ++ except socket.error as e: ++ print("error: %s" % str(e)) + if not running: + break +diff --git a/bind/bind-9.11.14/lib/dns/include/dns/message.h b/bind/bind-9.11.14/lib/dns/include/dns/message.h +index 6c9a14e..f53e848 100644 +--- a/bind/bind-9.11.14/lib/dns/include/dns/message.h ++++ b/bind/bind-9.11.14/lib/dns/include/dns/message.h +@@ -222,6 +222,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; +@@ -1434,6 +1435,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/bind/bind-9.11.14/lib/dns/message.c b/bind/bind-9.11.14/lib/dns/message.c +index 6aed685..3469191 100644 +--- a/bind/bind-9.11.14/lib/dns/message.c ++++ b/bind/bind-9.11.14/lib/dns/message.c +@@ -426,6 +426,7 @@ msginit(dns_message_t *m) { + m->cc_bad = 0; + m->tkey = 0; + m->rdclass_set = 0; ++ m->has_dname = 0; + m->querytsig = NULL; + } + +@@ -1622,6 +1623,11 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + 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; + } + + if (seen_problem) { +@@ -4396,3 +4402,9 @@ 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/bind/bind-9.11.14/lib/dns/resolver.c b/bind/bind-9.11.14/lib/dns/resolver.c +index efef946..5a74c4f 100644 +--- a/bind/bind-9.11.14/lib/dns/resolver.c ++++ b/bind/bind-9.11.14/lib/dns/resolver.c +@@ -7140,9 +7140,10 @@ validinanswer(dns_rdataset_t *rdataset, fetchctx_t *fctx) { + } + + static isc_result_t +-answer_response(fetchctx_t *fctx) { ++answer_response(resquery_t *query) { + isc_result_t result; + dns_message_t *message = NULL; ++ 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; +@@ -7155,6 +7156,8 @@ answer_response(fetchctx_t *fctx) { + dns_view_t *view = NULL; + dns_trust_t trust; + ++ REQUIRE(VALID_QUERY(query)); ++ fctx = query->fctx; + REQUIRE(VALID_FCTX(fctx)); + + FCTXTRACE("answer_response"); +@@ -7457,8 +7460,17 @@ answer_response(fetchctx_t *fctx) { + * + * 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); +@@ -7939,6 +7951,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; +@@ -8246,6 +8259,17 @@ 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 (message->cc_ok || message->tsig != NULL || ++ message->sig0 != NULL) ++ { ++ secured = true; ++ } ++ + /* + * The dispatcher should ensure we only get responses with QR set. + */ +@@ -8258,6 +8282,18 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + * ensured by the dispatch code). + */ + ++ if (!secured && (options & DNS_FETCHOPT_TCP) == 0) { ++ /* ++ * Check whether we need to retry over TCP for some other ++ * reason. ++ */ ++ if (dns_message_hasdname(message)) { ++ 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 +@@ -8612,7 +8648,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 || + ISFORWARDER(query->addrinfo)) + { +- result = answer_response(fctx); ++ result = answer_response(query); + if (result != ISC_R_SUCCESS) + FCTXTRACE3("answer_response (AA/fwd)", result); + } else if (iscname(fctx) && +@@ -8624,7 +8660,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); ++ result = answer_response(query); + if (result != ISC_R_SUCCESS) + FCTXTRACE3("answer_response (!ANY/!CNAME)", + result); +@@ -8633,7 +8669,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + /* + * Lame response !!!. + */ +- result = answer_response(fctx); ++ result = answer_response(query); + if (result != ISC_R_SUCCESS) + FCTXTRACE3("answer_response (!NS)", result); + } else { +-- +2.43.0 + diff --git a/dhcp.spec b/dhcp.spec index 803e10607a2a87da27689ba7be28bc7d134beafc..6a96d4f0ecbaa1e2295ab15c1d74a830a4803421 100644 --- a/dhcp.spec +++ b/dhcp.spec @@ -3,7 +3,7 @@ Name: dhcp Version: 4.4.2 -Release: 15 +Release: 16 Summary: Dynamic host configuration protocol software #Please don't change the epoch on this package Epoch: 12 @@ -73,6 +73,9 @@ Patch52: backport-0003-CVE-2024-1737.patch Patch53: backport-0004-CVE-2024-1737.patch Patch54: backport-CVE-2023-3341.patch Patch55: backport-CVE-2024-11187.patch +Patch56: backport-0001-CVE-2025-40778.patch +Patch57: backport-0002-CVE-2025-40778.patch +Patch58: backport-0003-CVE-2025-40778.patch BuildRequires: gcc autoconf automake libtool openldap-devel krb5-devel libcap-ng-devel bind-export-devel @@ -315,6 +318,12 @@ exit 0 %{_mandir}/man3/omapi.3.gz %changelog +* Thu Dec 04 2025 luoguocui - 12:4.4.2-16 +- Type:CVE +- ID:NA +- SUG:restart +- DESC:fix CVE-2025-40778 + * Thu Nov 27 2025 luoguocui - 12:4.4.2-15 - Ignore Wincompatible-pointer-types error when compiling with gcc-14.3