]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] rndc nta
authorEvan Hunt <each@isc.org>
Fri, 30 May 2014 05:22:53 +0000 (22:22 -0700)
committerEvan Hunt <each@isc.org>
Fri, 30 May 2014 05:22:53 +0000 (22:22 -0700)
3867. [func] "rndc nta" can now be used to set a temporary
negative trust anchor, which disables DNSSEC
validation below a specified name for a specified
period of time (not exceeding 24 hours).  This
can be used when validation for a domain is known
to be failing due to a configuration error on
the part of the domain owner rather than a
spoofing attack. [RT #29358]

34 files changed:
CHANGES
bin/named/control.c
bin/named/include/named/control.h
bin/named/include/named/server.h
bin/named/server.c
bin/rndc/rndc.docbook
bin/tests/system/dnssec/setup.sh
bin/tests/system/dnssec/tests.sh
doc/arm/Bv9ARM-book.xml
lib/dns/Makefile.in
lib/dns/dst_api.c
lib/dns/include/dns/keytable.h
lib/dns/include/dns/log.h
lib/dns/include/dns/nta.h [new file with mode: 0644]
lib/dns/include/dns/result.h
lib/dns/include/dns/types.h
lib/dns/include/dns/validator.h
lib/dns/include/dns/view.h
lib/dns/keytable.c
lib/dns/log.c
lib/dns/nta.c [new file with mode: 0644]
lib/dns/resolver.c
lib/dns/result.c
lib/dns/tests/Makefile.in
lib/dns/tests/dnstest.c
lib/dns/tests/dnstest.h
lib/dns/tests/keytable_test.c [new file with mode: 0644]
lib/dns/validator.c
lib/dns/view.c
lib/dns/win32/libdns.def.in
lib/dns/win32/libdns.dsp.in
lib/dns/win32/libdns.mak.in
lib/dns/win32/libdns.vcxproj.filters.in
lib/dns/win32/libdns.vcxproj.in

diff --git a/CHANGES b/CHANGES
index f5fa916cecee098ab1ea150c0246908d65f1a947..048da3a3c7e3a776816d75b9ad35e84060bcc319 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,12 @@
+3867.  [func]          "rndc nta" can now be used to set a temporary
+                       negative trust anchor, which disables DNSSEC
+                       validation below a specified name for a specified
+                       period of time (not exceeding 24 hours).  This
+                       can be used when validation for a domain is known
+                       to be failing due to a configuration error on
+                       the part of the domain owner rather than a
+                       spoofing attack. [RT #29358]
+
 3866.  [bug]           Named could die on disk full in generate_session_key.
                        [RT #36119]
 
index f82535838b2846026dcc7f7089f5ac5ced03c392..7f49eb3c8909e70e069f73e059166f4e1f5a82ca 100644 (file)
@@ -213,6 +213,8 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) {
                result = ns_server_signing(ns_g_server, command, text);
        } else if (command_compare(command, NS_COMMAND_ZONESTATUS)) {
                result = ns_server_zonestatus(ns_g_server, command, text);
+       } else if (command_compare(command, NS_COMMAND_NTA)) {
+               result = ns_server_nta(ns_g_server, command, text);
        } else {
                isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
                              NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
index fd9359988a179ddd10dc7a8a5e616d9682ac5c8d..33a16193d941b5573598c547697500dc963e5bd4 100644 (file)
@@ -67,6 +67,7 @@
 #define NS_COMMAND_SYNC                "sync"
 #define NS_COMMAND_SIGNING     "signing"
 #define NS_COMMAND_ZONESTATUS  "zonestatus"
+#define NS_COMMAND_NTA         "nta"
 
 isc_result_t
 ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp);
index d215c1dd71bd04350ee961ef40db16e0e781cbf2..fd6818dca3a91813efa0bbfc99e38385ad2e86b9 100644 (file)
@@ -392,4 +392,11 @@ ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text);
  */
 isc_result_t
 ns_server_zonestatus(ns_server_t *server, char *args, isc_buffer_t *text);
+
+/*%
+ * Adds a Negative Trust Anchor (NTA) for a specified name and
+ * duration, in a particular view if specified, or in all views.
+ */
+isc_result_t
+ns_server_nta(ns_server_t *server, char *args, isc_buffer_t *text);
 #endif /* NAMED_SERVER_H */
index 789ca8a1ca8d5c3ae6e3fc40866999407a0abac2..679cf28fffc2fe377e61e84f8a99f646a4973327 100644 (file)
@@ -78,6 +78,7 @@
 #include <dns/lib.h>
 #include <dns/master.h>
 #include <dns/masterdump.h>
+#include <dns/nta.h>
 #include <dns/order.h>
 #include <dns/peer.h>
 #include <dns/portlist.h>
@@ -94,6 +95,7 @@
 #include <dns/stats.h>
 #include <dns/tkey.h>
 #include <dns/tsig.h>
+#include <dns/ttl.h>
 #include <dns/view.h>
 #include <dns/zone.h>
 #include <dns/zt.h>
@@ -824,6 +826,14 @@ configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig,
                return (ISC_R_UNEXPECTED);
        }
 
+       result = dns_view_initntatable(view, mctx);
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                             "couldn't create NTA table");
+               return (ISC_R_UNEXPECTED);
+       }
+
        if (auto_dlv && view->rdclass == dns_rdataclass_in) {
                const cfg_obj_t *builtin_keys = NULL;
                const cfg_obj_t *builtin_managed_keys = NULL;
@@ -9756,3 +9766,133 @@ ns_server_zonestatus(ns_server_t *server, char *args, isc_buffer_t *text) {
                dns_zone_detach(&zone);
        return (result);
 }
+
+isc_result_t
+ns_server_nta(ns_server_t *server, char *args, isc_buffer_t *text) {
+       dns_view_t *view;
+       dns_ntatable_t *ntatable = NULL;
+       isc_result_t result;
+       char *ptr, *nametext, *viewname;
+       isc_stdtime_t now, when;
+       isc_time_t t;
+       char tbuf[64];
+       const char *msg = NULL;
+       dns_fixedname_t fn;
+       dns_name_t *ntaname;
+       dns_ttl_t ntattl;
+       isc_textregion_t tr;
+
+       dns_fixedname_init(&fn);
+       ntaname = dns_fixedname_name(&fn);
+
+       /* Skip the command name. */
+       ptr = next_token(&args, " \t");
+       if (ptr == NULL)
+               return (ISC_R_UNEXPECTEDEND);
+
+       /* Get the NTA name. */
+       nametext = next_token(&args, " \t");
+       if (nametext == NULL)
+               return (ISC_R_UNEXPECTEDEND);
+
+       if (strcmp(nametext, ".") == 0)
+               ntaname = dns_rootname;
+       else {
+               isc_buffer_t b;
+               isc_buffer_init(&b, nametext, strlen(nametext));
+               isc_buffer_add(&b, strlen(nametext));
+               CHECK(dns_name_fromtext(ntaname, &b, dns_rootname, 0, NULL));
+       }
+
+       /* Get the NTA duration. */
+       ptr = next_token(&args, " \t");
+       if (ptr == NULL)
+               return (ISC_R_UNEXPECTEDEND);
+
+       tr.base = ptr;
+       tr.length = strlen(ptr);
+       CHECK(dns_ttl_fromtext(&tr, &ntattl));
+
+       if (ntattl > 86400) {
+               msg = "NTA cannot exceed one day";
+               CHECK(ISC_R_RANGE);
+       }
+
+       /* Look for the view name. */
+       viewname = next_token(&args, " \t");
+
+       /* Set up the NTA */
+       isc_stdtime_get(&now);
+       when = now + ntattl;
+
+       result = isc_task_beginexclusive(server->task);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       for (view = ISC_LIST_HEAD(server->viewlist);
+            view != NULL;
+            view = ISC_LIST_NEXT(view, link))
+       {
+               if (viewname != NULL &&
+                   strcmp(view->name, viewname) != 0)
+                       continue;
+
+               if (ntatable != NULL)
+                       dns_ntatable_detach(&ntatable);
+
+               result = dns_view_getntatable(view, &ntatable);
+               if (result == ISC_R_NOTFOUND) {
+                       result = ISC_R_SUCCESS;
+                       continue;
+               }
+
+               result = dns_view_flushnode(view, ntaname, ISC_TRUE);
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                             "flush tree '%s' in cache view '%s': %s",
+                             nametext, view->name,
+                             isc_result_totext(result));
+
+               isc_time_set(&t, when, 0);
+               isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
+
+               if (ntattl > 0) {
+                       CHECK(dns_ntatable_add(ntatable, ntaname, when));
+
+                       CHECK(putstr(text, "Negative trust anchor added: "));
+                       CHECK(putstr(text, nametext));
+                       CHECK(putstr(text, "/"));
+                       CHECK(putstr(text, view->name));
+                       CHECK(putstr(text, ", expires "));
+                       CHECK(putstr(text, tbuf));
+
+                       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                                     NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                                     "added NTA '%s' (%d sec) in view '%s'",
+                                     nametext, ntattl, view->name);
+               } else {
+                       CHECK(dns_ntatable_delete(ntatable, ntaname));
+
+                       CHECK(putstr(text, "Negative trust anchor removed: "));
+                       CHECK(putstr(text, nametext));
+                       CHECK(putstr(text, "/"));
+                       CHECK(putstr(text, view->name));
+
+                       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                                     NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                                     "removed NTA '%s' in view %s",
+                                     nametext, view->name);
+               }
+
+               if (isc_buffer_availablelength(text) == 0)
+                       return (ISC_R_NOSPACE);
+
+               isc_buffer_putuint8(text, 0);
+       }
+       isc_task_endexclusive(server->task);
+
+       if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
+               isc_buffer_putstr(text, msg);
+ cleanup:
+       if (ntatable != NULL)
+               dns_ntatable_detach(&ntatable);
+       return (result);
+}
index e53159d2b6ab632909f6a2d32bff8befacae61f5..7c28ac480f5e268086c38cf743308f7087734c97 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><userinput>nta <replaceable>domain</replaceable> <replaceable>duration</replaceable> </userinput></term>
+        <listitem>
+          <para>
+            Sets a DNSSEC negative trust anchor (NTA)
+            for <option>domain</option>, with a lifetime of
+            <option>duration</option> (up to a limit of one day).
+          </para>
+          <para>
+            A negative trust anchor selectively disables
+            DNSSEC validation for zones that known to be
+            failing because of misconfiguration rather than
+            an attack.  When data to be validated is
+            at or below an active NTA (and above any other
+            configured trust anchors), <command>named</command> will
+            abort the DNSSEC validation process and treat the data as
+            insecure rather than bogus.  This continues until the
+            NTA's lifetime is elapsed, or until the server is
+            restarted (NTA's do not persist across restarts).
+          </para>
+          <para>
+            TTL-style suffixes can be used to specify
+            <option>duration</option> in seconds, minutes, or hours.
+          </para>
+          <para>
+            If the specified domain already has an NTA, its duration
+            will be updated to the new value.  Setting
+            <option>duration</option> to zero will delete the NTA.
+          </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><userinput>tsig-list</userinput></term>
         <listitem>
index 12a5f8503d9d27f5f0de87aa3c368e9e13a7d002..23143836b2a86aa21933f711d04dbd62ff1e8579 100644 (file)
@@ -25,6 +25,7 @@ test -r $RANDFILE || $GENRANDOM 400 $RANDFILE
 cd ns1 && $SHELL sign.sh
 
 echo "a.bogus.example. A       10.0.0.22" >>../ns3/bogus.example.db.signed
+echo "b.bogus.example. A       10.0.0.23" >>../ns3/bogus.example.db.signed
 
 cd ../ns3 && cp -f siginterval1.conf siginterval.conf
 cd ../ns4 && cp -f named1.conf named.conf
index 9e81c519458594beecf58b3781cce173a5bc33c2..2961b7504816f4fee14bccaf4715f64a1e02d210 100644 (file)
@@ -1648,6 +1648,33 @@ ret=0
 $DIG $DIGOPTS ns algroll. @10.53.0.4 > dig.out.ns4.test$n || ret=1
 grep "NOERROR" dig.out.ns4.test$n > /dev/null || ret=1
 grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:checking positive and negative validation with negative trust anchors ($n)"
+ret=0
+# check correct initial behavior
+$DIG $DIGOPTS a.bogus.example. a @10.53.0.4 > dig.out.ns4.test$n.1 || ret=1
+grep "status: SERVFAIL" dig.out.ns4.test$n.1 > /dev/null || ret=1
+$DIG $DIGOPTS a.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.2 || ret=1
+grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.2 > /dev/null || ret=1
+# add negative trust anchors
+$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta bogus.example 15s 2>&1 | sed 's/^/I:ns4 /'
+$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta secure.example 15s 2>&1 | sed 's/^/I:ns4 /'
+# check behavior with NTA's in place
+$DIG $DIGOPTS a.bogus.example. a @10.53.0.4 > dig.out.ns4.test$n.3 || ret=1
+grep "status: SERVFAIL" dig.out.ns4.test$n.3 > /dev/null && ret=1
+$DIG $DIGOPTS a.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.4 || ret=1
+grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.4 > /dev/null && ret=1
+echo "I: waiting for NTA expiration"
+sleep 15
+# check correct behavior after expiry
+$DIG $DIGOPTS b.bogus.example. a @10.53.0.4 > dig.out.ns4.test$n.5 || ret=1
+grep "status: SERVFAIL" dig.out.ns4.test$n.5 > /dev/null || ret=1
+$DIG $DIGOPTS b.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.6 || ret=1
+grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.6 > /dev/null || ret=1
+n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
index b0b691790da7aeb3327b4744d6e89ad8e14166cf..a3fc6740e8d5fa55aeb76304e235921d2d2de1ca 100644 (file)
@@ -10756,6 +10756,10 @@ rate-limit {
            level are inherited by all views, but keys defined in a view
            are only used within that view.
          </para>
+         <para>
+           Validation below specified names can be temporarily disabled
+           by using <command>rndc nta</command>.
+         </para>
        </sect2>
 
        <sect2>
index 1b83006f61339b079fe950232bc2a5bf41eb124d..c687d87723406f1c496f923f47e465f8b5d7e21a 100644 (file)
@@ -69,8 +69,8 @@ DNSOBJS =     acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \
                iptable.@O@ journal.@O@ keydata.@O@ keytable.@O@ \
                lib.@O@ log.@O@ lookup.@O@ \
                master.@O@ masterdump.@O@ message.@O@ \
-               name.@O@ ncache.@O@ nsec.@O@ nsec3.@O@ order.@O@ peer.@O@ \
-               portlist.@O@ private.@O@ \
+               name.@O@ ncache.@O@ nsec.@O@ nsec3.@O@ nta.@O@ \
+               order.@O@ peer.@O@ portlist.@O@ private.@O@ \
                rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \
                rdatalist.@O@ rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ \
                request.@O@ resolver.@O@ result.@O@ rootns.@O@ \
@@ -106,7 +106,8 @@ DNSSRCS =   acache.c acl.c adb.c byaddr.c \
                dlz.c dns64.c dnssec.c ds.c forward.c geoip.c \
                iptable.c journal.c keydata.c keytable.c lib.c log.c \
                lookup.c master.c masterdump.c message.c \
-               name.c ncache.c nsec.c nsec3.c order.c peer.c portlist.c \
+               name.c ncache.c nsec.c nsec3.c nta.c \
+               order.c peer.c portlist.c \
                rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c rdatalist.c \
                rdataset.c rdatasetiter.c rdataslab.c request.c \
                resolver.c result.c rootns.c rpz.c rrl.c rriterator.c \
index d02c367c07178b04cd2a0ebec7d29ad848cf5ef0..4e662ca8af23afbf4b73126d064b5457621cbd1c 100644 (file)
@@ -77,7 +77,8 @@
 static dst_func_t *dst_t_func[DST_MAX_ALGS];
 static isc_entropy_t *dst_entropy_pool = NULL;
 static unsigned int dst_entropy_flags = 0;
-static isc_boolean_t dst_initialized = ISC_FALSE;
+
+isc_boolean_t dst_initialized = ISC_FALSE;
 
 void gss_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);
 
index 3f4adaf6e3989cb0b9f8c672b2050bdd49cb56cb..c44fca8a40cd14f5eb20ef1dc07b9e0679222f2e 100644 (file)
@@ -386,7 +386,7 @@ dns_keytable_detachkeynode(dns_keytable_t *keytable,
 
 isc_result_t
 dns_keytable_issecuredomain(dns_keytable_t *keytable, dns_name_t *name,
-                           isc_boolean_t *wantdnssecp);
+                           dns_name_t *foundname, isc_boolean_t *wantdnssecp);
 /*%<
  * Is 'name' at or beneath a trusted key?
  *
@@ -396,12 +396,16 @@ dns_keytable_issecuredomain(dns_keytable_t *keytable, dns_name_t *name,
  *
  *\li  'name' is a valid absolute name.
  *
- *\li  '*wantsdnssecp' is a valid isc_boolean_t.
+ *\li  'foundanme' is NULL or is a pointer to an initialized dns_name_t
  *
+ *\li  '*wantsdnssecp' is a valid isc_boolean_t.
  * Ensures:
  *
  *\li  On success, *wantsdnssecp will be ISC_TRUE if and only if 'name'
- *     is at or beneath a trusted key.
+ *     is at or beneath a trusted key.  If 'foundname' is not NULL, then
+ *     it will be updated to contain the name of the closest enclosing
+ *     trust anchor.
  *
  * Returns:
  *
index 15f39767839ad8d9eb45823d686bbdd0aee86330..c8d0b59b895ceaa7a1d6efb299a0641c3259aa09 100644 (file)
@@ -78,6 +78,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[];
 #define DNS_LOGMODULE_DNSSEC           (&dns_modules[27])
 #define DNS_LOGMODULE_CRYPTO           (&dns_modules[28])
 #define DNS_LOGMODULE_PACKETS          (&dns_modules[29])
+#define DNS_LOGMODULE_NTA              (&dns_modules[30])
 
 ISC_LANG_BEGINDECLS
 
diff --git a/lib/dns/include/dns/nta.h b/lib/dns/include/dns/nta.h
new file mode 100644 (file)
index 0000000..7eeacb7
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2013  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.
+ */
+
+#ifndef DNS_NTA_H
+#define DNS_NTA_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * The NTA module provides services for storing and retrieving negative
+ * trust anchors, and determine whether a given domain is subject to
+ * DNSSEC validation.
+ */
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+struct dns_ntatable {
+       /* Unlocked. */
+       unsigned int            magic;
+       isc_mem_t               *mctx;
+       isc_rwlock_t            rwlock;
+       /* Locked by rwlock. */
+       isc_uint32_t            references;
+       dns_rbt_t               *table;
+};
+
+#define NTATABLE_MAGIC         ISC_MAGIC('N', 'T', 'A', 't')
+#define VALID_NTATABLE(nt)     ISC_MAGIC_VALID(nt, NTATABLE_MAGIC)
+
+struct dns_nta {
+       unsigned int            magic;
+       isc_refcount_t          refcount;
+       isc_stdtime_t           expiry;
+};
+
+#define NTA_MAGIC              ISC_MAGIC('N', 'T', 'A', 'n')
+#define VALID_NTA(nn)          ISC_MAGIC_VALID(nn, NTA_MAGIC)
+
+isc_result_t
+dns_ntatable_create(isc_mem_t *mctx, dns_ntatable_t **ntatablep);
+/*%<
+ * Create an NTA table.
+ *
+ * Requires:
+ *
+ *\li  'mctx' is a valid memory context.
+ *
+ *\li  ntatablep != NULL && *ntatablep == NULL
+ *
+ * Ensures:
+ *
+ *\li  On success, *ntatablep is a valid, empty NTA table.
+ *
+ * Returns:
+ *
+ *\li  ISC_R_SUCCESS
+ *\li  Any other result indicates failure.
+ */
+
+void
+dns_ntatable_attach(dns_ntatable_t *source, dns_ntatable_t **targetp);
+/*%<
+ * Attach *targetp to source.
+ *
+ * Requires:
+ *
+ *\li  'source' is a valid ntatable.
+ *
+ *\li  'targetp' points to a NULL dns_ntatable_t *.
+ *
+ * Ensures:
+ *
+ *\li  *targetp is attached to source.
+ */
+
+void
+dns_ntatable_detach(dns_ntatable_t **ntatablep);
+/*%<
+ * Detach *ntatablep from its ntatable.
+ *
+ * Requires:
+ *
+ *\li  'ntatablep' points to a valid ntatable.
+ *
+ * Ensures:
+ *
+ *\li  *ntatablep is NULL.
+ *
+ *\li  If '*ntatablep' is the last reference to the ntatable,
+ *             all resources used by the ntatable will be freed
+ */
+
+isc_result_t
+dns_ntatable_add(dns_ntatable_t *ntatable, dns_name_t *name,
+                isc_uint32_t expiry);
+/*%<
+ * Add a negative trust anchor to 'ntatable' for name 'name',
+ * which will expire at time 'expiry'.
+ *
+ * Notes:
+ *
+ *\li   If an NTA already exists in the table, its expiry time
+ *      is updated.
+ *
+ * Requires:
+ *
+ *\li  'ntatable' points to a valid ntatable.
+ *
+ *\li  'name' points to a valid name.
+ *
+ * Returns:
+ *
+ *\li  ISC_R_SUCCESS
+ *
+ *\li  Any other result indicates failure.
+ */
+
+isc_result_t
+dns_ntatable_delete(dns_ntatable_t *ntatable, dns_name_t *keyname);
+/*%<
+ * Delete node(s) from 'ntatable' matching name 'keyname'
+ *
+ * Requires:
+ *
+ *\li  'ntatable' points to a valid ntatable.
+ *
+ *\li  'name' is not NULL
+ *
+ * Returns:
+ *
+ *\li  ISC_R_SUCCESS
+ *
+ *\li  Any other result indicates failure.
+ */
+
+isc_result_t
+dns_ntatable_deletenta(dns_ntatable_t *ntatable, dns_name_t *name);
+/*%<
+ * Delete node from 'ntatable' matching the name 'name'
+ *
+ * Requires:
+ *
+ *\li  'ntatable' points to a valid ntatable.
+ *\li  'name' is a valid name.
+ *
+ * Returns:
+ *
+ *\li  ISC_R_SUCCESS
+ *
+ *\li  Any other result indicates failure.
+ */
+
+isc_boolean_t
+dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
+                    dns_name_t *name, dns_name_t *anchor);
+/*%<
+ * Return ISC_TRUE if 'name' is below a non-expired negative trust
+ * anchor which in turn is at or below 'anchor'.
+ *
+ * If 'ntatable' has not been initialized, return ISC_FALSE.
+ *
+ * Requires:
+ *
+ *\li  'ntatable' is NULL or is a valid ntatable.
+ *
+ *\li  'name' is a valid absolute name.
+ */
+
+isc_result_t
+dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp);
+/*%<
+ * Dump the NTA table on fp.
+ */
+
+isc_result_t
+dns_nta_create(isc_mem_t *mctx, dns_nta_t **target);
+/*%<
+ * Allocate space for an NTA
+ */
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_NTA_H */
index 12aacf9ba78a3e4290eba02202e44d9daae8da58..137bfd8567100e3d58dfe8e5e95d784b68ea9e8c 100644 (file)
 #define DNS_R_EXPIRED                  (ISC_RESULTCLASS_DNS + 107)
 #define DNS_R_NOTDYNAMIC               (ISC_RESULTCLASS_DNS + 108)
 #define DNS_R_BADEUI                   (ISC_RESULTCLASS_DNS + 109)
+#define DNS_R_NTACOVERED               (ISC_RESULTCLASS_DNS + 110)
 
-#define DNS_R_NRESULTS                 110     /*%< Number of results */
+#define DNS_R_NRESULTS                 111     /*%< Number of results */
 
 /*
  * DNS wire format rcodes.
index ecb3f04d15db5020dd17022c0731915a8791c72f..9da7f7d15655f53f7565ac8be28b9048dda8a271 100644 (file)
@@ -99,6 +99,8 @@ typedef isc_region_t                          dns_label_t;
 typedef struct dns_lookup                      dns_lookup_t;
 typedef struct dns_name                                dns_name_t;
 typedef ISC_LIST(dns_name_t)                   dns_namelist_t;
+typedef struct dns_nta                         dns_nta_t;
+typedef struct dns_ntatable                    dns_ntatable_t;
 typedef isc_uint16_t                           dns_opcode_t;
 typedef unsigned char                          dns_offsets_t[128];
 typedef struct dns_order                       dns_order_t;
index 8f640421481ddf835fb8fb785074d3847b97f00c..0abf1dce1f183d849975182b299796ccc9234353 100644 (file)
@@ -165,6 +165,7 @@ struct dns_validator {
        unsigned int                    depth;
        unsigned int                    authcount;
        unsigned int                    authfail;
+       isc_stdtime_t                   start;
 };
 
 /*%
index 9beadbf64bbeeae177e60c40070ba8306b1ba7b0..3a0c3f8602e81bb1c0840dd0ec1970b13ca3c2d9 100644 (file)
@@ -98,10 +98,11 @@ struct dns_view {
        dns_db_t *                      hints;
 
        /*
-        * security roots.
+        * security roots and negative trust anchors.
         * internal use only; access via * dns_view_getsecroots()
         */
        dns_keytable_t *                secroots_priv;
+       dns_ntatable_t *                ntatable_priv;
 
        isc_mutex_t                     lock;
        isc_boolean_t                   frozen;
@@ -1074,17 +1075,49 @@ dns_view_iscacheshared(dns_view_t *view);
  *\li  #ISC_FALSE otherwise.
  */
 
+isc_result_t
+dns_view_initntatable(dns_view_t *view, isc_mem_t *mctx);
+/*%<
+ * Initialize the negative trust anchor table for the view.
+ *
+ * Requires:
+ * \li 'view' is valid.
+ *
+ * Returns:
+ *\li  ISC_R_SUCCESS
+ *\li  Any other result indicates failure
+ */
+
+isc_result_t
+dns_view_getntatable(dns_view_t *view, dns_ntatable_t **ntp);
+/*%<
+ * Get the negative trust anchor table for this view.  Returns
+ * ISC_R_NOTFOUND if the table not been initialized for the view.
+ *
+ * '*ntp' is attached on success; the caller is responsible for
+ * detaching it with dns_ntatable_detach().
+ *
+ * Requires:
+ * \li 'view' is valid.
+ * \li 'nta' is not NULL and '*nta' is NULL.
+ *
+ * Returns:
+ *\li  ISC_R_SUCCESS
+ *\li  ISC_R_NOTFOUND
+ */
+
 isc_result_t
 dns_view_initsecroots(dns_view_t *view, isc_mem_t *mctx);
 /*%<
- * Initialize security roots for the view.  (Note that secroots is
+ * Initialize security roots for the view, detaching any previously
+ * existing security roots first.  (Note that secroots_priv is
  * NULL until this function is called, so any function using
- * secroots must check its validity first.  One way to do this is
- * use dns_view_getsecroots() and check its return value.)
+ * security roots must check that they have been initialized first.
+ * One way to do this is use dns_view_getsecroots() and check its
+ * return value.)
  *
  * Requires:
  * \li 'view' is valid.
- * \li 'view->secroots' is NULL.
  *
  * Returns:
  *\li  ISC_R_SUCCESS
@@ -1111,10 +1144,10 @@ dns_view_getsecroots(dns_view_t *view, dns_keytable_t **ktp);
 
 isc_result_t
 dns_view_issecuredomain(dns_view_t *view, dns_name_t *name,
-                        isc_boolean_t *secure_domain);
+                       isc_stdtime_t now, isc_boolean_t *secure_domain);
 /*%<
- * Is 'name' at or beneath a trusted key?  Put answer in
- * '*secure_domain'.
+ * Is 'name' at or beneath a trusted key, and not covered by a valid
+ * negative trust anchor?  Put answer in '*secure_domain'.
  *
  * Requires:
  * \li 'view' is valid.
@@ -1124,6 +1157,20 @@ dns_view_issecuredomain(dns_view_t *view, dns_name_t *name,
  *\li  Any other value indicates failure
  */
 
+isc_boolean_t
+dns_view_ntacovers(dns_view_t *view, isc_stdtime_t now,
+                  dns_name_t *name, dns_name_t *anchor);
+/*%<
+ * Is there a current negative trust anchor above 'name' and below 'anchor'?
+ *
+ * Requires:
+ * \li 'view' is valid.
+ *
+ * Returns:
+ *\li  ISC_R_TRUE
+ *\li  ISC_R_FALSE
+ */
+
 void
 dns_view_untrust(dns_view_t *view, dns_name_t *keyname,
                 dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx);
index 56fefcd2c122bde701ae5459bb2c828853848fe6..717e37636dbbd9c3d72205c8374c35c0f6ccd357 100644 (file)
@@ -524,10 +524,10 @@ dns_keytable_detachkeynode(dns_keytable_t *keytable, dns_keynode_t **keynodep)
 
 isc_result_t
 dns_keytable_issecuredomain(dns_keytable_t *keytable, dns_name_t *name,
-                           isc_boolean_t *wantdnssecp)
+                           dns_name_t *foundname, isc_boolean_t *wantdnssecp)
 {
        isc_result_t result;
-       void *data;
+       dns_rbtnode_t *node = NULL;
 
        /*
         * Is 'name' at or beneath a trusted key?
@@ -539,11 +539,10 @@ dns_keytable_issecuredomain(dns_keytable_t *keytable, dns_name_t *name,
 
        RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
 
-       data = NULL;
-       result = dns_rbt_findname(keytable->table, name, 0, NULL, &data);
-
+       result = dns_rbt_findnode(keytable->table, name, foundname, &node,
+                                 NULL, DNS_RBTFIND_NOOPTIONS, NULL, NULL);
        if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
-               INSIST(data != NULL);
+               INSIST(node->data != NULL);
                *wantdnssecp = ISC_TRUE;
                result = ISC_R_SUCCESS;
        } else if (result == ISC_R_NOTFOUND) {
index 56b616137e33c3c9a1701233476888943de1beb9..663d420338508482f920bd1ed33f37527a365fa0 100644 (file)
@@ -84,6 +84,7 @@ LIBDNS_EXTERNAL_DATA isc_logmodule_t dns_modules[] = {
        { "dns/dnssec",         0 },
        { "dns/crypto",         0 },
        { "dns/packets",        0 },
+       { "dns/nta",            0 },
        { NULL,                 0 }
 };
 
diff --git a/lib/dns/nta.c b/lib/dns/nta.c
new file mode 100644 (file)
index 0000000..3cd7dfc
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2013  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.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/rwlock.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/log.h>
+#include <dns/nta.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rbt.h>
+#include <dns/result.h>
+
+static void
+nta_detach(isc_mem_t *mctx, dns_nta_t **ntap) {
+       unsigned int refs;
+       dns_nta_t *nta = *ntap;
+
+       REQUIRE(VALID_NTA(nta));
+
+       *ntap = NULL;
+       isc_refcount_decrement(&nta->refcount, &refs);
+       if (refs == 0) {
+               isc_refcount_destroy(&nta->refcount);
+               isc_mem_put(mctx, nta, sizeof(dns_nta_t));
+       }
+}
+
+static void
+free_nta(void *data, void *arg) {
+       dns_nta_t *nta = (dns_nta_t *) data;
+       isc_mem_t *mctx = (isc_mem_t *) arg;
+
+       nta_detach(mctx, &nta);
+}
+
+isc_result_t
+dns_ntatable_create(isc_mem_t *mctx, dns_ntatable_t **ntatablep) {
+       dns_ntatable_t *ntatable;
+       isc_result_t result;
+
+       /*
+        * Create an NTA table.
+        */
+
+       REQUIRE(ntatablep != NULL && *ntatablep == NULL);
+
+       ntatable = isc_mem_get(mctx, sizeof(*ntatable));
+       if (ntatable == NULL)
+               return (ISC_R_NOMEMORY);
+
+       ntatable->table = NULL;
+       result = dns_rbt_create(mctx, free_nta, mctx, &ntatable->table);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_ntatable;
+
+       result = isc_rwlock_init(&ntatable->rwlock, 0, 0);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_rbt;
+
+       ntatable->mctx = NULL;
+       isc_mem_attach(mctx, &ntatable->mctx);
+       ntatable->references = 1;
+       ntatable->magic = NTATABLE_MAGIC;
+       *ntatablep = ntatable;
+
+       return (ISC_R_SUCCESS);
+
+   cleanup_rbt:
+       dns_rbt_destroy(&ntatable->table);
+
+   cleanup_ntatable:
+       isc_mem_put(mctx, ntatable, sizeof(*ntatable));
+
+       return (result);
+}
+
+void
+dns_ntatable_attach(dns_ntatable_t *source, dns_ntatable_t **targetp) {
+       REQUIRE(VALID_NTATABLE(source));
+       REQUIRE(targetp != NULL && *targetp == NULL);
+
+       RWLOCK(&source->rwlock, isc_rwlocktype_write);
+
+       INSIST(source->references > 0);
+       source->references++;
+       INSIST(source->references != 0);
+
+       RWUNLOCK(&source->rwlock, isc_rwlocktype_write);
+
+       *targetp = source;
+}
+
+void
+dns_ntatable_detach(dns_ntatable_t **ntatablep) {
+       isc_boolean_t destroy = ISC_FALSE;
+       dns_ntatable_t *ntatable;
+
+       REQUIRE(ntatablep != NULL && VALID_NTATABLE(*ntatablep));
+
+       ntatable = *ntatablep;
+       *ntatablep = NULL;
+
+       RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+       INSIST(ntatable->references > 0);
+       ntatable->references--;
+       if (ntatable->references == 0)
+               destroy = ISC_TRUE;
+       RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+
+       if (destroy) {
+               dns_rbt_destroy(&ntatable->table);
+               isc_rwlock_destroy(&ntatable->rwlock);
+               ntatable->magic = 0;
+               isc_mem_putanddetach(&ntatable->mctx,
+                                    ntatable, sizeof(*ntatable));
+       }
+}
+
+isc_result_t
+dns_ntatable_add(dns_ntatable_t *ntatable, dns_name_t *name,
+                isc_uint32_t expiry)
+{
+       isc_result_t result;
+       dns_nta_t *nta = NULL;
+       dns_rbtnode_t *node;
+
+       REQUIRE(VALID_NTATABLE(ntatable));
+
+       result = dns_nta_create(ntatable->mctx, &nta);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       nta->expiry = expiry;
+
+       RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+
+       node = NULL;
+       result = dns_rbt_addnode(ntatable->table, name, &node);
+       if (result == ISC_R_SUCCESS) {
+               node->data = nta;
+               nta = NULL;
+       } else if (result == ISC_R_EXISTS) {
+               dns_nta_t *n = node->data;
+               if (n == NULL) {
+                       node->data = nta;
+                       nta = NULL;
+               } else {
+                       n->expiry = nta->expiry;
+                       nta_detach(ntatable->mctx, &nta);
+               }
+               result = ISC_R_SUCCESS;
+       } 
+
+       RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+
+       if (nta != NULL)
+               nta_detach(ntatable->mctx, &nta);
+
+       return (result);
+}
+
+/*
+ * Caller must hold a write lock on rwlock.
+ */
+static isc_result_t
+delete(dns_ntatable_t *ntatable, dns_name_t *name) {
+       isc_result_t result;
+       dns_rbtnode_t *node = NULL;
+
+       REQUIRE(VALID_NTATABLE(ntatable));
+       REQUIRE(name != NULL);
+
+       result = dns_rbt_findnode(ntatable->table, name, NULL, &node, NULL,
+                                 DNS_RBTFIND_NOOPTIONS, NULL, NULL);
+       if (result == ISC_R_SUCCESS) {
+               if (node->data != NULL)
+                       result = dns_rbt_deletenode(ntatable->table,
+                                                   node, ISC_FALSE);
+               else
+                       result = ISC_R_NOTFOUND;
+       } else if (result == DNS_R_PARTIALMATCH)
+               result = ISC_R_NOTFOUND;
+
+       return (result);
+}
+
+isc_result_t
+dns_ntatable_delete(dns_ntatable_t *ntatable, dns_name_t *name) {
+       isc_result_t result;
+
+       RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+       result = delete(ntatable, name);
+       RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+       
+       return (result);
+}
+
+isc_boolean_t
+dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
+                    dns_name_t *name, dns_name_t *anchor)
+{
+       isc_result_t result;
+       dns_fixedname_t fn;
+       dns_rbtnode_t *node;
+       dns_name_t *foundname;
+       dns_nta_t *nta = NULL;
+       isc_boolean_t answer = ISC_FALSE;
+       isc_rwlocktype_t locktype = isc_rwlocktype_read;
+
+       REQUIRE(ntatable == NULL || VALID_NTATABLE(ntatable));
+       REQUIRE(dns_name_isabsolute(name));
+
+       if (ntatable == NULL)
+               return (ISC_FALSE);
+
+       dns_fixedname_init(&fn);
+       foundname = dns_fixedname_name(&fn);
+
+ relock:
+       RWLOCK(&ntatable->rwlock, locktype);
+ again:
+       node = NULL;
+       result = dns_rbt_findnode(ntatable->table, name, foundname, &node, NULL,
+                                 DNS_RBTFIND_NOOPTIONS, NULL, NULL);
+       if (result == DNS_R_PARTIALMATCH) {
+               if (dns_name_issubdomain(foundname, anchor))
+                       result = ISC_R_SUCCESS;
+       }
+       if (result == ISC_R_SUCCESS) {
+               nta = (dns_nta_t *) node->data;
+               answer = ISC_TF(nta->expiry > now);
+       }
+
+       /* Deal with expired NTA */
+       if (result == ISC_R_SUCCESS && !answer) {
+               char nb[DNS_NAME_FORMATSIZE];
+
+               if (locktype == isc_rwlocktype_read) {
+                       RWUNLOCK(&ntatable->rwlock, locktype);
+                       locktype = isc_rwlocktype_write;
+                       goto relock;
+               }
+
+               dns_name_format(foundname, nb, sizeof(nb));
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
+                             DNS_LOGMODULE_NTA, ISC_LOG_INFO,
+                             "deleting expired NTA at %s", nb);
+
+               result = delete(ntatable, foundname);
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
+                                     DNS_LOGMODULE_NTA, ISC_LOG_INFO,
+                                     "deleting NTA failed: %s",
+                                     isc_result_totext(result));
+               }
+               goto again;
+       }
+       RWUNLOCK(&ntatable->rwlock, locktype);
+
+       return (answer);
+}
+
+isc_result_t
+dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp) {
+       isc_result_t result;
+       dns_rbtnode_t *node;
+       dns_rbtnodechain_t chain;
+
+       REQUIRE(VALID_NTATABLE(ntatable));
+
+       RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
+       dns_rbtnodechain_init(&chain, ntatable->mctx);
+       result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
+       if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
+               goto cleanup;
+       for (;;) {
+               dns_rbtnodechain_current(&chain, NULL, NULL, &node);
+               if (node->data != NULL) {
+                       dns_nta_t *n = (dns_nta_t *) node->data;
+                       char nbuf[DNS_NAME_FORMATSIZE], tbuf[80];
+                       dns_name_t name;
+                       isc_time_t t;
+
+                       dns_name_init(&name, NULL);
+                       dns_rbt_namefromnode(node, &name);
+                       dns_name_format(&name, nbuf, sizeof(nbuf));
+                       isc_time_set(&t, n->expiry, 0);
+                       isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
+                       fprintf(fp, "%s : expiry %s\n", nbuf, tbuf);
+               }
+               result = dns_rbtnodechain_next(&chain, NULL, NULL);
+               if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+                       if (result == ISC_R_NOMORE)
+                               result = ISC_R_SUCCESS;
+                       break;
+               }
+       }
+
+   cleanup:
+       dns_rbtnodechain_invalidate(&chain);
+       RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
+       return (result);
+}
+
+isc_result_t
+dns_nta_create(isc_mem_t *mctx, dns_nta_t **target) {
+       isc_result_t result;
+       dns_nta_t *nta = NULL;
+
+       REQUIRE(target != NULL && *target == NULL);
+
+       nta = isc_mem_get(mctx, sizeof(dns_nta_t));
+       if (nta == NULL)
+               return (ISC_R_NOMEMORY);
+
+       nta->expiry = 0;
+
+       result = isc_refcount_init(&nta->refcount, 1);
+       if (result != ISC_R_SUCCESS) {
+               isc_mem_put(mctx, nta, sizeof(nta));
+               return (result);
+       }
+
+       nta->magic = NTA_MAGIC;
+
+       *target = nta;
+       return (ISC_R_SUCCESS);
+}
index 3a86ec7fa1c956499fb32fe8923809231a21c0c0..228961ad26653f074a34bfd7ca3dc012f2f43d2a 100644 (file)
@@ -1821,6 +1821,29 @@ compute_cc(resquery_t *query, unsigned char *sit, size_t len) {
 }
 #endif
 
+static isc_result_t
+issecuredomain(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
+              isc_stdtime_t now, isc_boolean_t *issecure)
+{
+       dns_name_t suffix;
+       unsigned int labels;
+
+       /*
+        * For DS variants we need to check fom the parent domain,
+        * since there may be a negative trust anchor for the name,
+        * while the enclosing domain where the DS record lives is
+        * under a secure entry point.
+        */
+       labels = dns_name_countlabels(name);
+       if (dns_rdatatype_atparent(type) && labels > 1) {
+               dns_name_init(&suffix, NULL);
+               dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
+               name = &suffix;
+       }
+
+       return (dns_view_issecuredomain(view, name, now, issecure));
+}
+
 static isc_result_t
 resquery_send(resquery_t *query) {
        fetchctx_t *fctx;
@@ -1922,8 +1945,9 @@ resquery_send(resquery_t *query) {
                fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
        else if (res->view->enablevalidation &&
                 ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0)) {
-               result = dns_view_issecuredomain(res->view, &fctx->name,
-                                                &secure_domain);
+               result = issecuredomain(res->view, &fctx->name,
+                                       fctx->type, query->start.seconds,
+                                       &secure_domain);
                if (result != ISC_R_SUCCESS)
                        secure_domain = ISC_FALSE;
                if (res->view->dlv != NULL)
@@ -4825,8 +4849,8 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
         * Is DNSSEC validation required for this name?
         */
        if (res->view->enablevalidation) {
-               result = dns_view_issecuredomain(res->view, name,
-                                                &secure_domain);
+               result = issecuredomain(res->view, name, fctx->type,
+                                       now, &secure_domain);
                if (result != ISC_R_SUCCESS)
                        return (result);
 
@@ -5360,8 +5384,8 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
         * Is DNSSEC validation required for this name?
         */
        if (fctx->res->view->enablevalidation) {
-               result = dns_view_issecuredomain(res->view, name,
-                                                &secure_domain);
+               result = issecuredomain(res->view, name, fctx->type,
+                                       now, &secure_domain);
                if (result != ISC_R_SUCCESS)
                        return (result);
 
index 39879532d4854731e044294b1e12e4fadd0e7efd..e4bc5528ca57d20e63c2f97251e8a694b0aa5f25 100644 (file)
@@ -163,6 +163,7 @@ static const char *text[DNS_R_NRESULTS] = {
        "expired",                             /*%< 107 DNS_R_EXPIRED */
        "not dynamic",                         /*%< 108 DNS_R_NOTDYNAMIC */
        "bad EUI"                              /*%< 109 DNS_R_BADEUI */
+       "covered by negative trust anchor"     /*%< 110 DNS_R_NTACOVERED */
 };
 
 static const char *rcode_text[DNS_R_NRCODERESULTS] = {
index 824476714f3296ba9525837bdae917c49379f435..87701061998b97a01eef1eddc68164dfd0f13999 100644 (file)
@@ -45,6 +45,7 @@ SRCS =                db_test.c \
                dnstest.c \
                geoip_test.c \
                gost_test.c \
+               keytable_test.c \
                master_test.c \
                nsec3_test.c \
                private_test.c \
@@ -66,6 +67,7 @@ TARGETS =     db_test@EXEEXT@ \
                dispatch_test@EXEEXT@ \
                geoip_test@EXEEXT@ \
                gost_test@EXEEXT@ \
+               keytable_test@EXEEXT@ \
                master_test@EXEEXT@ \
                nsec3_test@EXEEXT@ \
                private_test@EXEEXT@ \
@@ -94,6 +96,11 @@ master_test@EXEEXT@: master_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
                        master_test.@O@ dnstest.@O@ ${DNSLIBS} \
                                ${ISCLIBS} ${LIBS}
 
+keytable_test@EXEEXT@: keytable_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+       ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+                       keytable_test.@O@ dnstest.@O@ ${DNSLIBS} \
+                               ${ISCLIBS} ${LIBS}
+
 time_test@EXEEXT@: time_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
        ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
                        time_test.@O@ dnstest.@O@ ${DNSLIBS} \
index 1f6edb4c539988ddc7a3db80c7c32823e1359d17..c70e516829807b3bff2832174364c9b4d802a921 100644 (file)
@@ -43,8 +43,6 @@
 #include <dns/view.h>
 #include <dns/zone.h>
 
-#include <dst/dst.h>
-
 #include "dnstest.h"
 
 isc_mem_t *mctx = NULL;
@@ -187,6 +185,26 @@ dns_test_end(void) {
                isc_mem_destroy(&mctx);
 }
 
+/*
+ * Create a view.
+ */
+isc_result_t
+dns_test_makeview(const char *name, dns_view_t **viewp)
+{
+       isc_result_t result;
+       dns_view_t *view = NULL;
+
+       CHECK(dns_view_create(mctx, dns_rdataclass_in, name, &view));
+       *viewp = view;
+
+       return (ISC_R_SUCCESS);
+
+ cleanup:
+       if (view != NULL)
+               dns_view_detach(&view);
+       return (result);
+}
+
 /*
  * Create a zone with origin 'name', return a pointer to the zone object in
  * 'zonep'.  If 'view' is set, add the zone to that view; otherwise, create
index 863ffb6aa5821015e0cf3e5b45fc593ff4f3667d..cd430477b342a696a6b75c51773cd012245f3ccb 100644 (file)
@@ -57,6 +57,9 @@ dns_test_begin(FILE *logfile, isc_boolean_t create_managers);
 void
 dns_test_end(void);
 
+isc_result_t
+dns_test_makeview(const char *name, dns_view_t **viewp);
+
 isc_result_t
 dns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view,
                                  isc_boolean_t keepview);
diff --git a/lib/dns/tests/keytable_test.c b/lib/dns/tests/keytable_test.c
new file mode 100644 (file)
index 0000000..2d97463
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * Copyright (c) 2014 Infoblox Inc. All Rights Reserved.
+ *
+ * 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.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+#include <stdio.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/util.h>
+
+#include <dns/name.h>
+#include <dns/fixedname.h>
+#include <dns/keytable.h>
+#include <dns/nta.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatastruct.h>
+#include <dns/rootns.h>
+#include <dns/view.h>
+
+#include <dst/dst.h>
+
+#include "dnstest.h"
+
+dns_keytable_t *keytable = NULL;
+dns_ntatable_t *ntatable = NULL;
+
+static const char *keystr1 = "BQEAAAABok+vaUC9neRv8yeT/FEGgN7svR8s7VBUVSBd8NsAiV8AlaAg O5FHar3JQd95i/puZos6Vi6at9/JBbN8qVmO2AuiXxVqfxMKxIcy+LEB 0Vw4NaSJ3N3uaVREso6aTSs98H/25MjcwLOr7SFfXA7bGhZatLtYY/xu kp6Km5hMfkE=";
+static const dns_keytag_t keytag1 = 30591;
+
+static const char *keystr2 = "BQEAAAABwuHz9Cem0BJ0JQTO7C/a3McR6hMaufljs1dfG/inaJpYv7vH XTrAOm/MeKp+/x6eT4QLru0KoZkvZJnqTI8JyaFTw2OM/ItBfh/hL2lm Cft2O7n3MfeqYtvjPnY7dWghYW4sVfH7VVEGm958o9nfi79532Qeklxh x8pXWdeAaRU=";
+
+/*
+ * Test utilities.  In general, these assume input parameters are valid
+ * (checking with ATF_REQUIRE_EQ, thus aborting if not) and unlikely run time
+ * errors (such as memory allocation failure) won't happen.  This helps keep
+ * the test code concise.
+ */
+
+/*
+ * Utility to convert C-string to dns_name_t.  Return a pointer to
+ * static data, and so is not thread safe.
+ */
+static dns_name_t *
+str2name(const char *namestr) {
+       static dns_fixedname_t fname;
+       static dns_name_t *name;
+       static isc_buffer_t namebuf;
+       void *deconst_namestr;
+
+       dns_fixedname_init(&fname);
+       name = dns_fixedname_name(&fname);
+       DE_CONST(namestr, deconst_namestr); /* OK, since we don't modify it */
+       isc_buffer_init(&namebuf, deconst_namestr, strlen(deconst_namestr));
+       isc_buffer_add(&namebuf, strlen(namestr));
+       ATF_REQUIRE_EQ(dns_name_fromtext(name, &namebuf, dns_rootname, 0,
+                                        NULL), ISC_R_SUCCESS);
+
+       return (name);
+}
+
+static void
+create_key(isc_uint16_t flags, isc_uint8_t proto, isc_uint8_t alg,
+          const char *keynamestr, const char *keystr, dst_key_t **target)
+{
+       dns_rdata_dnskey_t keystruct;
+       unsigned char keydata[4096];
+       isc_buffer_t keydatabuf;
+       unsigned char rrdata[4096];
+       isc_buffer_t rrdatabuf;
+       isc_region_t r;
+       const dns_rdataclass_t rdclass = dns_rdataclass_in; /* for brevity */
+
+       keystruct.common.rdclass = rdclass;
+       keystruct.common.rdtype = dns_rdatatype_dnskey;
+       keystruct.mctx = NULL;
+       ISC_LINK_INIT(&keystruct.common, link);
+       keystruct.flags = flags;
+       keystruct.protocol = proto;
+       keystruct.algorithm = alg;
+
+       isc_buffer_init(&keydatabuf, keydata, sizeof(keydata));
+       isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));
+       ATF_REQUIRE_EQ(isc_base64_decodestring(keystr, &keydatabuf),
+                      ISC_R_SUCCESS);
+       isc_buffer_usedregion(&keydatabuf, &r);
+       keystruct.datalen = r.length;
+       keystruct.data = r.base;
+       ATF_REQUIRE_EQ(dns_rdata_fromstruct(NULL, keystruct.common.rdclass,
+                                           keystruct.common.rdtype,
+                                           &keystruct, &rrdatabuf),
+                      ISC_R_SUCCESS);
+
+       ATF_REQUIRE_EQ(dst_key_fromdns(str2name(keynamestr), rdclass,
+                                      &rrdatabuf, mctx, target),
+                      ISC_R_SUCCESS);
+}
+
+/* Common setup: create a keytable and ntatable to test with a few keys */
+static void
+create_tables() {
+       dst_key_t *key = NULL;
+       isc_stdtime_t now;
+
+       ATF_REQUIRE_EQ(dns_keytable_create(mctx, &keytable), ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_ntatable_create(mctx, &ntatable), ISC_R_SUCCESS);
+
+       /* Add a normal key */
+       create_key(257, 3, 5, "example.com", keystr1, &key);
+       ATF_REQUIRE_EQ(dns_keytable_add(keytable, ISC_FALSE, &key),
+                      ISC_R_SUCCESS);
+
+       /* Add a null key */
+       ATF_REQUIRE_EQ(dns_keytable_marksecure(keytable,
+                                              str2name("null.example")),
+                      ISC_R_SUCCESS);
+
+       /* Add a negative trust anchor, duration 1 hour */
+       isc_stdtime_get(&now);
+       ATF_REQUIRE_EQ(dns_ntatable_add(ntatable,
+                                       str2name("insecure.example"),
+                                       now + 3600),
+                      ISC_R_SUCCESS);
+}
+
+static void
+destroy_tables() {
+       if (ntatable != NULL)
+               dns_ntatable_detach(&ntatable);
+       if (keytable != NULL)
+               dns_keytable_detach(&keytable);
+}
+
+/*
+ * Individual unit tests
+ */
+
+ATF_TC(add);
+ATF_TC_HEAD(add, tc) {
+       atf_tc_set_md_var(tc, "descr", "add keys to the keytable");
+}
+ATF_TC_BODY(add, tc) {
+       dst_key_t *key = NULL;
+       dns_keynode_t *keynode = NULL;
+       dns_keynode_t *next_keynode = NULL;
+       dns_keynode_t *null_keynode = NULL;
+
+       UNUSED(tc);
+
+       ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+       create_tables();
+
+       /*
+        * Get the keynode for the example.com key.  There's no other key for
+        * the name, so nextkeynode() should return NOTFOUND.
+        */
+       ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("example.com"),
+                                        &keynode), ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode,
+                                               &next_keynode), ISC_R_NOTFOUND);
+
+       /*
+        * Try to add the same key.  This should have no effect, so
+        * nextkeynode() should still return NOTFOUND.
+        */
+       create_key(257, 3, 5, "example.com", keystr1, &key);
+       ATF_REQUIRE_EQ(dns_keytable_add(keytable, ISC_FALSE, &key),
+                      ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode,
+                                               &next_keynode), ISC_R_NOTFOUND);
+
+       /* Add another key (different keydata) */
+       dns_keytable_detachkeynode(keytable, &keynode);
+       create_key(257, 3, 5, "example.com", keystr2, &key);
+       ATF_REQUIRE_EQ(dns_keytable_add(keytable, ISC_FALSE, &key),
+                      ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("example.com"),
+                                        &keynode), ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode,
+                                               &next_keynode), ISC_R_SUCCESS);
+       dns_keytable_detachkeynode(keytable, &next_keynode);
+
+       /*
+        * Add a normal key to a name that has a null key.  The null key node
+        * will be updated with the normal key.
+        */
+       dns_keytable_detachkeynode(keytable, &keynode);
+       ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("null.example"),
+                                        &null_keynode), ISC_R_SUCCESS);
+       create_key(257, 3, 5, "null.example", keystr2, &key);
+       ATF_REQUIRE_EQ(dns_keytable_add(keytable, ISC_FALSE, &key),
+                      ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("null.example"),
+                                        &keynode), ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(keynode, null_keynode); /* should be the same node */
+       ATF_REQUIRE(dns_keynode_key(keynode) != NULL); /* now have a key */
+       dns_keytable_detachkeynode(keytable, &null_keynode);
+
+       /*
+        * Try to add a null key to a name that already has a key.  It's
+        * effectively no-op, so the same key node is still there, with no
+        * no next node.
+        * (Note: this and above checks confirm that if a name has a null key
+        * that's the only key for the name).
+        */
+       ATF_REQUIRE_EQ(dns_keytable_marksecure(keytable,
+                                              str2name("null.example")),
+                      ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("null.example"),
+                                        &null_keynode), ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(keynode, null_keynode);
+       ATF_REQUIRE(dns_keynode_key(keynode) != NULL);
+       ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode,
+                                               &next_keynode), ISC_R_NOTFOUND);
+       dns_keytable_detachkeynode(keytable, &null_keynode);
+
+       dns_keytable_detachkeynode(keytable, &keynode);
+       destroy_tables();
+       dns_test_end();
+}
+
+ATF_TC(delete);
+ATF_TC_HEAD(delete, tc) {
+       atf_tc_set_md_var(tc, "descr", "delete keys from the keytable");
+}
+ATF_TC_BODY(delete, tc) {
+       UNUSED(tc);
+
+       ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+       create_tables();
+
+       /* dns_keytable_delete requires exact match */
+       ATF_REQUIRE_EQ(dns_keytable_delete(keytable, str2name("example.org")),
+                      ISC_R_NOTFOUND);
+       ATF_REQUIRE_EQ(dns_keytable_delete(keytable, str2name("s.example.com")),
+                      ISC_R_NOTFOUND);
+       ATF_REQUIRE_EQ(dns_keytable_delete(keytable, str2name("example.com")),
+                      ISC_R_SUCCESS);
+
+       /* works also for nodes with a null key */
+       ATF_REQUIRE_EQ(dns_keytable_delete(keytable, str2name("null.example")),
+                      ISC_R_SUCCESS);
+
+       /* or a negative trust anchor */
+       ATF_REQUIRE_EQ(dns_ntatable_delete(ntatable,
+                                          str2name("insecure.example")),
+                      ISC_R_SUCCESS);
+
+       destroy_tables();
+       dns_test_end();
+}
+
+ATF_TC(deletekeynode);
+ATF_TC_HEAD(deletekeynode, tc) {
+       atf_tc_set_md_var(tc, "descr", "delete key nodes from the keytable");
+}
+ATF_TC_BODY(deletekeynode, tc) {
+       dst_key_t *key = NULL;
+
+       UNUSED(tc);
+
+       ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+       create_tables();
+
+       /* key name doesn't match */
+       create_key(257, 3, 5, "example.org", keystr1, &key);
+       ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+                      ISC_R_NOTFOUND);
+       dst_key_free(&key);
+
+       /* subdomain match is the same as no match */
+       create_key(257, 3, 5, "sub.example.com", keystr1, &key);
+       ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+                      ISC_R_NOTFOUND);
+       dst_key_free(&key);
+
+       /* name matches but key doesn't match (resulting in PARTIALMATCH) */
+       create_key(257, 3, 5, "example.com", keystr2, &key);
+       ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+                      DNS_R_PARTIALMATCH);
+       dst_key_free(&key);
+
+       /*
+        * exact match.  after deleting the node the internal rbt node will be
+        * empty, and any delete or deletekeynode attempt should result in
+        * NOTFOUND.
+        */
+       create_key(257, 3, 5, "example.com", keystr1, &key);
+       ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+                      ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+                      ISC_R_NOTFOUND);
+       ATF_REQUIRE_EQ(dns_keytable_delete(keytable, str2name("example.com")),
+                      ISC_R_NOTFOUND);
+       dst_key_free(&key);
+
+       /*
+        * The only null key node for a name can be deleted as long as the
+        * passed key name matches.
+        */
+       create_key(257, 3, 5, "null.example", keystr1, &key);
+       ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+                      ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+                      ISC_R_NOTFOUND);
+       dst_key_free(&key);
+
+       destroy_tables();
+       dns_test_end();
+}
+
+ATF_TC(find);
+ATF_TC_HEAD(find, tc) {
+       atf_tc_set_md_var(tc, "descr", "check find-variant operations");
+}
+ATF_TC_BODY(find, tc) {
+       dns_keynode_t *keynode = NULL;
+       dns_fixedname_t fname;
+       dns_name_t *name;
+
+       UNUSED(tc);
+
+       ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+       create_tables();
+
+       /*
+        * dns_keytable_find() requires exact name match.  It matches node
+        * that has a null key, too.  But it doesn't match a negative trust
+        * anchor.
+        */
+       ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("example.org"),
+                                        &keynode), ISC_R_NOTFOUND);
+       ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("sub.example.com"),
+                                        &keynode), ISC_R_NOTFOUND);
+       ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("example.com"),
+                                        &keynode), ISC_R_SUCCESS);
+       dns_keytable_detachkeynode(keytable, &keynode);
+       ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("null.example"),
+                                        &keynode), ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_keynode_key(keynode), NULL);
+       dns_keytable_detachkeynode(keytable, &keynode);
+       ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("insecure.example"),
+                                        &keynode), ISC_R_NOTFOUND);
+
+       /*
+        * dns_keytable_finddeepestmatch() allows partial match.  Also match
+        * nodes with a null key or a negative trust anchor.
+        */
+       dns_fixedname_init(&fname);
+       name = dns_fixedname_name(&fname);
+       ATF_REQUIRE_EQ(dns_keytable_finddeepestmatch(keytable,
+                                                    str2name("example.com"),
+                                                    name), ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_name_equal(name, str2name("example.com")), ISC_TRUE);
+       ATF_REQUIRE_EQ(dns_keytable_finddeepestmatch(keytable,
+                                                    str2name("s.example.com"),
+                                                    name), ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_name_equal(name, str2name("example.com")), ISC_TRUE);
+       ATF_REQUIRE_EQ(dns_keytable_finddeepestmatch(keytable,
+                                                    str2name("example.org"),
+                                                    name), ISC_R_NOTFOUND);
+       ATF_REQUIRE_EQ(dns_keytable_finddeepestmatch(keytable,
+                                                    str2name("null.example"),
+                                                    name), ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(dns_name_equal(name, str2name("null.example")),
+                      ISC_TRUE);
+
+       /*
+        * dns_keytable_findkeynode() requires exact name, algorithm, keytag
+        * match.  If algorithm or keytag doesn't match, should result in
+        * PARTIALMATCH.  Same for a node with a null key or a negative trust
+        * anchor.
+        */
+       ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+                                               str2name("example.org"),
+                                               5, keytag1, &keynode),
+                      ISC_R_NOTFOUND);
+       ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+                                               str2name("sub.example.com"),
+                                               5, keytag1, &keynode),
+                      ISC_R_NOTFOUND);
+       ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+                                               str2name("example.com"),
+                                               4, keytag1, &keynode),
+                      DNS_R_PARTIALMATCH); /* different algorithm */
+       ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+                                               str2name("example.com"),
+                                               5, keytag1 + 1, &keynode),
+                      DNS_R_PARTIALMATCH); /* different keytag */
+       ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+                                               str2name("null.example"),
+                                               5, 0, &keynode),
+                      DNS_R_PARTIALMATCH); /* null key */
+       ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+                                               str2name("example.com"),
+                                               5, keytag1, &keynode),
+                      ISC_R_SUCCESS); /* complete match */
+       dns_keytable_detachkeynode(keytable, &keynode);
+
+       destroy_tables();
+       dns_test_end();
+}
+
+ATF_TC(issecuredomain);
+ATF_TC_HEAD(issecuredomain, tc) {
+       atf_tc_set_md_var(tc, "descr", "check issecuredomain()");
+}
+ATF_TC_BODY(issecuredomain, tc) {
+       isc_boolean_t issecure;
+       const char **n;
+       const char *names[] = {"example.com", "sub.example.com",
+                              "null.example", "sub.null.example", NULL};
+
+       UNUSED(tc);
+       ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+       create_tables();
+
+       /*
+        * Domains that are an exact or partial match of a key name are
+        * considered secure.  It's the case even if the key is null
+        * (validation will then fail, but that's actually the intended effect
+        * of installing a null key).
+        */
+       for (n = names; *n != NULL; n++) {
+               ATF_REQUIRE_EQ(dns_keytable_issecuredomain(keytable,
+                                                          str2name(*n),
+                                                          NULL,
+                                                          &issecure),
+                              ISC_R_SUCCESS);
+               ATF_REQUIRE_EQ(issecure, ISC_TRUE);
+       }
+
+       /*
+        * Domains that are an exact or partial match of a negative trust
+        * anchor are considered insecure.
+        */
+       ATF_REQUIRE_EQ(dns_keytable_issecuredomain(keytable,
+                                                  str2name("insecure.example"),
+                                                  NULL,
+                                                  &issecure),
+                      ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(issecure, ISC_FALSE);
+       ATF_REQUIRE_EQ(dns_keytable_issecuredomain(
+                              keytable, str2name("sub.insecure.example"),
+                              NULL, &issecure), ISC_R_SUCCESS);
+       ATF_REQUIRE_EQ(issecure, ISC_FALSE);
+
+       destroy_tables();
+       dns_test_end();
+}
+
+ATF_TC(dump);
+ATF_TC_HEAD(dump, tc) {
+       atf_tc_set_md_var(tc, "descr", "check dns_keytable_dump()");
+}
+ATF_TC_BODY(dump, tc) {
+       UNUSED(tc);
+
+       ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+       create_tables();
+
+       /*
+        * Right now, we only confirm the dump attempt doesn't cause disruption
+        * (so we don't check the dump content).
+        */
+       ATF_REQUIRE_EQ(dns_keytable_dump(keytable, stdout), ISC_R_SUCCESS);
+
+       destroy_tables();
+       dns_test_end();
+}
+
+ATF_TC(nta);
+ATF_TC_HEAD(nta, tc) {
+       atf_tc_set_md_var(tc, "descr", "check negative trust anchors");
+}
+ATF_TC_BODY(nta, tc) {
+       isc_result_t result;
+       dst_key_t *key = NULL;
+       isc_boolean_t issecure, covered;
+       dns_view_t *view = NULL;
+       isc_stdtime_t now;
+
+       UNUSED(tc);
+
+       result = dns_test_begin(NULL, ISC_TRUE);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       result = dns_test_makeview("view", &view);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       result = dns_view_initsecroots(view, mctx);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       result = dns_view_getsecroots(view, &keytable);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       result = dns_view_initntatable(view, mctx);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       result = dns_view_getntatable(view, &ntatable);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       create_key(257, 3, 5, "example", keystr1, &key);
+       result = dns_keytable_add(keytable, ISC_FALSE, &key);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       isc_stdtime_get(&now);
+       result = dns_ntatable_add(ntatable,
+                                 str2name("insecure.example"), now + 1);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       /* Should be secure */
+       result = dns_view_issecuredomain(view,
+                                        str2name("test.secure.example"),
+                                        now, &issecure);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK(issecure);
+
+       /* Should not be secure */
+       result = dns_view_issecuredomain(view,
+                                        str2name("test.insecure.example"),
+                                        now, &issecure);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK(!issecure);
+
+       /* NTA covered */
+       covered = dns_view_ntacovers(view, now, str2name("insecure.example"),
+                                    dns_rootname);
+       ATF_CHECK(covered);
+
+       /* Not NTA covered */
+       covered = dns_view_ntacovers(view, now, str2name("secure.example"),
+                                    dns_rootname);
+       ATF_CHECK(!covered);
+
+       /* As of now + 2, the NTA should be clear */
+       result = dns_view_issecuredomain(view,
+                                        str2name("test.insecure.example"),
+                                        now + 2, &issecure);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK(issecure);
+
+       /* Now check deletion */
+       result = dns_ntatable_add(ntatable,
+                                 str2name("new.example"), now + 3600);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       result = dns_view_issecuredomain(view, str2name("test.new.example"),
+                                        now, &issecure);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK(!issecure);
+
+       result = dns_ntatable_delete(ntatable, str2name("new.example"));
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       result = dns_view_issecuredomain(view, str2name("test.new.example"),
+                                        now, &issecure);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK(issecure);
+
+       /* Clean up */
+       dns_ntatable_detach(&ntatable);
+       dns_keytable_detach(&keytable);
+       dns_view_detach(&view);
+
+       dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+       ATF_TP_ADD_TC(tp, add);
+       ATF_TP_ADD_TC(tp, delete);
+       ATF_TP_ADD_TC(tp, deletekeynode);
+       ATF_TP_ADD_TC(tp, find);
+       ATF_TP_ADD_TC(tp, issecuredomain);
+       ATF_TP_ADD_TC(tp, dump);
+       ATF_TP_ADD_TC(tp, nta);
+
+       return (atf_no_error());
+}
index 6e09762f60ef9d9754dfd838efc8fe46fb55eb80..f7d7d70e2049a08fd2fc45703c86e68b565a0dec 100644 (file)
@@ -15,8 +15,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
-
 #include <config.h>
 
 #include <isc/base32.h>
@@ -1649,12 +1647,9 @@ validate(dns_validator_t *val, isc_boolean_t resume) {
                        validator_log(val, ISC_LOG_DEBUG(3),
                                      "failed to verify rdataset");
                else {
-                       isc_stdtime_t now;
-
-                       isc_stdtime_get(&now);
                        dns_rdataset_trimttl(event->rdataset,
                                             event->sigrdataset,
-                                            val->siginfo, now,
+                                            val->siginfo, val->start,
                                             val->view->acceptexpired);
                }
 
@@ -3075,6 +3070,11 @@ startfinddlvsep(dns_validator_t *val, dns_name_t *unsecure) {
                markanswer(val, "startfinddlvsep (1)");
                return (ISC_R_SUCCESS);
        }
+       if (result == DNS_R_NTACOVERED) {
+               validator_log(val, ISC_LOG_DEBUG(3), "DLV covered by NTA");
+               validator_done(val, ISC_R_SUCCESS);
+               return (ISC_R_SUCCESS);
+       }
        if (result != ISC_R_SUCCESS) {
                validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s",
                              dns_result_totext(result));
@@ -3164,6 +3164,9 @@ finddlvsep(dns_validator_t *val, isc_boolean_t resume) {
                return (DNS_R_NOVALIDSIG);
        }
 
+       if (dns_view_ntacovers(val->view, val->start, dlvname, val->view->dlv))
+               return (DNS_R_NTACOVERED);
+
        while (dns_name_countlabels(dlvname) >=
               dns_name_countlabels(val->view->dlv) + val->dlvlabels) {
                dns_name_format(dlvname, namebuf, sizeof(namebuf));
@@ -3780,6 +3783,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
        dns_fixedname_init(&val->wild);
        dns_fixedname_init(&val->nearest);
        dns_fixedname_init(&val->closest);
+       isc_stdtime_get(&val->start);
        ISC_LINK_INIT(val, link);
        val->magic = VALIDATOR_MAGIC;
 
index c93a080747d9e85e27a54f1e7da175232a949e9c..d3462ffe2f0ef5fe250f9fb33165a11534c87cf2 100644 (file)
@@ -45,6 +45,7 @@
 #include <dns/keyvalues.h>
 #include <dns/master.h>
 #include <dns/masterdump.h>
+#include <dns/nta.h>
 #include <dns/order.h>
 #include <dns/peer.h>
 #include <dns/rrl.h>
@@ -110,6 +111,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
                }
        }
        view->secroots_priv = NULL;
+       view->ntatable_priv = NULL;
        view->fwdtable = NULL;
        result = dns_fwdtable_create(mctx, &view->fwdtable);
        if (result != ISC_R_SUCCESS) {
@@ -444,6 +446,8 @@ destroy(dns_view_t *view) {
                dns_stats_detach(&view->resquerystats);
        if (view->secroots_priv != NULL)
                dns_keytable_detach(&view->secroots_priv);
+       if (view->ntatable_priv != NULL)
+               dns_ntatable_detach(&view->ntatable_priv);
        for (dns64 = ISC_LIST_HEAD(view->dns64);
             dns64 != NULL;
             dns64 = ISC_LIST_HEAD(view->dns64)) {
@@ -1771,6 +1775,24 @@ dns_view_getresquerystats(dns_view_t *view, dns_stats_t **statsp) {
                dns_stats_attach(view->resquerystats, statsp);
 }
 
+isc_result_t
+dns_view_initntatable(dns_view_t *view, isc_mem_t *mctx) {
+       REQUIRE(DNS_VIEW_VALID(view));
+       if (view->ntatable_priv != NULL)
+               dns_ntatable_detach(&view->ntatable_priv);
+       return (dns_ntatable_create(mctx, &view->ntatable_priv));
+}
+
+isc_result_t
+dns_view_getntatable(dns_view_t *view, dns_ntatable_t **ntp) {
+       REQUIRE(DNS_VIEW_VALID(view));
+       REQUIRE(ntp != NULL && *ntp == NULL);
+       if (view->ntatable_priv == NULL)
+               return (ISC_R_NOTFOUND);
+       dns_ntatable_attach(view->ntatable_priv, ntp);
+       return (ISC_R_SUCCESS);
+}
+
 isc_result_t
 dns_view_initsecroots(dns_view_t *view, isc_mem_t *mctx) {
        REQUIRE(DNS_VIEW_VALID(view));
@@ -1789,15 +1811,46 @@ dns_view_getsecroots(dns_view_t *view, dns_keytable_t **ktp) {
        return (ISC_R_SUCCESS);
 }
 
+isc_boolean_t
+dns_view_ntacovers(dns_view_t *view, isc_stdtime_t now,
+                  dns_name_t *name, dns_name_t *anchor)
+{
+       REQUIRE(DNS_VIEW_VALID(view));
+
+       if (view->ntatable_priv == NULL)
+               return (ISC_FALSE);
+
+       return (dns_ntatable_covered(view->ntatable_priv, now, name, anchor));
+}
+
 isc_result_t
 dns_view_issecuredomain(dns_view_t *view, dns_name_t *name,
-                        isc_boolean_t *secure_domain) {
+                       isc_stdtime_t now, isc_boolean_t *secure_domain)
+{
+       isc_result_t result;
+       isc_boolean_t secure = ISC_FALSE;
+       dns_fixedname_t fn;
+       dns_name_t *anchor;
+
        REQUIRE(DNS_VIEW_VALID(view));
 
        if (view->secroots_priv == NULL)
                return (ISC_R_NOTFOUND);
-       return (dns_keytable_issecuredomain(view->secroots_priv, name,
-                                           secure_domain));
+
+       dns_fixedname_init(&fn);
+       anchor = dns_fixedname_name(&fn);
+
+       result = dns_keytable_issecuredomain(view->secroots_priv, name,
+                                            anchor, &secure);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       if (secure && view->ntatable_priv != NULL &&
+           dns_ntatable_covered(view->ntatable_priv, now, name, anchor))
+               secure = ISC_FALSE;
+
+       *secure_domain = secure;
+       return (ISC_R_SUCCESS);
 }
 
 void
index 746ed48a9e59baa2c61ec3716bfd98a3e5fbcf68..b3b71d30f908b156da288e21cb7e8b9584d6b125 100644 (file)
@@ -483,6 +483,14 @@ dns_nsec_isset
 dns_nsec_nseconly
 dns_nsec_setbit
 dns_nsec_typepresent
+dns_nta_create
+dns_ntatable_add
+dns_ntatable_attach
+dns_ntatable_covered
+dns_ntatable_create
+dns_ntatable_delete
+dns_ntatable_detach
+dns_ntatable_dump
 dns_opcode_totext
 dns_opcodestats_create
 dns_opcodestats_dump
index f7e5a6b98ae18805dacbd673ee640f339573812e..7df9f0d182f5e0146933a1703c831b9c28995e7e 100644 (file)
@@ -276,6 +276,10 @@ SOURCE=..\include\dns\nsec3.h
 # End Source File
 # Begin Source File
 
+SOURCE=..\include\dns\nta.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\include\dns\order.h
 # End Source File
 # Begin Source File
@@ -614,6 +618,10 @@ SOURCE=..\nsec3.c
 # End Source File
 # Begin Source File
 
+SOURCE=..\nta.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\order.c
 # End Source File
 # Begin Source File
index 9b96feb07aa07d9d3b2e597fc2924c37a1782936..6dd889f69ebd58b8ca03173cbf3ea149b95b4c08 100644 (file)
@@ -166,6 +166,7 @@ CLEAN :
        -@erase "$(INTDIR)\ncache.obj"
        -@erase "$(INTDIR)\nsec.obj"
        -@erase "$(INTDIR)\nsec3.obj"
+       -@erase "$(INTDIR)\nta.obj"
 @IF OPENSSL
        -@erase "$(INTDIR)\openssl_link.obj"
        -@erase "$(INTDIR)\openssldh_link.obj"
@@ -315,6 +316,7 @@ LINK32_OBJS= \
        "$(INTDIR)\ncache.obj" \
        "$(INTDIR)\nsec.obj" \
        "$(INTDIR)\nsec3.obj" \
+       "$(INTDIR)\nta.obj" \
        "$(INTDIR)\order.obj" \
        "$(INTDIR)\peer.obj" \
        "$(INTDIR)\portlist.obj" \
@@ -504,6 +506,8 @@ CLEAN :
        -@erase "$(INTDIR)\nsec.sbr"
        -@erase "$(INTDIR)\nsec3.obj"
        -@erase "$(INTDIR)\nsec3.sbr"
+       -@erase "$(INTDIR)\nta.obj"
+       -@erase "$(INTDIR)\nta.sbr"
 @IF OPENSSL
        -@erase "$(INTDIR)\openssl_link.obj"
        -@erase "$(INTDIR)\openssl_link.sbr"
@@ -705,6 +709,7 @@ BSC32_SBRS= \
        "$(INTDIR)\ncache.sbr" \
        "$(INTDIR)\nsec.sbr" \
        "$(INTDIR)\nsec3.sbr" \
+       "$(INTDIR)\nta.sbr" \
        "$(INTDIR)\order.sbr" \
        "$(INTDIR)\peer.sbr" \
        "$(INTDIR)\portlist.sbr" \
@@ -820,6 +825,7 @@ LINK32_OBJS= \
        "$(INTDIR)\ncache.obj" \
        "$(INTDIR)\nsec.obj" \
        "$(INTDIR)\nsec3.obj" \
+       "$(INTDIR)\nta.obj" \
        "$(INTDIR)\order.obj" \
        "$(INTDIR)\peer.obj" \
        "$(INTDIR)\portlist.obj" \
@@ -1557,6 +1563,24 @@ SOURCE=..\nsec3.c
        $(CPP) $(CPP_PROJ) $(SOURCE)
 
 
+!ENDIF 
+
+SOURCE=..\nta.c
+
+!IF  "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\nta.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\nta.obj"    "$(INTDIR)\nta.sbr" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
 !ENDIF 
 
 SOURCE=..\order.c
index 7a065da44c7f66bb8496437ca41229c190b5429b..77f211dd5e3daeab25d2367a1c6625d49af4f406 100644 (file)
     <ClCompile Include="..\nsec3.c">\r
       <Filter>Library Source Files</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\nta.c">\r
+      <Filter>Library Source Files</Filter>\r
+    </ClCompile>\r
     <ClCompile Include="..\order.c">\r
       <Filter>Library Source Files</Filter>\r
     </ClCompile>\r
     <ClInclude Include="..\include\dns\nsec3.h">\r
       <Filter>Library Header Files</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\include\dns\nta.h">\r
+      <Filter>Library Header Files</Filter>\r
+    </ClInclude>\r
     <ClInclude Include="..\include\dns\opcode.h">\r
       <Filter>Library Header Files</Filter>\r
     </ClInclude>\r
       <Filter>Dst Header Files</Filter>\r
     </ClInclude>\r
   </ItemGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r
index f402d7466b4f026a92df5411f7128fa6c635ed78..daa400f9023971f31b51f9b2ac173de2dfbe0404 100644 (file)
     <ClCompile Include="..\ncache.c" />\r
     <ClCompile Include="..\nsec.c" />\r
     <ClCompile Include="..\nsec3.c" />\r
+    <ClCompile Include="..\nta.c" />\r
 @IF OPENSSL\r
     <ClCompile Include="..\openssldh_link.c" />\r
     <ClCompile Include="..\openssldsa_link.c" />\r
     <ClInclude Include="..\include\dns\ncache.h" />\r
     <ClInclude Include="..\include\dns\nsec.h" />\r
     <ClInclude Include="..\include\dns\nsec3.h" />\r
+    <ClInclude Include="..\include\dns\nta.h" />\r
     <ClInclude Include="..\include\dns\opcode.h" />\r
     <ClInclude Include="..\include\dns\order.h" />\r
     <ClInclude Include="..\include\dns\peer.h" />\r
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
   <ImportGroup Label="ExtensionTargets">\r
   </ImportGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r