Add tests for CVE-2022-2795
Add a test ensuring that the amount of work fctx_getaddresses() performs for any encountered delegation is limited: delegate example.net to a set of 1,000 name servers in the redirect.com zone, the names of which all resolve to IP addresses that nothing listens on, and query for a name in the example.net domain, checking the number of times the findname() function gets executed in the process; fail if that count is excessively large. Since the size of the referral response sent by ans3 is about 20 kB, it cannot be sent back over UDP (EMSGSIZE) on some operating systems in their default configuration (e.g. FreeBSD - see the net.inet.udp.maxdgram sysctl). To enable reliable reproduction of CVE-2022-2795 (retry patterns vary across BIND 9 versions) and avoid false positives at the same time (thread scheduling - and therefore the number of fetch context restarts - vary across operating systems and across test runs), extend bin/tests/system/resolver/ans3/ans.pl so that it also listens on TCP and make "ns1" in the "resolver" system test always use TCP when communicating with "ans3". Also add a test (foo.bar.sub.tld1/TXT) that ensures the new limitations imposed on the resolution process by the mitigation for CVE-2022-2795 do not prevent valid, glueless delegation chains from working properly.
This commit is contained in:
committed by
Michal Nowak
parent
64287e4889
commit
604d8f0b96
@@ -73,6 +73,15 @@ for (;;) {
|
||||
# Data for the "cname + other data / 2" test: same RRs in opposite order
|
||||
$packet->push("answer", new Net::DNS::RR("cname2.example.com 300 A 1.2.3.4"));
|
||||
$packet->push("answer", new Net::DNS::RR("cname2.example.com 300 CNAME cname2.example.com"));
|
||||
} elsif ($qname =~ /redirect\.com/) {
|
||||
$packet->push("authority", new Net::DNS::RR("redirect.com 300 NS ns.redirect.com"));
|
||||
$packet->push("additional", new Net::DNS::RR("ns.redirect.com 300 A 10.53.0.6"));
|
||||
} elsif ($qname =~ /\.tld1/) {
|
||||
$packet->push("authority", new Net::DNS::RR("tld1 300 NS ns.tld1"));
|
||||
$packet->push("additional", new Net::DNS::RR("ns.tld1 300 A 10.53.0.6"));
|
||||
} elsif ($qname =~ /\.tld2/) {
|
||||
$packet->push("authority", new Net::DNS::RR("tld2 300 NS ns.tld2"));
|
||||
$packet->push("additional", new Net::DNS::RR("ns.tld2 300 A 10.53.0.7"));
|
||||
} elsif ($qname eq "org" && $qtype eq "NS") {
|
||||
$packet->header->aa(1);
|
||||
$packet->push("answer", new Net::DNS::RR("org 300 NS a.root-servers.nil."));
|
||||
|
||||
@@ -20,11 +20,21 @@ use IO::Socket;
|
||||
use Net::DNS;
|
||||
use Net::DNS::Packet;
|
||||
|
||||
# Ignore SIGPIPE so we won't fail if peer closes a TCP socket early
|
||||
local $SIG{PIPE} = 'IGNORE';
|
||||
|
||||
# Flush logged output after every line
|
||||
local $| = 1;
|
||||
|
||||
my $localport = int($ENV{'PORT'});
|
||||
if (!$localport) { $localport = 5300; }
|
||||
|
||||
my $sock = IO::Socket::INET->new(LocalAddr => "10.53.0.3",
|
||||
LocalPort => $localport, Proto => "udp") or die "$!";
|
||||
my $server_addr = "10.53.0.3";
|
||||
|
||||
my $udpsock = IO::Socket::INET->new(LocalAddr => "$server_addr",
|
||||
LocalPort => $localport, Proto => "udp", Reuse => 1) or die "$!";
|
||||
my $tcpsock = IO::Socket::INET->new(LocalAddr => "$server_addr",
|
||||
LocalPort => $localport, Proto => "tcp", Listen => 5, Reuse => 1) or die "$!";
|
||||
|
||||
my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!";
|
||||
print $pidf "$$\n" or die "cannot write pid file: $!";
|
||||
@@ -34,11 +44,8 @@ sub rmpid { unlink "ans.pid"; exit 1; };
|
||||
$SIG{INT} = \&rmpid;
|
||||
$SIG{TERM} = \&rmpid;
|
||||
|
||||
for (;;) {
|
||||
$sock->recv($buf, 512);
|
||||
|
||||
print "**** request from " , $sock->peerhost, " port ", $sock->peerport, "\n";
|
||||
|
||||
sub handleQuery {
|
||||
my $buf = shift;
|
||||
my $packet;
|
||||
|
||||
if ($Net::DNS::VERSION > 0.68) {
|
||||
@@ -105,6 +112,13 @@ for (;;) {
|
||||
$packet->header->aa(0);
|
||||
$packet->push("authority", new Net::DNS::RR("lame.example.org 300 NS ns.lame.example.org"));
|
||||
$packet->push("additional", new Net::DNS::RR("ns.lame.example.org 300 A 10.53.0.3"));
|
||||
} elsif ($qname eq "large-referral.example.net") {
|
||||
for (my $i = 1; $i < 1000; $i++) {
|
||||
$packet->push("authority", new Net::DNS::RR("large-referral.example.net 300 NS ns" . $i . ".fake.redirect.com"));
|
||||
}
|
||||
# No glue records
|
||||
} elsif ($qname eq "foo.bar.sub.tld1") {
|
||||
$packet->push("answer", new Net::DNS::RR("$qname 300 TXT baz"));
|
||||
} elsif ($qname eq "cname.sub.example.org") {
|
||||
$packet->push("answer",
|
||||
new Net::DNS::RR($qname .
|
||||
@@ -132,9 +146,46 @@ for (;;) {
|
||||
$packet->push("answer", new Net::DNS::RR("www.example.com 300 A 1.2.3.4"));
|
||||
}
|
||||
|
||||
$sock->send($packet->data);
|
||||
|
||||
print "RESPONSE:\n";
|
||||
$packet->print;
|
||||
print "\n";
|
||||
|
||||
return $packet->data;
|
||||
}
|
||||
|
||||
# Main
|
||||
my $rin;
|
||||
my $rout;
|
||||
for (;;) {
|
||||
$rin = '';
|
||||
vec($rin, fileno($tcpsock), 1) = 1;
|
||||
vec($rin, fileno($udpsock), 1) = 1;
|
||||
|
||||
select($rout = $rin, undef, undef, undef);
|
||||
|
||||
if (vec($rout, fileno($udpsock), 1)) {
|
||||
printf "UDP request\n";
|
||||
my $buf;
|
||||
$udpsock->recv($buf, 512);
|
||||
my $result = handleQuery($buf);
|
||||
my $num_chars = $udpsock->send($result);
|
||||
print " Sent $num_chars bytes via UDP\n";
|
||||
} elsif (vec($rout, fileno($tcpsock), 1)) {
|
||||
my $conn = $tcpsock->accept;
|
||||
my $buf;
|
||||
for (;;) {
|
||||
my $lenbuf;
|
||||
my $n = $conn->sysread($lenbuf, 2);
|
||||
last unless $n == 2;
|
||||
my $len = unpack("n", $lenbuf);
|
||||
$n = $conn->sysread($buf, $len);
|
||||
last unless $n == $len;
|
||||
print "TCP request\n";
|
||||
my $result = handleQuery($buf);
|
||||
$len = length($result);
|
||||
$conn->syswrite(pack("n", $len), 2);
|
||||
$n = $conn->syswrite($result, $len);
|
||||
print " Sent: $n chars via TCP\n";
|
||||
}
|
||||
$conn->close;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ options {
|
||||
attach-cache "globalcache";
|
||||
};
|
||||
|
||||
server 10.53.0.3 {
|
||||
tcp-only yes;
|
||||
};
|
||||
|
||||
server 10.42.23.3/32 {
|
||||
notify-source 10.42.22.1;
|
||||
query-source address 10.42.22.1 port 0;
|
||||
@@ -64,3 +68,12 @@ view "default" {
|
||||
file "root.hint";
|
||||
};
|
||||
};
|
||||
|
||||
key rndc_key {
|
||||
secret "1234abcd8765";
|
||||
algorithm @DEFAULT_HMAC@;
|
||||
};
|
||||
|
||||
controls {
|
||||
inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
|
||||
};
|
||||
|
||||
@@ -62,6 +62,16 @@ zone "broken" {
|
||||
allow-update { any; };
|
||||
};
|
||||
|
||||
zone "redirect.com" {
|
||||
type primary;
|
||||
file "redirect.com.db";
|
||||
};
|
||||
|
||||
zone "tld1" {
|
||||
type primary;
|
||||
file "tld1.db";
|
||||
};
|
||||
|
||||
zone "no-edns-version.tld" {
|
||||
type primary;
|
||||
file "no-edns-version.tld.db";
|
||||
|
||||
27
bin/tests/system/resolver/ns6/redirect.com.db
Normal file
27
bin/tests/system/resolver/ns6/redirect.com.db
Normal file
@@ -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 600
|
||||
@ IN SOA ns hostmaster 1 1800 900 604800 600
|
||||
@ IN NS ns
|
||||
ns IN A 10.53.0.6
|
||||
|
||||
; 10.53.1.* are non-responsive IP addresses
|
||||
$GENERATE 1-100 ns$.fake IN A 10.53.1.$
|
||||
$GENERATE 101-200 ns$.fake IN A 10.53.1.${-100}
|
||||
$GENERATE 201-300 ns$.fake IN A 10.53.1.${-200}
|
||||
$GENERATE 301-400 ns$.fake IN A 10.53.1.${-300}
|
||||
$GENERATE 401-500 ns$.fake IN A 10.53.1.${-400}
|
||||
$GENERATE 501-600 ns$.fake IN A 10.53.1.${-500}
|
||||
$GENERATE 601-700 ns$.fake IN A 10.53.1.${-600}
|
||||
$GENERATE 701-800 ns$.fake IN A 10.53.1.${-700}
|
||||
$GENERATE 801-900 ns$.fake IN A 10.53.1.${-800}
|
||||
$GENERATE 901-1000 ns$.fake IN A 10.53.1.${-900}
|
||||
17
bin/tests/system/resolver/ns6/tld1.db
Normal file
17
bin/tests/system/resolver/ns6/tld1.db
Normal file
@@ -0,0 +1,17 @@
|
||||
; 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 600
|
||||
@ IN SOA ns hostmaster 1 1800 900 604800 600
|
||||
@ IN NS ns
|
||||
ns IN A 10.53.0.6
|
||||
|
||||
$GENERATE 1-21 sub IN NS sub-ns$.tld2.
|
||||
@@ -63,3 +63,13 @@ zone "all-cnames" {
|
||||
type primary;
|
||||
file "all-cnames.db";
|
||||
};
|
||||
|
||||
zone "tld2" {
|
||||
type primary;
|
||||
file "tld2.db";
|
||||
};
|
||||
|
||||
zone "sub.tld1" {
|
||||
type primary;
|
||||
file "sub.tld1.db";
|
||||
};
|
||||
|
||||
@@ -63,3 +63,13 @@ zone "all-cnames" {
|
||||
type primary;
|
||||
file "all-cnames.db";
|
||||
};
|
||||
|
||||
zone "tld2" {
|
||||
type primary;
|
||||
file "tld2.db";
|
||||
};
|
||||
|
||||
zone "sub.tld1" {
|
||||
type primary;
|
||||
file "sub.tld1.db";
|
||||
};
|
||||
|
||||
17
bin/tests/system/resolver/ns7/sub.tld1.db
Normal file
17
bin/tests/system/resolver/ns7/sub.tld1.db
Normal file
@@ -0,0 +1,17 @@
|
||||
; 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 600
|
||||
@ IN SOA ns hostmaster 1 1800 900 604800 600
|
||||
|
||||
$GENERATE 1-21 @ IN NS sub-ns$.tld2.
|
||||
|
||||
$GENERATE 1-21 bar IN NS bar-sub-ns$.tld2.
|
||||
18
bin/tests/system/resolver/ns7/tld2.db
Normal file
18
bin/tests/system/resolver/ns7/tld2.db
Normal file
@@ -0,0 +1,18 @@
|
||||
; 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 600
|
||||
@ IN SOA ns hostmaster 1 1800 900 604800 600
|
||||
@ IN NS ns
|
||||
ns IN A 10.53.0.7
|
||||
|
||||
$GENERATE 1-21 sub-ns$ IN A 10.53.0.7
|
||||
$GENERATE 1-21 bar-sub-ns$ IN A 10.53.0.3
|
||||
@@ -812,5 +812,36 @@ grep "status: SERVFAIL" dig.out.ns1.${n} > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "check handling of large referrals to unresponsive name servers ($n)"
|
||||
ret=0
|
||||
dig_with_opts +timeout=15 large-referral.example.net @10.53.0.1 a > dig.out.ns1.test${n} || ret=1
|
||||
grep "status: SERVFAIL" dig.out.ns1.test${n} > /dev/null || ret=1
|
||||
# Check the total number of findname() calls triggered by a single query
|
||||
# for large-referral.example.net/A.
|
||||
findname_call_count="$(grep -c "large-referral\.example\.net.*FINDNAME" ns1/named.run)"
|
||||
if [ "${findname_call_count}" -gt 1000 ]; then
|
||||
echo_i "failed: ${findname_call_count} (> 1000) findname() calls detected for large-referral.example.net"
|
||||
ret=1
|
||||
fi
|
||||
# Check whether the limit of NS RRs processed for any delegation
|
||||
# encountered was not exceeded.
|
||||
if grep -Eq "dns_adb_createfind: started (A|AAAA) fetch for name ns21.fake.redirect.com" ns1/named.run; then
|
||||
echo_i "failed: unexpected address fetch(es) were triggered for ns21.fake.redirect.com"
|
||||
ret=1
|
||||
fi
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "checking query resolution for a domain with a valid glueless delegation chain ($n)"
|
||||
ret=0
|
||||
rndccmd 10.53.0.1 flush || ret=1
|
||||
dig_with_opts foo.bar.sub.tld1 @10.53.0.1 TXT > dig.out.ns1.test${n} || ret=1
|
||||
grep "status: NOERROR" dig.out.ns1.test${n} > /dev/null || ret=1
|
||||
grep "IN.*TXT.*baz" dig.out.ns1.test${n} > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
|
||||
Reference in New Issue
Block a user