From 8462dfb880040cde3a60f047ec18808737fd7e85 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 19 Dec 2012 09:55:02 +1100 Subject: [PATCH 1/2] 3443. [bug] The NOQNAME proof was not being returned from cached insecure responses. [RT #21409] --- CHANGES | 3 + bin/tests/system/conf.sh.in | 2 +- bin/tests/system/wildcard/clean.sh | 30 ++ bin/tests/system/wildcard/ns1/named.conf | 45 ++ bin/tests/system/wildcard/ns1/nsec.db.in | 7 + bin/tests/system/wildcard/ns1/nsec3.db.in | 7 + .../system/wildcard/ns1/private.nsec.db.in | 6 + .../system/wildcard/ns1/private.nsec3.db.in | 8 + bin/tests/system/wildcard/ns1/root.db.in | 7 + bin/tests/system/wildcard/ns1/sign.sh | 120 +++++ bin/tests/system/wildcard/ns2/hints | 18 + bin/tests/system/wildcard/ns2/named.conf | 33 ++ bin/tests/system/wildcard/ns3/hints | 18 + bin/tests/system/wildcard/ns3/named.conf | 35 ++ bin/tests/system/wildcard/ns4/named.conf | 37 ++ bin/tests/system/wildcard/setup.sh | 21 + bin/tests/system/wildcard/tests.sh | 136 +++++ lib/dns/include/dns/nsec.h | 13 + lib/dns/include/dns/nsec3.h | 8 + lib/dns/include/dns/types.h | 3 + lib/dns/nsec.c | 159 ++++++ lib/dns/nsec3.c | 283 ++++++++++ lib/dns/resolver.c | 186 ++++++- lib/dns/validator.c | 492 +----------------- 24 files changed, 1198 insertions(+), 479 deletions(-) create mode 100644 bin/tests/system/wildcard/clean.sh create mode 100644 bin/tests/system/wildcard/ns1/named.conf create mode 100644 bin/tests/system/wildcard/ns1/nsec.db.in create mode 100644 bin/tests/system/wildcard/ns1/nsec3.db.in create mode 100644 bin/tests/system/wildcard/ns1/private.nsec.db.in create mode 100644 bin/tests/system/wildcard/ns1/private.nsec3.db.in create mode 100644 bin/tests/system/wildcard/ns1/root.db.in create mode 100755 bin/tests/system/wildcard/ns1/sign.sh create mode 100644 bin/tests/system/wildcard/ns2/hints create mode 100644 bin/tests/system/wildcard/ns2/named.conf create mode 100644 bin/tests/system/wildcard/ns3/hints create mode 100644 bin/tests/system/wildcard/ns3/named.conf create mode 100644 bin/tests/system/wildcard/ns4/named.conf create mode 100644 bin/tests/system/wildcard/setup.sh create mode 100644 bin/tests/system/wildcard/tests.sh diff --git a/CHANGES b/CHANGES index a0def2e0a5..8ee1d0b76a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +3443. [bug] The NOQNAME proof was not being returned from cached + insecure responses. [RT #21409] + 3442. [port] Net::DNS 0.69 introduced a non backwards compatible change. [RT #32216] diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index f180b24b08..8e5e7dc434 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -61,7 +61,7 @@ SUBDIRS="acl allow_query addzone autosign builtin cacheclean checkconf inline limits logfileconfig lwresd masterfile masterformat metadata notify nsupdate pending pkcs11 redirect resolver rndc rpz rrsetorder rsabigexponent sortlist smartsign staticstub statistics stub - tkey tsig tsiggss unknown upforwd verify views xfer xferquota + tkey tsig tsiggss unknown upforwd verify views wildcard xfer xferquota zonechecks" # PERL will be an empty string if no perl interpreter was found. diff --git a/bin/tests/system/wildcard/clean.sh b/bin/tests/system/wildcard/clean.sh new file mode 100644 index 0000000000..650092c295 --- /dev/null +++ b/bin/tests/system/wildcard/clean.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# +# Copyright (C) 2004, 2007, 2010 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. + +# $Id: clean.sh,v 1.1.2.1 2010/06/01 03:55:01 marka Exp $ + +rm -f random.data +rm -f ns*/named.run +rm -f ns1/K* +rm -f ns1/*.db +rm -f ns1/*.signed +rm -f ns1/dsset-* +rm -f ns1/trusted.conf +rm -f ns1/private.nsec.conf +rm -f ns1/private.nsec3.conf +rm -f ns1/signer.err +rm -f */named.memstats +rm -f dig.out.ns*.test* diff --git a/bin/tests/system/wildcard/ns1/named.conf b/bin/tests/system/wildcard/ns1/named.conf new file mode 100644 index 0000000000..84b606b699 --- /dev/null +++ b/bin/tests/system/wildcard/ns1/named.conf @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 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. + */ + +/* $Id: named.conf,v 1.1.2.3 2010/06/01 07:04:49 marka Exp $ */ + +controls { /* empty */ }; + +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; + notify yes; + dnssec-enable yes; +}; + +zone "." { type master; file "root.db.signed"; }; + +zone "nsec" { type master; file "nsec.db.signed"; }; +zone "private.nsec" { type master; file "private.nsec.db.signed"; }; + +/* + * The contents of nsec3 and private.nsec3 are specially choosen to + * have seperate NSEC3 records for the "no qname proof" and the + * "closest encloser proof". + */ +zone "nsec3" { type master; file "nsec3.db.signed"; }; +zone "private.nsec3" { type master; file "private.nsec3.db.signed"; }; diff --git a/bin/tests/system/wildcard/ns1/nsec.db.in b/bin/tests/system/wildcard/ns1/nsec.db.in new file mode 100644 index 0000000000..5cdcace9b8 --- /dev/null +++ b/bin/tests/system/wildcard/ns1/nsec.db.in @@ -0,0 +1,7 @@ +; Copyright +$TTL 120 +@ SOA a.root-servers.nil. hostmaster.root-servers.nil. 1 1800 900 604800 86400 +@ NS a.root-servers.nil. +private NS a.root-servers.nil. +*.wild CNAME a. +a.wild A 1.2.3.5 diff --git a/bin/tests/system/wildcard/ns1/nsec3.db.in b/bin/tests/system/wildcard/ns1/nsec3.db.in new file mode 100644 index 0000000000..5cdcace9b8 --- /dev/null +++ b/bin/tests/system/wildcard/ns1/nsec3.db.in @@ -0,0 +1,7 @@ +; Copyright +$TTL 120 +@ SOA a.root-servers.nil. hostmaster.root-servers.nil. 1 1800 900 604800 86400 +@ NS a.root-servers.nil. +private NS a.root-servers.nil. +*.wild CNAME a. +a.wild A 1.2.3.5 diff --git a/bin/tests/system/wildcard/ns1/private.nsec.db.in b/bin/tests/system/wildcard/ns1/private.nsec.db.in new file mode 100644 index 0000000000..e90b801b6d --- /dev/null +++ b/bin/tests/system/wildcard/ns1/private.nsec.db.in @@ -0,0 +1,6 @@ +; Copyright +$TTL 120 +@ SOA a.root-servers.nil. hostmaster.root-servers.nil. 1 1800 900 604800 86400 +@ NS a.root-servers.nil. +*.wild CNAME a. +a.wild A 1.2.3.5 diff --git a/bin/tests/system/wildcard/ns1/private.nsec3.db.in b/bin/tests/system/wildcard/ns1/private.nsec3.db.in new file mode 100644 index 0000000000..cc0e773969 --- /dev/null +++ b/bin/tests/system/wildcard/ns1/private.nsec3.db.in @@ -0,0 +1,8 @@ +; Copyright + +$TTL 120 +@ SOA a.root-servers.nil. hostmaster.root-servers.nil. 1 1800 900 604800 86400 +@ NS a.root-servers.nil. +b A 1.2.3.4 +*.wild CNAME a. +a.wild A 1.2.3.5 diff --git a/bin/tests/system/wildcard/ns1/root.db.in b/bin/tests/system/wildcard/ns1/root.db.in new file mode 100644 index 0000000000..eda13ea9d0 --- /dev/null +++ b/bin/tests/system/wildcard/ns1/root.db.in @@ -0,0 +1,7 @@ +; Copyright +$TTL 120 +@ SOA a.root-servers.nil hostmaster.root-servers.nil 1 1800 900 604800 86400 +@ NS a.root-servers.nil +a.root-servers.nil A 10.53.0.1 +nsec NS a.root-servers.nil +nsec3 NS a.root-servers.nil diff --git a/bin/tests/system/wildcard/ns1/sign.sh b/bin/tests/system/wildcard/ns1/sign.sh new file mode 100755 index 0000000000..a7b63f178f --- /dev/null +++ b/bin/tests/system/wildcard/ns1/sign.sh @@ -0,0 +1,120 @@ +#!/bin/sh +# +# Copyright (C) 2004, 2007, 2009, 2010 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. + +# $Id: sign.sh,v 1.1.2.2 2010/06/01 06:38:47 marka Exp $ + +SYSTEMTESTTOP=../.. +. $SYSTEMTESTTOP/conf.sh + +RANDFILE=../random.data +dssets= + +zone=nsec. +infile=nsec.db.in +zonefile=nsec.db +outfile=nsec.db.signed +dssets="$dssets dsset-$zone" + +keyname1=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null` +keyname2=`$KEYGEN -f KSK -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null` + +cat $infile $keyname1.key $keyname2.key > $zonefile + +$SIGNER -r $RANDFILE -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err +echo "I: signed $zone" + +zone=private.nsec. +infile=private.nsec.db.in +zonefile=private.nsec.db +outfile=private.nsec.db.signed + +keyname1=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null` +keyname2=`$KEYGEN -f KSK -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null` + +cat $infile $keyname1.key $keyname2.key > $zonefile + +$SIGNER -r $RANDFILE -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err +echo "I: signed $zone" + +grep -v '^;' $keyname2.key | $PERL -n -e ' +local ($dn, $class, $type, $flags, $proto, $alg, @rest) = split; +local $key = join("", @rest); +print < private.nsec.conf + +zone=nsec3. +infile=nsec3.db.in +zonefile=nsec3.db +outfile=nsec3.db.signed +dssets="$dssets dsset-$zone" + +keyname1=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 1024 -n zone $zone 2> /dev/null` +keyname2=`$KEYGEN -f KSK -r $RANDFILE -a NSEC3RSASHA1 -b 1024 -n zone $zone 2> /dev/null` + +cat $infile $keyname1.key $keyname2.key > $zonefile + +$SIGNER -r $RANDFILE -3 - -i 10 -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err +echo "I: signed $zone" + +zone=private.nsec3. +infile=private.nsec3.db.in +zonefile=private.nsec3.db +outfile=private.nsec3.db.signed + +keyname1=`$KEYGEN -r $RANDFILE -a NSEC3RSASHA1 -b 1024 -n zone $zone 2> /dev/null` +keyname2=`$KEYGEN -f KSK -r $RANDFILE -a NSEC3RSASHA1 -b 1024 -n zone $zone 2> /dev/null` + +cat $infile $keyname1.key $keyname2.key > $zonefile + +$SIGNER -r $RANDFILE -3 - -i 10 -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err +echo "I: signed $zone" + +grep -v '^;' $keyname2.key | $PERL -n -e ' +local ($dn, $class, $type, $flags, $proto, $alg, @rest) = split; +local $key = join("", @rest); +print < private.nsec3.conf + +zone=. +infile=root.db.in +zonefile=root.db +outfile=root.db.signed + +keyname1=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null` +keyname2=`$KEYGEN -f KSK -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone 2> /dev/null` + +cat $infile $keyname1.key $keyname2.key $dssets >$zonefile + +$SIGNER -r $RANDFILE -o $zone -f $outfile $zonefile > /dev/null 2> signer.err || cat signer.err +echo "I: signed $zone" + +grep -v '^;' $keyname2.key | $PERL -n -e ' +local ($dn, $class, $type, $flags, $proto, $alg, @rest) = split; +local $key = join("", @rest); +print < trusted.conf diff --git a/bin/tests/system/wildcard/ns2/hints b/bin/tests/system/wildcard/ns2/hints new file mode 100644 index 0000000000..21b5bfa57f --- /dev/null +++ b/bin/tests/system/wildcard/ns2/hints @@ -0,0 +1,18 @@ +; Copyright (C) 2010 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. + +; $Id: hints,v 1.1.2.1 2010/06/01 03:55:02 marka Exp $ + +. 0 NS ns.root-servers.nil. +ns.root-servers.nil. 0 A 10.53.0.1 diff --git a/bin/tests/system/wildcard/ns2/named.conf b/bin/tests/system/wildcard/ns2/named.conf new file mode 100644 index 0000000000..0baec5ff52 --- /dev/null +++ b/bin/tests/system/wildcard/ns2/named.conf @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010 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. + */ + +/* $Id: named.conf,v 1.1.2.1 2010/06/01 03:55:02 marka Exp $ */ + +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; + notify yes; +}; + +zone "." { type hint; file "hints"; }; diff --git a/bin/tests/system/wildcard/ns3/hints b/bin/tests/system/wildcard/ns3/hints new file mode 100644 index 0000000000..21b5bfa57f --- /dev/null +++ b/bin/tests/system/wildcard/ns3/hints @@ -0,0 +1,18 @@ +; Copyright (C) 2010 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. + +; $Id: hints,v 1.1.2.1 2010/06/01 03:55:02 marka Exp $ + +. 0 NS ns.root-servers.nil. +ns.root-servers.nil. 0 A 10.53.0.1 diff --git a/bin/tests/system/wildcard/ns3/named.conf b/bin/tests/system/wildcard/ns3/named.conf new file mode 100644 index 0000000000..32e0426927 --- /dev/null +++ b/bin/tests/system/wildcard/ns3/named.conf @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 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. + */ + +/* $Id: named.conf,v 1.1.2.1 2010/06/01 03:55:02 marka Exp $ */ + +controls { /* empty */ }; + +options { + query-source address 10.53.0.3; + notify-source 10.53.0.3; + transfer-source 10.53.0.3; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + recursion yes; + notify yes; +}; + +include "../ns1/trusted.conf"; + +zone "." { type hint; file "hints"; }; diff --git a/bin/tests/system/wildcard/ns4/named.conf b/bin/tests/system/wildcard/ns4/named.conf new file mode 100644 index 0000000000..d0cb8694cd --- /dev/null +++ b/bin/tests/system/wildcard/ns4/named.conf @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 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. + */ + +/* $Id: named.conf,v 1.1.2.1 2010/06/01 03:55:02 marka Exp $ */ + +controls { /* empty */ }; + +options { + query-source address 10.53.0.4; + notify-source 10.53.0.4; + transfer-source 10.53.0.4; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.4; }; + listen-on-v6 { none; }; + recursion yes; + notify yes; + forward only; + forwarders { 10.53.0.2; }; +}; + +include "../ns1/trusted.conf"; +include "../ns1/private.nsec.conf"; +include "../ns1/private.nsec3.conf"; diff --git a/bin/tests/system/wildcard/setup.sh b/bin/tests/system/wildcard/setup.sh new file mode 100644 index 0000000000..df21b29f92 --- /dev/null +++ b/bin/tests/system/wildcard/setup.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# +# Copyright (C) 2004, 2007, 2009 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. + +# $Id: setup.sh,v 1.1.2.1 2010/06/01 03:55:01 marka Exp $ + +../../../tools/genrandom 400 random.data + +(cd ns1 && sh -e sign.sh) diff --git a/bin/tests/system/wildcard/tests.sh b/bin/tests/system/wildcard/tests.sh new file mode 100644 index 0000000000..b037778f48 --- /dev/null +++ b/bin/tests/system/wildcard/tests.sh @@ -0,0 +1,136 @@ +#!/bin/sh +# +# Copyright (C) 2004, 2007, 2010 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. + +# $Id: tests.sh,v 1.1.2.3 2010/06/01 06:57:31 marka Exp $ + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +status=0 +n=0 + +rm -f dig.out.* + +DIGOPTS="+tcp +noadd +nosea +nostat +nocmd +dnssec -p 5300" + +n=`expr $n + 1` +echo "I: checking that NSEC wildcard non-existance proof is returned auth ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.nsec +norec @10.53.0.1 > dig.out.ns1.test$n || ret=1 +grep -i 'a\.wild\.nsec\..*NSEC.*nsec\..*NSEC' dig.out.ns1.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that NSEC wildcard non-existance proof is returned non-validating ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.nsec @10.53.0.2 > dig.out.ns2.test$n || ret=1 +grep -i 'a\.wild\.nsec\..*NSEC.*nsec\..*NSEC' dig.out.ns2.test$n > /dev/null || ret=1 +grep -i 'flags:.* ad[ ;]' dig.out.ns2.test$n > /dev/null && ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that NSEC wildcard non-existance proof is returned validating ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.nsec @10.53.0.3 > dig.out.ns3.test$n || ret=1 +grep -i 'a\.wild\.nsec\..*NSEC.*nsec\..*NSEC' dig.out.ns3.test$n > /dev/null || ret=1 +grep -i 'flags:.* ad[ ;]' dig.out.ns3.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that returned NSEC wildcard non-existance proof validates ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.nsec @10.53.0.4 > dig.out.ns4.test$n || ret=1 +grep -i 'a\.wild\.nsec\..*NSEC.*nsec\..*NSEC' dig.out.ns4.test$n > /dev/null || ret=1 +grep -i 'flags:.* ad[ ;]' dig.out.ns4.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that NSEC wildcard non-existance proof is returned private, validating ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.private.nsec @10.53.0.3 > dig.out.ns3.test$n || ret=1 +grep -i 'a\.wild\.private\.nsec\..*NSEC.*private\.nsec\..*NSEC' dig.out.ns3.test$n > /dev/null || ret=1 +grep -i 'flags:.* ad[ ;]' dig.out.ns3.test$n > /dev/null && ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that returned NSEC wildcard non-existance proof for private zone validates ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.private.nsec @10.53.0.4 > dig.out.ns4.test$n || ret=1 +grep -i 'a\.wild\.private\.nsec\..*NSEC.*private\.nsec\..*NSEC' dig.out.ns4.test$n > /dev/null || ret=1 +grep -i 'flags:.* ad[ ;]' dig.out.ns4.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that NSEC3 wildcard non-existance proof is returned auth ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.nsec3 +norec @10.53.0.1 > dig.out.ns1.test$n || ret=1 +grep -i 'O3TJ8D9AJ54CBTFCQCJ3QK49CH7SF6H9\.nsec3\..*V5DLFB6UJNHR94LQ61FO607KGK12H88A' dig.out.ns1.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that NSEC3 wildcard non-existance proof is returned non-validating ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.nsec3 @10.53.0.2 > dig.out.ns2.test$n || ret=1 +grep -i 'O3TJ8D9AJ54CBTFCQCJ3QK49CH7SF6H9\.nsec3\..*V5DLFB6UJNHR94LQ61FO607KGK12H88A' dig.out.ns2.test$n > /dev/null || ret=1 +grep -i 'flags:.* ad[ ;]' dig.out.ns2.test$n > /dev/null && ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that NSEC3 wildcard non-existance proof is returned validating ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.nsec3 @10.53.0.3 > dig.out.ns3.test$n || ret=1 +grep -i 'O3TJ8D9AJ54CBTFCQCJ3QK49CH7SF6H9\.nsec3\..*V5DLFB6UJNHR94LQ61FO607KGK12H88A' dig.out.ns3.test$n > /dev/null || ret=1 +grep -i 'flags:.* ad[ ;]' dig.out.ns3.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that returned NSEC3 wildcard non-existance proof validates ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.nsec3 @10.53.0.4 > dig.out.ns4.test$n || ret=1 +grep -i 'O3TJ8D9AJ54CBTFCQCJ3QK49CH7SF6H9\.nsec3\..*V5DLFB6UJNHR94LQ61FO607KGK12H88A' dig.out.ns4.test$n > /dev/null || ret=1 +grep -i 'flags:.* ad[ ;]' dig.out.ns4.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that NSEC3 wildcard non-existance proof is returned private, validating ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.private.nsec3 @10.53.0.3 > dig.out.ns3.test$n || ret=1 +grep -i 'UDBSP4R8OUOT6HSO39VD8B5LMOSHRD5N\.private\.nsec3\..*NSEC3.*ASDRUIB7GO00OR92S5OUGI404LT27RNU' dig.out.ns3.test$n > /dev/null || ret=1 +grep -i 'flags:.* ad[ ;]' dig.out.ns3.test$n > /dev/null && ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I: checking that returned NSEC3 wildcard non-existance proof for private zone validates ($n)" +ret=0 +$DIG $DIGOPTS a b.wild.private.nsec3 @10.53.0.4 > dig.out.ns4.test$n || ret=1 +grep -i 'UDBSP4R8OUOT6HSO39VD8B5LMOSHRD5N\.private\.nsec3\..*NSEC3.*ASDRUIB7GO00OR92S5OUGI404LT27RNU' dig.out.ns4.test$n > /dev/null || ret=1 +grep -i 'flags:.* ad[ ;]' dig.out.ns4.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:exit status: $status" +exit $status diff --git a/lib/dns/include/dns/nsec.h b/lib/dns/include/dns/nsec.h index bdcd2eeccf..62c1c94c83 100644 --- a/lib/dns/include/dns/nsec.h +++ b/lib/dns/include/dns/nsec.h @@ -98,6 +98,19 @@ dns_nsec_isset(const unsigned char *array, unsigned int type); * Test if the corresponding 'type' bit is set in 'array'. */ +isc_result_t +dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name, + dns_name_t *nsecname, dns_rdataset_t *nsecset, + isc_boolean_t *exists, isc_boolean_t *data, + dns_name_t *wild, dns_nseclog_t log, void *arg); +/*% + * Return ISC_R_SUCCESS if we can determine that the name doesn't exist + * or we can determine whether there is data or not at the name. + * If the name does not exist return the wildcard name. + * + * Return ISC_R_IGNORE when the NSEC is not the appropriate one. + */ + ISC_LANG_ENDDECLS #endif /* DNS_NSEC_H */ diff --git a/lib/dns/include/dns/nsec3.h b/lib/dns/include/dns/nsec3.h index 1b89cceb53..8cf14dc1e6 100644 --- a/lib/dns/include/dns/nsec3.h +++ b/lib/dns/include/dns/nsec3.h @@ -248,6 +248,14 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, * Mark NSEC3PARAM for deletion. */ +isc_result_t +dns_nsec3_noexistnodata(dns_rdatatype_t type, dns_name_t* name, + dns_name_t *nsec3name, dns_rdataset_t *nsec3set, + dns_name_t *zonename, isc_boolean_t *exists, + isc_boolean_t *data, isc_boolean_t *optout, + isc_boolean_t *unknown, isc_boolean_t *setclosest, + isc_boolean_t *setnearest, dns_name_t *closest, + dns_name_t *nearest, dns_nseclog_t logit, void *arg); ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 0874faad96..2aa7459986 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -400,4 +400,7 @@ typedef isc_boolean_t typedef isc_result_t (*dns_deserializefunc_t)(void *, FILE *, off_t); +typedef void +(*dns_nseclog_t)(void *val, int , const char *, ...); + #endif /* DNS_TYPES_H */ diff --git a/lib/dns/nsec.c b/lib/dns/nsec.c index 69207d09de..5ae1cf8739 100644 --- a/lib/dns/nsec.c +++ b/lib/dns/nsec.c @@ -21,6 +21,7 @@ #include +#include #include #include @@ -290,3 +291,161 @@ dns_nsec_nseconly(dns_db_t *db, dns_dbversion_t *version, } return (result); } + +/*% + * Return ISC_R_SUCCESS if we can determine that the name doesn't exist + * or we can determine whether there is data or not at the name. + * If the name does not exist return the wildcard name. + * + * Return ISC_R_IGNORE when the NSEC is not the appropriate one. + */ +isc_result_t +dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name, + dns_name_t *nsecname, dns_rdataset_t *nsecset, + isc_boolean_t *exists, isc_boolean_t *data, + dns_name_t *wild, dns_nseclog_t logit, void *arg) +{ + int order; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_result_t result; + dns_namereln_t relation; + unsigned int olabels, nlabels, labels; + dns_rdata_nsec_t nsec; + isc_boolean_t atparent; + isc_boolean_t ns; + isc_boolean_t soa; + + REQUIRE(exists != NULL); + REQUIRE(data != NULL); + REQUIRE(nsecset != NULL && + nsecset->type == dns_rdatatype_nsec); + + result = dns_rdataset_first(nsecset); + if (result != ISC_R_SUCCESS) { + (*logit)(arg, ISC_LOG_DEBUG(3), "failure processing NSEC set"); + return (result); + } + dns_rdataset_current(nsecset, &rdata); + + (*logit)(arg, ISC_LOG_DEBUG(3), "looking for relevant nsec"); + relation = dns_name_fullcompare(name, nsecname, &order, &olabels); + + if (order < 0) { + /* + * The name is not within the NSEC range. + */ + (*logit)(arg, ISC_LOG_DEBUG(3), + "NSEC does not cover name, before NSEC"); + return (ISC_R_IGNORE); + } + + if (order == 0) { + /* + * The names are the same. If we are validating "." + * then atparent should not be set as there is no parent. + */ + atparent = (olabels != 1) && dns_rdatatype_atparent(type); + ns = dns_nsec_typepresent(&rdata, dns_rdatatype_ns); + soa = dns_nsec_typepresent(&rdata, dns_rdatatype_soa); + if (ns && !soa) { + if (!atparent) { + /* + * This NSEC record is from somewhere higher in + * the DNS, and at the parent of a delegation. + * It can not be legitimately used here. + */ + (*logit)(arg, ISC_LOG_DEBUG(3), + "ignoring parent nsec"); + return (ISC_R_IGNORE); + } + } else if (atparent && ns && soa) { + /* + * This NSEC record is from the child. + * It can not be legitimately used here. + */ + (*logit)(arg, ISC_LOG_DEBUG(3), + "ignoring child nsec"); + return (ISC_R_IGNORE); + } + if (type == dns_rdatatype_cname || type == dns_rdatatype_nxt || + type == dns_rdatatype_nsec || type == dns_rdatatype_key || + !dns_nsec_typepresent(&rdata, dns_rdatatype_cname)) { + *exists = ISC_TRUE; + *data = dns_nsec_typepresent(&rdata, type); + (*logit)(arg, ISC_LOG_DEBUG(3), + "nsec proves name exists (owner) data=%d", + *data); + return (ISC_R_SUCCESS); + } + (*logit)(arg, ISC_LOG_DEBUG(3), "NSEC proves CNAME exists"); + return (ISC_R_IGNORE); + } + + if (relation == dns_namereln_subdomain && + dns_nsec_typepresent(&rdata, dns_rdatatype_ns) && + !dns_nsec_typepresent(&rdata, dns_rdatatype_soa)) + { + /* + * This NSEC record is from somewhere higher in + * the DNS, and at the parent of a delegation. + * It can not be legitimately used here. + */ + (*logit)(arg, ISC_LOG_DEBUG(3), "ignoring parent nsec"); + return (ISC_R_IGNORE); + } + + result = dns_rdata_tostruct(&rdata, &nsec, NULL); + if (result != ISC_R_SUCCESS) + return (result); + relation = dns_name_fullcompare(&nsec.next, name, &order, &nlabels); + if (order == 0) { + dns_rdata_freestruct(&nsec); + (*logit)(arg, ISC_LOG_DEBUG(3), + "ignoring nsec matches next name"); + return (ISC_R_IGNORE); + } + + if (order < 0 && !dns_name_issubdomain(nsecname, &nsec.next)) { + /* + * The name is not within the NSEC range. + */ + dns_rdata_freestruct(&nsec); + (*logit)(arg, ISC_LOG_DEBUG(3), + "ignoring nsec because name is past end of range"); + return (ISC_R_IGNORE); + } + + if (order > 0 && relation == dns_namereln_subdomain) { + (*logit)(arg, ISC_LOG_DEBUG(3), + "nsec proves name exist (empty)"); + dns_rdata_freestruct(&nsec); + *exists = ISC_TRUE; + *data = ISC_FALSE; + return (ISC_R_SUCCESS); + } + if (wild != NULL) { + dns_name_t common; + dns_name_init(&common, NULL); + if (olabels > nlabels) { + labels = dns_name_countlabels(nsecname); + dns_name_getlabelsequence(nsecname, labels - olabels, + olabels, &common); + } else { + labels = dns_name_countlabels(&nsec.next); + dns_name_getlabelsequence(&nsec.next, labels - nlabels, + nlabels, &common); + } + result = dns_name_concatenate(dns_wildcardname, &common, + wild, NULL); + if (result != ISC_R_SUCCESS) { + dns_rdata_freestruct(&nsec); + (*logit)(arg, ISC_LOG_DEBUG(3), + "failure generating wildcard name"); + return (result); + } + } + dns_rdata_freestruct(&nsec); + (*logit)(arg, ISC_LOG_DEBUG(3), "nsec range ok"); + *exists = ISC_FALSE; + return (ISC_R_SUCCESS); +} diff --git a/lib/dns/nsec3.c b/lib/dns/nsec3.c index b63e11787f..935f515d23 100644 --- a/lib/dns/nsec3.c +++ b/lib/dns/nsec3.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -1802,3 +1803,285 @@ dns_nsec3_maxiterations(dns_db_t *db, dns_dbversion_t *version, dns_rdataset_disassociate(&rdataset); return (result); } + +isc_result_t +dns_nsec3_noexistnodata(dns_rdatatype_t type, dns_name_t* name, + dns_name_t *nsec3name, dns_rdataset_t *nsec3set, + dns_name_t *zonename, isc_boolean_t *exists, + isc_boolean_t *data, isc_boolean_t *optout, + isc_boolean_t *unknown, isc_boolean_t *setclosest, + isc_boolean_t *setnearest, dns_name_t *closest, + dns_name_t *nearest, dns_nseclog_t logit, void *arg) +{ + char namebuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fzone; + dns_fixedname_t qfixed; + dns_label_t hashlabel; + dns_name_t *qname; + dns_name_t *zone; + dns_rdata_nsec3_t nsec3; + dns_rdata_t rdata = DNS_RDATA_INIT; + int order; + int scope; + isc_boolean_t atparent; + isc_boolean_t first; + isc_boolean_t ns; + isc_boolean_t soa; + isc_buffer_t buffer; + isc_result_t answer = ISC_R_IGNORE; + isc_result_t result; + unsigned char hash[NSEC3_MAX_HASH_LENGTH]; + unsigned char owner[NSEC3_MAX_HASH_LENGTH]; + unsigned int length; + unsigned int qlabels; + unsigned int zlabels; + + REQUIRE((exists == NULL && data == NULL) || + (exists != NULL && data != NULL)); + REQUIRE(nsec3set != NULL && nsec3set->type == dns_rdatatype_nsec3); + REQUIRE((setclosest == NULL && closest == NULL) || + (setclosest != NULL && closest != NULL)); + REQUIRE((setnearest == NULL && nearest == NULL) || + (setnearest != NULL && nearest != NULL)); + + result = dns_rdataset_first(nsec3set); + if (result != ISC_R_SUCCESS) { + (*logit)(arg, ISC_LOG_DEBUG(3), "failure processing NSEC3 set"); + return (result); + } + + dns_rdataset_current(nsec3set, &rdata); + + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + if (result != ISC_R_SUCCESS) + return (result); + + (*logit)(arg, ISC_LOG_DEBUG(3), "looking for relevant NSEC3"); + + dns_fixedname_init(&fzone); + zone = dns_fixedname_name(&fzone); + zlabels = dns_name_countlabels(nsec3name); + + /* + * NSEC3 records must have two or more labels to be valid. + */ + if (zlabels < 2) + return (ISC_R_IGNORE); + + /* + * Strip off the NSEC3 hash to get the zone. + */ + zlabels--; + dns_name_split(nsec3name, zlabels, NULL, zone); + + /* + * If not below the zone name we can ignore this record. + */ + if (!dns_name_issubdomain(name, zone)) + return (ISC_R_IGNORE); + + /* + * Is this zone the same or deeper than the current zone? + */ + if (dns_name_countlabels(zonename) == 0 || + dns_name_issubdomain(zone, zonename)) + dns_name_copy(zone, zonename, NULL); + + if (!dns_name_equal(zone, zonename)) + return (ISC_R_IGNORE); + + /* + * Are we only looking for the most enclosing zone? + */ + if (exists == NULL || data == NULL) + return (ISC_R_SUCCESS); + + /* + * Only set unknown once we are sure that this NSEC3 is from + * the deepest covering zone. + */ + if (!dns_nsec3_supportedhash(nsec3.hash)) { + if (unknown != NULL) + *unknown = ISC_TRUE; + return (ISC_R_IGNORE); + } + + /* + * Recover the hash from the first label. + */ + dns_name_getlabel(nsec3name, 0, &hashlabel); + isc_region_consume(&hashlabel, 1); + isc_buffer_init(&buffer, owner, sizeof(owner)); + result = isc_base32hex_decoderegion(&hashlabel, &buffer); + if (result != ISC_R_SUCCESS) + return (result); + + /* + * The hash lengths should match. If not ignore the record. + */ + if (isc_buffer_usedlength(&buffer) != nsec3.next_length) + return (ISC_R_IGNORE); + + /* + * Work out what this NSEC3 covers. + * Inside (<0) or outside (>=0). + */ + scope = memcmp(owner, nsec3.next, nsec3.next_length); + + /* + * Prepare to compute all the hashes. + */ + dns_fixedname_init(&qfixed); + qname = dns_fixedname_name(&qfixed); + dns_name_downcase(name, qname, NULL); + qlabels = dns_name_countlabels(qname); + first = ISC_TRUE; + + while (qlabels >= zlabels) { + length = isc_iterated_hash(hash, nsec3.hash, nsec3.iterations, + nsec3.salt, nsec3.salt_length, + qname->ndata, qname->length); + /* + * The computed hash length should match. + */ + if (length != nsec3.next_length) { + (*logit)(arg, ISC_LOG_DEBUG(3), + "ignoring NSEC bad length %u vs %u", + length, nsec3.next_length); + return (ISC_R_IGNORE); + } + + order = memcmp(hash, owner, length); + if (first && order == 0) { + /* + * The hashes are the same. + */ + atparent = dns_rdatatype_atparent(type); + ns = dns_nsec3_typepresent(&rdata, dns_rdatatype_ns); + soa = dns_nsec3_typepresent(&rdata, dns_rdatatype_soa); + if (ns && !soa) { + if (!atparent) { + /* + * This NSEC3 record is from somewhere + * higher in the DNS, and at the + * parent of a delegation. It can not + * be legitimately used here. + */ + (*logit)(arg, ISC_LOG_DEBUG(3), + "ignoring parent NSEC3"); + return (ISC_R_IGNORE); + } + } else if (atparent && ns && soa) { + /* + * This NSEC3 record is from the child. + * It can not be legitimately used here. + */ + (*logit)(arg, ISC_LOG_DEBUG(3), + "ignoring child NSEC3"); + return (ISC_R_IGNORE); + } + if (type == dns_rdatatype_cname || + type == dns_rdatatype_nxt || + type == dns_rdatatype_nsec || + type == dns_rdatatype_key || + !dns_nsec3_typepresent(&rdata, dns_rdatatype_cname)) { + *exists = ISC_TRUE; + *data = dns_nsec3_typepresent(&rdata, type); + (*logit)(arg, ISC_LOG_DEBUG(3), + "NSEC3 proves name exists (owner) " + "data=%d", *data); + return (ISC_R_SUCCESS); + } + (*logit)(arg, ISC_LOG_DEBUG(3), + "NSEC3 proves CNAME exists"); + return (ISC_R_IGNORE); + } + + if (order == 0 && + dns_nsec3_typepresent(&rdata, dns_rdatatype_ns) && + !dns_nsec3_typepresent(&rdata, dns_rdatatype_soa)) + { + /* + * This NSEC3 record is from somewhere higher in + * the DNS, and at the parent of a delegation. + * It can not be legitimately used here. + */ + (*logit)(arg, ISC_LOG_DEBUG(3), + "ignoring parent NSEC3"); + return (ISC_R_IGNORE); + } + + /* + * Potential closest encloser. + */ + if (order == 0) { + if (closest != NULL && + (dns_name_countlabels(closest) == 0 || + dns_name_issubdomain(qname, closest)) && + !dns_nsec3_typepresent(&rdata, dns_rdatatype_ds) && + !dns_nsec3_typepresent(&rdata, dns_rdatatype_dname) && + (dns_nsec3_typepresent(&rdata, dns_rdatatype_soa) || + !dns_nsec3_typepresent(&rdata, dns_rdatatype_ns))) + { + + dns_name_format(qname, namebuf, + sizeof(namebuf)); + (*logit)(arg, ISC_LOG_DEBUG(3), + "NSEC3 indicates potential closest " + "encloser: '%s'", namebuf); + dns_name_copy(qname, closest, NULL); + *setclosest = ISC_TRUE; + } + dns_name_format(qname, namebuf, sizeof(namebuf)); + (*logit)(arg, ISC_LOG_DEBUG(3), + "NSEC3 at super-domain %s", namebuf); + return (answer); + } + + /* + * Find if the name does not exist. + * + * We continue as we need to find the name closest to the + * closest encloser that doesn't exist. + * + * We also need to continue to ensure that we are not + * proving the non-existence of a record in a sub-zone. + * If that would be the case we will return ISC_R_IGNORE + * above. + */ + if ((scope < 0 && order > 0 && + memcmp(hash, nsec3.next, length) < 0) || + (scope >= 0 && (order > 0 || + memcmp(hash, nsec3.next, length) < 0))) + { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(qname, namebuf, sizeof(namebuf)); + (*logit)(arg, ISC_LOG_DEBUG(3), "NSEC3 proves " + "name does not exist: '%s'", namebuf); + if (nearest != NULL && + (dns_name_countlabels(nearest) == 0 || + dns_name_issubdomain(nearest, qname))) { + dns_name_copy(qname, nearest, NULL); + *setnearest = ISC_TRUE; + } + + *exists = ISC_FALSE; + *data = ISC_FALSE; + if (optout != NULL) { + if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0) + (*logit)(arg, ISC_LOG_DEBUG(3), + "NSEC3 indicates optout"); + *optout = + ISC_TF(nsec3.flags & DNS_NSEC3FLAG_OPTOUT); + } + answer = ISC_R_SUCCESS; + } + + qlabels--; + if (qlabels > 0) + dns_name_split(qname, qlabels, NULL, qname); + first = ISC_FALSE; + } + return (answer); +} diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 4ffbec35bd..5c76ef684a 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -43,6 +44,8 @@ #include #include #include +#include +#include #include #include #include @@ -76,7 +79,7 @@ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ - "fctx %p(%s'): %s", fctx, fctx->info, (m)) + "fctx %p(%s): %s", fctx, fctx->info, (m)) #define FCTXTRACE2(m1, m2) \ isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ @@ -475,6 +478,9 @@ static void validated(isc_task_t *task, isc_event_t *event); static isc_boolean_t maybe_destroy(fetchctx_t *fctx, isc_boolean_t locked); static void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason, badnstype_t badtype); +static inline isc_result_t findnoqname(fetchctx_t *fctx, dns_name_t *name, + dns_rdatatype_t type, + dns_name_t **noqname); /*% * Increment resolver-related statistics counters. @@ -4252,7 +4258,6 @@ validated(isc_task_t *task, isc_event_t *event) { FCTXTRACE("validation OK"); if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) { - result = dns_rdataset_addnoqname(vevent->rdataset, vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]); RUNTIME_CHECK(result == ISC_R_SUCCESS); @@ -4263,6 +4268,18 @@ validated(isc_task_t *task, isc_event_t *event) { vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER]); RUNTIME_CHECK(result == ISC_R_SUCCESS); } + } else if (vevent->rdataset->trust == dns_trust_answer && + vevent->rdataset->type != dns_rdatatype_rrsig) + { + isc_result_t tresult; + dns_name_t *noqname = NULL; + tresult = findnoqname(fctx, vevent->name, + vevent->rdataset->type, &noqname); + if (tresult == ISC_R_SUCCESS && noqname != NULL) { + tresult = dns_rdataset_addnoqname(vevent->rdataset, + noqname); + RUNTIME_CHECK(tresult == ISC_R_SUCCESS); + } } /* @@ -4403,6 +4420,133 @@ validated(isc_task_t *task, isc_event_t *event) { isc_event_free(&event); } +static void +log(void *arg, int level, const char *fmt, ...) { + char msgbuf[2048]; + va_list args; + fetchctx_t *fctx = arg; + + va_start(args, fmt); + vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); + va_end(args); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, level, + "fctx %p(%s): %s", fctx, fctx->info, msgbuf); +} + +static inline isc_result_t +findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, + dns_name_t **noqname) +{ + dns_rdataset_t *nrdataset, *next, *sigrdataset; + dns_rdata_rrsig_t rrsig; + isc_result_t result; + unsigned int labels; + dns_section_t section; + dns_name_t *zonename; + dns_fixedname_t fzonename; + dns_name_t *closest; + dns_fixedname_t fclosest; + dns_name_t *nearest; + dns_fixedname_t fnearest; + + FCTXTRACE("findnoqname"); + + REQUIRE(noqname != NULL && *noqname == NULL); + + /* + * Find the SIG for this rdataset, if we have it. + */ + for (sigrdataset = ISC_LIST_HEAD(name->list); + sigrdataset != NULL; + sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { + if (sigrdataset->type == dns_rdatatype_rrsig && + sigrdataset->covers == type) + break; + } + + if (sigrdataset == NULL) + return (ISC_R_NOTFOUND); + + labels = dns_name_countlabels(name); + + for (result = dns_rdataset_first(sigrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(sigrdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(sigrdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &rrsig, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + /* Wildcard has rrsig.labels < labels - 1. */ + if (rrsig.labels + 1 >= labels) + continue; + break; + } + + if (result == ISC_R_NOMORE) + return (ISC_R_NOTFOUND); + if (result != ISC_R_SUCCESS) + return (result); + + dns_fixedname_init(&fzonename); + zonename = dns_fixedname_name(&fzonename); + dns_fixedname_init(&fclosest); + closest = dns_fixedname_name(&fclosest); + dns_fixedname_init(&fnearest); + nearest = dns_fixedname_name(&fnearest); + +#define NXND(x) ((x) == ISC_R_SUCCESS) + + section = DNS_SECTION_AUTHORITY; + for (result = dns_message_firstname(fctx->rmessage, section); + result == ISC_R_SUCCESS; + result = dns_message_nextname(fctx->rmessage, section)) { + dns_name_t *nsec = NULL; + dns_message_currentname(fctx->rmessage, section, &nsec); + for (nrdataset = ISC_LIST_HEAD(nsec->list); + nrdataset != NULL; nrdataset = next) { + isc_boolean_t data = ISC_FALSE, exists = ISC_FALSE; + isc_boolean_t optout = ISC_FALSE, unknown = ISC_FALSE; + isc_boolean_t setclosest = ISC_FALSE; + isc_boolean_t setnearest = ISC_FALSE; + char namebuf[DNS_NAME_FORMATSIZE]; + + next = ISC_LIST_NEXT(nrdataset, link); + if (nrdataset->type != dns_rdatatype_nsec && + nrdataset->type != dns_rdatatype_nsec3) + continue; + dns_name_format(nsec, namebuf, sizeof(namebuf)); + if (nrdataset->type == dns_rdatatype_nsec && + NXND(dns_nsec_noexistnodata(type, name, nsec, + nrdataset, &exists, + &data, NULL, log, + fctx))) + { + if (!exists) + *noqname = nsec; + } + + if (nrdataset->type == dns_rdatatype_nsec3 && + NXND(dns_nsec3_noexistnodata(type, name, nsec, + nrdataset, zonename, + &exists, &data, + &optout, &unknown, + &setclosest, + &setnearest, + closest, nearest, + log, fctx))) + { + if (!exists && setnearest) + *noqname = nsec; + } + } + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + return (result); +} + static inline isc_result_t cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now) @@ -4535,6 +4679,17 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, if (rdataset->ttl > res->view->maxcachettl) rdataset->ttl = res->view->maxcachettl; + /* + * Find the SIG for this rdataset, if we have it. + */ + for (sigrdataset = ISC_LIST_HEAD(name->list); + sigrdataset != NULL; + sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { + if (sigrdataset->type == dns_rdatatype_rrsig && + sigrdataset->covers == rdataset->type) + break; + } + /* * If this RRset is in a secure domain, is in bailiwick, * and is not glue, attempt DNSSEC validation. (We do not @@ -4555,16 +4710,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, */ if (rdataset->type == dns_rdatatype_rrsig) continue; - /* - * Find the SIG for this rdataset, if we have it. - */ - for (sigrdataset = ISC_LIST_HEAD(name->list); - sigrdataset != NULL; - sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { - if (sigrdataset->type == dns_rdatatype_rrsig && - sigrdataset->covers == rdataset->type) - break; - } + if (sigrdataset == NULL) { if (!ANSWER(rdataset) && need_validation) { /* @@ -4725,6 +4871,21 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, options = DNS_DBADD_FORCE; } else options = 0; + + if (ANSWER(rdataset) && + rdataset->type != dns_rdatatype_rrsig) { + isc_result_t tresult; + dns_name_t *noqname = NULL; + tresult = findnoqname(fctx, name, + rdataset->type, &noqname); + if (tresult == ISC_R_SUCCESS && + noqname != NULL) { + tresult = dns_rdataset_addnoqname( + rdataset, noqname); + RUNTIME_CHECK(tresult == ISC_R_SUCCESS); + } + } + /* * Now we can add the rdataset. */ @@ -4733,6 +4894,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, rdataset, options, addedrdataset); + if (result == DNS_R_UNCHANGED) { if (ANSWER(rdataset) && ardataset != NULL && diff --git a/lib/dns/validator.c b/lib/dns/validator.c index f85125a199..9dcb9ad179 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -157,7 +157,7 @@ validator_logv(dns_validator_t *val, isc_logcategory_t *category, ISC_FORMAT_PRINTF(5, 0); static void -validator_log(dns_validator_t *val, int level, const char *fmt, ...) +validator_log(void *val, int level, const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4); static void @@ -843,452 +843,6 @@ cnamevalidated(isc_task_t *task, isc_event_t *event) { destroy(val); } -/*% - * Return ISC_R_SUCCESS if we can determine that the name doesn't exist - * or we can determine whether there is data or not at the name. - * If the name does not exist return the wildcard name. - * - * Return ISC_R_IGNORE when the NSEC is not the appropriate one. - */ -static isc_result_t -nsecnoexistnodata(dns_validator_t *val, dns_name_t *name, dns_name_t *nsecname, - dns_rdataset_t *nsecset, isc_boolean_t *exists, - isc_boolean_t *data, dns_name_t *wild) -{ - int order; - dns_rdata_t rdata = DNS_RDATA_INIT; - isc_result_t result; - dns_namereln_t relation; - unsigned int olabels, nlabels, labels; - dns_rdata_nsec_t nsec; - isc_boolean_t atparent; - isc_boolean_t ns; - isc_boolean_t soa; - - REQUIRE(exists != NULL); - REQUIRE(data != NULL); - REQUIRE(nsecset != NULL && - nsecset->type == dns_rdatatype_nsec); - - result = dns_rdataset_first(nsecset); - if (result != ISC_R_SUCCESS) { - validator_log(val, ISC_LOG_DEBUG(3), - "failure processing NSEC set"); - return (result); - } - dns_rdataset_current(nsecset, &rdata); - - validator_log(val, ISC_LOG_DEBUG(3), "looking for relevant nsec"); - relation = dns_name_fullcompare(name, nsecname, &order, &olabels); - - if (order < 0) { - /* - * The name is not within the NSEC range. - */ - validator_log(val, ISC_LOG_DEBUG(3), - "NSEC does not cover name, before NSEC"); - return (ISC_R_IGNORE); - } - - if (order == 0) { - /* - * The names are the same. If we are validating "." - * then atparent should not be set as there is no parent. - */ - atparent = (olabels != 1) && - dns_rdatatype_atparent(val->event->type); - ns = dns_nsec_typepresent(&rdata, dns_rdatatype_ns); - soa = dns_nsec_typepresent(&rdata, dns_rdatatype_soa); - if (ns && !soa) { - if (!atparent) { - /* - * This NSEC record is from somewhere higher in - * the DNS, and at the parent of a delegation. - * It can not be legitimately used here. - */ - validator_log(val, ISC_LOG_DEBUG(3), - "ignoring parent nsec"); - return (ISC_R_IGNORE); - } - } else if (atparent && ns && soa) { - /* - * This NSEC record is from the child. - * It can not be legitimately used here. - */ - validator_log(val, ISC_LOG_DEBUG(3), - "ignoring child nsec"); - return (ISC_R_IGNORE); - } - if (val->event->type == dns_rdatatype_cname || - val->event->type == dns_rdatatype_nxt || - val->event->type == dns_rdatatype_nsec || - val->event->type == dns_rdatatype_key || - !dns_nsec_typepresent(&rdata, dns_rdatatype_cname)) { - *exists = ISC_TRUE; - *data = dns_nsec_typepresent(&rdata, val->event->type); - validator_log(val, ISC_LOG_DEBUG(3), - "nsec proves name exists (owner) data=%d", - *data); - return (ISC_R_SUCCESS); - } - validator_log(val, ISC_LOG_DEBUG(3), "NSEC proves CNAME exists"); - return (ISC_R_IGNORE); - } - - if (relation == dns_namereln_subdomain && - dns_nsec_typepresent(&rdata, dns_rdatatype_ns) && - !dns_nsec_typepresent(&rdata, dns_rdatatype_soa)) - { - /* - * This NSEC record is from somewhere higher in - * the DNS, and at the parent of a delegation. - * It can not be legitimately used here. - */ - validator_log(val, ISC_LOG_DEBUG(3), "ignoring parent nsec"); - return (ISC_R_IGNORE); - } - - result = dns_rdata_tostruct(&rdata, &nsec, NULL); - if (result != ISC_R_SUCCESS) - return (result); - relation = dns_name_fullcompare(&nsec.next, name, &order, &nlabels); - if (order == 0) { - dns_rdata_freestruct(&nsec); - validator_log(val, ISC_LOG_DEBUG(3), - "ignoring nsec matches next name"); - return (ISC_R_IGNORE); - } - - if (order < 0 && !dns_name_issubdomain(nsecname, &nsec.next)) { - /* - * The name is not within the NSEC range. - */ - dns_rdata_freestruct(&nsec); - validator_log(val, ISC_LOG_DEBUG(3), - "ignoring nsec because name is past end of range"); - return (ISC_R_IGNORE); - } - - if (order > 0 && relation == dns_namereln_subdomain) { - validator_log(val, ISC_LOG_DEBUG(3), - "nsec proves name exist (empty)"); - dns_rdata_freestruct(&nsec); - *exists = ISC_TRUE; - *data = ISC_FALSE; - return (ISC_R_SUCCESS); - } - if (wild != NULL) { - dns_name_t common; - dns_name_init(&common, NULL); - if (olabels > nlabels) { - labels = dns_name_countlabels(nsecname); - dns_name_getlabelsequence(nsecname, labels - olabels, - olabels, &common); - } else { - labels = dns_name_countlabels(&nsec.next); - dns_name_getlabelsequence(&nsec.next, labels - nlabels, - nlabels, &common); - } - result = dns_name_concatenate(dns_wildcardname, &common, - wild, NULL); - if (result != ISC_R_SUCCESS) { - dns_rdata_freestruct(&nsec); - validator_log(val, ISC_LOG_DEBUG(3), - "failure generating wildcard name"); - return (result); - } - } - dns_rdata_freestruct(&nsec); - validator_log(val, ISC_LOG_DEBUG(3), "nsec range ok"); - *exists = ISC_FALSE; - return (ISC_R_SUCCESS); -} - -static isc_result_t -nsec3noexistnodata(dns_validator_t *val, dns_name_t* name, - dns_name_t *nsec3name, dns_rdataset_t *nsec3set, - dns_name_t *zonename, isc_boolean_t *exists, - isc_boolean_t *data, isc_boolean_t *optout, - isc_boolean_t *unknown, isc_boolean_t *setclosest, - isc_boolean_t *setnearest, dns_name_t *closest, - dns_name_t *nearest) -{ - char namebuf[DNS_NAME_FORMATSIZE]; - dns_fixedname_t fzone; - dns_fixedname_t qfixed; - dns_label_t hashlabel; - dns_name_t *qname; - dns_name_t *zone; - dns_rdata_nsec3_t nsec3; - dns_rdata_t rdata = DNS_RDATA_INIT; - int order; - int scope; - isc_boolean_t atparent; - isc_boolean_t first; - isc_boolean_t ns; - isc_boolean_t soa; - isc_buffer_t buffer; - isc_result_t answer = ISC_R_IGNORE; - isc_result_t result; - unsigned char hash[NSEC3_MAX_HASH_LENGTH]; - unsigned char owner[NSEC3_MAX_HASH_LENGTH]; - unsigned int length; - unsigned int qlabels; - unsigned int zlabels; - - REQUIRE((exists == NULL && data == NULL) || - (exists != NULL && data != NULL)); - REQUIRE(nsec3set != NULL && nsec3set->type == dns_rdatatype_nsec3); - REQUIRE((setclosest == NULL && closest == NULL) || - (setclosest != NULL && closest != NULL)); - REQUIRE((setnearest == NULL && nearest == NULL) || - (setnearest != NULL && nearest != NULL)); - - result = dns_rdataset_first(nsec3set); - if (result != ISC_R_SUCCESS) { - validator_log(val, ISC_LOG_DEBUG(3), - "failure processing NSEC3 set"); - return (result); - } - - dns_rdataset_current(nsec3set, &rdata); - - result = dns_rdata_tostruct(&rdata, &nsec3, NULL); - if (result != ISC_R_SUCCESS) - return (result); - - validator_log(val, ISC_LOG_DEBUG(3), "looking for relevant NSEC3"); - - dns_fixedname_init(&fzone); - zone = dns_fixedname_name(&fzone); - zlabels = dns_name_countlabels(nsec3name); - - /* - * NSEC3 records must have two or more labels to be valid. - */ - if (zlabels < 2) - return (ISC_R_IGNORE); - - /* - * Strip off the NSEC3 hash to get the zone. - */ - zlabels--; - dns_name_split(nsec3name, zlabels, NULL, zone); - - /* - * If not below the zone name we can ignore this record. - */ - if (!dns_name_issubdomain(name, zone)) - return (ISC_R_IGNORE); - - /* - * Is this zone the same or deeper than the current zone? - */ - if (dns_name_countlabels(zonename) == 0 || - dns_name_issubdomain(zone, zonename)) - dns_name_copy(zone, zonename, NULL); - - if (!dns_name_equal(zone, zonename)) - return (ISC_R_IGNORE); - - /* - * Are we only looking for the most enclosing zone? - */ - if (exists == NULL || data == NULL) - return (ISC_R_SUCCESS); - - /* - * Only set unknown once we are sure that this NSEC3 is from - * the deepest covering zone. - */ - if (!dns_nsec3_supportedhash(nsec3.hash)) { - if (unknown != NULL) - *unknown = ISC_TRUE; - return (ISC_R_IGNORE); - } - - /* - * Recover the hash from the first label. - */ - dns_name_getlabel(nsec3name, 0, &hashlabel); - isc_region_consume(&hashlabel, 1); - isc_buffer_init(&buffer, owner, sizeof(owner)); - result = isc_base32hex_decoderegion(&hashlabel, &buffer); - if (result != ISC_R_SUCCESS) - return (result); - - /* - * The hash lengths should match. If not ignore the record. - */ - if (isc_buffer_usedlength(&buffer) != nsec3.next_length) - return (ISC_R_IGNORE); - - /* - * Work out what this NSEC3 covers. - * Inside (<0) or outside (>=0). - */ - scope = memcmp(owner, nsec3.next, nsec3.next_length); - - /* - * Prepare to compute all the hashes. - */ - dns_fixedname_init(&qfixed); - qname = dns_fixedname_name(&qfixed); - dns_name_downcase(name, qname, NULL); - qlabels = dns_name_countlabels(qname); - first = ISC_TRUE; - - while (qlabels >= zlabels) { - length = isc_iterated_hash(hash, nsec3.hash, nsec3.iterations, - nsec3.salt, nsec3.salt_length, - qname->ndata, qname->length); - /* - * The computed hash length should match. - */ - if (length != nsec3.next_length) { - validator_log(val, ISC_LOG_DEBUG(3), - "ignoring NSEC bad length %u vs %u", - length, nsec3.next_length); - return (ISC_R_IGNORE); - } - - order = memcmp(hash, owner, length); - if (first && order == 0) { - /* - * The hashes are the same. - */ - atparent = dns_rdatatype_atparent(val->event->type); - ns = dns_nsec3_typepresent(&rdata, dns_rdatatype_ns); - soa = dns_nsec3_typepresent(&rdata, dns_rdatatype_soa); - if (ns && !soa) { - if (!atparent) { - /* - * This NSEC3 record is from somewhere - * higher in the DNS, and at the - * parent of a delegation. It can not - * be legitimately used here. - */ - validator_log(val, ISC_LOG_DEBUG(3), - "ignoring parent NSEC3"); - return (ISC_R_IGNORE); - } - } else if (atparent && ns && soa) { - /* - * This NSEC3 record is from the child. - * It can not be legitimately used here. - */ - validator_log(val, ISC_LOG_DEBUG(3), - "ignoring child NSEC3"); - return (ISC_R_IGNORE); - } - if (val->event->type == dns_rdatatype_cname || - val->event->type == dns_rdatatype_nxt || - val->event->type == dns_rdatatype_nsec || - val->event->type == dns_rdatatype_key || - !dns_nsec3_typepresent(&rdata, dns_rdatatype_cname)) { - *exists = ISC_TRUE; - *data = dns_nsec3_typepresent(&rdata, - val->event->type); - validator_log(val, ISC_LOG_DEBUG(3), - "NSEC3 proves name exists (owner) " - "data=%d", *data); - return (ISC_R_SUCCESS); - } - validator_log(val, ISC_LOG_DEBUG(3), - "NSEC3 proves CNAME exists"); - return (ISC_R_IGNORE); - } - - if (order == 0 && - dns_nsec3_typepresent(&rdata, dns_rdatatype_ns) && - !dns_nsec3_typepresent(&rdata, dns_rdatatype_soa)) - { - /* - * This NSEC3 record is from somewhere higher in - * the DNS, and at the parent of a delegation. - * It can not be legitimately used here. - */ - validator_log(val, ISC_LOG_DEBUG(3), - "ignoring parent NSEC3"); - return (ISC_R_IGNORE); - } - - /* - * Potential closest encloser. - */ - if (order == 0) { - if (closest != NULL && - (dns_name_countlabels(closest) == 0 || - dns_name_issubdomain(qname, closest)) && - !dns_nsec3_typepresent(&rdata, dns_rdatatype_ds) && - !dns_nsec3_typepresent(&rdata, dns_rdatatype_dname) && - (dns_nsec3_typepresent(&rdata, dns_rdatatype_soa) || - !dns_nsec3_typepresent(&rdata, dns_rdatatype_ns))) - { - - dns_name_format(qname, namebuf, - sizeof(namebuf)); - validator_log(val, ISC_LOG_DEBUG(3), - "NSEC3 indicates potential " - "closest encloser: '%s'", - namebuf); - dns_name_copy(qname, closest, NULL); - *setclosest = ISC_TRUE; - } - dns_name_format(qname, namebuf, sizeof(namebuf)); - validator_log(val, ISC_LOG_DEBUG(3), - "NSEC3 at super-domain %s", namebuf); - return (answer); - } - - /* - * Find if the name does not exist. - * - * We continue as we need to find the name closest to the - * closest encloser that doesn't exist. - * - * We also need to continue to ensure that we are not - * proving the non-existence of a record in a sub-zone. - * If that would be the case we will return ISC_R_IGNORE - * above. - */ - if ((scope < 0 && order > 0 && - memcmp(hash, nsec3.next, length) < 0) || - (scope >= 0 && (order > 0 || - memcmp(hash, nsec3.next, length) < 0))) - { - char namebuf[DNS_NAME_FORMATSIZE]; - - dns_name_format(qname, namebuf, sizeof(namebuf)); - validator_log(val, ISC_LOG_DEBUG(3), "NSEC3 proves " - "name does not exist: '%s'", namebuf); - if (nearest != NULL && - (dns_name_countlabels(nearest) == 0 || - dns_name_issubdomain(nearest, qname))) { - dns_name_copy(qname, nearest, NULL); - *setnearest = ISC_TRUE; - } - - *exists = ISC_FALSE; - *data = ISC_FALSE; - if (optout != NULL) { - if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0) - validator_log(val, ISC_LOG_DEBUG(3), - "NSEC3 indicates optout"); - *optout = - ISC_TF(nsec3.flags & DNS_NSEC3FLAG_OPTOUT); - } - answer = ISC_R_SUCCESS; - } - - qlabels--; - if (qlabels > 0) - dns_name_split(qname, qlabels, NULL, qname); - first = ISC_FALSE; - } - return (answer); -} - /*% * Callback for when NSEC records have been validated. * @@ -1344,8 +898,9 @@ authvalidated(isc_task_t *task, isc_event_t *event) { rdataset->trust == dns_trust_secure && (NEEDNODATA(val) || NEEDNOQNAME(val)) && !FOUNDNODATA(val) && !FOUNDNOQNAME(val) && - nsecnoexistnodata(val, val->event->name, devent->name, - rdataset, &exists, &data, wild) + dns_nsec_noexistnodata(val->event->type, val->event->name, + devent->name, rdataset, &exists, + &data, wild, validator_log, val) == ISC_R_SUCCESS) { if (exists && !data) { @@ -2845,8 +2400,9 @@ checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename) if (rdataset->type == dns_rdatatype_nsec && (NEEDNODATA(val) || NEEDNOWILDCARD(val)) && !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) && - nsecnoexistnodata(val, wild, name, rdataset, - &exists, &data, NULL) + dns_nsec_noexistnodata(val->event->type, wild, name, + rdataset, &exists, &data, NULL, + validator_log, val) == ISC_R_SUCCESS) { dns_name_t **proofs = val->event->proofs; @@ -2869,10 +2425,11 @@ checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename) if (rdataset->type == dns_rdatatype_nsec3 && (NEEDNODATA(val) || NEEDNOWILDCARD(val)) && !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) && - nsec3noexistnodata(val, wild, name, rdataset, - zonename, &exists, &data, - NULL, NULL, NULL, NULL, NULL, - NULL) == ISC_R_SUCCESS) + dns_nsec3_noexistnodata(val->event->type, wild, name, + rdataset, zonename, &exists, &data, + NULL, NULL, NULL, NULL, NULL, NULL, + validator_log, val) + == ISC_R_SUCCESS) { dns_name_t **proofs = val->event->proofs; if (exists && !data) @@ -2934,11 +2491,12 @@ findnsec3proofs(dns_validator_t *val) { rdataset->trust != dns_trust_secure) continue; - result = nsec3noexistnodata(val, val->event->name, - name, rdataset, - zonename, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL); + result = dns_nsec3_noexistnodata(val->event->type, + val->event->name, name, + rdataset, zonename, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, validator_log, + val); if (result != ISC_R_IGNORE && result != ISC_R_SUCCESS) { if (dns_rdataset_isassociated(&trdataset)) dns_rdataset_disassociate(&trdataset); @@ -2986,11 +2544,13 @@ findnsec3proofs(dns_validator_t *val) { setclosest = setnearest = ISC_FALSE; optout = ISC_FALSE; unknown = ISC_FALSE; - result = nsec3noexistnodata(val, val->event->name, name, - rdataset, zonename, &exists, - &data, &optout, &unknown, - setclosestp, &setnearest, - closestp, nearest); + result = dns_nsec3_noexistnodata(val->event->type, + val->event->name, + name, rdataset, zonename, + &exists, &data, &optout, + &unknown, setclosestp, + &setnearest, closestp, + nearest, validator_log, val); if (unknown) val->attributes |= VALATTR_FOUNDUNKNOWN; if (result != ISC_R_SUCCESS) @@ -4366,7 +3926,7 @@ validator_logv(dns_validator_t *val, isc_logcategory_t *category, } static void -validator_log(dns_validator_t *val, int level, const char *fmt, ...) { +validator_log(void *val, int level, const char *fmt, ...) { va_list ap; if (! isc_log_wouldlog(dns_lctx, level)) From b16174507d1429ae4dbf7b4939f9e45ca7b76d74 Mon Sep 17 00:00:00 2001 From: Tinderbox User Date: Tue, 18 Dec 2012 23:45:50 +0000 Subject: [PATCH 2/2] update copyright notice --- lib/dns/include/dns/nsec.h | 8 ++++---- lib/dns/resolver.c | 14 +++++++------- lib/dns/validator.c | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/dns/include/dns/nsec.h b/lib/dns/include/dns/nsec.h index 62c1c94c83..440ee4e015 100644 --- a/lib/dns/include/dns/nsec.h +++ b/lib/dns/include/dns/nsec.h @@ -99,10 +99,10 @@ dns_nsec_isset(const unsigned char *array, unsigned int type); */ isc_result_t -dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name, - dns_name_t *nsecname, dns_rdataset_t *nsecset, - isc_boolean_t *exists, isc_boolean_t *data, - dns_name_t *wild, dns_nseclog_t log, void *arg); +dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name, + dns_name_t *nsecname, dns_rdataset_t *nsecset, + isc_boolean_t *exists, isc_boolean_t *data, + dns_name_t *wild, dns_nseclog_t log, void *arg); /*% * Return ISC_R_SUCCESS if we can determine that the name doesn't exist * or we can determine whether there is data or not at the name. diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 5c76ef684a..42d15bbecf 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -4519,23 +4519,23 @@ findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, dns_name_format(nsec, namebuf, sizeof(namebuf)); if (nrdataset->type == dns_rdatatype_nsec && NXND(dns_nsec_noexistnodata(type, name, nsec, - nrdataset, &exists, + nrdataset, &exists, &data, NULL, log, - fctx))) + fctx))) { if (!exists) *noqname = nsec; } if (nrdataset->type == dns_rdatatype_nsec3 && - NXND(dns_nsec3_noexistnodata(type, name, nsec, - nrdataset, zonename, - &exists, &data, + NXND(dns_nsec3_noexistnodata(type, name, nsec, + nrdataset, zonename, + &exists, &data, &optout, &unknown, &setclosest, &setnearest, closest, nearest, - log, fctx))) + log, fctx))) { if (!exists && setnearest) *noqname = nsec; @@ -4877,7 +4877,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, isc_result_t tresult; dns_name_t *noqname = NULL; tresult = findnoqname(fctx, name, - rdataset->type, &noqname); + rdataset->type, &noqname); if (tresult == ISC_R_SUCCESS && noqname != NULL) { tresult = dns_rdataset_addnoqname( diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 9dcb9ad179..319fc2bab9 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -2546,7 +2546,7 @@ findnsec3proofs(dns_validator_t *val) { unknown = ISC_FALSE; result = dns_nsec3_noexistnodata(val->event->type, val->event->name, - name, rdataset, zonename, + name, rdataset, zonename, &exists, &data, &optout, &unknown, setclosestp, &setnearest, closestp,