diff --git a/bin/tests/system/dnssec/ns1/sign.sh b/bin/tests/system/dnssec/ns1/sign.sh index 4df96bf1e015a29a254e5699c9088fdd551f2af1..effebd6f55eee8fb63068976de3efe92b83ebdf6 100644 --- a/bin/tests/system/dnssec/ns1/sign.sh +++ b/bin/tests/system/dnssec/ns1/sign.sh @@ -44,6 +44,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 diff --git a/bin/tests/system/dnssec/ns9/named.conf.in b/bin/tests/system/dnssec/ns9/named.conf.in new file mode 100644 index 0000000000000000000000000000000000000000..d655112a70efc7c3ed775632117ccf4f0a04da26 --- /dev/null +++ b/bin/tests/system/dnssec/ns9/named.conf.in @@ -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"; diff --git a/bin/tests/system/dnssec/setup.sh b/bin/tests/system/dnssec/setup.sh index 223815320464d3a9e093dd9b6454afb78d8abc91..9798e34be797e109e78a6e206a6864d5ed5fd75a 100644 --- a/bin/tests/system/dnssec/setup.sh +++ b/bin/tests/system/dnssec/setup.sh @@ -27,6 +27,8 @@ 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 diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index a60c0f069696948546858c99aab724bd98525161..a914f5f501d785300fe1c1d8a6108d9de01b6582 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -2300,9 +2300,31 @@ fi # cleanup rndccmd 10.53.0.4 nta -remove secure.example > rndc.out.ns4.test$n.3 2>/dev/null +n=$((n+1)) if [ "$ret" -ne 0 ]; then echo_i "failed - NTA lifetime clamping failed"; fi status=$((status+ret)) -ret=0 + +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_with_opts @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_with_opts @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_with_opts @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=$((status+ret)) echo_i "completed NTA tests" diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 69e91fa716f037679e205e510ac298c8bd1da155..c4671b47bf960f6f78a48cc3ebeb0c607faac774 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -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, const 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. diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 6681196da13982afac5e88ca9a35db0f548adbae..b2bc2516d6f00dec99f9bb95c293d39c2ed51a7d 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -2326,8 +2326,7 @@ compute_cc(resquery_t *query, unsigned char *cookie, size_t len) { static isc_result_t issecuredomain(dns_view_t *view, const 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; @@ -2345,7 +2344,8 @@ issecuredomain(dns_view_t *view, const 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 isc_result_t @@ -2449,24 +2449,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; + } } /* @@ -5921,7 +5927,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); } @@ -6516,7 +6522,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); diff --git a/lib/dns/tests/keytable_test.c b/lib/dns/tests/keytable_test.c index c8301fb15845b7fc8dd79c6308fef6835a94ec86..19e1a6a250ce18a0ca81a7b5401f5f10e38160f7 100644 --- a/lib/dns/tests/keytable_test.c +++ b/lib/dns/tests/keytable_test.c @@ -674,15 +674,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 */ @@ -698,14 +700,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"), @@ -713,16 +717,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 */ diff --git a/lib/dns/view.c b/lib/dns/view.c index c6d48178b7c15683f9df299efedecb0272ebdff1..1a85855774c7f2073fef04dba8852f1c48d0a9ab 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -1908,7 +1908,7 @@ dns_view_ntacovers(dns_view_t *view, isc_stdtime_t now, isc_result_t dns_view_issecuredomain(dns_view_t *view, const 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; @@ -1918,19 +1918,29 @@ dns_view_issecuredomain(dns_view_t *view, const 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);