From 2ae41895dec9e8bc4676ac8ff71d0848366d5cf9 Mon Sep 17 00:00:00 2001 From: luoguocui Date: Sat, 22 Nov 2025 09:20:42 +0000 Subject: [PATCH] [Backport]fix CVE-2025-40778 Reference:https://gitlab.isc.org/isc-projects/bind9/-/commit/9ff9b88a conflict:yes Type:CVE --- backport-CVE-2025-40778.patch | 916 ++++++++++++++++++++++++++++++++++ dhcp.spec | 9 +- 2 files changed, 924 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..5c11153 --- /dev/null +++ b/backport-CVE-2025-40778.patch @@ -0,0 +1,916 @@ +From 0e4cd87bed5efc61443337034a9d96287b4885dc Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Mon, 29 Sep 2025 22:42:44 -0700 +Subject: [PATCH] CVE-2025-40778 + +Conflict: Context adaptation +Reference:https://gitlab.isc.org/isc-projects/bind9/-/commit/9ff9b88a6f +--- + .../bin/tests/system/chain/ans3/ans.py | 216 ++++++++++++++++++ + .../bin/tests/system/chain/ans4/ans.py | 58 ++++- + .../bin/tests/system/cookie/ans9/ans.py | 139 ++++++----- + .../lib/dns/include/dns/message.h | 8 + + bind/bind-9.11.36/lib/dns/message.c | 12 + + bind/bind-9.11.36/lib/dns/resolver.c | 84 +++++-- + bind/bind-9.11.36/lib/dns/win32/libdns.def.in | 1 + + 7 files changed, 432 insertions(+), 86 deletions(-) + create mode 100644 bind/bind-9.11.36/bin/tests/system/chain/ans3/ans.py + +diff --git a/bind/bind-9.11.36/bin/tests/system/chain/ans3/ans.py b/bind/bind-9.11.36/bin/tests/system/chain/ans3/ans.py +new file mode 100644 +index 0000000..d6d4eea +--- /dev/null ++++ b/bind/bind-9.11.36/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.36/bin/tests/system/chain/ans4/ans.py b/bind/bind-9.11.36/bin/tests/system/chain/ans4/ans.py +index 45d6504..e4fc15a 100755 +--- a/bind/bind-9.11.36/bin/tests/system/chain/ans4/ans.py ++++ b/bind/bind-9.11.36/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.36/bin/tests/system/cookie/ans9/ans.py b/bind/bind-9.11.36/bin/tests/system/cookie/ans9/ans.py +index b454fc8..dd48bdb 100644 +--- a/bind/bind-9.11.36/bin/tests/system/cookie/ans9/ans.py ++++ b/bind/bind-9.11.36/bin/tests/system/cookie/ans9/ans.py +@@ -1,13 +1,13 @@ +-############################################################################ + # 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 ++# 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. +-############################################################################ + + from __future__ import print_function + import os +@@ -35,28 +35,27 @@ from dns.rdataclass import * + from dns.rdatatype import * + from dns.tsig import * + ++ + # Log query to file + def logquery(type, qname): + with open("qlog", "a") as f: + f.write("%s %s\n", type, qname) + ++ + # DNS 2.0 keyring specifies the algorithm + try: +- keyring = dns.tsigkeyring.from_text({ "foo" : { +- "hmac-sha256", +- "aaaaaaaaaaaa" +- } , +- "fake" : { +- "hmac-sha256", +- "aaaaaaaaaaaa" +- } +- }) ++ keyring = dns.tsigkeyring.from_text( ++ { ++ "foo": {"hmac-sha256", "aaaaaaaaaaaa"}, ++ "fake": {"hmac-sha256", "aaaaaaaaaaaa"}, ++ } ++ ) + except: +- keyring = dns.tsigkeyring.from_text({ "foo" : "aaaaaaaaaaaa", +- "fake" : "aaaaaaaaaaaa" }) ++ keyring = dns.tsigkeyring.from_text({"foo": "aaaaaaaaaaaa", "fake": "aaaaaaaaaaaa"}) + + dopass2 = False + ++ + ############################################################################ + # + # This server will serve valid and spoofed answers. A spoofed answer will +@@ -81,7 +80,7 @@ def create_response(msg, tcp, first, ns10): + m = dns.message.from_wire(msg, keyring=keyring) + qname = m.question[0].name.to_text() + lqname = qname.lower() +- labels = lqname.split('.') ++ labels = lqname.split(".") + rrtype = m.question[0].rdtype + typename = dns.rdatatype.to_text(rrtype) + +@@ -113,27 +112,35 @@ def create_response(msg, tcp, first, ns10): + # Add a server cookie to the response + if labels[0] != "nocookie": + for o in m.options: +- if o.otype == 10: # Use 10 instead of COOKIE +- if first and labels[0] == "withtsig" and not tcp: +- r.use_tsig(keyring = keyring, +- keyname = dns.name.from_text("fake"), +- algorithm = HMAC_SHA256) +- elif labels[0] != "tcponly" or tcp: +- cookie = o +- if len(o.data) == 8: +- cookie.data = o.data + o.data +- else: +- cookie.data = o.data +- r.use_edns(options=[cookie]) ++ if o.otype == 10: # Use 10 instead of COOKIE ++ if first and labels[0] == "withtsig" and not tcp: ++ r.use_tsig( ++ keyring=keyring, ++ keyname=dns.name.from_text("fake"), ++ algorithm=HMAC_SHA256, ++ ) ++ elif labels[0] != "tcponly" or tcp: ++ cookie = o ++ try: ++ if len(o.server) == 0: ++ cookie.server = o.client ++ except AttributeError: # dnspython<2.7.0 compat ++ if len(o.data) == 8: ++ cookie.data = o.data + o.data ++ else: ++ cookie.data = o.data ++ r.use_edns(options=[cookie]) + r.flags |= dns.flags.AA + return r + ++ + def sigterm(signum, frame): +- print ("Shutting down now...") +- os.remove('ans.pid') ++ print("Shutting down now...") ++ os.remove("ans.pid") + running = False + sys.exit(0) + ++ + ############################################################################ + # Main + # +@@ -146,8 +153,10 @@ ip4_addr2 = "10.53.0.10" + ip6_addr1 = "fd92:7065:b8e:ffff::9" + ip6_addr2 = "fd92:7065:b8e:ffff::10" + +-try: port=int(os.environ['PORT']) +-except: port=5300 ++try: ++ port = int(os.environ["PORT"]) ++except: ++ port = 5300 + + query4_udp1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + query4_udp1.bind((ip4_addr1, port)) +@@ -195,24 +204,32 @@ except: + + signal.signal(signal.SIGTERM, sigterm) + +-f = open('ans.pid', 'w') ++f = open("ans.pid", "w") + pid = os.getpid() +-print (pid, file=f) ++print(pid, file=f) + f.close() + + running = True + +-print ("Using DNS version %s" % dns.version.version) +-print ("Listening on %s port %d" % (ip4_addr1, port)) +-print ("Listening on %s port %d" % (ip4_addr2, port)) ++print("Using DNS version %s" % dns.version.version) ++print("Listening on %s port %d" % (ip4_addr1, port)) ++print("Listening on %s port %d" % (ip4_addr2, port)) + if havev6: +- print ("Listening on %s port %d" % (ip6_addr1, port)) +- print ("Listening on %s port %d" % (ip6_addr2, port)) +-print ("Ctrl-c to quit") ++ print("Listening on %s port %d" % (ip6_addr1, port)) ++ print("Listening on %s port %d" % (ip6_addr2, port)) ++print("Ctrl-c to quit") + + if havev6: +- input = [query4_udp1, query6_udp1, query4_tcp1, query6_tcp1, +- query4_udp2, query6_udp2, query4_tcp2, query6_tcp2] ++ input = [ ++ query4_udp1, ++ query6_udp1, ++ query4_tcp1, ++ query6_tcp1, ++ query4_udp2, ++ query6_udp2, ++ query4_tcp2, ++ query6_tcp2, ++ ] + else: + input = [query4_udp1, query4_tcp1, query4_udp2, query4_tcp2] + +@@ -228,14 +245,19 @@ while running: + + for s in inputready: + ns10 = False +- if s == query4_udp1 or s == query6_udp1 or \ +- s == query4_udp2 or s == query6_udp2: ++ if s == query4_udp1 or s == query6_udp1 or s == query4_udp2 or s == query6_udp2: + if s == query4_udp1 or s == query6_udp1: +- print ("UDP Query received on %s" % +- (ip4_addr1 if s == query4_udp1 else ip6_addr1), end=" ") ++ print( ++ "UDP Query received on %s" ++ % (ip4_addr1 if s == query4_udp1 else ip6_addr1), ++ end=" ", ++ ) + if s == query4_udp2 or s == query6_udp2: +- print ("UDP Query received on %s" % +- (ip4_addr2 if s == query4_udp2 else ip6_addr2), end=" ") ++ print( ++ "UDP Query received on %s" ++ % (ip4_addr2 if s == query4_udp2 else ip6_addr2), ++ end=" ", ++ ) + ns10 = True + # Handle incoming queries + msg = s.recvfrom(65535) +@@ -244,31 +266,36 @@ while running: + print(dns.rcode.to_text(rsp.rcode())) + s.sendto(rsp.to_wire(), msg[1]) + if dopass2: +- print ("Sending second UDP response without TSIG", end=" ") ++ print("Sending second UDP response without TSIG", end=" ") + rsp = create_response(msg[0], False, False, ns10) + s.sendto(rsp.to_wire(), msg[1]) + print(dns.rcode.to_text(rsp.rcode())) + +- if s == query4_tcp1 or s == query6_tcp1 or \ +- s == query4_tcp2 or s == query6_tcp2: ++ if s == query4_tcp1 or s == query6_tcp1 or s == query4_tcp2 or s == query6_tcp2: + try: + (cs, _) = s.accept() + if s == query4_tcp1 or s == query6_tcp1: +- print ("TCP Query received on %s" % +- (ip4_addr1 if s == query4_tcp1 else ip6_addr1), end=" ") ++ print( ++ "TCP Query received on %s" ++ % (ip4_addr1 if s == query4_tcp1 else ip6_addr1), ++ end=" ", ++ ) + if s == query4_tcp2 or s == query6_tcp2: +- print ("TCP Query received on %s" % +- (ip4_addr2 if s == query4_tcp2 else ip6_addr2), end=" ") ++ print( ++ "TCP Query received on %s" ++ % (ip4_addr2 if s == query4_tcp2 else ip6_addr2), ++ end=" ", ++ ) + ns10 = True + # get TCP message length + buf = cs.recv(2) +- length = struct.unpack('>H', buf[:2])[0] ++ length = struct.unpack(">H", buf[:2])[0] + # grep DNS message + msg = cs.recv(length) + rsp = create_response(msg, True, True, ns10) + print(dns.rcode.to_text(rsp.rcode())) + wire = rsp.to_wire() +- cs.send(struct.pack('>H', len(wire))) ++ cs.send(struct.pack(">H", len(wire))) + cs.send(wire) + cs.close() + except s.timeout: +diff --git a/bind/bind-9.11.36/lib/dns/include/dns/message.h b/bind/bind-9.11.36/lib/dns/include/dns/message.h +index f64522b..0dfb261 100644 +--- a/bind/bind-9.11.36/lib/dns/include/dns/message.h ++++ b/bind/bind-9.11.36/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/bind/bind-9.11.36/lib/dns/message.c b/bind/bind-9.11.36/lib/dns/message.c +index 0c71f79..276b71c 100644 +--- a/bind/bind-9.11.36/lib/dns/message.c ++++ b/bind/bind-9.11.36/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; +@@ -1671,6 +1672,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) { +@@ -4542,3 +4548,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.36/lib/dns/resolver.c b/bind/bind-9.11.36/lib/dns/resolver.c +index ac9a9ef..00e8fdf 100644 +--- a/bind/bind-9.11.36/lib/dns/resolver.c ++++ b/bind/bind-9.11.36/lib/dns/resolver.c +@@ -427,6 +427,7 @@ typedef struct { + typedef struct { + fetchctx_t * fctx; + dns_message_t * rmessage; ++ dns_name_t * ns_name; + } dns_chkarg_t; + + struct dns_fetch { +@@ -6254,7 +6255,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; +@@ -6272,7 +6275,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. +@@ -6381,7 +6386,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; +@@ -6455,7 +6460,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; +@@ -7063,7 +7068,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 +@@ -7108,9 +7113,10 @@ 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; ++ chkarg.ns_name = ns_name; + (void)dns_rdataset_additionaldata(ns_rdataset, + check_answer, &chkarg); + } +@@ -7188,8 +7194,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; +@@ -7202,6 +7209,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"); +@@ -7266,7 +7275,9 @@ 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; + } + +@@ -7321,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); + } +@@ -7352,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); + } + } 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 || +@@ -7381,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); + for (sigrdataset = ISC_LIST_HEAD(aname->list); +@@ -7502,12 +7515,23 @@ 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. +@@ -7518,7 +7542,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 |= +@@ -7542,6 +7566,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 +8008,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; +@@ -8293,6 +8319,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 (rmessage->cc_ok || rmessage->tsig != NULL || ++ rmessage->sig0 != NULL) ++ { ++ secured = true; ++ } ++ + /* + * The dispatcher should ensure we only get responses with QR set. + */ +@@ -8310,10 +8347,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + * This may be a misconfigured anycast server or an attempt to send + * a spoofed response. Skip if we have a valid tsig. + */ +- if (dns_message_gettsig(rmessage, NULL) == NULL && +- !rmessage->cc_ok && !rmessage->cc_bad && +- (options & DNS_FETCHOPT_TCP) == 0) +- { ++ 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) +@@ -8336,6 +8370,16 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + * 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(rmessage)) { ++ options |= DNS_FETCHOPT_TCP; ++ resend = true; ++ goto done; ++ } + } + + /* +@@ -8692,7 +8736,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) && +@@ -8704,7 +8748,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); +@@ -8713,7 +8757,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 { +diff --git a/bind/bind-9.11.36/lib/dns/win32/libdns.def.in b/bind/bind-9.11.36/lib/dns/win32/libdns.def.in +index 9c2ef79..bd1a17d 100644 +--- a/bind/bind-9.11.36/lib/dns/win32/libdns.def.in ++++ b/bind/bind-9.11.36/lib/dns/win32/libdns.def.in +@@ -537,6 +537,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.33.0 + diff --git a/dhcp.spec b/dhcp.spec index a2b850a..116293a 100644 --- a/dhcp.spec +++ b/dhcp.spec @@ -3,7 +3,7 @@ Name: dhcp Version: 4.4.3 -Release: 10 +Release: 11 Summary: Dynamic host configuration protocol software #Please don't change the epoch on this package Epoch: 12 @@ -71,6 +71,7 @@ Patch52: backport-0003-CVE-2024-1737.patch Patch53: backport-0004-CVE-2024-1737.patch Patch54: backport-0028-CVE-2023-3341.patch Patch55: backport-0029-CVE-2024-11187.patch +Patch56: backport-CVE-2025-40778.patch BuildRequires: gcc autoconf automake libtool openldap-devel krb5-devel libcap-ng-devel BuildRequires: systemd systemd-devel @@ -319,6 +320,12 @@ exit 0 %{_mandir}/man3/omapi.3.gz %changelog +* Sat Nov 22 2025 luoguocui - 12:4.4.3-11 +- Type:CVE +- ID:NA +- SUG:restart +- DESC:fix CVE-2025-40778 + * Thu Mar 20 2025 zhangpan - 12:4.4.3-10 - Type:CVE - ID:NA -- Gitee