import CS bind-9.16.23-40.el9

This commit is contained in:
AlmaLinux RelEng Bot 2026-03-30 10:16:10 -04:00
parent 1f304c04cc
commit 9c3c5b566d
17 changed files with 2084 additions and 73 deletions

View File

@ -0,0 +1,784 @@
From 39a43add7ad19fc2d83950a9af0c4f8cf580588b Mon Sep 17 00:00:00 2001
From: Evan Hunt <each@isc.org>
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

View File

@ -0,0 +1,303 @@
From 6254679e5250a9f0d6079ec082cffdad4315372d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org>
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 <isc/types.h>
/*! \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 <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include <stdio.h>
-#include <isc/once.h>
+#include <isc/os.h>
#include <isc/platform.h>
#include <isc/random.h>
-#include <isc/result.h>
#include <isc/thread.h>
-#include <isc/types.h>
#include <isc/util.h>
#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

View File

@ -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);

View File

@ -0,0 +1,61 @@
From cc01143082bc688a371a7378ef284c898eedc9df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org>
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

View File

@ -0,0 +1,172 @@
From b095854ee885dd5960cb54e7fbefb962498e9fdb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org>
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 <dns/rdatastruct.h>
#include <dns/types.h>
+#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 <dns/ncache.h>
#include <dns/rdata.h>
#include <dns/rdataset.h>
+#include <dns/result.h>
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

View File

@ -0,0 +1,168 @@
From bddd8950ff3d82d503dfb64e67d24ce364e5a5af Mon Sep 17 00:00:00 2001
From: Petr Mensik <pemensik@redhat.com>
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 <isc/stats.h>
#include <isc/util.h>
+#include <dns/rdataset.h>
#include <dns/stats.h>
#include <dns/tkey.h>
@@ -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

View File

@ -0,0 +1,80 @@
From 11f35bdef6f070412cb2c1c65d9537b82ea38c8a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
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

View File

@ -0,0 +1,102 @@
From d023241c7e2921926be8bd1784424eefba2a3c54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
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 <pemensik@redhat.com>
---
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

View File

@ -0,0 +1,54 @@
From 9140eac85cda21fa86f2768f7ccaf6800776c726 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
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

View File

@ -0,0 +1,90 @@
From 5bc7cd7a7b9c37e5c70ccf74c5485a02411aaef5 Mon Sep 17 00:00:00 2001
From: Petr Mensik <pemensik@redhat.com>
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

View File

@ -0,0 +1,44 @@
From dc97bfa842b9f0d59db075bdcade599f3767f1ba Mon Sep 17 00:00:00 2001
From: Petr Mensik <pemensik@redhat.com>
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

3
SOURCES/named.sysusers Normal file
View File

@ -0,0 +1,3 @@
#Type Name ID GECOS Home directory Shell
u named 25 "Named" /var/named /sbin/nologin
g named 25

View File

@ -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 <pemensik@redhat.com> - 32:9.16.23-40
- Add forgotten _libdir/named into bind-chroot tmpfiles (RHEL-135629)
* Thu Jan 22 2026 Fedor Vorobev <fvorobev@redhat.com> - 32:9.16.23-39
- Backport fix for nameserver line processing. (RHEL-79714)
* Fri Dec 12 2025 Petr Menšík <pemensik@redhat.com> - 32:9.16.23-38
- Add sysusers named user creation (RHEL-132053)
* Fri Dec 12 2025 Petr Menšík <pemensik@redhat.com> - 32:9.16.23-37
- Create /var/named directories for bind-chroot (RHEL-132053)
* Wed Oct 29 2025 Petr Menšík <pemensik@redhat.com> - 32:9.16.23-36
- Copy named.* files from /var/named into /usr/share/named
* Wed Oct 29 2025 Petr Menšík <pemensik@redhat.com> - 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 <pemensik@redhat.com> - 32:9.16.23-34
- Fix failures in idna system test (RHEL-66172)
* Fri Sep 12 2025 Petr Menšík <<pemensik@redhat.com>> - 32:9.16.23-33
- logrotate: skip if empty and remove old variants (RHEL-113942)
* Wed Sep 03 2025 Petr Menšík <pemensik@redhat.com> - 32:9.16.23-32
- Decode IDN names on input in all situations in utilities (RHEL-66172)
* Wed Jul 09 2025 Petr Menšík <pemensik@redhat.com> - 32:9.16.23-31
- Add runtime tunable limit by environment NAMED_MAXADDITIONAL (RHEL-84006)
* Fri Jun 20 2025 Petr Menšík <pemensik@redhat.com> - 32:9.16.23-30
- Change additional NS to be served partially (RHEL-84006)
* Tue Jun 10 2025 Petr Menšík <pemensik@redhat.com> - 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 <pemensik@redhat.com> - 32:9.16.23-28
- Fix test backport changes
* Wed Feb 05 2025 Petr Menšík <pemensik@redhat.com> - 32:9.16.23-27
- Limit additional section records CPU processing (CVE-2024-11187)
* Wed Feb 05 2025 Petr Menšík <pemensik@redhat.com> - 32:9.16.23-26
- Switch to autopatch changes applying
* Fri Sep 06 2024 Petr Menšík <pemensik@redhat.com> - 32:9.16.23-25
- Bump version above RHEL 9.5
* Fri Aug 09 2024 Petr Menšík <pemensik@redhat.com> - 32:9.16.23-24
- Minor fix of reclimit test backport (CVE-2024-1737)