]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Make NTAs work with validating forwarders
authorMichał Kępień <michal@isc.org>
Wed, 24 Apr 2019 09:17:15 +0000 (11:17 +0200)
committerEvan Hunt <each@isc.org>
Fri, 10 May 2019 04:05:50 +0000 (21:05 -0700)
If named is configured to perform DNSSEC validation and also forwards
all queries ("forward only;") to validating resolvers, negative trust
anchors do not work properly because the CD bit is not set in queries
sent to the forwarders.  As a result, instead of retrieving bogus DNSSEC
material and making validation decisions based on its configuration,
named is only receiving SERVFAIL responses to queries for bogus data.
Fix by ensuring the CD bit is always set in queries sent to forwarders
if the query name is covered by an NTA.

(cherry picked from commit 5e8048827015f4a04e61ae5f3c92758755fee6c3)

bin/tests/system/dnssec/ns1/sign.sh
bin/tests/system/dnssec/ns2/sign.sh
bin/tests/system/dnssec/ns9/named.conf.in [new file with mode: 0644]
bin/tests/system/dnssec/setup.sh
bin/tests/system/dnssec/tests.sh
lib/dns/include/dns/view.h
lib/dns/resolver.c
lib/dns/tests/keytable_test.c
lib/dns/view.c
util/copyrights

index 0bcf5673cd35bad16d1e762e6bbf8f77648f5ba2..313d098ab923173345960fa0a9a03e922ec6fbea 100644 (file)
@@ -42,6 +42,7 @@ cp trusted.conf ../ns3/trusted.conf
 cp trusted.conf ../ns4/trusted.conf
 cp trusted.conf ../ns6/trusted.conf
 cp trusted.conf ../ns7/trusted.conf
+cp trusted.conf ../ns9/trusted.conf
 
 # ...or with a managed key.
 keyfile_to_managed_keys $keyname > managed.conf
index 584c36ecdf50188ce3dbbd591e8ef250e696a747..b93651aee88eb2f30ffbaad5e17b65d366b33b22 100644 (file)
@@ -18,7 +18,7 @@ SYSTEMTESTTOP=../..
 echo_i "ns2/sign.sh"
 
 # Get the DS records for the "trusted." and "managed." zones.
-for subdomain in secure unsupported disabled enabled
+for subdomain in secure unsupported
 do
        cp ../ns3/dsset-$subdomain.managed$TP .
        cp ../ns3/dsset-$subdomain.trusted$TP .
diff --git a/bin/tests/system/dnssec/ns9/named.conf.in b/bin/tests/system/dnssec/ns9/named.conf.in
new file mode 100644 (file)
index 0000000..d655112
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+// NS9
+
+options {
+       query-source address 10.53.0.9;
+       notify-source 10.53.0.9;
+       transfer-source 10.53.0.9;
+       port @PORT@;
+       pid-file "named.pid";
+       listen-on { 10.53.0.9; };
+       listen-on-v6 { none; };
+       recursion yes;
+       dnssec-enable yes;
+       dnssec-validation yes;
+       forward only;
+       forwarders { 10.53.0.4; };
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-sha256;
+};
+
+controls {
+       inet 10.53.0.9 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+include "trusted.conf";
index c6767054bcad659dca3e2b46eff2e811947b5bbd..a6838de97c1a52f5c6469be39883460c3120afe3 100644 (file)
@@ -26,6 +26,7 @@ copy_setports ns5/named1.conf.in ns5/named.conf
 copy_setports ns6/named.conf.in ns6/named.conf
 copy_setports ns7/named.conf.in ns7/named.conf
 copy_setports ns8/named.conf.in ns8/named.conf
+copy_setports ns9/named.conf.in ns9/named.conf
 
 cd ns1
 $SHELL sign.sh
index 71bf407e160923a643db3c8951d71904626926d0..51dc1178db96af7e65f0815388327e7099719825 100644 (file)
@@ -2280,7 +2280,29 @@ $RNDCCMD 10.53.0.4 nta -remove secure.example > rndc.out.ns4.test$n.3 2>/dev/nul
 
 if [ $ret != 0 ]; then echo_i "failed - NTA lifetime clamping failed"; fi
 status=`expr $status + $ret`
-ret=0
+
+n=`expr $n + 1`
+echo_i "checking that NTAs work with 'forward only;' to a validating resolver ($n)"
+ret=0
+# Sanity check behavior without an NTA in place.
+$DIG $DIGOPTS @10.53.0.9 badds.example. SOA > dig.out.ns9.test$n.1 || ret=1
+grep "SERVFAIL" dig.out.ns9.test$n.1 > /dev/null || ret=1
+grep "ANSWER: 0" dig.out.ns9.test$n.1 > /dev/null || ret=1
+grep "flags:[^;]* ad[ ;].*QUERY" dig.out.ns9.test$n.1 > /dev/null && ret=1
+# Add an NTA, expecting that to cause resolution to succeed.
+$RNDCCMD 10.53.0.9 nta badds.example > rndc.out.ns9.test$n.1 2>&1 || ret=1
+$DIG $DIGOPTS @10.53.0.9 badds.example. SOA > dig.out.ns9.test$n.2 || ret=1
+grep "NOERROR" dig.out.ns9.test$n.2 > /dev/null || ret=1
+grep "ANSWER: 2" dig.out.ns9.test$n.2 > /dev/null || ret=1
+grep "flags:[^;]* ad[ ;].*QUERY" dig.out.ns9.test$n.2 > /dev/null && ret=1
+# Remove the NTA, expecting that to cause resolution to fail again.
+$RNDCCMD 10.53.0.9 nta -remove badds.example > rndc.out.ns9.test$n.2 2>&1 || ret=1
+$DIG $DIGOPTS @10.53.0.9 badds.example. SOA > dig.out.ns9.test$n.3 || ret=1
+grep "SERVFAIL" dig.out.ns9.test$n.3 > /dev/null || ret=1
+grep "ANSWER: 0" dig.out.ns9.test$n.3 > /dev/null || ret=1
+grep "flags:[^;]* ad[ ;].*QUERY" dig.out.ns9.test$n.3 > /dev/null && ret=1
+if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
 
 echo_i "completed NTA tests"
 
index 8e21298c03944ef40634858a7f373a88b414d491..335e46129fa5da7ef84c006239cda0f7242a2a0a 100644 (file)
@@ -1189,14 +1189,16 @@ 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_stdtime_t now, bool checknta,
+                       isc_stdtime_t now, bool checknta, bool *ntap,
                        bool *secure_domain);
 /*%<
  * Is 'name' at or beneath a trusted key, and not covered by a valid
  * negative trust anchor?  Put answer in '*secure_domain'.
  *
  * If 'checknta' is false, ignore the NTA table in determining
- * whether this is a secure domain.
+ * whether this is a secure domain. If 'checknta' is not false, and if
+ * 'ntap' is non-NULL, then '*ntap' will be updated with true if the
+ * name is covered by an NTA.
  *
  * Requires:
  * \li 'view' is valid.
index cb678e085e94a69ad2ffa5d23a80424a2f56d613..3a945366f3d31f613efa0f076f32a2256620732d 100644 (file)
@@ -2086,8 +2086,7 @@ compute_cc(resquery_t *query, unsigned char *cookie, size_t len) {
 
 static isc_result_t
 issecuredomain(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
-              isc_stdtime_t now, bool checknta,
-              bool *issecure)
+              isc_stdtime_t now, bool checknta, bool *ntap, bool *issecure)
 {
        dns_name_t suffix;
        unsigned int labels;
@@ -2105,7 +2104,8 @@ issecuredomain(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
                name = &suffix;
        }
 
-       return (dns_view_issecuredomain(view, name, now, checknta, issecure));
+       return (dns_view_issecuredomain(view, name, now, checknta,
+                                       ntap, issecure));
 }
 
 static bool
@@ -2114,17 +2114,20 @@ wouldvalidate(fetchctx_t *fctx) {
        isc_result_t result;
        isc_stdtime_t now;
 
-       if (!fctx->res->view->enablevalidation)
+       if (!fctx->res->view->enablevalidation) {
                return (false);
+       }
 
-       if (fctx->res->view->dlv != NULL)
+       if (fctx->res->view->dlv != NULL) {
                return (true);
+       }
 
        isc_stdtime_get(&now);
        result = dns_view_issecuredomain(fctx->res->view, &fctx->name,
-                                        now, true, &secure_domain);
-       if (result != ISC_R_SUCCESS)
+                                        now, true, NULL, &secure_domain);
+       if (result != ISC_R_SUCCESS) {
                return (false);
+       }
        return (secure_domain);
 }
 
@@ -2229,24 +2232,30 @@ resquery_send(resquery_t *query) {
         * question is under a secure entry point and this is a
         * recursive/forward query -- unless the client said not to.
         */
-       if ((query->options & DNS_FETCHOPT_NOCDFLAG) != 0)
+       if ((query->options & DNS_FETCHOPT_NOCDFLAG) != 0) {
                /* Do nothing */
-               ;
-       else if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0)
+       } else if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) {
                fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
-       else if (res->view->enablevalidation &&
-                ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0))
+       else if (res->view->enablevalidation &&
+                  ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0))
        {
                bool checknta = ((query->options & DNS_FETCHOPT_NONTA) == 0);
+               bool ntacovered = false;
                result = issecuredomain(res->view, &fctx->name, fctx->type,
                                        isc_time_seconds(&query->start),
-                                       checknta, &secure_domain);
-               if (result != ISC_R_SUCCESS)
+                                       checknta, &ntacovered, &secure_domain);
+               if (result != ISC_R_SUCCESS) {
                        secure_domain = false;
-               if (res->view->dlv != NULL)
+               }
+               if (res->view->dlv != NULL) {
                        secure_domain = true;
-               if (secure_domain)
+               }
+
+               if (secure_domain ||
+                   (ISFORWARDER(query->addrinfo) && ntacovered))
+               {
                        fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
+               }
        }
 
        /*
@@ -5419,7 +5428,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
 
        if (res->view->enablevalidation) {
                result = issecuredomain(res->view, name, fctx->type,
-                                       now, checknta, &secure_domain);
+                                       now, checknta, NULL, &secure_domain);
                if (result != ISC_R_SUCCESS) {
                        return (result);
                }
@@ -6006,7 +6015,7 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 
        if (fctx->res->view->enablevalidation) {
                result = issecuredomain(res->view, name, fctx->type,
-                                       now, checknta, &secure_domain);
+                                       now, checknta, NULL, &secure_domain);
                if (result != ISC_R_SUCCESS)
                        return (result);
 
index 17da3f399885ecf8d60a444985dd69aa052d188f..6329daf1aab5cdb2265afb6eb7a4c2f0290dc901 100644 (file)
@@ -554,15 +554,17 @@ nta_test(void **state) {
        /* Should be secure */
        result = dns_view_issecuredomain(myview,
                                         str2name("test.secure.example"),
-                                        now, true, &issecure);
+                                        now, true, &covered, &issecure);
        assert_int_equal(result, ISC_R_SUCCESS);
+       assert_false(covered);
        assert_true(issecure);
 
        /* Should not be secure */
        result = dns_view_issecuredomain(myview,
                                         str2name("test.insecure.example"),
-                                        now, true, &issecure);
+                                        now, true, &covered, &issecure);
        assert_int_equal(result, ISC_R_SUCCESS);
+       assert_true(covered);
        assert_false(issecure);
 
        /* NTA covered */
@@ -578,14 +580,16 @@ nta_test(void **state) {
        /* As of now + 2, the NTA should be clear */
        result = dns_view_issecuredomain(myview,
                                         str2name("test.insecure.example"),
-                                        now + 2, true, &issecure);
+                                        now + 2, true, &covered, &issecure);
        assert_int_equal(result, ISC_R_SUCCESS);
+       assert_false(covered);
        assert_true(issecure);
 
        /* Now check deletion */
        result = dns_view_issecuredomain(myview, str2name("test.new.example"),
-                                        now, true, &issecure);
+                                        now, true, &covered, &issecure);
        assert_int_equal(result, ISC_R_SUCCESS);
+       assert_false(covered);
        assert_true(issecure);
 
        result = dns_ntatable_add(ntatable, str2name("new.example"),
@@ -593,16 +597,18 @@ nta_test(void **state) {
        assert_int_equal(result, ISC_R_SUCCESS);
 
        result = dns_view_issecuredomain(myview, str2name("test.new.example"),
-                                        now, true, &issecure);
+                                        now, true, &covered, &issecure);
        assert_int_equal(result, ISC_R_SUCCESS);
+       assert_true(covered);
        assert_false(issecure);
 
        result = dns_ntatable_delete(ntatable, str2name("new.example"));
        assert_int_equal(result, ISC_R_SUCCESS);
 
        result = dns_view_issecuredomain(myview, str2name("test.new.example"),
-                                        now, true, &issecure);
+                                        now, true, &covered, &issecure);
        assert_int_equal(result, ISC_R_SUCCESS);
+       assert_false(covered);
        assert_true(issecure);
 
        /* Clean up */
index 3a61e816fe4540a735bced4161b07b5f53283c49..12b608e03c598fb2e8e0c7d6d0213894a24fffec 100644 (file)
@@ -1941,7 +1941,7 @@ dns_view_ntacovers(dns_view_t *view, isc_stdtime_t now,
 
 isc_result_t
 dns_view_issecuredomain(dns_view_t *view, dns_name_t *name,
-                       isc_stdtime_t now, bool checknta,
+                       isc_stdtime_t now, bool checknta, bool *ntap,
                        bool *secure_domain)
 {
        isc_result_t result;
@@ -1951,19 +1951,29 @@ dns_view_issecuredomain(dns_view_t *view, dns_name_t *name,
 
        REQUIRE(DNS_VIEW_VALID(view));
 
-       if (view->secroots_priv == NULL)
+       if (view->secroots_priv == NULL) {
                return (ISC_R_NOTFOUND);
+       }
 
        anchor = dns_fixedname_initname(&fn);
 
        result = dns_keytable_issecuredomain(view->secroots_priv, name,
                                             anchor, &secure);
-       if (result != ISC_R_SUCCESS)
+       if (result != ISC_R_SUCCESS) {
                return (result);
+       }
 
+       if (ntap != NULL) {
+               *ntap = false;
+       }
        if (checknta && secure && view->ntatable_priv != NULL &&
            dns_ntatable_covered(view->ntatable_priv, now, name, anchor))
+       {
+               if (ntap != NULL) {
+                       *ntap = true;
+               }
                secure = false;
+       }
 
        *secure_domain = secure;
        return (ISC_R_SUCCESS);
index 45a57bfdbcbe98a0f7b989c2d211ee1be701b526..bd5095954005a39e5be35a33f141506cded09b1f 100644 (file)
 ./bin/tests/system/dnssec/ns7/sign.sh          SH      2014,2016,2018,2019
 ./bin/tests/system/dnssec/ns7/split-rrsig.db.in        ZONE    2014,2016,2018,2019
 ./bin/tests/system/dnssec/ns8/named.conf.in    CONF-C  2019
+./bin/tests/system/dnssec/ns9/named.conf.in    CONF-C  2019
 ./bin/tests/system/dnssec/ntadiff.pl           PERL    2015,2016,2018,2019
 ./bin/tests/system/dnssec/prereq.sh            SH      2000,2001,2002,2004,2006,2007,2009,2012,2014,2015,2016,2018,2019
 ./bin/tests/system/dnssec/setup.sh             SH      2000,2001,2004,2007,2009,2011,2012,2013,2014,2015,2016,2018,2019