diff --git a/SOURCES/bind-9.16-CVE-2025-40778.patch b/SOURCES/bind-9.16-CVE-2025-40778.patch new file mode 100644 index 0000000..99b8c28 --- /dev/null +++ b/SOURCES/bind-9.16-CVE-2025-40778.patch @@ -0,0 +1,784 @@ +From 39a43add7ad19fc2d83950a9af0c4f8cf580588b Mon Sep 17 00:00:00 2001 +From: Evan Hunt +Date: Mon, 29 Sep 2025 21:46:59 -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) +(cherry picked from commit 2f0f44d493c382a7f0a3adfe7c4976b18a3d480b) + +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). + +Modified resolver.c + +(cherry picked from commit a41054e9e606a61f1b3c8bc0c54e2f1059347165) +(cherry picked from commit 50479358efdf432d690415131b74b5df158a9d69) + +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) +(cherry picked from commit 33a7db1fe964e55b76b4ac003ecc56cc67028bd9) +--- + bin/tests/system/chain/ans3/ans.pl | 129 ----------------- + bin/tests/system/chain/ans3/ans.py | 217 +++++++++++++++++++++++++++++ + bin/tests/system/chain/ans4/ans.py | 58 ++++++-- + lib/dns/include/dns/message.h | 10 ++ + lib/dns/message.c | 12 ++ + lib/dns/resolver.c | 114 ++++++++++++--- + 6 files changed, 380 insertions(+), 160 deletions(-) + delete mode 100644 bin/tests/system/chain/ans3/ans.pl + create mode 100644 bin/tests/system/chain/ans3/ans.py + +diff --git a/bin/tests/system/chain/ans3/ans.pl b/bin/tests/system/chain/ans3/ans.pl +deleted file mode 100644 +index 31d22d807b..0000000000 +--- a/bin/tests/system/chain/ans3/ans.pl ++++ /dev/null +@@ -1,129 +0,0 @@ +-#!/usr/bin/env perl +-# +-# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +-# +-# 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. +- +-use strict; +-use warnings; +- +-use IO::File; +-use Getopt::Long; +-use Net::DNS::Nameserver; +- +-my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!"; +-print $pidf "$$\n" or die "cannot write pid file: $!"; +-$pidf->close or die "cannot close pid file: $!"; +-sub rmpid { unlink "ans.pid"; exit 1; }; +- +-$SIG{INT} = \&rmpid; +-$SIG{TERM} = \&rmpid; +- +-my $localaddr = "10.53.0.3"; +- +-my $localport = int($ENV{'PORT'}); +-if (!$localport) { $localport = 5300; } +- +-my $verbose = 0; +-my $ttl = 60; +-my $zone = "example.broken"; +-my $nsname = "ns3.$zone"; +-my $synth = "synth-then-dname.$zone"; +-my $synth2 = "synth2-then-dname.$zone"; +- +-sub reply_handler { +- my ($qname, $qclass, $qtype, $peerhost, $query, $conn) = @_; +- my ($rcode, @ans, @auth, @add); +- +- print ("request: $qname/$qtype\n"); +- STDOUT->flush(); +- +- if ($qname eq "example.broken") { +- if ($qtype eq "SOA") { +- my $rr = new Net::DNS::RR("$qname $ttl $qclass SOA . . 0 0 0 0 0"); +- push @ans, $rr; +- } elsif ($qtype eq "NS") { +- my $rr = new Net::DNS::RR("$qname $ttl $qclass NS $nsname"); +- push @ans, $rr; +- $rr = new Net::DNS::RR("$nsname $ttl $qclass A $localaddr"); +- push @add, $rr; +- } +- $rcode = "NOERROR"; +- } elsif ($qname eq "cname-to-$synth2") { +- my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.$synth2"); +- push @ans, $rr; +- $rr = new Net::DNS::RR("name.$synth2 $ttl $qclass CNAME name"); +- push @ans, $rr; +- $rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME ."); +- push @ans, $rr; +- $rcode = "NOERROR"; +- } elsif ($qname eq "$synth" || $qname eq "$synth2") { +- if ($qtype eq "DNAME") { +- my $rr = new Net::DNS::RR("$qname $ttl $qclass DNAME ."); +- push @ans, $rr; +- } +- $rcode = "NOERROR"; +- } elsif ($qname eq "name.$synth") { +- my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name."); +- push @ans, $rr; +- $rr = new Net::DNS::RR("$synth $ttl $qclass DNAME ."); +- push @ans, $rr; +- $rcode = "NOERROR"; +- } elsif ($qname eq "name.$synth2") { +- my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name."); +- push @ans, $rr; +- $rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME ."); +- push @ans, $rr; +- $rcode = "NOERROR"; +- # The following three 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. +- } elsif ($qname eq "ns3.example.dname") { +- if ($qtype eq "A") { +- my $rr = new Net::DNS::RR("$qname $ttl $qclass A 10.53.0.3"); +- push @ans, $rr; +- } +- if ($qtype eq "AAAA") { +- my $rr = new Net::DNS::RR("example.dname. $ttl $qclass SOA . . 0 0 0 0 $ttl"); +- push @auth, $rr; +- } +- $rcode = "NOERROR"; +- } elsif ($qname eq "self.example.self.example.dname") { +- my $rr = new Net::DNS::RR("self.example.dname. $ttl $qclass DNAME dname."); +- push @ans, $rr; +- $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME self.example.dname."); +- push @ans, $rr; +- $rcode = "NOERROR"; +- } elsif ($qname eq "self.example.dname") { +- if ($qtype eq "DNAME") { +- my $rr = new Net::DNS::RR("$qname $ttl $qclass DNAME dname."); +- push @ans, $rr; +- } +- $rcode = "NOERROR"; +- } else { +- $rcode = "REFUSED"; +- } +- return ($rcode, \@ans, \@auth, \@add, { aa => 1 }); +-} +- +-GetOptions( +- 'port=i' => \$localport, +- 'verbose!' => \$verbose, +-); +- +-my $ns = Net::DNS::Nameserver->new( +- LocalAddr => $localaddr, +- LocalPort => $localport, +- ReplyHandler => \&reply_handler, +- Verbose => $verbose, +-); +- +-$ns->main_loop; +diff --git a/bin/tests/system/chain/ans3/ans.py b/bin/tests/system/chain/ans3/ans.py +new file mode 100644 +index 0000000000..0a031c1145 +--- /dev/null ++++ b/bin/tests/system/chain/ans3/ans.py +@@ -0,0 +1,217 @@ ++# 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 = f"ns3.{zone}" ++ synth = f"synth-then-dname.{zone}" ++ synth2 = f"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(f"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 == f"cname-to-{synth2}": ++ r.answer.append(dns.rrset.from_text(qname, ttl, IN, CNAME, f"name.{synth2}")) ++ r.answer.append(dns.rrset.from_text(f"name.{synth2}", ttl, IN, CNAME, "name.")) ++ r.answer.append(dns.rrset.from_text(synth2, ttl, IN, DNAME, ".")) ++ elif qname == f"{synth}" or qname == f"{synth2}": ++ if qtype == "DNAME": ++ r.answer.append(dns.rrset.from_text(qname, ttl, IN, DNAME, ".")) ++ elif qname == f"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 == f"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/bin/tests/system/chain/ans4/ans.py b/bin/tests/system/chain/ans4/ans.py +index 45d650417f..e4fc15a280 100755 +--- a/bin/tests/system/chain/ans4/ans.py ++++ b/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/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h +index 68c13ee6c5..53c96a5827 100644 +--- a/lib/dns/include/dns/message.h ++++ b/lib/dns/include/dns/message.h +@@ -1,6 +1,8 @@ + /* + * 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/. +@@ -233,6 +235,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; +@@ -1449,4 +1452,11 @@ dns_message_clonebuffer(dns_message_t *msg); + * \li msg be a valid message. + */ + ++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 +diff --git a/lib/dns/message.c b/lib/dns/message.c +index 04315bc6d0..aa434a75f1 100644 +--- a/lib/dns/message.c ++++ b/lib/dns/message.c +@@ -438,6 +438,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 = "\t"; + m->indent.count = 0; +@@ -1717,6 +1718,11 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + */ + msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; + free_name = false; ++ } else if (rdtype == dns_rdatatype_dname && ++ sectionid == DNS_SECTION_ANSWER && ++ msg->opcode == dns_opcode_query) ++ { ++ msg->has_dname = 1; + } + rdataset = NULL; + +@@ -4750,3 +4756,9 @@ dns_message_clonebuffer(dns_message_t *msg) { + msg->free_query = 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 ad0a1b8102..5973bc1d55 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -751,6 +751,7 @@ typedef struct respctx { + bool get_nameservers; /* get a new NS rrset at + * zone cut? */ + bool resend; /* resend this query? */ ++ bool secured; /* message was signed or had a valid cookie */ + bool nextitem; /* invalid response; keep + * listening for the correct one */ + bool truncated; /* response was truncated */ +@@ -7155,7 +7156,8 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset, bool external, + * locally served zone. + */ + static inline bool +-name_external(const dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) { ++name_external(const dns_name_t *name, dns_rdatatype_t type, respctx_t *rctx) { ++ fetchctx_t *fctx = rctx->fctx; + isc_result_t result; + dns_forwarders_t *forwarders = NULL; + dns_fixedname_t fixed, zfixed; +@@ -7167,7 +7169,9 @@ name_external(const dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) { + unsigned int labels; + dns_namereln_t rel; + +- apex = ISFORWARDER(fctx->addrinfo) ? fctx->fwdname : &fctx->domain; ++ apex = (/*ISDUALSTACK(fctx->addrinfo) || */!ISFORWARDER(fctx->addrinfo)) ++ ? rctx->ns_name != NULL ? rctx->ns_name : &fctx->domain ++ : fctx->fwdname; + + /* + * The name is outside the queried namespace. +@@ -7275,7 +7279,7 @@ check_section(void *arg, const dns_name_t *addname, dns_rdatatype_t type, + result = dns_message_findname(rctx->query->rmessage, section, addname, + dns_rdatatype_any, 0, &name, NULL); + if (result == ISC_R_SUCCESS) { +- external = name_external(name, type, fctx); ++ external = name_external(name, type, rctx); + if (type == dns_rdatatype_a) { + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; +@@ -7884,6 +7888,47 @@ betterreferral(respctx_t *rctx) { + return (false); + } + ++static bool ++rctx_need_tcpretry(respctx_t *rctx) { ++ resquery_t *query = rctx->query; ++ if ((rctx->retryopts & DNS_FETCHOPT_TCP) != 0) { ++ /* TCP is already in the retry flags */ ++ return false; ++ } ++ ++ /* ++ * If the message was secured, no need to continue. ++ */ ++ if (rctx->secured) { ++ return false; ++ } ++ ++ /* ++ * Currently the only extra reason why we might need to ++ * retry a UDP response over TCP is a DNAME in the message. ++ */ ++ if (dns_message_hasdname(query->rmessage)) { ++ return true; ++ } ++ ++ return false; ++} ++ ++static isc_result_t ++rctx_tcpretry(respctx_t *rctx) { ++ /* ++ * Do we need to retry a UDP response over TCP? ++ */ ++ if (rctx_need_tcpretry(rctx)) { ++ rctx->retryopts |= DNS_FETCHOPT_TCP; ++ rctx->resend = true; ++ rctx_done(rctx, ISC_R_SUCCESS); ++ return ISC_R_COMPLETE; ++ } ++ ++ return ISC_R_SUCCESS; ++} ++ + /* + * resquery_response(): + * Handles responses received in response to iterative queries sent by +@@ -8042,6 +8087,11 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + break; + } + ++ /* ++ * The dispatcher should ensure we only get responses with QR set. ++ */ ++ INSIST((query->rmessage->flags & DNS_MESSAGEFLAG_QR) != 0); ++ + /* + * If the message is signed, check the signature. If not, this + * returns success anyway. +@@ -8058,9 +8108,16 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + } + + /* +- * The dispatcher should ensure we only get responses with QR set. ++ * Remember whether this message was signed or had a ++ * valid client cookie; if not, we may need to retry over ++ * TCP later. + */ +- INSIST((query->rmessage->flags & DNS_MESSAGEFLAG_QR) != 0); ++ if (query->rmessage->cc_ok || query->rmessage->tsig != NULL || ++ query->rmessage->sig0 != NULL) ++ { ++ rctx.secured = true; ++ } ++ + /* + * INSIST() that the message comes from the place we sent it to, + * since the dispatch code should ensure this. +@@ -8074,10 +8131,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(query->rmessage, NULL) == NULL && +- !query->rmessage->cc_ok && !query->rmessage->cc_bad && +- (rctx.retryopts & DNS_FETCHOPT_TCP) == 0) +- { ++ if (!rctx.secured && (rctx.retryopts & DNS_FETCHOPT_TCP) == 0) { + unsigned char cookie[COOKIE_BUFFER_SIZE]; + if (dns_adb_getcookie(fctx->adb, query->addrinfo, cookie, + sizeof(cookie)) > CLIENT_COOKIE_SIZE) +@@ -8103,6 +8157,17 @@ resquery_response(isc_task_t *task, isc_event_t *event) { + */ + } + ++ /* ++ * Check whether we need to retry over TCP for some other reason. ++ */ ++ result = rctx_tcpretry(&rctx); ++ if (result == ISC_R_COMPLETE) { ++ return; ++ } ++ ++ /* ++ * Check for EDNS issues. ++ */ + rctx_edns(&rctx); + + /* +@@ -8830,8 +8895,8 @@ rctx_answer_positive(respctx_t *rctx) { + } + + /* +- * Cache records in the authority section, if +- * there are any suitable for caching. ++ * Cache records in the authority section, if there are ++ * any suitable for caching. + */ + rctx_authority_positive(rctx); + +@@ -8902,7 +8967,7 @@ rctx_answer_scan(respctx_t *rctx) { + /* + * Don't accept DNAME from parent namespace. + */ +- if (name_external(name, dns_rdatatype_dname, fctx)) { ++ if (name_external(name, dns_rdatatype_dname, rctx)) { + continue; + } + +@@ -9200,14 +9265,14 @@ rctx_answer_dname(respctx_t *rctx) { + + /* + * rctx_authority_positive(): +- * Examine the records in the authority section (if there are any) for a +- * positive answer. We expect the names for all rdatasets in this section +- * to be subdomains of the domain being queried; any that are not are +- * skipped. We expect to find only *one* owner name; any names +- * after the first one processed are ignored. We expect to find only +- * rdatasets of type NS, RRSIG, or SIG; all others are ignored. Whatever +- * remains can be cached at trust level authauthority or additional +- * (depending on whether the AA bit was set on the answer). ++ * If a positive answer was received over TCP or secured with a cookie ++ * or TSIG, examine the authority section. We expect names for all ++ * rdatasets in this section to be subdomains of the domain being queried; ++ * any that are not are skipped. We expect to find only *one* owner name; ++ * any names after the first one processed are ignored. We expect to find ++ * only rdatasets of type NS; all others are ignored. Whatever remains can ++ * be cached at trust level authauthority or additional (depending on ++ * whether the AA bit was set on the answer). + */ + static void + rctx_authority_positive(respctx_t *rctx) { +@@ -9215,6 +9280,11 @@ rctx_authority_positive(respctx_t *rctx) { + bool done = false; + isc_result_t result; + ++ /* If it's spoofable, don't cache it. */ ++ if (!rctx->secured && (rctx->query->options & DNS_FETCHOPT_TCP) == 0) { ++ return; ++ } ++ + result = dns_message_firstname(rctx->query->rmessage, + DNS_SECTION_AUTHORITY); + while (!done && result == ISC_R_SUCCESS) { +@@ -9223,7 +9293,9 @@ rctx_authority_positive(respctx_t *rctx) { + dns_message_currentname(rctx->query->rmessage, + DNS_SECTION_AUTHORITY, &name); + +- if (!name_external(name, dns_rdatatype_ns, fctx)) { ++ if (!name_external(name, dns_rdatatype_ns, rctx) && ++ dns_name_issubdomain(&fctx->name, name)) ++ { + dns_rdataset_t *rdataset = NULL; + + /* +-- +2.51.1 + diff --git a/SOURCES/bind-9.16-CVE-2025-40780.patch b/SOURCES/bind-9.16-CVE-2025-40780.patch new file mode 100644 index 0000000..5508810 --- /dev/null +++ b/SOURCES/bind-9.16-CVE-2025-40780.patch @@ -0,0 +1,303 @@ +From 6254679e5250a9f0d6079ec082cffdad4315372d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= +Date: Tue, 19 Aug 2025 19:22:18 +0200 +Subject: [PATCH] Use cryptographically-secure pseudo-random generator + everywhere + +It was discovered in an upcoming academic paper that a xoshiro128** +internal state can be recovered by an external 3rd party allowing to +predict UDP ports and DNS IDs in the outgoing queries. This could lead +to an attacker spoofing the DNS answers with great efficiency and +poisoning the DNS cache. + +Change the internal random generator to system CSPRNG with buffering to +avoid excessive syscalls. + +Thanks Omer Ben Simhon and Amit Klein of Hebrew University of Jerusalem +for responsibly reporting this to us. Very cool research! + +(cherry picked from commit cffcab9d5f3e709002f331b72498fcc229786ae2) +(cherry picked from commit d9b5ef342916462bfd63391831d96afc80c12df3) +--- + lib/isc/include/isc/os.h | 5 + + lib/isc/include/isc/random.h | 2 +- + lib/isc/random.c | 181 ++++++++++++++++++----------------- + lib/isc/tests/random_test.c | 4 +- + 4 files changed, 102 insertions(+), 90 deletions(-) + +diff --git a/lib/isc/include/isc/os.h b/lib/isc/include/isc/os.h +index ce7615a0e3..5def473d2b 100644 +--- a/lib/isc/include/isc/os.h ++++ b/lib/isc/include/isc/os.h +@@ -18,6 +18,11 @@ + + ISC_LANG_BEGINDECLS + ++/*%< ++ * Hardcode the L1 cacheline size of the CPU to 64. ++ */ ++#define ISC_OS_CACHELINE_SIZE 64 ++ + unsigned int + isc_os_ncpus(void); + /*%< +diff --git a/lib/isc/include/isc/random.h b/lib/isc/include/isc/random.h +index 556e74754a..2c16472a5d 100644 +--- a/lib/isc/include/isc/random.h ++++ b/lib/isc/include/isc/random.h +@@ -18,7 +18,7 @@ + #include + + /*! \file isc/random.h +- * \brief Implements wrapper around a non-cryptographically secure ++ * \brief Implements wrapper around a cryptographically secure + * pseudo-random number generator. + * + */ +diff --git a/lib/isc/random.c b/lib/isc/random.c +index 753453ff3c..717e1f0dcd 100644 +--- a/lib/isc/random.c ++++ b/lib/isc/random.c +@@ -29,131 +29,136 @@ + */ + + #include +-#include +-#include +-#include ++#include + +-#include ++#include + #include + #include +-#include + #include +-#include + #include + + #include "entropy_private.h" + +-/* +- * The specific implementation for PRNG is included as a C file +- * that has to provide a static variable named seed, and a function +- * uint32_t next(void) that provides next random number. +- * +- * The implementation must be thread-safe. +- */ +- +-/* +- * Two contestants have been considered: the xoroshiro family of the +- * functions by Villa&Blackman, and PCG by O'Neill. After +- * consideration, the xoshiro128starstar function has been chosen as +- * the uint32_t random number provider because it is very fast and has +- * good enough properties for our usage pattern. +- */ +-#include "xoshiro128starstar.c" ++#define ISC_RANDOM_BUFSIZE (ISC_OS_CACHELINE_SIZE / sizeof(uint32_t)) + +-ISC_THREAD_LOCAL isc_once_t isc_random_once = ISC_ONCE_INIT; ++ISC_THREAD_LOCAL uint32_t isc__random_pool[ISC_RANDOM_BUFSIZE]; ++ISC_THREAD_LOCAL size_t isc__random_pos = ISC_RANDOM_BUFSIZE; + +-static void +-isc_random_initialize(void) { +- int useed[4] = { 0, 0, 0, 1 }; ++static uint32_t ++random_u32(void) { + #if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* +- * Set a constant seed to help in problem reproduction should fuzzing +- * find a crash or a hang. The seed array must be non-zero else +- * xoshiro128starstar will generate an infinite series of zeroes. ++ * A fixed stream of numbers helps with problem reproduction when ++ * fuzzing. The first result needs to be non-zero as expected by ++ * random_test.c (it starts with ISC_RANDOM_BUFSIZE, see above). + */ +-#else /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ +- isc_entropy_get(useed, sizeof(useed)); ++ return (uint32_t)(isc__random_pos++); + #endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ +- memmove(seed, useed, sizeof(seed)); ++ ++ if (isc__random_pos == ISC_RANDOM_BUFSIZE) { ++ isc_entropy_get(isc__random_pool, sizeof(isc__random_pool)); ++ isc__random_pos = 0; ++ } ++ ++ return isc__random_pool[isc__random_pos++]; + } + + uint8_t + isc_random8(void) { +- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) == +- ISC_R_SUCCESS); +- return (next() & 0xff); ++ return (uint8_t)random_u32(); + } + + uint16_t + isc_random16(void) { +- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) == +- ISC_R_SUCCESS); +- return (next() & 0xffff); ++ return (uint16_t)random_u32(); + } + + uint32_t + isc_random32(void) { +- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) == +- ISC_R_SUCCESS); +- return (next()); ++ return random_u32(); + } + + void + isc_random_buf(void *buf, size_t buflen) { +- int i; +- uint32_t r; ++ REQUIRE(buflen == 0 || buf != NULL); + +- REQUIRE(buf != NULL); +- REQUIRE(buflen > 0); +- +- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) == +- ISC_R_SUCCESS); +- +- for (i = 0; i + sizeof(r) <= buflen; i += sizeof(r)) { +- r = next(); +- memmove((uint8_t *)buf + i, &r, sizeof(r)); ++ if (buf == NULL || buflen == 0) { ++ return; + } +- r = next(); +- memmove((uint8_t *)buf + i, &r, buflen % sizeof(r)); +- return; ++ ++ isc_entropy_get(buf, buflen); + } + + uint32_t +-isc_random_uniform(uint32_t upper_bound) { +- /* Copy of arc4random_uniform from OpenBSD */ +- uint32_t r, min; +- +- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) == +- ISC_R_SUCCESS); +- +- if (upper_bound < 2) { +- return (0); +- } +- +-#if (ULONG_MAX > 0xffffffffUL) +- min = 0x100000000UL % upper_bound; +-#else /* if (ULONG_MAX > 0xffffffffUL) */ +- /* Calculate (2**32 % upper_bound) avoiding 64-bit math */ +- if (upper_bound > 0x80000000) { +- min = 1 + ~upper_bound; /* 2**32 - upper_bound */ +- } else { +- /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */ +- min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound; +- } +-#endif /* if (ULONG_MAX > 0xffffffffUL) */ +- ++isc_random_uniform(uint32_t limit) { + /* +- * This could theoretically loop forever but each retry has +- * p > 0.5 (worst case, usually far better) of selecting a +- * number inside the range we need, so it should rarely need +- * to re-roll. ++ * Daniel Lemire's nearly-divisionless unbiased bounded random numbers. ++ * ++ * https://lemire.me/blog/?p=17551 ++ * ++ * The raw random number generator `next()` returns a 32-bit value. ++ * We do a 64-bit multiply `next() * limit` and treat the product as a ++ * 32.32 fixed-point value less than the limit. Our result will be the ++ * integer part (upper 32 bits), and we will use the fraction part ++ * (lower 32 bits) to determine whether or not we need to resample. + */ +- for (;;) { +- r = next(); +- if (r >= min) { +- break; ++ uint64_t num = (uint64_t)random_u32() * (uint64_t)limit; ++ /* ++ * In the fast path, we avoid doing a division in most cases by ++ * comparing the fraction part of `num` with the limit, which is ++ * a slight over-estimate for the exact resample threshold. ++ */ ++ if ((uint32_t)(num) < limit) { ++ /* ++ * We are in the slow path where we re-do the approximate test ++ * more accurately. The exact threshold for the resample loop ++ * is the remainder after dividing the raw RNG limit `1 << 32` ++ * by the caller's limit. We use a trick to calculate it ++ * within 32 bits: ++ * ++ * (1 << 32) % limit ++ * == ((1 << 32) - limit) % limit ++ * == (uint32_t)(-limit) % limit ++ * ++ * This division is safe: we know that `limit` is strictly ++ * greater than zero because of the slow-path test above. ++ */ ++ uint32_t residue = (uint32_t)(-limit) % limit; ++ /* ++ * Unless we get one of `N = (1 << 32) - residue` valid ++ * values, we reject the sample. This `N` is a multiple of ++ * `limit`, so our results will be unbiased; and `N` is the ++ * largest multiple that fits in 32 bits, so rejections are as ++ * rare as possible. ++ * ++ * There are `limit` possible values for the integer part of ++ * our fixed-point number. Each one corresponds to `N/limit` ++ * or `N/limit + 1` possible fraction parts. For our result to ++ * be unbiased, every possible integer part must have the same ++ * number of possible valid fraction parts. So, when we get ++ * the superfluous value in the `N/limit + 1` cases, we need ++ * to reject and resample. ++ * ++ * Because of the multiplication, the possible values in the ++ * fraction part are equally spaced by `limit`, with varying ++ * gaps at each end of the fraction's 32-bit range. We will ++ * choose a range of size `N` (a multiple of `limit`) into ++ * which valid fraction values must fall, with the rest of the ++ * 32-bit range covered by the `residue`. Lemire's paper says ++ * that exactly `N/limit` possible values spaced apart by ++ * `limit` will fit into our size `N` valid range, regardless ++ * of the size of the end gaps, the phase alignment of the ++ * values, or the position of the range. ++ * ++ * So, when a fraction value falls in the `residue` outside ++ * our valid range, it is superfluous, and we resample. ++ */ ++ while ((uint32_t)(num) < residue) { ++ num = (uint64_t)random_u32() * (uint64_t)limit; + } + } +- +- return (r % upper_bound); ++ /* ++ * Return the integer part (upper 32 bits). ++ */ ++ return (uint32_t)(num >> 32); + } +diff --git a/lib/isc/tests/random_test.c b/lib/isc/tests/random_test.c +index 7161cd96a9..f47137d3ac 100644 +--- a/lib/isc/tests/random_test.c ++++ b/lib/isc/tests/random_test.c +@@ -345,7 +345,9 @@ random_test(pvalue_func_t *func, isc_random_func test_func) { + } + break; + case ISC_RANDOM_BYTES: +- isc_random_buf(values, sizeof(values)); ++ for (i = 0; i < ARRAY_SIZE(values); i++) { ++ values[i] = isc_random32(); ++ } + break; + case ISC_RANDOM_UNIFORM: + uniform_values = (uint16_t *)values; +-- +2.51.1 + diff --git a/SOURCES/bind-9.16-properly-process-extra-nameserver-lines.patch b/SOURCES/bind-9.16-properly-process-extra-nameserver-lines.patch new file mode 100644 index 0000000..abfa7cf --- /dev/null +++ b/SOURCES/bind-9.16-properly-process-extra-nameserver-lines.patch @@ -0,0 +1,38 @@ +diff --git a/CHANGES b/CHANGES +index 2b12128544..42c13c9dbd 100644 +--- a/CHANGES ++++ b/CHANGES +@@ -1,3 +1,7 @@ ++6173. [bug] Properly process extra "nameserver" lines in ++ resolv.conf otherwise the next line is not properly ++ processed. [GL #4066] ++ + --- 9.16.23 released --- + + 5752. [bug] Fix an assertion failure caused by missing member zones +diff --git a/lib/irs/resconf.c b/lib/irs/resconf.c +index da6066db7b..775f4e86a4 100644 +--- a/lib/irs/resconf.c ++++ b/lib/irs/resconf.c +@@ -286,10 +286,6 @@ resconf_parsenameserver(irs_resconf_t *conf, FILE *fp) { + int cp; + isc_result_t result; + +- if (conf->numns == RESCONFMAXNAMESERVERS) { +- return (ISC_R_SUCCESS); +- } +- + cp = getword(fp, word, sizeof(word)); + if (strlen(word) == 0U) { + return (ISC_R_UNEXPECTEDEND); /* Nothing on line. */ +@@ -301,6 +297,10 @@ resconf_parsenameserver(irs_resconf_t *conf, FILE *fp) { + return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */ + } + ++ if (conf->numns == RESCONFMAXNAMESERVERS) { ++ return (ISC_R_SUCCESS); ++ } ++ + result = add_server(conf->mctx, word, &conf->nameservers); + if (result != ISC_R_SUCCESS) { + return (result); diff --git a/SOURCES/bind-9.18-CVE-2024-11187-pre-test.patch b/SOURCES/bind-9.18-CVE-2024-11187-pre-test.patch new file mode 100644 index 0000000..1010d1c --- /dev/null +++ b/SOURCES/bind-9.18-CVE-2024-11187-pre-test.patch @@ -0,0 +1,61 @@ +From cc01143082bc688a371a7378ef284c898eedc9df Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= +Date: Tue, 7 Jan 2025 15:22:40 +0100 +Subject: [PATCH] Isolate using the -T noaa flag only for part of the resolver + test + +Instead of running the whole resolver/ns4 server with -T noaa flag, +use it only for the part where it is actually needed. The -T noaa +could interfere with other parts of the test because the answers don't +have the authoritative-answer bit set, and we could have false +positives (or false negatives) in the test because the authoritative +server doesn't follow the DNS protocol for all the tests in the resolver +system test. + +(cherry picked from commit e51d4d3b88af00d6667f2055087ebfc47fb3107c) +--- + bin/tests/system/resolver/ns4/named.noaa | 5 ----- + bin/tests/system/resolver/tests.sh | 8 ++++++++ + 2 files changed, 8 insertions(+), 5 deletions(-) + delete mode 100644 bin/tests/system/resolver/ns4/named.noaa + +diff --git a/bin/tests/system/resolver/ns4/named.noaa b/bin/tests/system/resolver/ns4/named.noaa +deleted file mode 100644 +index 3b121ad..0000000 +--- a/bin/tests/system/resolver/ns4/named.noaa ++++ /dev/null +@@ -1,5 +0,0 @@ +-Copyright (C) Internet Systems Consortium, Inc. ("ISC") +- +-See COPYRIGHT in the source root or https://isc.org/copyright.html for terms. +- +-Add -T noaa. +diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh +index 711ee05..2eae16f 100755 +--- a/bin/tests/system/resolver/tests.sh ++++ b/bin/tests/system/resolver/tests.sh +@@ -289,6 +289,10 @@ done + if [ $ret != 0 ]; then echo_i "failed"; fi + status=`expr $status + $ret` + ++stop_server resolver ns4 ++touch ns4/named.noaa ++start_server --noclean --restart --port ${PORT} resolver ns4 || ret=1 ++ + n=`expr $n + 1` + echo_i "RT21594 regression test check setup ($n)" + ret=0 +@@ -325,6 +329,10 @@ grep "status: NXDOMAIN" dig.ns5.out.${n} > /dev/null || ret=1 + if [ $ret != 0 ]; then echo_i "failed"; fi + status=`expr $status + $ret` + ++stop_server resolver ns4 ++rm ns4/named.noaa ++start_server --noclean --restart --port ${PORT} resolver ns4 || ret=1 ++ + n=`expr $n + 1` + echo_i "check that replacement of additional data by a negative cache no data entry clears the additional RRSIGs ($n)" + ret=0 +-- +2.48.1 + diff --git a/SOURCES/bind-9.18-CVE-2024-11187.patch b/SOURCES/bind-9.18-CVE-2024-11187.patch new file mode 100644 index 0000000..2b74038 --- /dev/null +++ b/SOURCES/bind-9.18-CVE-2024-11187.patch @@ -0,0 +1,172 @@ +From b095854ee885dd5960cb54e7fbefb962498e9fdb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= +Date: Thu, 14 Nov 2024 10:37:29 +0100 +Subject: [PATCH] Limit the additional processing for large RDATA sets + +When answering queries, don't add data to the additional section if +the answer has more than 13 names in the RDATA. This limits the +number of lookups into the database(s) during a single client query, +reducing query processing load. + +Also, don't append any additional data to type=ANY queries. The +answer to ANY is already big enough. + +(cherry picked from commit a1982cf1bb95c818aa7b58988b5611dec80f2408) +PatchNumber: 47 +--- + bin/tests/system/additional/tests.sh | 2 +- + lib/dns/include/dns/rdataset.h | 12 ++++++++++++ + lib/dns/rbtdb.c | 2 +- + lib/dns/rdataset.c | 11 +++++++++++ + lib/ns/query.c | 15 ++++++++++----- + 5 files changed, 35 insertions(+), 7 deletions(-) + +diff --git a/bin/tests/system/additional/tests.sh b/bin/tests/system/additional/tests.sh +index 025f11f..846c800 100644 +--- a/bin/tests/system/additional/tests.sh ++++ b/bin/tests/system/additional/tests.sh +@@ -260,7 +260,7 @@ n=`expr $n + 1` + echo_i "testing with 'minimal-any no;' ($n)" + ret=0 + $DIG $DIGOPTS -t ANY www.rt.example @10.53.0.1 > dig.out.$n || ret=1 +-grep "ANSWER: 3, AUTHORITY: 2, ADDITIONAL: 2" dig.out.$n > /dev/null || ret=1 ++grep "ANSWER: 3, AUTHORITY: 2, ADDITIONAL: 1" dig.out.$n > /dev/null || ret=1 + if [ $ret -eq 1 ] ; then + echo_i "failed"; status=$((status+1)) + fi +diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h +index f2585ef..54ad7b2 100644 +--- a/lib/dns/include/dns/rdataset.h ++++ b/lib/dns/include/dns/rdataset.h +@@ -53,6 +53,8 @@ + #include + #include + ++#define DNS_RDATASET_MAXADDITIONAL 13 ++ + ISC_LANG_BEGINDECLS + + typedef enum { +@@ -458,13 +460,23 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset, + *\li If a call to dns_rdata_additionaldata() is not successful, the + * result returned will be the result of dns_rdataset_additionaldata(). + * ++ *\li If 'limit' is non-zero and the number of the rdatasets is larger ++ * than 'limit', no additional data will be processed. ++ * + * Returns: + * + *\li #ISC_R_SUCCESS + * ++ *\li #DNS_R_TOOMANYRECORDS in case rdataset count is larger than 'limit' ++ * + *\li Any error that dns_rdata_additionaldata() can return. + */ + ++isc_result_t ++dns_rdataset_additionaldata2(dns_rdataset_t *rdataset, ++ dns_additionaldatafunc_t add, void *arg, ++ size_t limit); ++ + isc_result_t + dns_rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, + dns_rdataset_t *neg, dns_rdataset_t *negsig); +diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c +index ed5015c..7586e2a 100644 +--- a/lib/dns/rbtdb.c ++++ b/lib/dns/rbtdb.c +@@ -10626,7 +10626,7 @@ no_glue: + maybe_rehash_gluetable(rbtversion); + idx = hash_32(hash, rbtversion->glue_table_bits); + +- (void)dns_rdataset_additionaldata(rdataset, glue_nsdname_cb, &ctx); ++ (void)dns_rdataset_additionaldata2(rdataset, glue_nsdname_cb, &ctx, 0); + + cur = isc_mem_get(rbtdb->common.mctx, sizeof(*cur)); + +diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c +index bf9e7af..ffe6163 100644 +--- a/lib/dns/rdataset.c ++++ b/lib/dns/rdataset.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + static const char *trustnames[] = { + "none", "pending-additional", +@@ -577,6 +578,12 @@ dns_rdataset_towire(dns_rdataset_t *rdataset, const dns_name_t *owner_name, + isc_result_t + dns_rdataset_additionaldata(dns_rdataset_t *rdataset, + dns_additionaldatafunc_t add, void *arg) { ++ return dns_rdataset_additionaldata2(rdataset, add, arg, 0); ++} ++ ++isc_result_t ++dns_rdataset_additionaldata2(dns_rdataset_t *rdataset, ++ dns_additionaldatafunc_t add, void *arg, size_t limit) { + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_result_t result; + +@@ -588,6 +595,10 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset, + REQUIRE(DNS_RDATASET_VALID(rdataset)); + REQUIRE((rdataset->attributes & DNS_RDATASETATTR_QUESTION) == 0); + ++ if (limit != 0 && dns_rdataset_count(rdataset) > limit) { ++ return DNS_R_TOOMANYRECORDS; ++ } ++ + result = dns_rdataset_first(rdataset); + if (result != ISC_R_SUCCESS) { + return (result); +diff --git a/lib/ns/query.c b/lib/ns/query.c +index f7b3f78..5bc2908 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -2030,8 +2030,9 @@ addname: + * This cannot go more than MAX_RESTARTS levels deep. + */ + if (trdataset != NULL && dns_rdatatype_followadditional(type)) { +- eresult = dns_rdataset_additionaldata( +- trdataset, query_additional_cb, qctx); ++ eresult = dns_rdataset_additionaldata2( ++ trdataset, query_additional_cb, qctx, ++ DNS_RDATASET_MAXADDITIONAL); + } + + cleanup: +@@ -2122,7 +2123,8 @@ regular: + * Add other additional data if needed. + * We don't care if dns_rdataset_additionaldata() fails. + */ +- (void)dns_rdataset_additionaldata(rdataset, query_additional_cb, qctx); ++ (void)dns_rdataset_additionaldata2(rdataset, query_additional_cb, ++ qctx, DNS_RDATASET_MAXADDITIONAL); + CTRACE(ISC_LOG_DEBUG(3), "query_additional: done"); + } + +@@ -2148,7 +2150,8 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep, + * To the current response for 'client', add the answer RRset + * '*rdatasetp' and an optional signature set '*sigrdatasetp', with + * owner name '*namep', to section 'section', unless they are +- * already there. Also add any pertinent additional data. ++ * already there. Also add any pertinent additional data, unless ++ * the query was for type ANY. + * + * If 'dbuf' is not NULL, then '*namep' is the name whose data is + * stored in 'dbuf'. In this case, query_addrrset() guarantees that +@@ -2203,7 +2206,9 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep, + */ + query_addtoname(mname, rdataset); + query_setorder(qctx, mname, rdataset); +- query_additional(qctx, rdataset); ++ if (qctx->qtype != dns_rdatatype_any) { ++ query_additional(qctx, rdataset); ++ } + + /* + * Note: we only add SIGs if we've added the type they cover, so +-- +2.48.1 + diff --git a/SOURCES/bind-9.18-configurable-additional-records.patch b/SOURCES/bind-9.18-configurable-additional-records.patch new file mode 100644 index 0000000..48ac2f6 --- /dev/null +++ b/SOURCES/bind-9.18-configurable-additional-records.patch @@ -0,0 +1,168 @@ +From bddd8950ff3d82d503dfb64e67d24ce364e5a5af Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Mon, 16 Jun 2025 19:36:13 +0200 +Subject: [PATCH] Support runtime configurable limit of additional records + +Use environment variable NAMED_MAXADDITIONAL to change default built-in +limit. Uses environment variable to avoid the need to support the variable +option in the more recent versions and after upgrades. + +Use debug 1 verbosity for logging parsed limit at the start, but not +changing production logs. +--- + bin/named/main.c | 5 +++++ + bin/named/named.rst | 9 +++++++++ + lib/ns/include/ns/server.h | 13 +++++++++++++ + lib/ns/query.c | 4 ++-- + lib/ns/server.c | 25 +++++++++++++++++++++++++ + 5 files changed, 54 insertions(+), 2 deletions(-) + +diff --git a/bin/named/main.c b/bin/named/main.c +index f62f82c..0de658a 100644 +--- a/bin/named/main.c ++++ b/bin/named/main.c +@@ -1327,6 +1327,11 @@ setup(void) { + if (sigvalinsecs) { + ns_server_setoption(sctx, NS_SERVER_SIGVALINSECS, true); + } ++ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, ++ NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), ++ "using max %u additional records", ++ ns_server_getmaxadditionalrecords(sctx)); ++ + } + + static void +diff --git a/bin/named/named.rst b/bin/named/named.rst +index 3cd6350..06af140 100644 +--- a/bin/named/named.rst ++++ b/bin/named/named.rst +@@ -228,6 +228,15 @@ Files + ``/var/run/named/named.pid`` + The default process-id file. + ++Environment ++~~~~~~~~~~~ ++ ++NAMED_MAXADDITIONAL ++ Red Hat specific extension. Accepts numeric value of maximal NS ++ records, which would get fetched additional addresses. Default ++ value is 13. Allows runtime configurable limit introduced in ++ CVE-2024-11187 fixes. ++ + Notes + ~~~~~ + +diff --git a/lib/ns/include/ns/server.h b/lib/ns/include/ns/server.h +index 0abb579..4864ba4 100644 +--- a/lib/ns/include/ns/server.h ++++ b/lib/ns/include/ns/server.h +@@ -122,6 +122,8 @@ struct ns_server { + isc_stats_t *tcpoutstats4; + isc_stats_t *tcpinstats6; + isc_stats_t *tcpoutstats6; ++ ++ unsigned int max_additional_records; + }; + + struct ns_altsecret { +@@ -166,6 +168,17 @@ ns_server_setserverid(ns_server_t *sctx, const char *serverid); + *\li 'sctx' is valid. + */ + ++unsigned int ++ns_server_getmaxadditionalrecords(ns_server_t *sctx); ++/*%< ++ * Returns the maximal number of records with additional addresses ++ * provided. ++ * ++ * Requires: ++ *\li 'sctx' is valid. ++ */ ++ ++ + void + ns_server_setoption(ns_server_t *sctx, unsigned int option, bool value); + /*%< +diff --git a/lib/ns/query.c b/lib/ns/query.c +index ce1d710..37893e2 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -2032,7 +2032,7 @@ addname: + if (trdataset != NULL && dns_rdatatype_followadditional(type)) { + eresult = dns_rdataset_additionaldata2( + trdataset, query_additional_cb, qctx, +- DNS_RDATASET_MAXADDITIONAL); ++ client->sctx->max_additional_records); + } + + cleanup: +@@ -2124,7 +2124,7 @@ regular: + * We don't care if dns_rdataset_additionaldata() fails. + */ + (void)dns_rdataset_additionaldata2(rdataset, query_additional_cb, +- qctx, DNS_RDATASET_MAXADDITIONAL); ++ qctx, client->sctx->max_additional_records); + CTRACE(ISC_LOG_DEBUG(3), "query_additional: done"); + } + +diff --git a/lib/ns/server.c b/lib/ns/server.c +index 540bc2e..0a4abb3 100644 +--- a/lib/ns/server.c ++++ b/lib/ns/server.c +@@ -17,6 +17,7 @@ + #include + #include + ++#include + #include + #include + +@@ -33,6 +34,22 @@ + RUNTIME_CHECK(result == ISC_R_SUCCESS); \ + } while (0) + ++/* ++ * CVE-2024-11187 introduced a limit on maximal number of records, ++ * for which additional records would be fetched. Make this limit ++ * configurable runtime only by environment. ++ */ ++static size_t ++initialize_maxadditional(void) { ++ const char *limits; ++ ++ limits = getenv("NAMED_MAXADDITIONAL"); ++ if (limits != NULL) { ++ return strtol(limits, NULL, 10); ++ } ++ return DNS_RDATASET_MAXADDITIONAL; ++} ++ + isc_result_t + ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview, + ns_server_t **sctxp) { +@@ -90,6 +107,7 @@ ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview, + + sctx->udpsize = 1232; + sctx->transfer_tcp_message_size = 20480; ++ sctx->max_additional_records = initialize_maxadditional(); + + sctx->fuzztype = isc_fuzz_none; + sctx->fuzznotify = NULL; +@@ -213,6 +231,13 @@ ns_server_setserverid(ns_server_t *sctx, const char *serverid) { + return (ISC_R_SUCCESS); + } + ++unsigned int ++ns_server_getmaxadditionalrecords(ns_server_t *sctx) { ++ REQUIRE(SCTX_VALID(sctx)); ++ ++ return sctx->max_additional_records; ++} ++ + void + ns_server_setoption(ns_server_t *sctx, unsigned int option, bool value) { + REQUIRE(SCTX_VALID(sctx)); +-- +2.49.0 + diff --git a/SOURCES/bind-9.18-dig-idn-input-always-test.patch b/SOURCES/bind-9.18-dig-idn-input-always-test.patch new file mode 100644 index 0000000..8fdca3d --- /dev/null +++ b/SOURCES/bind-9.18-dig-idn-input-always-test.patch @@ -0,0 +1,80 @@ +From 11f35bdef6f070412cb2c1c65d9537b82ea38c8a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Tue, 16 Sep 2025 11:46:03 +0200 +Subject: [PATCH] Fix expectations on idna system test + +IDNA tests always redirect output into the file. That means its +behaviour has changed and is now processing IDN input by default and +just disables IDN output by default. + +New behaviour when redirected is the same as +idnin +noidnout, but does +not fail hard on input errors. + +bind 9.16 version. +--- + bin/tests/system/idna/tests.sh | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/bin/tests/system/idna/tests.sh b/bin/tests/system/idna/tests.sh +index a04012a..5541b94 100644 +--- a/bin/tests/system/idna/tests.sh ++++ b/bin/tests/system/idna/tests.sh +@@ -185,7 +185,7 @@ idna_enabled_test() { + # Note that ASCII characters are converted to lower-case. + + text="Checking valid non-ASCII label" +- idna_test "$text" "" "München" "M\195\188nchen." ++ idna_test "$text" "" "München" "xn--mnchen-3ya." + idna_test "$text" "+noidnin +noidnout" "München" "M\195\188nchen." + idna_test "$text" "+noidnin +idnout" "München" "M\195\188nchen." + idna_test "$text" "+idnin +noidnout" "München" "xn--mnchen-3ya." +@@ -210,7 +210,7 @@ idna_enabled_test() { + # for the valid U-label. + + text="Checking that non-transitional IDNA processing is used" +- idna_test "$text" "" "faß.de" "fa\195\159.de." ++ idna_test "$text" "" "faß.de" "xn--fa-hia.de." + idna_test "$text" "+noidnin +noidnout" "faß.de" "fa\195\159.de." + idna_test "$text" "+noidnin +idnout" "faß.de" "fa\195\159.de." + idna_test "$text" "+idnin +noidnout" "faß.de" "xn--fa-hia.de." +@@ -220,7 +220,7 @@ idna_enabled_test() { + # onto the Greek sigma character ("σ") in IDNA2003. + + text="Second check that non-transitional IDNA processing is used" +- idna_test "$text" "" "βόλος.com" "\206\178\207\140\206\187\206\191\207\130.com." ++ idna_test "$text" "" "βόλος.com" "xn--nxasmm1c.com." + idna_test "$text" "+noidnin +noidnout" "βόλος.com" "\206\178\207\140\206\187\206\191\207\130.com." + idna_test "$text" "+noidnin +idnout" "βόλος.com" "\206\178\207\140\206\187\206\191\207\130.com." + idna_test "$text" "+idnin +noidnout" "βόλος.com" "xn--nxasmm1c.com." +@@ -284,7 +284,7 @@ idna_enabled_test() { + idna_test "$text" "" "xn--xx" "xn--xx." + idna_test "$text" "+noidnin +noidnout" "xn--xx" "xn--xx." + idna_fail "$text" "+noidnin +idnout" "xn--xx" +- idna_fail "$text" "+idnin +noidnout" "xn--xx" ++ idna_test "$text" "+idnin +noidnout" "xn--xx" "xn--xx." + idna_fail "$text" "+idnin +idnout" "xn--xx" + + # Fake A-label - the string does not translate to anything. +@@ -293,7 +293,7 @@ idna_enabled_test() { + idna_test "$text" "" "xn--ahahah" "xn--ahahah." + idna_test "$text" "+noidnin +noidnout" "xn--ahahah" "xn--ahahah." + idna_fail "$text" "+noidnin +idnout" "xn--ahahah" +- idna_fail "$text" "+idnin +noidnout" "xn--ahahah" ++ idna_test "$text" "+idnin +noidnout" "xn--ahahah" "xn--ahahah." + idna_fail "$text" "+idnin +idnout" "xn--ahahah" + + # Too long a label. The punycode string is too long (at 64 characters). +@@ -326,8 +326,8 @@ idna_enabled_test() { + idna_test "$text" "" "√.com" "\226\136\154.com." + idna_test "$text" "+noidnin +noidnout" "√.com" "\226\136\154.com." + idna_test "$text" "+noidnin +idnout" "√.com" "\226\136\154.com." +- idna_fail "$text" "+idnin +noidnout" "√.com" +- idna_fail "$text" "+idnin +idnout" "√.com" ++ idna_test "$text" "+idnin +noidnout" "√.com" "\226\136\154.com." ++ idna_test "$text" "+idnin +idnout" "√.com" "\226\136\154.com." + + # Tests of a valid unicode string but an invalid U-label (output) + # +-- +2.51.0 + diff --git a/SOURCES/bind-9.18-dig-idn-input-always.patch b/SOURCES/bind-9.18-dig-idn-input-always.patch new file mode 100644 index 0000000..72faf02 --- /dev/null +++ b/SOURCES/bind-9.18-dig-idn-input-always.patch @@ -0,0 +1,102 @@ +From d023241c7e2921926be8bd1784424eefba2a3c54 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Wed, 6 Nov 2024 21:29:47 +0100 +Subject: [PATCH] Allow always IDN input in dig +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Even when stdout is non-interactive terminal, allow unicode characters +to be encoded into ACE form. Still disable IDN output, but unless ++noidnin or IDN_DISABLE=1 env is detected, consider input as locale +defined name. + +Provides more isolated change, which issue #3527 introduced similar +behavior into 9.19 with more changes. + +Ignore input IDN errors when stdout is not terminal + +Attempt to prevent visible regressions when enabling IDN on input +always. Instead of new hard failures preventing IDN decoding of input +name just use original input. + +Should make the change backward compatible. When on interactive terminal +behave the same way as before and emit hard errors. Become more +forgiving in scripts where stdout leads to script. Decoding output is +not enabled there and if input decoding fails, just use input as it was. + +Change dig manual +idnin + +Note in manual IDN input is always enabled. But it silently ignores +errors when stdout is not a terminal to prevent regressions. + +Signed-off-by: Petr Menšík +--- + bin/dig/dig.rst | 6 +++--- + bin/dig/dighost.c | 19 +++++++++++++++---- + 2 files changed, 18 insertions(+), 7 deletions(-) + +diff --git a/bin/dig/dig.rst b/bin/dig/dig.rst +index a1d7cfe..85acde1 100644 +--- a/bin/dig/dig.rst ++++ b/bin/dig/dig.rst +@@ -358,9 +358,9 @@ abbreviation is unambiguous; for example, ``+cd`` is equivalent to + This option processes [or does not process] IDN domain names on input. This requires + ``IDN SUPPORT`` to have been enabled at compile time. + +- The default is to process IDN input when standard output is a tty. +- The IDN processing on input is disabled when ``dig`` output is redirected +- to files, pipes, and other non-tty file descriptors. ++ The default is to process IDN input. The input IDN processing errors are ignored ++ when :program:`dig` output is redirected to files, pipes, and other non-tty file ++ descriptors. + + ``+[no]idnout`` + This option converts [or does not convert] puny code on output. This requires +diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c +index 7e88b6b..fbd5ad9 100644 +--- a/bin/dig/dighost.c ++++ b/bin/dig/dighost.c +@@ -620,6 +620,9 @@ clone_server_list(dig_serverlist_t src, dig_serverlist_t *dest) { + dig_lookup_t * + make_empty_lookup(void) { + dig_lookup_t *looknew; ++#ifdef HAVE_LIBIDN2 ++ bool idn_allowed = (getenv("IDN_DISABLE") == NULL); ++#endif + + debug("make_empty_lookup()"); + +@@ -671,8 +674,8 @@ make_empty_lookup(void) { + looknew->qr = false; + looknew->accept_reply_unexpected_src = false; + #ifdef HAVE_LIBIDN2 +- looknew->idnin = isatty(1) ? (getenv("IDN_DISABLE") == NULL) : false; +- looknew->idnout = looknew->idnin; ++ looknew->idnin = idn_allowed; ++ looknew->idnout = isatty(1) && idn_allowed; + #else /* ifdef HAVE_LIBIDN2 */ + looknew->idnin = false; + looknew->idnout = false; +@@ -4493,8 +4496,16 @@ idn_locale_to_ace(const char *src, char *dst, size_t dstlen) { + */ + res = idn2_to_ascii_lz(src, &ascii_src, IDN2_NONTRANSITIONAL); + if (res != IDN2_OK) { +- fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnin", +- src, idn2_strerror(res)); ++ if (isatty(1)) { ++ fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnin", ++ src, idn2_strerror(res)); ++ } else { ++ /* In case of non-terminal output silently ignore errors ++ * in IDN input decoding. */ ++ (void)strlcpy(dst, src, dstlen); ++ resetlocale(LC_ALL); ++ return; ++ } + } + + /* +-- +2.50.1 + diff --git a/SOURCES/bind-9.18-partial-additional-records.patch b/SOURCES/bind-9.18-partial-additional-records.patch new file mode 100644 index 0000000..5aaf66f --- /dev/null +++ b/SOURCES/bind-9.18-partial-additional-records.patch @@ -0,0 +1,54 @@ +From 9140eac85cda21fa86f2768f7ccaf6800776c726 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Thu, 19 Jun 2025 19:51:43 +0200 +Subject: [PATCH] Limit number of additional records fetched + +Limit number of started fetches for additional zone instead of doing +none. Keep limit of NS filled with additional records, but present at +least some if possible. + +Might help broken implementations relying on receiving addresses in the +response for NS query in authoritative zone. +--- + lib/dns/rdataset.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c +index ffe6163..cfdb7d5 100644 +--- a/lib/dns/rdataset.c ++++ b/lib/dns/rdataset.c +@@ -586,6 +586,7 @@ dns_rdataset_additionaldata2(dns_rdataset_t *rdataset, + dns_additionaldatafunc_t add, void *arg, size_t limit) { + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_result_t result; ++ size_t n = 0; + + /* + * For each rdata in rdataset, call 'add' for each name and type in the +@@ -595,10 +596,6 @@ dns_rdataset_additionaldata2(dns_rdataset_t *rdataset, + REQUIRE(DNS_RDATASET_VALID(rdataset)); + REQUIRE((rdataset->attributes & DNS_RDATASETATTR_QUESTION) == 0); + +- if (limit != 0 && dns_rdataset_count(rdataset) > limit) { +- return DNS_R_TOOMANYRECORDS; +- } +- + result = dns_rdataset_first(rdataset); + if (result != ISC_R_SUCCESS) { + return (result); +@@ -608,7 +605,11 @@ dns_rdataset_additionaldata2(dns_rdataset_t *rdataset, + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_additionaldata(&rdata, add, arg); + if (result == ISC_R_SUCCESS) { +- result = dns_rdataset_next(rdataset); ++ if (limit != 0 && ++n >= limit) { ++ result = DNS_R_TOOMANYRECORDS; ++ } else { ++ result = dns_rdataset_next(rdataset); ++ } + } + dns_rdata_reset(&rdata); + } while (result == ISC_R_SUCCESS); +-- +2.49.0 + diff --git a/SOURCES/bind-9.18-query-fname-relative.patch b/SOURCES/bind-9.18-query-fname-relative.patch new file mode 100644 index 0000000..219721a --- /dev/null +++ b/SOURCES/bind-9.18-query-fname-relative.patch @@ -0,0 +1,90 @@ +From 5bc7cd7a7b9c37e5c70ccf74c5485a02411aaef5 Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Fri, 25 Apr 2025 02:00:00 +0200 +Subject: [PATCH] Insert additional checks ensuring name is not relative + +Mitigation for crashes put in various places, where obviously relative +uninitialized name must not appear. This seems unnecessary once true +cause were identified, but may prevent similar places. +--- + lib/ns/query.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/lib/ns/query.c b/lib/ns/query.c +index 11d2520..7e8a4d2 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -2203,6 +2203,20 @@ regular: + CTRACE(ISC_LOG_DEBUG(3), "query_additional: done"); + } + ++static void ++log_query_relative(query_ctx_t *qctx, const char *func, const dns_name_t *name) { ++ if (isc_log_wouldlog(ns_lctx, ISC_LOG_DEBUG(1))) { ++ char namebuf[DNS_NAME_FORMATSIZE] = "!"; ++ dns_name_format(name, namebuf, sizeof(namebuf)); ++ ns_client_log( ++ qctx->client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_QUERY, ++ ISC_LOG_DEBUG(1), ++ "%s: fname=%s leading to relative name, aborting query.", ++ func, namebuf ++ ); ++ } ++} ++ + static void + query_addrrset(query_ctx_t *qctx, dns_name_t **namep, + dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp, +@@ -2275,6 +2289,11 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep, + client->query.attributes &= ~NS_QUERYATTR_SECURE; + } + ++ if (!qctx->is_zone && mname && !dns_name_isabsolute(mname)) { ++ log_query_relative(qctx, "query_addrrset", mname); ++ QUERY_ERROR(qctx, DNS_R_SERVFAIL); ++ return; ++ } + /* + * Update message name, set rdataset order, and do additional + * section processing if needed. +@@ -8074,6 +8093,11 @@ query_respond_any(query_ctx_t *qctx) { + : qctx->tname; + query_prefetch(qctx->client, name, + qctx->rdataset); ++ if (name && !dns_name_isabsolute(name)) { ++ log_query_relative(qctx, "query_respond_any", name); ++ result = DNS_R_DROP; ++ break; ++ } + } + + /* +@@ -10696,6 +10720,11 @@ query_cname(query_ctx_t *qctx) { + + if (!qctx->is_zone && RECURSIONOK(qctx->client)) { + query_prefetch(qctx->client, qctx->fname, qctx->rdataset); ++ if (qctx->fname && !dns_name_isabsolute(qctx->fname)) { ++ log_query_relative(qctx, "query_cname", qctx->fname); ++ QUERY_ERROR(qctx, DNS_R_SERVFAIL); ++ return (ns_query_done(qctx)); ++ } + } + + query_addrrset(qctx, &qctx->fname, &qctx->rdataset, sigrdatasetp, +@@ -10801,7 +10830,13 @@ query_dname(query_ctx_t *qctx) { + + if (!qctx->is_zone && RECURSIONOK(qctx->client)) { + query_prefetch(qctx->client, qctx->fname, qctx->rdataset); ++ if (qctx->fname && !dns_name_isabsolute(qctx->fname)) { ++ log_query_relative(qctx, "query_dname", qctx->fname); ++ QUERY_ERROR(qctx, DNS_R_SERVFAIL); ++ return (ns_query_done(qctx)); ++ } + } ++ + query_addrrset(qctx, &qctx->fname, &qctx->rdataset, sigrdatasetp, + qctx->dbuf, DNS_SECTION_ANSWER); + +-- +2.49.0 + diff --git a/SOURCES/bind-9.21-resume-qmin-cname.patch b/SOURCES/bind-9.21-resume-qmin-cname.patch new file mode 100644 index 0000000..645864a --- /dev/null +++ b/SOURCES/bind-9.21-resume-qmin-cname.patch @@ -0,0 +1,44 @@ +From dc97bfa842b9f0d59db075bdcade599f3767f1ba Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Tue, 3 Jun 2025 21:00:58 +0200 +Subject: [PATCH] Handle CNAME and DNAME in resume_min in a special way + +When authoritative zone is loaded when query minimization query for the +same zone is already pending, it might receive unexpected result codes. + +Normally DNS_R_CNAME would follow to query_cname after processing sent +events, but dns_view_findzonecut does not fill CNAME target into +event->foundevent. Usual lookup via query_lookup would always have that +filled. + +Ideally we would restart the query with unmodified search name, if +unexpected change from recursing to local zone cut were detected. Until +dns_view_findzonecut is modified to export zone/cache source of the cut, +at least fail queries which went into unexpected state. +--- + lib/dns/resolver.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c +index be1d735..ad0a1b8 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -4508,6 +4508,15 @@ resume_qmin(isc_task_t *task, isc_event_t *event) { + if (result == DNS_R_NXDOMAIN) { + result = DNS_R_SERVFAIL; + } ++ /* ++ * CNAME or DNAME means zone were added with that record ++ * after the start of query minimization queries. It means ++ * we do not have initialized correct hevent->foundname ++ * and have to fail. ++ */ ++ if (result == DNS_R_CNAME || result == DNS_R_DNAME) { ++ result = DNS_R_SERVFAIL; ++ } + + if (result != ISC_R_SUCCESS) { + fctx_done(fctx, result, __LINE__); +-- +2.49.0 + diff --git a/SOURCES/bind-chroot.tmpfiles.d b/SOURCES/bind-chroot.tmpfiles.d new file mode 100644 index 0000000..13992fd --- /dev/null +++ b/SOURCES/bind-chroot.tmpfiles.d @@ -0,0 +1,38 @@ +# vim: ft=conf: +# TODO: these definitions are in different form in rpm spec %files chroot section +# find a way to have it defined only once +#defattr(0664,root,named,-) +c /var/named/chroot/dev/null 0664 root named - 1:3 +c /var/named/chroot/dev/random 0664 root named - 1:8 +c /var/named/chroot/dev/urandom 0664 root named - 1:9 +c /var/named/chroot/dev/zero 0664 root named - 1:5 +#defattr(0640,root,named,0750) +d /var/named/chroot 0750 root named - +d /var/named/chroot/dev 0750 root named - +d /var/named/chroot/etc 0750 root named - +d /var/named/chroot/etc/named 0750 root named - +d /var/named/chroot/etc/pki 0750 root named - +d /var/named/chroot/etc/pki/dnssec-keys 0750 root named - +d /var/named/chroot/etc/crypto-policies 0750 root named - +d /var/named/chroot/etc/crypto-policies/back-ends 0750 root named - +d /var/named/chroot/var 0750 root named - +d /var/named/chroot/run 0750 root named - +#defattr(-,root,root,-) +d /var/named/chroot/usr - root root - +d /var/named/chroot/usr/lib64 - root root - +d /var/named/chroot/usr/lib64/bind - root root - +d /var/named/chroot/usr/lib64/named - root root - +d /var/named/chroot/usr/share/GeoIP - root root - +d /var/named/chroot/usr/share/named - root root - +d /var/named/chroot/proc - root root - +d /var/named/chroot/proc/sys - root root - +d /var/named/chroot/proc/sys/net - root root - +d /var/named/chroot/proc/sys/net/ipv4 - root root - +#defattr(0660,root,named,01770) +d /var/named/chroot/var/named 01770 root named - +#defattr(0660,named,named,0770) +d /var/named/chroot/var/tmp 0770 named named - +d /var/named/chroot/var/log 0770 named named - +#defattr(-,named,named,-) +d /var/named/chroot/run/named - named named - +L /var/named/chroot/var/run - named named - ../run diff --git a/SOURCES/bind.tmpfiles.d b/SOURCES/bind.tmpfiles.d index 640a656..95d4975 100644 --- a/SOURCES/bind.tmpfiles.d +++ b/SOURCES/bind.tmpfiles.d @@ -1 +1,10 @@ +# vim: ft=conf: d /run/named 0755 named named - +d /var/named 01770 root named - +d /var/named/slaves 0770 named named - +d /var/named/data 0770 named named - +d /var/named/dynamic 0770 named named - +L /var/named/named.ca 0640 named named - ../../../usr/share/named/named.ca +L /var/named/named.localhost 0640 named named - ../../../usr/share/named/named.localhost +L /var/named/named.loopback 0640 named named - ../../../usr/share/named/named.loopback +L /var/named/named.empty 0640 named named - ../../../usr/share/named/named.empty diff --git a/SOURCES/named-chroot.files b/SOURCES/named-chroot.files index 75e6aa1..8511722 100644 --- a/SOURCES/named-chroot.files +++ b/SOURCES/named-chroot.files @@ -3,6 +3,7 @@ # if they are missing or empty in target directory. /etc/localtime /etc/named.root.key +/etc/named.ca /etc/named.conf /etc/named.rfc1912.zones /etc/rndc.conf @@ -19,6 +20,7 @@ /usr/lib64/named /usr/lib/named /usr/share/GeoIP +/usr/share/named /run/named /proc/sys/net/ipv4/ip_local_port_range # Warning: the order is important diff --git a/SOURCES/named.logrotate b/SOURCES/named.logrotate index 5df448f..74ceff8 100644 --- a/SOURCES/named.logrotate +++ b/SOURCES/named.logrotate @@ -2,11 +2,10 @@ missingok su named named create 0644 named named + notifempty postrotate /usr/bin/systemctl reload named.service > /dev/null 2>&1 || true /usr/bin/systemctl reload named-chroot.service > /dev/null 2>&1 || true - /usr/bin/systemctl reload named-sdb.service > /dev/null 2>&1 || true - /usr/bin/systemctl reload named-sdb-chroot.service > /dev/null 2>&1 || true /usr/bin/systemctl reload named-pkcs11.service > /dev/null 2>&1 || true endscript } diff --git a/SOURCES/named.sysusers b/SOURCES/named.sysusers new file mode 100644 index 0000000..f173c78 --- /dev/null +++ b/SOURCES/named.sysusers @@ -0,0 +1,3 @@ +#Type Name ID GECOS Home directory Shell +u named 25 "Named" /var/named /sbin/nologin +g named 25 diff --git a/SPECS/bind.spec b/SPECS/bind.spec index 1901dde..a48bde8 100644 --- a/SPECS/bind.spec +++ b/SPECS/bind.spec @@ -6,6 +6,8 @@ # bcond_without is built by default, unless --without X is passed # bcond_with is built only when --with X is passed to build %bcond_with SYSTEMTEST +# enable RSA1 during SYSTEMTEST +%bcond_with CRYPTO_POLICY_RSA1 %bcond_without GSSTSIG # it is not possible to build the package without PKCS11 sub-package # due to extensive changes to Makefiles @@ -23,14 +25,12 @@ %bcond_with DOCPDF %bcond_with TSAN -%{?!bind_uid: %global bind_uid 25} -%{?!bind_gid: %global bind_gid 25} %{!?_pkgdocdir:%global _pkgdocdir %{_docdir}/%{name}-%{version}} %global bind_dir /var/named %global chroot_prefix %{bind_dir}/chroot %global chroot_create_directories /dev /run/named %{_localstatedir}/{log,named,tmp} \\\ %{_sysconfdir}/{crypto-policies/back-ends,pki/dnssec-keys,named} \\\ - %{_libdir}/bind %{_libdir}/named %{_datadir}/GeoIP /proc/sys/net/ipv4 + %{_libdir}/bind %{_libdir}/named %{_datadir}/{GeoIP,named} /proc/sys/net/ipv4 %global selinuxbooleans named_write_master_zones=1 @@ -54,7 +54,7 @@ Summary: The Berkeley Internet Name Domain (BIND) DNS (Domain Name System) serv Name: bind License: MPLv2.0 Version: 9.16.23 -Release: 24%{?dist} +Release: 40%{?dist} Epoch: 32 Url: https://www.isc.org/downloads/bind/ # @@ -85,6 +85,8 @@ Source46: named-setup-rndc.service Source47: named-pkcs11.service Source48: setup-named-softhsm.sh Source49: named-chroot.files +Source50: named.sysusers +Source51: bind-chroot.tmpfiles.d # Common patches Patch10: bind-9.5-PIE.patch @@ -174,10 +176,36 @@ Patch212: bind-9.16-CVE-2024-1737-types.patch Patch213: bind-9.16-CVE-2024-1737-types-test.patch # backport issue fix Patch214: bind-9.16-CVE-2024-1737-records-test2.patch +# https://gitlab.isc.org/isc-projects/bind9/-/commit/c6e6a7af8ac6b575dd3657b0f5cf4248d734c2b0 +Patch215: bind-9.18-CVE-2024-11187-pre-test.patch +Patch216: bind-9.18-CVE-2024-11187.patch +# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/10562 +# https://gitlab.isc.org/isc-projects/bind9/-/issues/5357 +# downstream patch fixing bind-dyndb-ldap causing issue +Patch217: bind-9.21-resume-qmin-cname.patch +# downstream only, extra check for above change, RHEL-30407 +Patch218: bind-9.18-query-fname-relative.patch +Patch219: bind-9.18-partial-additional-records.patch +# downstream only, RHEL-84006 +Patch220: bind-9.18-configurable-additional-records.patch +# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/9723 +# downstream only +Patch221: bind-9.18-dig-idn-input-always.patch +# downstream only too +Patch222: bind-9.18-dig-idn-input-always-test.patch +# https://gitlab.isc.org/isc-projects/bind9/commit/d9b5ef342916462bfd63391831d96afc80c12df3 +Patch224: bind-9.16-CVE-2025-40780.patch +# https://gitlab.isc.org/isc-projects/bind9/commit/2f0f44d493c382a7f0a3adfe7c4976b18a3d480b +# https://gitlab.isc.org/isc-projects/bind9/commit/50479358efdf432d690415131b74b5df158a9d69 +# https://gitlab.isc.org/isc-projects/bind9/commit/33a7db1fe964e55b76b4ac003ecc56cc67028bd9 +Patch225: bind-9.16-CVE-2025-40778.patch +# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/7942 +Patch226: bind-9.16-properly-process-extra-nameserver-lines.patch %{?systemd_ordering} +# https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers +%{?sysusers_requires_compat} Requires: coreutils -Requires(pre): shadow-utils Requires(post): shadow-utils Requires(post): glibc-common Requires(post): grep @@ -216,6 +244,7 @@ BuildRequires: softhsm %if %{with SYSTEMTEST} # bin/tests/system dependencies BuildRequires: perl(Net::DNS) perl(Net::DNS::Nameserver) perl(Time::HiRes) perl(Getopt::Long) +BuildRequires: perl(English) BuildRequires: python-dns # manual configuration requires this tool BuildRequires: iproute @@ -465,69 +494,21 @@ in HTML and PDF format. # RHEL does not yet support this verification %{gpgverify} --keyring='%{SOURCE4}' --signature='%{SOURCE2}' --data='%{SOURCE0}' %endif -%setup -q +%autosetup -N # Common patches -%patch10 -p1 -b .PIE -%patch16 -p1 -b .redhat_doc -%patch72 -p1 -b .64bit -%patch106 -p1 -b .rh490837 -%patch112 -p1 -b .rh645544 -%patch130 -p1 -b .libdb -%patch157 -p1 -b .fips-tests -%patch164 -p1 -b .rh1666814 -%patch170 -p1 -b .featuretest-named -%patch171 -p1 -b .test-variant -%patch172 -p1 -b .CVE-2022-0396 -%patch173 -p1 -b .CVE-2021-25220 -%patch174 -p1 -b .CVE-2021-25220-test -%patch175 -p1 -b .CVE-2022-3080 -%patch176 -p1 -b .CVE-2022-38177 -%patch177 -p1 -b .CVE-2022-38178 -%patch178 -p1 -b .CVE-2022-2795 -%patch179 -p1 -b .rh2101712 -%patch181 -p1 -b .rh2133889 -%patch182 -p1 -b .CVE-2022-3094 -%patch183 -p1 -b .CVE-2022-3094 -%patch184 -p1 -b .CVE-2022-3094 -%patch185 -p1 -b .CVE-2022-3094-test -%patch186 -p1 -b .CVE-2022-3736 -%patch187 -p1 -b .CVE-2022-3924 -%patch188 -p1 -b .CVE-2023-2828 -%patch189 -p1 -b .CVE-2023-2911-1 -%patch190 -p1 -b .CVE-2023-2911-2 -%patch191 -p1 -b .CVE-2023-2911-3 -%patch192 -p1 -b .CVE-2023-3341 -%patch193 -p1 -b .b.root-servers.net -%patch194 -p1 -b .CVE-2023-4408 -%patch195 -p1 -b .CVE-2023-5517 -%patch196 -p1 -b .CVE-2023-5679 -%patch197 -p1 -b .CVE-2023-6516 -%patch198 -p1 -b .CVE-2023-50387 -%patch199 -p1 -%patch200 -p1 -%patch201 -p1 -b .test-variant-def -%patch202 -p1 -b .mempool-attach -%patch203 -p1 -b .isc_hp-CVE-2023-50387 -%patch204 -p1 -b .CVE-2023-6516-test -%patch205 -p1 -b .RHEL-39131 -%patch206 -p1 -b .CVE-2024-1975 -%patch207 -p1 -b .CVE-2024-1737 -%patch208 -p1 -b .CVE-2024-4076 -%patch210 -p1 -b .CVE-2024-1737-records -%patch211 -p1 -b .CVE-2024-1737-records-test -%patch212 -p1 -b .CVE-2024-1737-types -%patch213 -p1 -b .CVE-2024-1737-types-test -%patch214 -p1 -b .CVE-2024-1737-records-test2 +%autopatch -p1 -m 1 -M 134 +# PKCS11 patches 135 136 and 149 are applied later. +%autopatch -p1 -m 150 %if %{with PKCS11} -%patch135 -p1 -b .config-pkcs11 +%autopatch -p1 135 cp -r bin/named{,-pkcs11} cp -r bin/dnssec{,-pkcs11} cp -r lib/dns{,-pkcs11} cp -r lib/ns{,-pkcs11} -%patch136 -p1 -b .dist_pkcs11 -%patch149 -p1 -b .kyua-pkcs11 +%autopatch -p1 136 +%autopatch -p1 149 %endif # Sparc and s390 arches need to use -fPIE @@ -538,6 +519,10 @@ done %endif sed -e 's|"$TOP/config.guess"|"$TOP_SRCDIR/config.guess"|' -i bin/tests/system/ifconfig.sh +# allow running as root from mock or test machines +sed -e 's, "enable-developer",& \&\& systemctl is-system-running \&>/dev/null \&\& ! [ -e /mnt/tests ],' \ + -i bin/tests/system/run.sh + :; @@ -712,15 +697,29 @@ else sh bin/tests/system/ifconfig.sh up perl bin/tests/system/testsock.pl && CONFIGURED=build fi + if [ -n "$CONFIGURED" ] then set -e + %if %{with CRYPTO_POLICY_RSA1} + # Override crypto-policy to allow RSASHA1 key operations + OPENSSL_CONF="$(mktemp openssl-XXXXXX.cnf)" + cat > "$OPENSSL_CONF" << 'EOF' +.include = /etc/ssl/openssl.cnf +[evp_properties] +rh-allow-sha1-signatures = yes +EOF + export OPENSSL_CONF + %endif pushd build/bin/tests chown -R ${USER} . # Can be unknown user - %make_build test 2>&1 | tee test.log + %make_build test e=$? popd [ "$CONFIGURED" = build ] && sh bin/tests/system/ifconfig.sh down + %if %{with CRYPTO_POLICY_RSA1} + export -b OPENSSL_CONF + %endif if [ "$e" -ne 0 ]; then echo "ERROR: this build of BIND failed 'make test'. Aborting." exit $e; @@ -769,6 +768,9 @@ install -m 644 %{SOURCE38} ${RPM_BUILD_ROOT}%{_unitdir} install -m 644 %{SOURCE44} ${RPM_BUILD_ROOT}%{_unitdir} install -m 644 %{SOURCE46} ${RPM_BUILD_ROOT}%{_unitdir} +mkdir -p ${RPM_BUILD_ROOT}%{_sysusersdir} +install -m 644 %{SOURCE50} ${RPM_BUILD_ROOT}%{_sysusersdir}/named.conf + %if %{with PKCS11} install -m 644 %{SOURCE47} ${RPM_BUILD_ROOT}%{_unitdir} %else @@ -873,21 +875,28 @@ touch ${RPM_BUILD_ROOT}%{_sysconfdir}/rndc.{key,conf} install -m 644 %{SOURCE27} ${RPM_BUILD_ROOT}%{_sysconfdir}/named.root.key install -m 644 %{SOURCE36} ${RPM_BUILD_ROOT}%{_sysconfdir}/trusted-key.key mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/named +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/named +install -p -m 644 %{SOURCE17} ${RPM_BUILD_ROOT}%{_datadir}/named/named.ca +install -p -m 644 %{SOURCE18} ${RPM_BUILD_ROOT}%{_datadir}/named/named.localhost +install -p -m 644 %{SOURCE19} ${RPM_BUILD_ROOT}%{_datadir}/named/named.loopback +install -p -m 644 %{SOURCE20} ${RPM_BUILD_ROOT}%{_datadir}/named/named.empty # data files: mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/named -install -m 640 %{SOURCE17} ${RPM_BUILD_ROOT}%{_localstatedir}/named/named.ca -install -m 640 %{SOURCE18} ${RPM_BUILD_ROOT}%{_localstatedir}/named/named.localhost -install -m 640 %{SOURCE19} ${RPM_BUILD_ROOT}%{_localstatedir}/named/named.loopback -install -m 640 %{SOURCE20} ${RPM_BUILD_ROOT}%{_localstatedir}/named/named.empty -install -m 640 %{SOURCE23} ${RPM_BUILD_ROOT}%{_sysconfdir}/named.rfc1912.zones +# Create duplicate copies for maximal backward compatibility +install -p -m 644 %{SOURCE17} ${RPM_BUILD_ROOT}%{_localstatedir}/named/named.ca +install -p -m 644 %{SOURCE18} ${RPM_BUILD_ROOT}%{_localstatedir}/named/named.localhost +install -p -m 644 %{SOURCE19} ${RPM_BUILD_ROOT}%{_localstatedir}/named/named.loopback +install -p -m 644 %{SOURCE20} ${RPM_BUILD_ROOT}%{_localstatedir}/named/named.empty +install -p -m 640 %{SOURCE23} ${RPM_BUILD_ROOT}%{_sysconfdir}/named.rfc1912.zones # sample bind configuration files for %%doc: mkdir -p sample/etc sample/var/named/{data,slaves} install -m 644 %{SOURCE25} sample/etc/named.conf -# Copy default configuration to %%doc to make it usable from system-config-bind +# Copy default configuration to %%doc install -m 644 %{SOURCE16} named.conf.default install -m 644 %{SOURCE23} sample/etc/named.rfc1912.zones +# Extra copies in documentation too. install -m 644 %{SOURCE18} %{SOURCE19} %{SOURCE20} sample/var/named install -m 644 %{SOURCE17} sample/var/named/named.ca for f in my.internal.zone.db slaves/my.slave.internal.zone.db slaves/my.ddns.internal.zone.db my.external.zone.db; do @@ -897,15 +906,15 @@ done :; mkdir -p ${RPM_BUILD_ROOT}%{_tmpfilesdir} -install -m 644 %{SOURCE35} ${RPM_BUILD_ROOT}%{_tmpfilesdir}/named.conf +install -p -m 644 %{SOURCE35} ${RPM_BUILD_ROOT}%{_tmpfilesdir}/named.conf +install -p -m 644 %{SOURCE51} ${RPM_BUILD_ROOT}%{_tmpfilesdir}/%{name}-chroot.conf mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/rwtab.d -install -m 644 %{SOURCE43} ${RPM_BUILD_ROOT}%{_sysconfdir}/rwtab.d/named +install -p -m 644 %{SOURCE43} ${RPM_BUILD_ROOT}%{_sysconfdir}/rwtab.d/named %pre if [ "$1" -eq 1 ]; then - /usr/sbin/groupadd -g %{bind_gid} -f -r named >/dev/null 2>&1 || :; - /usr/sbin/useradd -u %{bind_uid} -r -N -M -g named -s /sbin/nologin -d /var/named -c Named named >/dev/null 2>&1 || :; + %sysusers_create_compat %{SOURCE50} fi; :; @@ -1024,6 +1033,7 @@ fi; %{_unitdir}/named.service %{_unitdir}/named-setup-rndc.service %{_sbindir}/named-journalprint +%{_sysusersdir}/named.conf %{_sbindir}/named-checkconf %{_bindir}/named-rrchecker %{_bindir}/mdig @@ -1056,6 +1066,7 @@ fi; %dir %{_localstatedir}/named/dynamic %ghost %{_localstatedir}/log/named.log %defattr(0640,root,named,0750) +%{_datadir}/named/ %config %verify(not link) %{_localstatedir}/named/named.ca %config %verify(not link) %{_localstatedir}/named/named.localhost %config %verify(not link) %{_localstatedir}/named/named.loopback @@ -1151,6 +1162,7 @@ fi; %{_unitdir}/named-chroot.service %{_unitdir}/named-chroot-setup.service %{_libexecdir}/setup-named-chroot.sh +%{_tmpfilesdir}/%{name}-chroot.conf %defattr(0664,root,named,-) %ghost %dev(c,1,3) %verify(not mtime) %{chroot_prefix}/dev/null %ghost %dev(c,1,8) %verify(not mtime) %{chroot_prefix}/dev/random @@ -1174,6 +1186,7 @@ fi; %dir %{chroot_prefix}/%{_libdir}/bind %dir %{chroot_prefix}/%{_libdir}/named %dir %{chroot_prefix}/%{_datadir}/GeoIP +%dir %{chroot_prefix}/%{_datadir}/named %{chroot_prefix}/proc %defattr(0660,root,named,01770) %dir %{chroot_prefix}%{_localstatedir}/named @@ -1247,6 +1260,57 @@ fi; %endif %changelog +* Wed Jan 28 2026 Petr Menšík - 32:9.16.23-40 +- Add forgotten _libdir/named into bind-chroot tmpfiles (RHEL-135629) + +* Thu Jan 22 2026 Fedor Vorobev - 32:9.16.23-39 +- Backport fix for nameserver line processing. (RHEL-79714) + +* Fri Dec 12 2025 Petr Menšík - 32:9.16.23-38 +- Add sysusers named user creation (RHEL-132053) + +* Fri Dec 12 2025 Petr Menšík - 32:9.16.23-37 +- Create /var/named directories for bind-chroot (RHEL-132053) + +* Wed Oct 29 2025 Petr Menšík - 32:9.16.23-36 +- Copy named.* files from /var/named into /usr/share/named + +* Wed Oct 29 2025 Petr Menšík - 32:9.16.23-35 +- Prevent cache poisoning due to weak PRNG (CVE-2025-40780) +- Replace downstream fixes with upstream changes +- Address various spoofing attacks (CVE-2025-40778) + +* Tue Sep 16 2025 Petr Menšík - 32:9.16.23-34 +- Fix failures in idna system test (RHEL-66172) + +* Fri Sep 12 2025 Petr Menšík <> - 32:9.16.23-33 +- logrotate: skip if empty and remove old variants (RHEL-113942) + +* Wed Sep 03 2025 Petr Menšík - 32:9.16.23-32 +- Decode IDN names on input in all situations in utilities (RHEL-66172) + +* Wed Jul 09 2025 Petr Menšík - 32:9.16.23-31 +- Add runtime tunable limit by environment NAMED_MAXADDITIONAL (RHEL-84006) + +* Fri Jun 20 2025 Petr Menšík - 32:9.16.23-30 +- Change additional NS to be served partially (RHEL-84006) + +* Tue Jun 10 2025 Petr Menšík - 32:9.18.23-29 +- Prevent name.c:670 attributes assertion failed (RHEL-30407) +- Add extra checks for relative names + +* Sat Feb 15 2025 Petr Menšík - 32:9.16.23-28 +- Fix test backport changes + +* Wed Feb 05 2025 Petr Menšík - 32:9.16.23-27 +- Limit additional section records CPU processing (CVE-2024-11187) + +* Wed Feb 05 2025 Petr Menšík - 32:9.16.23-26 +- Switch to autopatch changes applying + +* Fri Sep 06 2024 Petr Menšík - 32:9.16.23-25 +- Bump version above RHEL 9.5 + * Fri Aug 09 2024 Petr Menšík - 32:9.16.23-24 - Minor fix of reclimit test backport (CVE-2024-1737)