From 756855d9f066fb0ff4028dd22fb409afb8babe2f Mon Sep 17 00:00:00 2001 From: luoguocui Date: Thu, 4 Dec 2025 23:29:15 +0800 Subject: [PATCH] [Backport]fix CVE-2025-40778 Reference:https://gitlab.isc.org/isc-projects/bind9/-/commit/41ab0709 https://gitlab.isc.org/isc-projects/bind9/-/commit/ba8e3eb3 https://gitlab.isc.org/isc-projects/bind9/-/commit/0e4cd87b conflict:yes Type:CVE --- backport-0001-CVE-2025-40778.patch | 36 ++ backport-0002-CVE-2025-40778.patch | 207 ++++++++++ backport-0003-CVE-2025-40778.patch | 635 +++++++++++++++++++++++++++++ dhcp.spec | 11 +- 4 files changed, 888 insertions(+), 1 deletion(-) create mode 100644 backport-0001-CVE-2025-40778.patch create mode 100644 backport-0002-CVE-2025-40778.patch create mode 100644 backport-0003-CVE-2025-40778.patch diff --git a/backport-0001-CVE-2025-40778.patch b/backport-0001-CVE-2025-40778.patch new file mode 100644 index 0000000..815bbfc --- /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 0000000..3b76cba --- /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 0000000..cb1b879 --- /dev/null +++ b/backport-0003-CVE-2025-40778.patch @@ -0,0 +1,635 @@ +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: the patch based on commit cdf73095abbff, merge together +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 | 101 +++++++- + bind/bind-9.11.14/lib/dns/win32/libdns.def.in | 1 + + 6 files changed, 374 insertions(+), 22 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 ba652fc..0087de1 100644 +--- a/bind/bind-9.11.14/lib/dns/resolver.c ++++ b/bind/bind-9.11.14/lib/dns/resolver.c +@@ -2073,7 +2073,12 @@ add_serveraddr(uint8_t *buf, const size_t bufsize, const resquery_t *query) + return (addr2buf(buf, bufsize, &query->addrinfo->sockaddr)); + } + ++/* ++ * Client cookie is 8 octets. ++ * Server cookie is [8..32] octets. ++ */ + #define CLIENT_COOKIE_SIZE 8U ++#define COOKIE_BUFFER_SIZE (8U + 32U) + + static void + compute_cc(const resquery_t *query, uint8_t *cookie, const size_t len) { +@@ -2350,7 +2355,7 @@ resquery_send(resquery_t *query) { + unsigned int flags = query->addrinfo->flags; + bool reqnsid = res->view->requestnsid; + bool sendcookie = res->view->sendcookie; +- unsigned char cookie[64]; ++ unsigned char cookie[COOKIE_BUFFER_SIZE]; + + if ((flags & FCTX_ADDRINFO_EDNSOK) != 0 && + (query->options & DNS_FETCHOPT_EDNS512) == 0) { +@@ -2434,9 +2439,11 @@ resquery_send(resquery_t *query) { + inc_stats(fctx->res, + dns_resstatscounter_cookieout); + } else { +- compute_cc(query, cookie, 8); ++ compute_cc(query, cookie, ++ CLIENT_COOKIE_SIZE); + ednsopts[ednsopt].value = cookie; +- ednsopts[ednsopt].length = 8; ++ ednsopts[ednsopt].length = ++ CLIENT_COOKIE_SIZE; + inc_stats(fctx->res, + dns_resstatscounter_cookienew); + } +@@ -7140,9 +7147,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 +7163,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"); +@@ -7456,8 +7466,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); +@@ -7853,7 +7872,7 @@ process_opt(resquery_t *query, dns_rdataset_t *opt) { + uint16_t optlen; + unsigned char *optvalue; + dns_adbaddrinfo_t *addrinfo; +- unsigned char cookie[8]; ++ unsigned char cookie[CLIENT_COOKIE_SIZE]; + bool seen_cookie = false; + bool seen_nsid = false; + +@@ -7889,8 +7908,10 @@ process_opt(resquery_t *query, dns_rdataset_t *opt) { + compute_cc(query, cookie, sizeof(cookie)); + INSIST(query->fctx->rmessage->cc_bad == 0 && + query->fctx->rmessage->cc_ok == 0); +- if (optlen >= 8U && +- memcmp(cookie, optvalue, 8) == 0) { ++ if (optlen >= CLIENT_COOKIE_SIZE && ++ memcmp(cookie, optvalue, ++ CLIENT_COOKIE_SIZE) == 0) ++ { + query->fctx->rmessage->cc_ok = 1; + inc_stats(query->fctx->res, + dns_resstatscounter_cookieok); +@@ -7937,6 +7958,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; +@@ -8241,9 +8263,24 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + result = dns_message_checksig(message, res->view); + if (result != ISC_R_SUCCESS) { + FCTXTRACE3("signature check failed", result); ++ if (result == DNS_R_UNEXPECTEDTSIG || ++ result == DNS_R_EXPECTEDTSIG) { ++ nextitem = true; ++ } + 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. + */ +@@ -8256,6 +8293,46 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + * ensured by the dispatch code). + */ + ++ /* ++ * If we have had a server cookie and don't get one retry over TCP. ++ * This may be a misconfigured anycast server or an attempt to send ++ * a spoofed response. Skip if we have a valid tsig. ++ */ ++ if (!secured && (options & DNS_FETCHOPT_TCP) == 0) { ++ unsigned char cookie[COOKIE_BUFFER_SIZE]; ++ if (dns_adb_getcookie(fctx->adb, query->addrinfo, cookie, ++ sizeof(cookie)) > CLIENT_COOKIE_SIZE) ++ { ++ if (isc_log_wouldlog(dns_lctx, ISC_LOG_INFO)) { ++ char addrbuf[ISC_SOCKADDR_FORMATSIZE]; ++ isc_sockaddr_format(&query->addrinfo->sockaddr, ++ addrbuf, sizeof(addrbuf)); ++ isc_log_write( ++ dns_lctx, DNS_LOGCATEGORY_RESOLVER, ++ DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, ++ "missing expected cookie from %s", ++ addrbuf); ++ } ++ options |= DNS_FETCHOPT_TCP; ++ resend = true; ++ goto done; ++ } ++ /* ++ * XXXMPA When support for DNS COOKIE becomes ubiquitous, fall ++ * back to TCP for all non-COOKIE responses. ++ */ ++ ++ /* ++ * 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 +@@ -8386,7 +8463,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + message->rcode != dns_rcode_nxdomain) { + isc_buffer_t b; + char code[64]; +- unsigned char cookie[64]; ++ unsigned char cookie[COOKIE_BUFFER_SIZE]; + + /* + * Some servers do not ignore unknown EDNS options. +@@ -8610,7 +8687,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) && +@@ -8622,7 +8699,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); +@@ -8631,7 +8708,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 { +diff --git a/bind/bind-9.11.14/lib/dns/win32/libdns.def.in b/bind/bind-9.11.14/lib/dns/win32/libdns.def.in +index 63be973..38311bf 100644 +--- a/bind/bind-9.11.14/lib/dns/win32/libdns.def.in ++++ b/bind/bind-9.11.14/lib/dns/win32/libdns.def.in +@@ -536,6 +536,7 @@ dns_message_gettemprdataset + dns_message_gettimeadjust + dns_message_gettsig + dns_message_gettsigkey ++dns_message_hasdname + dns_message_logfmtpacket + dns_message_logfmtpacket2 + dns_message_logpacket +-- +2.43.0 + diff --git a/dhcp.spec b/dhcp.spec index 803e106..6a96d4f 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 -- Gitee