import UBI bind-9.18.33-4.el10_0.2
This commit is contained in:
parent
bc54d12211
commit
4c7dcf224b
781
bind-9.18-CVE-2025-40778.patch
Normal file
781
bind-9.18-CVE-2025-40778.patch
Normal file
@ -0,0 +1,781 @@
|
||||
From 918f465fd4157c821f135aa53405c37e05b68903 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Andrews <marka@isc.org>
|
||||
Date: Thu, 10 Jul 2025 09:37:36 +1000
|
||||
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 025d61bacd0f57f994a631654aff7a933d89a547)
|
||||
|
||||
Further restrict addresses that are cached when processing referrals
|
||||
|
||||
Use the owner name of the NS record as the bailwick apex name
|
||||
when determining which additional records to cache, rather than
|
||||
the name of the delegating zone (or a parent thereof).
|
||||
|
||||
(cherry picked from commit a41054e9e606a61f1b3c8bc0c54e2f1059347165)
|
||||
(cherry picked from commit cd17dfe696cdf9b8ef23fbc8738de7c79f957846)
|
||||
|
||||
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 4c6d03b0bb2ffbafcde8e8a5bc0e49908b978a72)
|
||||
---
|
||||
bin/tests/system/chain/ans3/ans.pl | 143 -------------------
|
||||
bin/tests/system/chain/ans3/ans.py | 217 +++++++++++++++++++++++++++++
|
||||
bin/tests/system/chain/ans4/ans.py | 57 ++++++--
|
||||
lib/dns/include/dns/message.h | 8 ++
|
||||
lib/dns/message.c | 12 ++
|
||||
lib/dns/resolver.c | 110 ++++++++++++---
|
||||
6 files changed, 374 insertions(+), 173 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 e42240be63..0000000000
|
||||
--- a/bin/tests/system/chain/ans3/ans.pl
|
||||
+++ /dev/null
|
||||
@@ -1,143 +0,0 @@
|
||||
-#!/usr/bin/env perl
|
||||
-
|
||||
-# 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.
|
||||
-
|
||||
-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; };
|
||||
-sub term { };
|
||||
-
|
||||
-$SIG{INT} = \&rmpid;
|
||||
-if ($Net::DNS::VERSION > 1.41) {
|
||||
- $SIG{TERM} = \&term;
|
||||
-} else {
|
||||
- $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,
|
||||
-);
|
||||
-
|
||||
-if ($Net::DNS::VERSION >= 1.42) {
|
||||
- $ns->start_server();
|
||||
- select(undef, undef, undef, undef);
|
||||
- $ns->stop_server();
|
||||
- unlink "ans.pid";
|
||||
-} else {
|
||||
- $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 839067faa5..66f0193cac 100755
|
||||
--- a/bin/tests/system/chain/ans4/ans.py
|
||||
+++ b/bin/tests/system/chain/ans4/ans.py
|
||||
@@ -316,16 +316,30 @@ try:
|
||||
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
|
||||
@@ -350,9 +364,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:
|
||||
@@ -375,12 +389,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 f15884a183..c2efc19ca6 100644
|
||||
--- a/lib/dns/include/dns/message.h
|
||||
+++ b/lib/dns/include/dns/message.h
|
||||
@@ -283,6 +283,7 @@ struct dns_message {
|
||||
unsigned int tkey : 1;
|
||||
unsigned int rdclass_set : 1;
|
||||
unsigned int fuzzing : 1;
|
||||
+ unsigned int has_dname : 1;
|
||||
|
||||
unsigned int opt_reserved;
|
||||
unsigned int sig_reserved;
|
||||
@@ -1526,4 +1527,11 @@ dns_message_response_minttl(dns_message_t *msg, dns_ttl_t *pttl);
|
||||
* \li 'pttl != NULL'.
|
||||
*/
|
||||
|
||||
+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 1ac370bc05..f939fa94a7 100644
|
||||
--- a/lib/dns/message.c
|
||||
+++ b/lib/dns/message.c
|
||||
@@ -428,6 +428,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;
|
||||
@@ -1710,6 +1711,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;
|
||||
|
||||
@@ -4861,3 +4867,9 @@ dns_message_response_minttl(dns_message_t *msg, dns_ttl_t *pttl) {
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
+
|
||||
+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 a0c8d42565..f57fa80b19 100644
|
||||
--- a/lib/dns/resolver.c
|
||||
+++ b/lib/dns/resolver.c
|
||||
@@ -798,6 +798,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 */
|
||||
@@ -7021,7 +7022,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;
|
||||
@@ -7034,7 +7036,7 @@ name_external(const dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) {
|
||||
dns_namereln_t rel;
|
||||
|
||||
apex = (ISDUALSTACK(fctx->addrinfo) || !ISFORWARDER(fctx->addrinfo))
|
||||
- ? fctx->domain
|
||||
+ ? rctx->ns_name != NULL ? rctx->ns_name : fctx->domain
|
||||
: fctx->fwdname;
|
||||
|
||||
/*
|
||||
@@ -7143,7 +7145,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;
|
||||
@@ -7773,6 +7775,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
|
||||
@@ -7962,6 +8005,17 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
|
||||
return;
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Remember whether this message was signed or had a
|
||||
+ * valid client cookie; if not, we may need to retry over
|
||||
+ * TCP later.
|
||||
+ */
|
||||
+ if (query->rmessage->cc_ok || query->rmessage->tsig != NULL ||
|
||||
+ query->rmessage->sig0 != NULL)
|
||||
+ {
|
||||
+ rctx.secured = true;
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* The dispatcher should ensure we only get responses with QR
|
||||
* set.
|
||||
@@ -7973,10 +8027,7 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
|
||||
* TCP. 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)
|
||||
@@ -7988,8 +8039,7 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
|
||||
isc_log_write(
|
||||
dns_lctx, DNS_LOGCATEGORY_RESOLVER,
|
||||
DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
|
||||
- "missing expected cookie "
|
||||
- "from %s",
|
||||
+ "missing expected cookie from %s",
|
||||
addrbuf);
|
||||
}
|
||||
rctx.retryopts |= DNS_FETCHOPT_TCP;
|
||||
@@ -7999,6 +8049,17 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * 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);
|
||||
|
||||
/*
|
||||
@@ -8781,8 +8842,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);
|
||||
|
||||
@@ -8854,7 +8915,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;
|
||||
}
|
||||
|
||||
@@ -9155,14 +9216,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) {
|
||||
@@ -9170,6 +9231,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) {
|
||||
@@ -9178,7 +9244,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.0
|
||||
|
||||
330
bind-9.18-CVE-2025-40780.patch
Normal file
330
bind-9.18-CVE-2025-40780.patch
Normal file
@ -0,0 +1,330 @@
|
||||
From 22667bc8d6e6971dc9e25f713ca4805112b71791 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 8330b49fb90bfeae14b47b7983e9459cc2bbaffe)
|
||||
---
|
||||
lib/isc/include/isc/random.h | 2 +-
|
||||
lib/isc/random.c | 225 ++++++++++++++---------------------
|
||||
tests/isc/random_test.c | 4 +-
|
||||
3 files changed, 96 insertions(+), 135 deletions(-)
|
||||
|
||||
diff --git a/lib/isc/include/isc/random.h b/lib/isc/include/isc/random.h
|
||||
index 1e30d0c87d..fd55343778 100644
|
||||
--- a/lib/isc/include/isc/random.h
|
||||
+++ b/lib/isc/include/isc/random.h
|
||||
@@ -20,7 +20,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 fb0466953d..6f37f5dedf 100644
|
||||
--- a/lib/isc/random.c
|
||||
+++ b/lib/isc/random.c
|
||||
@@ -31,176 +31,135 @@
|
||||
*/
|
||||
|
||||
#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/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.
|
||||
- */
|
||||
-
|
||||
-/*
|
||||
- * Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
|
||||
- *
|
||||
- * To the extent possible under law, the author has dedicated all
|
||||
- * copyright and related and neighboring rights to this software to the
|
||||
- * public domain worldwide. This software is distributed without any
|
||||
- * warranty.
|
||||
- *
|
||||
- * See <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
- */
|
||||
+#define ISC_RANDOM_BUFSIZE (ISC_OS_CACHELINE_SIZE / sizeof(uint32_t))
|
||||
|
||||
-/*
|
||||
- * This is xoshiro128** 1.0, our 32-bit all-purpose, rock-solid generator.
|
||||
- * It has excellent (sub-ns) speed, a state size (128 bits) that is large
|
||||
- * enough for mild parallelism, and it passes all tests we are aware of.
|
||||
- *
|
||||
- * For generating just single-precision (i.e., 32-bit) floating-point
|
||||
- * numbers, xoshiro128+ is even faster.
|
||||
- *
|
||||
- * The state must be seeded so that it is not everywhere zero.
|
||||
- */
|
||||
-static thread_local uint32_t seed[4] = { 0 };
|
||||
+thread_local static uint32_t isc__random_pool[ISC_RANDOM_BUFSIZE];
|
||||
+thread_local static size_t isc__random_pos = ISC_RANDOM_BUFSIZE;
|
||||
|
||||
static uint32_t
|
||||
-rotl(const uint32_t x, int k) {
|
||||
- return (x << k) | (x >> (32 - k));
|
||||
-}
|
||||
-
|
||||
-static uint32_t
|
||||
-next(void) {
|
||||
- uint32_t result_starstar, t;
|
||||
-
|
||||
- result_starstar = rotl(seed[0] * 5, 7) * 9;
|
||||
- t = seed[1] << 9;
|
||||
-
|
||||
- seed[2] ^= seed[0];
|
||||
- seed[3] ^= seed[1];
|
||||
- seed[1] ^= seed[2];
|
||||
- seed[0] ^= seed[3];
|
||||
-
|
||||
- seed[2] ^= t;
|
||||
-
|
||||
- seed[3] = rotl(seed[3], 11);
|
||||
-
|
||||
- return result_starstar;
|
||||
-}
|
||||
-
|
||||
-static thread_local isc_once_t isc_random_once = ISC_ONCE_INIT;
|
||||
-
|
||||
-static void
|
||||
-isc_random_initialize(void) {
|
||||
- int useed[4] = { 0, 0, 0, 1 };
|
||||
+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(buf != NULL);
|
||||
- REQUIRE(buflen > 0);
|
||||
-
|
||||
- RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
|
||||
- ISC_R_SUCCESS);
|
||||
+ REQUIRE(buflen == 0 || buf != NULL);
|
||||
|
||||
- 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/tests/isc/random_test.c b/tests/isc/random_test.c
|
||||
index ccba317023..52f219bb3a 100644
|
||||
--- a/tests/isc/random_test.c
|
||||
+++ b/tests/isc/random_test.c
|
||||
@@ -321,7 +321,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.0
|
||||
|
||||
223
bind-9.18-CVE-2025-8677.patch
Normal file
223
bind-9.18-CVE-2025-8677.patch
Normal file
@ -0,0 +1,223 @@
|
||||
From b76aec47a318a1f1c0664b329697e41e4d94427c Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org>
|
||||
Date: Tue, 22 Jul 2025 08:07:02 +0200
|
||||
Subject: [PATCH] Fail the DNSSEC validation if matching but invalid DNSKEY is
|
||||
found
|
||||
|
||||
If a matching but cryptographically invalid key was encountered during
|
||||
the DNSSEC validation, the key would be just skipped and not counted
|
||||
towards validation failures. Treat such DNSSEC keys as hard failures
|
||||
and fail the DNSSEC validation immediatelly instead of continuing the
|
||||
DNSSEC validation with the next DNSKEYs in the RRset.
|
||||
|
||||
Co-authored-by: Matthijs Mekking <matthijs@isc.org>
|
||||
|
||||
(cherry picked from commit f00117a4226be90d1bc865aff19bddf114242914)
|
||||
(cherry picked from commit 7c5b8ef055900224f0424c341927562c5a9ebe19)
|
||||
|
||||
Fix an issue with selfsigned_dnskey() return value
|
||||
|
||||
The selfsigned_dnskey() function currently returns boolean. There
|
||||
was a recent change to make it return a isc_result_t error code,
|
||||
which is implicitly converted to bool, which is obviously an error.
|
||||
|
||||
If instead of the result code we return true/false, it still doesn't
|
||||
indicate the error to the caller that has happened before.
|
||||
|
||||
Change the function to return isc_result_t, and change the caller
|
||||
routine to process the new return type.
|
||||
|
||||
(cherry picked from commit 40c396ba2d955c32d70db04e900e40bf96519c59)
|
||||
---
|
||||
lib/dns/validator.c | 79 +++++++++++++++++++++++++++++----------------
|
||||
1 file changed, 51 insertions(+), 28 deletions(-)
|
||||
|
||||
diff --git a/lib/dns/validator.c b/lib/dns/validator.c
|
||||
index 712fc0755a..15e177e4d7 100644
|
||||
--- a/lib/dns/validator.c
|
||||
+++ b/lib/dns/validator.c
|
||||
@@ -431,6 +431,8 @@ fetch_callback_dnskey(isc_task_t *task, isc_event_t *event) {
|
||||
result = select_signing_key(val, rdataset);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
val->keyset = &val->frdataset;
|
||||
+ } else {
|
||||
+ val->failed = true;
|
||||
}
|
||||
}
|
||||
result = validate_answer(val, true);
|
||||
@@ -1161,6 +1163,8 @@ select_signing_key(dns_validator_t *val, dns_rdataset_t *rdataset) {
|
||||
goto done;
|
||||
}
|
||||
dst_key_free(&val->key);
|
||||
+ } else {
|
||||
+ break;
|
||||
}
|
||||
dns_rdata_reset(&rdata);
|
||||
result = dns_rdataset_next(rdataset);
|
||||
@@ -1285,13 +1289,15 @@ seek_dnskey(dns_validator_t *val) {
|
||||
"keyset with trust %s",
|
||||
dns_trust_totext(val->frdataset.trust));
|
||||
result = select_signing_key(val, val->keyset);
|
||||
- if (result != ISC_R_SUCCESS) {
|
||||
+ if (result == ISC_R_NOTFOUND) {
|
||||
/*
|
||||
- * Either the key we're looking for is not
|
||||
- * in the rrset, or something bad happened.
|
||||
- * Give up.
|
||||
+ * The key we're looking for is not
|
||||
+ * in the rrset
|
||||
*/
|
||||
result = DNS_R_CONTINUE;
|
||||
+ } else if (result != ISC_R_SUCCESS) {
|
||||
+ /* Something bad happened. Give up. */
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1352,17 +1358,17 @@ compute_keytag(dns_rdata_t *rdata) {
|
||||
/*%
|
||||
* Is the DNSKEY rrset in val->event->rdataset self-signed?
|
||||
*/
|
||||
-static bool
|
||||
+static isc_result_t
|
||||
selfsigned_dnskey(dns_validator_t *val) {
|
||||
dns_rdataset_t *rdataset = val->event->rdataset;
|
||||
dns_rdataset_t *sigrdataset = val->event->sigrdataset;
|
||||
dns_name_t *name = val->event->name;
|
||||
isc_result_t result;
|
||||
isc_mem_t *mctx = val->view->mctx;
|
||||
- bool answer = false;
|
||||
+ bool match = false;
|
||||
|
||||
if (rdataset->type != dns_rdatatype_dnskey) {
|
||||
- return false;
|
||||
+ return DNS_R_NOKEYMATCH;
|
||||
}
|
||||
|
||||
for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS;
|
||||
@@ -1384,8 +1390,6 @@ selfsigned_dnskey(dns_validator_t *val) {
|
||||
result == ISC_R_SUCCESS;
|
||||
result = dns_rdataset_next(sigrdataset))
|
||||
{
|
||||
- dst_key_t *dstkey = NULL;
|
||||
-
|
||||
dns_rdata_reset(&sigrdata);
|
||||
dns_rdataset_current(sigrdataset, &sigrdata);
|
||||
result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
|
||||
@@ -1400,18 +1404,16 @@ selfsigned_dnskey(dns_validator_t *val) {
|
||||
|
||||
/*
|
||||
* If the REVOKE bit is not set we have a
|
||||
- * theoretically self signed DNSKEY RRset.
|
||||
- * This will be verified later.
|
||||
+ * theoretically self-signed DNSKEY RRset;
|
||||
+ * this will be verified later.
|
||||
+ *
|
||||
+ * We don't return the answer yet, though,
|
||||
+ * because we need to check the remaining keys
|
||||
+ * and possbly remove them if they're revoked.
|
||||
*/
|
||||
if ((key.flags & DNS_KEYFLAG_REVOKE) == 0) {
|
||||
- answer = true;
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- result = dns_dnssec_keyfromrdata(name, &keyrdata, mctx,
|
||||
- &dstkey);
|
||||
- if (result != ISC_R_SUCCESS) {
|
||||
- continue;
|
||||
+ match = true;
|
||||
+ break;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1421,6 +1423,14 @@ selfsigned_dnskey(dns_validator_t *val) {
|
||||
if (DNS_TRUST_PENDING(rdataset->trust) &&
|
||||
dns_view_istrusted(val->view, name, &key))
|
||||
{
|
||||
+ dst_key_t *dstkey = NULL;
|
||||
+
|
||||
+ result = dns_dnssec_keyfromrdata(
|
||||
+ name, &keyrdata, mctx, &dstkey);
|
||||
+ if (result != ISC_R_SUCCESS) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
result = dns_dnssec_verify(
|
||||
name, rdataset, dstkey, true,
|
||||
val->view->maxbits, mctx, &sigrdata,
|
||||
@@ -1433,6 +1443,8 @@ selfsigned_dnskey(dns_validator_t *val) {
|
||||
*/
|
||||
dns_view_untrust(val->view, name, &key);
|
||||
}
|
||||
+
|
||||
+ dst_key_free(&dstkey);
|
||||
} else if (rdataset->trust >= dns_trust_secure) {
|
||||
/*
|
||||
* We trust this RRset so if the key is
|
||||
@@ -1440,12 +1452,14 @@ selfsigned_dnskey(dns_validator_t *val) {
|
||||
*/
|
||||
dns_view_untrust(val->view, name, &key);
|
||||
}
|
||||
-
|
||||
- dst_key_free(&dstkey);
|
||||
}
|
||||
}
|
||||
|
||||
- return answer;
|
||||
+ if (!match) {
|
||||
+ return DNS_R_NOKEYMATCH;
|
||||
+ }
|
||||
+
|
||||
+ return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
/*%
|
||||
@@ -1680,10 +1694,7 @@ check_signer(dns_validator_t *val, dns_rdata_t *keyrdata, uint16_t keyid,
|
||||
val->event->name, keyrdata, val->view->mctx,
|
||||
&dstkey);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
- /*
|
||||
- * This really shouldn't happen, but...
|
||||
- */
|
||||
- continue;
|
||||
+ return result;
|
||||
}
|
||||
}
|
||||
result = verify(val, dstkey, &rdata, sig.keyid);
|
||||
@@ -3064,11 +3075,22 @@ validator_start(isc_task_t *task, isc_event_t *event) {
|
||||
|
||||
INSIST(dns_rdataset_isassociated(val->event->rdataset));
|
||||
INSIST(dns_rdataset_isassociated(val->event->sigrdataset));
|
||||
- if (selfsigned_dnskey(val)) {
|
||||
+
|
||||
+ result = selfsigned_dnskey(val);
|
||||
+ switch (result) {
|
||||
+ case ISC_R_SUCCESS:
|
||||
result = validate_dnskey(val);
|
||||
- } else {
|
||||
+ break;
|
||||
+ case DNS_R_NOKEYMATCH:
|
||||
result = validate_answer(val, false);
|
||||
+ break;
|
||||
+ default:
|
||||
+ validator_log(val, ISC_LOG_INFO,
|
||||
+ "invalid selfsigned DNSKEY: %s",
|
||||
+ isc_result_totext(result));
|
||||
+ goto cleanup;
|
||||
}
|
||||
+
|
||||
if (result == DNS_R_NOVALIDSIG &&
|
||||
(val->attributes & VALATTR_TRIEDVERIFY) == 0)
|
||||
{
|
||||
@@ -3137,6 +3159,7 @@ validator_start(isc_task_t *task, isc_event_t *event) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
+cleanup:
|
||||
if (result != DNS_R_WAIT) {
|
||||
want_destroy = exit_check(val);
|
||||
validator_done(val, result);
|
||||
--
|
||||
2.51.0
|
||||
|
||||
172
bind-9.20-CVE-2025-8677-dual-signing-test.patch
Normal file
172
bind-9.20-CVE-2025-8677-dual-signing-test.patch
Normal file
@ -0,0 +1,172 @@
|
||||
From 1cbe670c421ca866fe8cbde661801e89e254a46d Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org>
|
||||
Date: Sat, 1 Nov 2025 12:00:59 +0100
|
||||
Subject: [PATCH] Add a system test with one good and one bad algorithm
|
||||
|
||||
The case where there would be one supported algorithm and one already
|
||||
unsupported (like RSAMD5 or RSASHA1) was missing.
|
||||
|
||||
(cherry picked from commit 3aa6f585e0466700e5d4b64fffccf883bb1c21dd)
|
||||
---
|
||||
bin/tests/system/dnssec/ns2/example.db.in | 4 +++
|
||||
bin/tests/system/dnssec/ns2/sign.sh | 3 ++-
|
||||
bin/tests/system/dnssec/ns3/named.conf.in | 6 +++++
|
||||
bin/tests/system/dnssec/ns3/sign.sh | 31 ++++++++++++++++++++++
|
||||
bin/tests/system/dnssec/ns3/template.db.in | 27 +++++++++++++++++++
|
||||
bin/tests/system/dnssec/tests.sh | 11 ++++++++
|
||||
bin/tests/system/dnssec/tests_sh_dnssec.py | 1 +
|
||||
7 files changed, 82 insertions(+), 1 deletion(-)
|
||||
create mode 100644 bin/tests/system/dnssec/ns3/template.db.in
|
||||
|
||||
diff --git a/bin/tests/system/dnssec/ns2/example.db.in b/bin/tests/system/dnssec/ns2/example.db.in
|
||||
index f711f5823f..63d41e5e00 100644
|
||||
--- a/bin/tests/system/dnssec/ns2/example.db.in
|
||||
+++ b/bin/tests/system/dnssec/ns2/example.db.in
|
||||
@@ -168,4 +168,8 @@ ns.managed-future A 10.53.0.3
|
||||
revkey NS ns.revkey
|
||||
ns.revkey A 10.53.0.3
|
||||
|
||||
+; A secure subdomain with extra bad key
|
||||
+extrabadkey NS ns3.extrabadkey
|
||||
+ns3.extrabadkey A 10.53.0.3
|
||||
+
|
||||
dname-at-apex-nsec3 NS ns3
|
||||
diff --git a/bin/tests/system/dnssec/ns2/sign.sh b/bin/tests/system/dnssec/ns2/sign.sh
|
||||
index b60e82a631..eb008067a4 100644
|
||||
--- a/bin/tests/system/dnssec/ns2/sign.sh
|
||||
+++ b/bin/tests/system/dnssec/ns2/sign.sh
|
||||
@@ -62,7 +62,8 @@ for subdomain in secure badds bogus dynamic keyless nsec3 optout \
|
||||
ttlpatch split-dnssec split-smart expired expiring upper lower \
|
||||
dnskey-unknown dnskey-unsupported dnskey-unsupported-2 \
|
||||
dnskey-nsec3-unknown managed-future revkey \
|
||||
- dname-at-apex-nsec3 occluded; do
|
||||
+ dname-at-apex-nsec3 occluded rsasha1 rsasha1-1024 \
|
||||
+ extrabadkey; do
|
||||
cp "../ns3/dsset-$subdomain.example." .
|
||||
done
|
||||
|
||||
diff --git a/bin/tests/system/dnssec/ns3/named.conf.in b/bin/tests/system/dnssec/ns3/named.conf.in
|
||||
index 680cff58d5..3536046319 100644
|
||||
--- a/bin/tests/system/dnssec/ns3/named.conf.in
|
||||
+++ b/bin/tests/system/dnssec/ns3/named.conf.in
|
||||
@@ -84,6 +84,12 @@ zone "insecure2.example" {
|
||||
allow-update { any; };
|
||||
};
|
||||
|
||||
+zone "extrabadkey.example" {
|
||||
+ type primary;
|
||||
+ file "extrabadkey.example.db.signed";
|
||||
+ allow-update { any; };
|
||||
+};
|
||||
+
|
||||
zone "insecure.nsec3.example" {
|
||||
type primary;
|
||||
file "insecure.nsec3.example.db";
|
||||
diff --git a/bin/tests/system/dnssec/ns3/sign.sh b/bin/tests/system/dnssec/ns3/sign.sh
|
||||
index 14fc709bfb..743a0e4121 100644
|
||||
--- a/bin/tests/system/dnssec/ns3/sign.sh
|
||||
+++ b/bin/tests/system/dnssec/ns3/sign.sh
|
||||
@@ -673,3 +673,34 @@ $DSFROMKEY "$dnskeyname.key" >"dsset-delegation.${zone}."
|
||||
cat "$infile" "${kskname}.key" "${zskname}.key" "${keyname}.key" \
|
||||
"${dnskeyname}.key" "dsset-delegation.${zone}." >"$zonefile"
|
||||
"$SIGNER" -P -o "$zone" "$zonefile" >/dev/null
|
||||
+
|
||||
+#
|
||||
+#
|
||||
+#
|
||||
+zone=extrabadkey.example.
|
||||
+infile=template.db.in
|
||||
+zonefile=extrabadkey.example.db
|
||||
+
|
||||
+# Add KSK and ZSK that we will mangle to RSAMD5
|
||||
+ksk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -f KSK "$zone")
|
||||
+zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
|
||||
+cat "$infile" "$ksk.key" "$zsk.key" > "$zonefile"
|
||||
+"$SIGNER" -g -O full -o "$zone" "$zonefile" >/dev/null 2>&1
|
||||
+
|
||||
+# Mangle the signatures to RSAMD5 and save them for future use
|
||||
+sed -ne "s/\(IN[[:space:]]*RRSIG[[:space:]]*[A-Z]*\) $DEFAULT_ALGORITHM_NUMBER /\1 1 /p" < "$zonefile.signed" > "$zonefile.signed.rsamd5"
|
||||
+
|
||||
+# Now add normal KSK and ZSK to the zone file
|
||||
+ksk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -f KSK "$zone")
|
||||
+zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
|
||||
+cat "$infile" "$ksk.key" "$zsk.key" > "$zonefile"
|
||||
+
|
||||
+# Mangle the DNSKEY algorithm numbers and add them to the signed zone file
|
||||
+cat "$ksk.key" "$zsk.key" | sed -e "s/\(IN[[:space:]]*DNSKEY[[:space:]]*[0-9]* 3\) $DEFAULT_ALGORITHM_NUMBER /\1 1 /" >> "$zonefile"
|
||||
+
|
||||
+# Sign normally
|
||||
+"$SIGNER" -g -o "$zone" "$zonefile" >/dev/null 2>&1
|
||||
+
|
||||
+# Add the mangled signatures to signed zone file
|
||||
+cat "$zonefile.signed.rsamd5" >> "$zonefile.signed"
|
||||
+rm "$zonefile.signed.rsamd5"
|
||||
diff --git a/bin/tests/system/dnssec/ns3/template.db.in b/bin/tests/system/dnssec/ns3/template.db.in
|
||||
new file mode 100644
|
||||
index 0000000000..f603e448ff
|
||||
--- /dev/null
|
||||
+++ b/bin/tests/system/dnssec/ns3/template.db.in
|
||||
@@ -0,0 +1,27 @@
|
||||
+; 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.
|
||||
+
|
||||
+$TTL 300 ; 5 minutes
|
||||
+@ IN SOA mname1. . (
|
||||
+ 2000042407 ; serial
|
||||
+ 20 ; refresh (20 seconds)
|
||||
+ 20 ; retry (20 seconds)
|
||||
+ 1814400 ; expire (3 weeks)
|
||||
+ 3600 ; minimum (1 hour)
|
||||
+ )
|
||||
+ NS ns3
|
||||
+ns3 A 10.53.0.3
|
||||
+
|
||||
+a A 10.0.0.1
|
||||
+a.b A 10.0.0.1
|
||||
+b A 10.0.0.2
|
||||
+d A 10.0.0.4
|
||||
+z A 10.0.0.26
|
||||
diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh
|
||||
index b58b11a5c7..d3690eee6b 100644
|
||||
--- a/bin/tests/system/dnssec/tests.sh
|
||||
+++ b/bin/tests/system/dnssec/tests.sh
|
||||
@@ -4497,5 +4497,16 @@ n=$((n + 1))
|
||||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
+echo_i "checking extra-bad-algorithm positive validation ($n)"
|
||||
+ret=0
|
||||
+dig_with_opts +noauth a.extrabadkey.example. @10.53.0.3 A >dig.out.ns3.test$n || ret=1
|
||||
+dig_with_opts +noauth a.extrabadkey.example. @10.53.0.4 A >dig.out.ns4.test$n || ret=1
|
||||
+digcomp --lc dig.out.ns3.test$n dig.out.ns4.test$n || ret=1
|
||||
+grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1
|
||||
+grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1
|
||||
+n=$((n + 1))
|
||||
+test "$ret" -eq 0 || echo_i "failed"
|
||||
+status=$((status + ret))
|
||||
+
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
diff --git a/bin/tests/system/dnssec/tests_sh_dnssec.py b/bin/tests/system/dnssec/tests_sh_dnssec.py
|
||||
index 529a4a0e10..f731ea2ab4 100644
|
||||
--- a/bin/tests/system/dnssec/tests_sh_dnssec.py
|
||||
+++ b/bin/tests/system/dnssec/tests_sh_dnssec.py
|
||||
@@ -92,6 +92,7 @@ pytestmark = pytest.mark.extra_artifacts(
|
||||
"ns3/example.bk",
|
||||
"ns3/expired.example.db",
|
||||
"ns3/expiring.example.db",
|
||||
+ "ns3/extrabadkey.example.db",
|
||||
"ns3/future.example.db",
|
||||
"ns3/keyless.example.db",
|
||||
"ns3/kskonly.example.db",
|
||||
--
|
||||
2.51.1
|
||||
|
||||
36
bind-9.20-CVE-2025-8677-dual-signing.patch
Normal file
36
bind-9.20-CVE-2025-8677-dual-signing.patch
Normal file
@ -0,0 +1,36 @@
|
||||
From 5126c74ec19f56294cbfdc312f75778d8f249e59 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
|
||||
Date: Mon, 3 Nov 2025 22:03:54 +0100
|
||||
Subject: [PATCH] Do not abort key search on unsupported algorithm
|
||||
|
||||
When supported and unsupported algorithm rrsig is present, some keys may
|
||||
return unsupported algorithm error. Continue to next key without
|
||||
counting this to validation failures.
|
||||
|
||||
(cherry picked from commit 38ddff3336e08983a4c0b5f3ea4eb35bb0f6ac81)
|
||||
---
|
||||
lib/dns/validator.c | 8 +++++++-
|
||||
1 file changed, 7 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/dns/validator.c b/lib/dns/validator.c
|
||||
index 15e177e4d7..a9db844c27 100644
|
||||
--- a/lib/dns/validator.c
|
||||
+++ b/lib/dns/validator.c
|
||||
@@ -1163,7 +1163,13 @@ select_signing_key(dns_validator_t *val, dns_rdataset_t *rdataset) {
|
||||
goto done;
|
||||
}
|
||||
dst_key_free(&val->key);
|
||||
- } else {
|
||||
+ } else if (result != DST_R_UNSUPPORTEDALG) {
|
||||
+ /* Unsupported alg happens when RRset is signed by both
|
||||
+ * supported and unsupported alg. */
|
||||
+ validator_log(val, ISC_LOG_DEBUG(3),
|
||||
+ "select_signing_key alg %d keyid %d: %s",
|
||||
+ siginfo->algorithm, siginfo->keyid,
|
||||
+ isc_result_totext(result));
|
||||
break;
|
||||
}
|
||||
dns_rdata_reset(&rdata);
|
||||
--
|
||||
2.51.1
|
||||
|
||||
23
bind.spec
23
bind.spec
@ -80,7 +80,7 @@ License: MPL-2.0 AND ISC AND MIT AND BSD-3-Clause AND BSD-2-Clause
|
||||
# Before rebasing bind, ensure bind-dyndb-ldap is ready to be rebuild and use side-tag with it.
|
||||
# Updating just bind will cause freeipa-dns-server package to be uninstallable.
|
||||
Version: 9.18.33
|
||||
Release: 4%{?dist}
|
||||
Release: 4%{?dist}.2
|
||||
Epoch: 32
|
||||
Url: https://www.isc.org/downloads/bind/
|
||||
#
|
||||
@ -133,6 +133,19 @@ Patch30: bind-9.20-nsupdate-tls-test.patch
|
||||
Patch32: bind-9.21-resume-qmin-cname.patch
|
||||
# downstream only, extra check for above change, RHEL-30407
|
||||
Patch33: bind-9.18-query-fname-relative.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/40c396ba2d955c32d70db04e900e40bf96519c59
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/7c5b8ef055900224f0424c341927562c5a9ebe19
|
||||
Patch34: bind-9.18-CVE-2025-8677.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/025d61bacd0f57f994a631654aff7a933d89a547
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/cd17dfe696cdf9b8ef23fbc8738de7c79f957846
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/4c6d03b0bb2ffbafcde8e8a5bc0e49908b978a72
|
||||
Patch35: bind-9.18-CVE-2025-40778.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/commit/8330b49fb90bfeae14b47b7983e9459cc2bbaffe
|
||||
Patch36: bind-9.18-CVE-2025-40780.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/11194
|
||||
Patch226: bind-9.20-CVE-2025-8677-dual-signing.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/11195
|
||||
Patch227: bind-9.20-CVE-2025-8677-dual-signing-test.patch
|
||||
|
||||
%{?systemd_ordering}
|
||||
# https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers
|
||||
@ -918,6 +931,14 @@ fi;
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Fri Oct 31 2025 Petr Menšík <pemensik@redhat.com> - 32:9.18.33-4.2
|
||||
- Fix upstream reported regression in recent CVE fix (CVE-2025-8677)
|
||||
|
||||
* Thu Oct 23 2025 Petr Menšík <pemensik@redhat.com> - 32:9.18.33-4.1
|
||||
- Refuse malformed DNSKEY records (CVE-2025-8677)
|
||||
- Address various spoofing attacks (CVE-2025-40778)
|
||||
- Prevent cache poisoning due to weak PRNG (CVE-2025-40780)
|
||||
|
||||
* Tue Jun 10 2025 Petr Mensik <pemensik@redhat.com> - 32:9.18.33-4
|
||||
- Prevent name.c:670 attributes assertion failed (RHEL-30407)
|
||||
- Add extra checks for relative names
|
||||
|
||||
Loading…
Reference in New Issue
Block a user