2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 05:57:52 +00:00

Fix a bug in RPZ that could cause unwanted recursion (#39229)

Conflicts:
	doc/arm/notes.xml
This commit is contained in:
Mukund Sivaraman 2015-05-07 08:26:27 +05:30
parent 012142bbe0
commit b947e1a521
22 changed files with 991 additions and 27 deletions

View File

@ -1,3 +1,9 @@
4116. [bug] Fix a bug in RPZ that could cause some policy
zones that did not specifically require
recursion to be treated as if they did;
consequently, setting qname-wait-recurse no; was
sometimes ineffective. [RT #39229]
4115. [func] "rndc -r" now prints the result code (e.g.,
ISC_R_SUCCESS, ISC_R_TIMEOUT, etc) after
running the requested command. [RT #38913]

View File

@ -19,6 +19,7 @@ involving a different DNS setup. They are:
(not a complete resolver test suite)
rrl/ query rate limiting
rpz/ Tests of response policy zone (RPZ) rewriting
rpzrecurse/ Another set of RPZ tests to check recursion behavior
stub/ Tests of stub zone functionality
unknown/ Unknown type and class tests
upforwd/ Update forwarding tests

View File

@ -72,8 +72,9 @@ SUBDIRS="acl additional allow_query addzone autosign builtin
legacy limits logfileconfig lwresd
masterfile masterformat metadata mkeys
notify nslookup nsupdate pending pipelined @PKCS11_TEST@
reclimit redirect resolver rndc rpz rrl rrchecker rrsetorder
rsabigexponent runtime sit sfcache smartsign sortlist spf
reclimit redirect resolver rndc rpz rpzrecurse
rrl rrchecker rrsetorder rsabigexponent runtime
sit sfcache smartsign sortlist spf
staticstub statistics stub tcp tkey tsig tsiggss unknown
upforwd verify views wildcard xfer xferquota zero zonechecks"

View File

@ -635,7 +635,7 @@ for i in 1 2 3 4 5; do
nsd $ns5 delete '*.example.com.policy1.' example.com.policy1.
done
echo "I:checking checking that going from a empty policy zone works"
echo "I:checking that going from a empty policy zone works"
nsd $ns5 add '*.x.servfail.policy2.' x.servfail.policy2.
sleep 1
$RNDCCMD $ns7 reload policy2

View File

@ -0,0 +1,2 @@
/rpz
/rpz.o

View File

@ -0,0 +1,113 @@
These tests check RPZ recursion behavior (including skipping
recursion when appropriate).
The general structure of the tests is:
* The resolver (ns2) with an unqualified view containing the policy
zones, the response-policy statement, and a root hint zone
* The auth server that contains two authoritative zones, l1.l0 and
l2.l1.l0, both delegated to itself. l2.l1.l0 specifies a non-existent
zone data file and so will generate SERVFAILs for any queries to it.
The l2.l1.l0 zone was chosen to generate SERVFAIL responses because RPZ
evaluation will use that error response whenever it encounters it during
processing, thus making it a binary indicator for whether or not
recursion was attempted. This also allows us to not worry about having
to craft 'ip', 'nsdname', and 'nsip' rules that matched the queries.
Each test is intended to be fed a number of queries constructed as
qXX.l2.l1.l0, where XX is the 1-based query sequence number (e.g. the
first query of each test is q01.l2.l1.l0).
For all the tests the triggers are constructed as follows:
client-ip - match 127.0.0.1/32
ip - match 255.255.255.255/32 (does not matter due to SERVFAIL)
nsdname - match does.not.matter (and it doesn't)
nsip - match 255.255.255.255/32 (also does not matter)
qname - match qXX.l2.l1.l0, where XX is the query sequence number that
is intended to be matched by this qname rule.
Here's the detail on the test cases:
Group 1 - testing skipping recursion for a single policy zone with only
records that allow recursion to be skipped
Test 1a:
1 policy zone containing 1 'client-ip' trigger
1 query, expected to skip recursion
Test 1b:
1 policy zone containing 1 'qname' trigger (q01)
2 queries, q01 is expected to skip recursion, q02 is expected to
recurse
Test 1c:
1 policy zone containing both a 'client-ip' and 'qname' trigger (q02)
1 query, expected to skip recursion
Group 2 - testing skipping recursion with multiple policy zones when all
zones have only trigger types eligible to skip recursion with
Test 2a:
32 policy zones, each containing 1 'qname' trigger (qNN, where NN is
the zone's sequence 1-based sequence number formatted to 2 digits,
so each of the first 32 queries should match a different zone)
33 queries, the first 32 of which are expected to skip recursion
while the 33rd is expected to recurse
Group 3 - Testing interaction of triggers that require recursion when in
a single zone, both alone and with triggers that allow recursion to be
skipped
Test 3a:
1 policy zone containing 1 'ip' trigger
1 query, expected to recurse
Test 3b:
1 policy zone containing 1 'nsdname' trigger
1 query, expected to recurse
Test 3c:
1 policy zone containing 1 'nsip' trigger
1 query, expected to recurse
Test 3d:
1 policy zone containing 1 'ip' trigger and 1 'qname' trigger (q02)
2 queries, the first should not recurse and the second should recurse
Test 3e:
1 policy zone containing 1 'nsdname' trigger and 1 'qname' trigger
(q02)
2 queries, the first should not recurse and the second should recurse
Test 3f:
1 policy zone containing 1 'nsip' trigger and 1 'qname' trigger (q02)
2 queries, the first should not recurse and the second should recurse
Group 4 - contains 32 subtests designed to verify that recursion is
skippable for only the appropriate zones based on the order specified in
the 'response-policy' statement
Tests 4aa to 4bf:
32 policy zones per test, one of which is configured with 1 'ip'
trigger and one 'qname' trigger while the others are configured
only with 1 'qname' trigger. The zone with both triggers starts
listed first and is moved backwards by one position with each
test. The 'qname' triggers in the zones are structured so that
the zones are tested starting with the first zone and the 'ip'
trigger is tested before the 'qname' trigger for that zone.
33 queries per test, where the number expected to skip recursion
matches the test sequence number: e.g. 1 skip for 4aa, 26 skips
for 4az, and 32 skips for 4bf
Group 5 - This test verifies that the "pivot" policy zone for whether or
not recursion can be skipped is the first listed zone with applicable
trigger types rather than a later listed zone.
Test 5a:
5 policy zones, the 1st, 3rd, and 5th configured with 1 'qname'
trigger each (q01, q04, and q06, respectively), the 2nd and 4th
each configured with an 'ip' and 'qname' trigger (q02 and q05,
respectively for the 'qname' triggers
6 queries, of which only q01 and q02 are expected to skip recursion

View File

@ -0,0 +1,24 @@
# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# Clean up after rpz tests.
rm -f dig.out.*
rm -f ns2/named.conf
rm -f ns2/*.local
rm -f ns2/*.queries
rm -f ns2/named.*.conf
rm -f ns*/named.lock
rm -f ns*/named.memstats
rm -f ns*/named.run

View File

@ -0,0 +1,6 @@
$TTL 60
@ IN SOA root.ns ns 1996072700 3600 1800 86400 60
NS ns
ns A 10.53.0.1
l1 NS ns.l1
ns.l1 A 10.53.0.1

View File

@ -0,0 +1,6 @@
$TTL 60
@ IN SOA root.ns ns 1996072700 3600 1800 86400 60
NS ns
ns A 10.53.0.1
l2 NS ns.l2
ns.l2 A 10.53.0.1

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
options {
query-source address 10.53.0.1;
notify-source 10.53.0.1;
transfer-source 10.53.0.1;
port 5300;
pid-file "named.pid";
listen-on { 10.53.0.1; };
listen-on-v6 { none; };
recursion no;
};
zone "." {
type master;
file "root.db";
};
zone "l0" {
type master;
file "db.l0";
};
zone "l1.l0" {
type master;
file "db.l1.l0";
};
zone "l2.l1.l0" {
type master;
file "does-not-exist";
};

View File

@ -0,0 +1,27 @@
; Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; copyright notice and this permission notice appear in all copies.
;
; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
$TTL 300
. IN SOA muks.isc.org. a.root.servers.nil. (
2010 ; serial
600 ; refresh
600 ; retry
1200 ; expire
600 ; minimum
)
. NS a.root-servers.nil.
a.root-servers.nil. A 10.53.0.1
l0. NS ns.l0.
ns.l0. A 10.53.0.1

View File

@ -0,0 +1,3 @@
/*.local
/*.queries
/*.conf

View File

@ -0,0 +1,9 @@
# common configuration
include "named.conf.header";
view "recursive" {
zone "." {
type hint;
file "root.hint";
};
};

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
controls { /* empty */ };
options {
query-source address 10.53.0.2;
notify-source 10.53.0.2;
transfer-source 10.53.0.2;
port 5300;
pid-file "named.pid";
listen-on { 10.53.0.2; };
listen-on-v6 { none; };
recursion yes;
querylog yes;
};

View File

@ -0,0 +1,17 @@
; Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; copyright notice and this permission notice appear in all copies.
;
; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
$TTL 999999
. IN NS a.root-servers.nil.
a.root-servers.nil. IN A 10.53.0.1

View File

@ -0,0 +1,29 @@
#!/bin/sh
#
# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
ret=0
../rpz/rpz nsdname || ret=1
../rpz/rpz nsip || ret=1
if [ $ret != 0 ]; then
echo "I:This test requires NSIP AND NSDNAME support in RPZ." >&2
exit 1
fi
exec $SHELL ../testcrypto.sh

View File

@ -0,0 +1,21 @@
#!/bin/sh
#
# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
perl testgen.pl
cp -f ns2/named.conf.default ns2/named.conf

View File

@ -0,0 +1,299 @@
#!/usr/bin/env perl
use strict;
use warnings;
my $boilerplate_header = <<'EOB';
# common configuration
include "named.conf.header";
view "recursive" {
zone "." {
type hint;
file "root.hint";
};
# policy configuration to be tested
response-policy {
EOB
my $boilerplate_middle = <<'EOB';
} qname-wait-recurse no;
# policy zones to be tested
EOB
my $boilerplate_end = <<'EOB';
};
EOB
my $policy_zone_header = <<'EOH';
$TTL 60
@ IN SOA root.ns ns 1996072700 3600 1800 86400 60
NS ns
ns A 127.0.0.1
EOH
sub policy_client_ip {
return "32.1.0.0.127.rpz-client-ip CNAME .\n";
}
sub policy_qname {
my $query_nbr = shift;
return sprintf "q%02d.l2.l1.l0 CNAME .\n", $query_nbr;
}
sub policy_ip {
return "32.255.255.255.255.rpz-ip CNAME .\n";
}
sub policy_nsdname {
return "does.not.matter.rpz-nsdname CNAME .\n";
}
sub policy_nsip {
return "32.255.255.255.255.rpz-ip CNAME .\n";
}
my %static_triggers = (
'client-ip' => \&policy_client_ip,
'ip' => \&policy_ip,
'nsdname' => \&policy_nsdname,
'nsip' => \&policy_nsip,
);
sub mkconf {
my $case_id = shift;
my $n_queries = shift;
{ # generate the query list
my $query_list_filename = "ns2/$case_id.queries";
my $query_list_fh;
open $query_list_fh, ">$query_list_filename" or die;
for( my $i = 1; $i <= $n_queries; $i++ ) {
print $query_list_fh sprintf "q%02d.l2.l1.l0\n", $i;
}
}
my @zones;
{ # generate the conf file
my $conf_filename = "ns2/named.$case_id.conf";
my $conf_fh;
open $conf_fh, ">$conf_filename" or die;
print $conf_fh $boilerplate_header;
my $zone_seq = 0;
@zones = map {
[
sprintf( "$case_id.%02d.policy.local", $zone_seq++ ),
$_,
];
} @_;
print $conf_fh map { qq{ zone "$_->[0]";\n} } @zones;
print $conf_fh $boilerplate_middle;
print $conf_fh map { qq{ zone "$_->[0]" { type master; file "db.$_->[0]"; };\n} } @zones;
print $conf_fh $boilerplate_end;
}
# generate the policy zone contents
foreach my $policy_zone_info( @zones ) {
my $policy_zone_name = $policy_zone_info->[0];
my $policy_zone_contents = $policy_zone_info->[1];
my $policy_zone_filename = "ns2/db.$policy_zone_name";
my $policy_zone_fh;
open $policy_zone_fh, ">$policy_zone_filename" or die;
print $policy_zone_fh $policy_zone_header;
foreach my $trigger( @$policy_zone_contents ) {
if( exists $static_triggers{$trigger} ) {
# matches a trigger type with a static value
print $policy_zone_fh $static_triggers{$trigger}->();
}
else {
# a qname trigger, where what was specified is the query number it should match
print $policy_zone_fh policy_qname( $trigger );
}
}
}
}
mkconf(
'1a',
1,
[ 'client-ip' ],
);
mkconf(
'1b',
2,
[ 1 ],
);
mkconf(
'1c',
1,
[ 'client-ip', 2 ],
);
mkconf(
'2a',
33,
map { [ $_ ]; } 1 .. 32
);
mkconf(
'3a',
1,
[ 'ip' ],
);
mkconf(
'3b',
1,
[ 'nsdname' ],
);
mkconf(
'3c',
1,
[ 'nsip' ],
);
mkconf(
'3d',
2,
[ 'ip', 1 ]
);
mkconf(
'3e',
2,
[ 'nsdname', 1 ]
);
mkconf(
'3f',
2,
[ 'nsip', 1 ]
);
{
my $seq_code = 'aa';
my $seq_nbr = 0;
while( $seq_nbr < 32 ) {
mkconf(
"4$seq_code",
33,
( map { [ $_ ]; } 1 .. $seq_nbr ),
[ 'ip', $seq_nbr + 2 ],
( map { [ $_ + 2 ]; } ($seq_nbr + 1) .. 31 ),
);
$seq_code++;
$seq_nbr++;
}
}
mkconf(
'5a',
6,
[ 1 ],
[ 2, 'ip' ],
[ 4 ],
[ 5, 'ip' ],
[ 6 ],
);
__END__
0x01 - has client-ip
32.1.0.0.127.rpz-client-ip CNAME .
0x02 - has qname
qX.l2.l1.l0 CNAME .
0x10 - has ip
32.255.255.255.255.rpz-ip CNAME .
0x20 - has nsdname
does.not.matter.rpz-nsdname CNAME .
0x40 - has nsip
32.255.255.255.255.rpz-nsip CNAME .
$case.$seq.policy.local
case 1a = 0x01
.q01 = (00,0x01)=-r
case 1b = 0x02
.q01 = (00,0x02)=-r
.q02 = (--,----)=+r
case 1c = 0x03
.q01 = (00,0x01)=-r
case 2a = 0x03{32}
.q01 = (00,0x02)=-r
.q02 = (01,0x02)=-r
...
.q31 = (30,0x02)=-r
.q32 = (31,0x02)=-r
.q33 = (--,----)=+r
case 3a = 0x10
.q01 = (00,0x10)=+r
case 3b = 0x20
.q01 = (00,0x20)=+r
case 3c = 0x40
.q01 = (00,0x40)=+r
case 3d = 0x12
.q01 = (00,0x10)=+r
.q02 = (00,0x02)=-r
case 3e = 0x22
.q01 = (00,0x20)=+r
.q02 = (00,0x02)=-r
case 3f = 0x42
.q01 = (00,0x40)=+r
.q02 = (00,0x02)=-r
case 4aa = 0x12,0x02{31}
.q01 = (00,0x10)=+r
.q02 = (00,0x02)=-r
.q03 = (01,0x02)=+r
...
.q32 = (30,0x02)=+r
.q33 = (31,0x02)=+r
case 4__ = 0x02{n(1->30)},0x12,0x02{31-n}
.q01 = (00,0x02)=-r
...
.q(n+1) = (n,0x10)=+r
.q(n+2) = (n,0x02)=-r
...
.q33 = (31,0x02)=+r
case 4bf = 0x02{31},0x12
.q01 = (00,0x02)=-r
.q02 = (01,0x02)=-r
...
.q31 = (30,0x02)=-r
.q32 = (31,0x10)=+r
.q33 = (31,0x02)=-r
case 5a = 0x02,0x12,0x02,0x12,0x02
.q01 = (00,0x02)=-r
.q02 = (01,0x02)=-r
.q03 = (01,0x10)=+r
.q04 = (02,0x02)=+r
.q05 = (03,0x02)=+r
.q06 = (04,0x02)=+r

View File

@ -0,0 +1,168 @@
#!/bin/sh
#
# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
status=0
t=0
# $1 = test name (such as 1a, 1b, etc. for which named.$1.conf exists)
run_server() {
TESTNAME=$1
echo "I:stopping resolver"
$PERL $SYSTEMTESTTOP/stop.pl . ns2
sleep 1
echo "I:starting resolver using named.$TESTNAME.conf"
cp -f ns2/named.$TESTNAME.conf ns2/named.conf
$PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns2
}
run_query() {
TESTNAME=$1
LINE=$2
NAME=`tail -n +"$LINE" ns2/$TESTNAME.queries | head -n 1`
$DIG $DIGOPTS $NAME a @10.53.0.2 -p 5300 -b 127.0.0.1 > dig.out.${t}
grep "status: SERVFAIL" dig.out.${t} > /dev/null 2>&1 && return 1
return 0
}
# $1 = test name (such as 1a, 1b, etc. for which $1.queries exists)
# $2 = line number in query file to test (the name to query is taken from this line)
expect_norecurse() {
TESTNAME=$1
LINE=$2
NAME=`tail -n +"$LINE" ns2/$TESTNAME.queries | head -n 1`
t=`expr $t + 1`
echo "I:testing $NAME doesn't recurse (${t})"
run_query $TESTNAME $LINE || {
echo "I:test ${t} failed"
status=1
}
}
# $1 = test name (such as 1a, 1b, etc. for which $1.queries exists)
# $2 = line number in query file to test (the name to query is taken from this line)
expect_recurse() {
TESTNAME=$1
LINE=$2
NAME=`tail -n +"$LINE" ns2/$TESTNAME.queries | head -n 1`
t=`expr $t + 1`
echo "I:testing $NAME recurses (${t})"
run_query $TESTNAME $LINE && {
echo "I:test $t failed"
status=1
}
}
t=`expr $t + 1`
echo "I:testing that l1.l0 exists without RPZ (${t})"
$DIG $DIGOPTS l1.l0 ns @10.53.0.2 -p 5300 > dig.out.${t}
grep "status: NOERROR" dig.out.${t} > /dev/null 2>&1 || {
echo "I:test $t failed"
status=1
}
t=`expr $t + 1`
echo "I:testing that l2.l1.l0 returns SERVFAIL without RPZ (${t})"
$DIG $DIGOPTS l2.l1.l0 ns @10.53.0.2 -p 5300 > dig.out.${t}
grep "status: SERVFAIL" dig.out.${t} > /dev/null 2>&1 || {
echo "I:test $t failed"
status=1
}
# Group 1
run_server 1a
expect_norecurse 1a 1
run_server 1b
expect_norecurse 1b 1
expect_recurse 1b 2
run_server 1c
expect_norecurse 1c 1
# Group 2
run_server 2a
for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
do
expect_norecurse 2a $n
done
expect_recurse 2a 33
# Group 3
run_server 3a
expect_recurse 3a 1
run_server 3b
expect_recurse 3b 1
run_server 3c
expect_recurse 3c 1
run_server 3d
expect_norecurse 3d 1
expect_recurse 3d 2
run_server 3e
expect_norecurse 3e 1
expect_recurse 3e 2
run_server 3f
expect_norecurse 3f 1
expect_recurse 3f 2
# Group 4
testlist="aa ap bf"
values="1 16 32"
# Uncomment the following to test every skip value instead of
# only a sample of values
#
#testlist="aa ab ac ad ae af ag ah ai aj ak al am an ao ap \
# aq ar as at au av aw ax ay az ba bb bc bd be bf"
#values="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
# 21 22 23 24 25 26 27 28 29 30 31 32"
set -- $values
for n in $testlist; do
run_server 4$n
ni=$1
t=`expr $t + 1`
echo "I:testing that ${ni} of 33 queries skip recursion (${t})"
c=0
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 \
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
do
run_query 4$n $i
c=`expr $c + $?`
done
skipped=`expr 33 - $c`
if [ $skipped != $ni ]; then
echo "I:test $t failed (actual=$skipped, expected=$ni)"
status=1
fi
shift
done
# Group 5
run_server 5a
expect_norecurse 5a 1
expect_norecurse 5a 2
expect_recurse 5a 3
expect_recurse 5a 4
expect_recurse 5a 5
expect_recurse 5a 6
echo "I:exit status: $status"
exit $status

View File

@ -9950,7 +9950,7 @@ deny-answer-aliases { "example.net"; };
<command>DISABLED</command> actions) must be chosen.
Triggers or the records that encode them are chosen for the
rewriting in the following order:
<itemizedlist>
<orderedlist>
<listitem>Choose the triggered record in the zone that appears
first in the <command>response-policy</command> option.
</listitem>
@ -9967,7 +9967,7 @@ deny-answer-aliases { "example.net"; };
prefer the IP or NSIP trigger that matches
the smallest IP address.
</listitem>
</itemizedlist>
</orderedlist>
</para>
<para>

View File

@ -620,6 +620,17 @@
<command>dig +short</command>. [RT #39291]
</para>
</listitem>
<listitem>
<para>
A bug in the RPZ implementation could cause some policy
zones that did not specifically require recursion to be
treated as if they did; consequently, setting
<command>qname-wait-recurse no;</command> was
sometimes ineffective. This has been corrected.
In most configurations, behavioral changes due to this
fix will not be noticeable. [RT #39229]
</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="end_of_life">

View File

@ -55,7 +55,7 @@
*
* Each leaf indicates that an IP address is listed in the IP address or the
* name server IP address policy sub-zone (or both) of the corresponding
* response response zone. The policy data such as a CNAME or an A record
* response policy zone. The policy data such as a CNAME or an A record
* is kept in the policy zone. After an IP address has been found in a radix
* tree, the node in the policy zone's database is found by converting
* the IP address to a domain name in a canonical form.
@ -133,11 +133,6 @@ struct dns_rpz_cidr_node {
dns_rpz_addr_zbits_t sum;
};
/*
* The data in a RBT node has two pairs of bits for policy zones.
* One pair is for the corresponding name of the node such as example.com
* and the other pair is for a wildcard child such as *.example.com.
*/
/*
* A pair of arrays of bits flagging the existence of
* QNAME and NSDNAME policy triggers.
@ -148,6 +143,11 @@ struct dns_rpz_nm_zbits {
dns_rpz_zbits_t ns;
};
/*
* The data in a RBT node has two pairs of bits for policy zones.
* One pair is for the corresponding name of the node such as example.com
* and the other pair is for a wildcard child such as *.example.com.
*/
typedef struct dns_rpz_nm_data dns_rpz_nm_data_t;
struct dns_rpz_nm_data {
dns_rpz_nm_zbits_t set;
@ -259,11 +259,15 @@ dns_rpz_policy2str(dns_rpz_policy_t policy) {
return (str);
}
/*
* Return the bit number of the highest set bit in 'zbit'.
* (for example, 0x01 returns 0, 0xFF returns 7, etc.)
*/
static int
zbit_to_num(dns_rpz_zbits_t zbit) {
dns_rpz_num_t rpz_num;
INSIST(zbit != 0);
REQUIRE(zbit != 0);
rpz_num = 0;
#if DNS_RPZ_MAX_ZONES > 32
if ((zbit & 0xffffffff00000000L) != 0) {
@ -376,30 +380,172 @@ set_sum_pair(dns_rpz_cidr_node_t *cnode) {
static void
fix_qname_skip_recurse(dns_rpz_zones_t *rpzs) {
dns_rpz_zbits_t zbits;
dns_rpz_zbits_t mask;
/* qname_wait_recurse and qname_skip_recurse are used to
* implement the "qname-wait-recurse" config option.
*
* By default, "qname-wait-recurse" is yes, so no
* processing happens without recursion. In this case,
* qname_wait_recurse is true, and qname_skip_recurse
* (a bit field indicating which policy zones can be
* processed without recursion) is set to all 0's by
* fix_qname_skip_recurse().
*
* When "qname-wait-recurse" is no, qname_skip_recurse may be
* set to a non-zero value by fix_qname_skip_recurse(). The mask
* has to have bits set for for the policy zones for which
* processing may continue without recursion, and bits cleared
* for the rest.
*
* (1) The ARM says:
*
* The "qname-wait-recurse no" option overrides that default
* behavior when recursion cannot change a non-error
* response. The option does not affect QNAME or client-IP
* triggers in policy zones listed after other zones
* containing IP, NSIP and NSDNAME triggers, because those may
* depend on the A, AAAA, and NS records that would be found
* during recursive resolution.
*
* Let's consider the following:
*
* zbits_req = (rpzs->have.ipv4 | rpzs->have.ipv6 |
* rpzs->have.nsdname |
* rpzs->have.nsipv4 | rpzs->have.nsipv6);
*
* zbits_req now contains bits set for zones which require
* recursion.
*
* But going by the description in the ARM, if the first policy
* zone requires recursion, then all zones after that (higher
* order bits) have to wait as well. If the Nth zone requires
* recursion, then (N+1)th zone onwards all need to wait.
*
* So mapping this, examples:
*
* zbits_req = 0b000 mask = 0xffffffff (no zones have to wait for
* recursion)
* zbits_req = 0b001 mask = 0x00000000 (all zones have to wait)
* zbits_req = 0b010 mask = 0x00000001 (the first zone doesn't have to
* wait, second zone onwards need
* to wait)
* zbits_req = 0b011 mask = 0x00000000 (all zones have to wait)
* zbits_req = 0b100 mask = 0x00000011 (the 1st and 2nd zones don't
* have to wait, third zone
* onwards need to wait)
*
* More generally, we have to count the number of trailing 0
* bits in zbits_req and only these can be processed without
* recursion. All the rest need to wait.
*
* (2) The ARM says that "qname-wait-recurse no" option
* overrides the default behavior when recursion cannot change a
* non-error response. So, in the order of listing of policy
* zones, within the first policy zone where recursion may be
* required, we should first allow CLIENT-IP and QNAME policy
* records to be attempted without recursion.
*/
/*
* Get a mask covering all policy zones that are not subordinate to
* other policy zones containing triggers that require that the
* qname be resolved before they can be checked.
*/
if (rpzs->p.qname_wait_recurse) {
zbits = 0;
} else {
zbits = (rpzs->have.ipv4 || rpzs->have.ipv6 ||
rpzs->have.nsdname ||
rpzs->have.nsipv4 || rpzs->have.nsipv6);
if (zbits == 0) {
zbits = DNS_RPZ_ALL_ZBITS;
} else {
zbits = DNS_RPZ_ZMASK(zbit_to_num(zbits));
}
}
rpzs->have.qname_skip_recurse = zbits;
rpzs->have.client_ip = rpzs->have.client_ipv4 | rpzs->have.client_ipv6;
rpzs->have.ip = rpzs->have.ipv4 | rpzs->have.ipv6;
rpzs->have.nsip = rpzs->have.nsipv4 | rpzs->have.nsipv6;
if (rpzs->p.qname_wait_recurse) {
mask = 0;
} else {
dns_rpz_zbits_t zbits_req;
dns_rpz_zbits_t zbits_notreq;
dns_rpz_zbits_t mask2;
dns_rpz_zbits_t req_mask;
/*
* Get the masks of zones with policies that
* do/don't require recursion
*/
zbits_req = (rpzs->have.ipv4 | rpzs->have.ipv6 |
rpzs->have.nsdname |
rpzs->have.nsipv4 | rpzs->have.nsipv6);
zbits_notreq = (rpzs->have.client_ip | rpzs->have.qname);
if (zbits_req == 0) {
mask = DNS_RPZ_ALL_ZBITS;
goto set;
}
/*
* req_mask is a mask covering used bits in
* zbits_req. (For instance, 0b1 => 0b1, 0b101 => 0b111,
* 0b11010101 => 0b11111111).
*/
req_mask = zbits_req;
req_mask |= req_mask >> 1;
req_mask |= req_mask >> 2;
req_mask |= req_mask >> 4;
req_mask |= req_mask >> 8;
req_mask |= req_mask >> 16;
#if DNS_RPZ_MAX_ZONES > 32
req_mask |= req_mask >> 32;
#endif
/*
* There's no point in skipping recursion for a later
* zone if it is required in a previous zone.
*/
if ((zbits_notreq & req_mask) == 0) {
mask = 0;
goto set;
}
/*
* This bit arithmetic creates a mask of zones in which
* it is okay to skip recursion. After the first zone
* that has to wait for recursion, all the others have
* to wait as well, so we want to create a mask in which
* all the trailing zeroes in zbits_req are are 1, and
* more significant bits are 0. (For instance,
* 0x0700 => 0x00ff, 0x0007 => 0x0000)
*/
mask = ~(zbits_req | -zbits_req);
/*
* As mentioned in (2) above, the zone corresponding to
* the least significant zero could have its CLIENT-IP
* and QNAME policies checked before recursion, if it
* has any of those policies. So if it does, we
* can set its 0 to 1.
*
* Locate the least significant 0 bit in the mask (for
* instance, 0xff => 0x100)...
*/
mask2 = (mask << 1) & ~mask;
/*
* Also set the bit for zone 0, because if it's in
* zbits_notreq then it's definitely okay to attempt to
* skip recursion for zone 0...
*/
mask2 |= 1;
/* Clear any bits *not* in zbits_notreq... */
mask2 &= zbits_notreq;
/* And merge the result into the skip-recursion mask */
mask |= mask2;
}
set:
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB,
DNS_RPZ_DEBUG_QUIET,
"computed RPZ qname_skip_recurse mask=0x%llx",
(isc_uint64_t) mask);
rpzs->have.qname_skip_recurse = mask;
}
static void