Commit 5e804882 authored by Michał Kępień's avatar Michał Kępień Committed by Evan Hunt

Make NTAs work with validating forwarders

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.
parent 7d1a7c46
...@@ -44,6 +44,7 @@ cp trusted.conf ../ns3/trusted.conf ...@@ -44,6 +44,7 @@ cp trusted.conf ../ns3/trusted.conf
cp trusted.conf ../ns4/trusted.conf cp trusted.conf ../ns4/trusted.conf
cp trusted.conf ../ns6/trusted.conf cp trusted.conf ../ns6/trusted.conf
cp trusted.conf ../ns7/trusted.conf cp trusted.conf ../ns7/trusted.conf
cp trusted.conf ../ns9/trusted.conf
# ...or with a managed key. # ...or with a managed key.
keyfile_to_managed_keys "$keyname" > managed.conf keyfile_to_managed_keys "$keyname" > managed.conf
......
/*
* 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";
...@@ -27,6 +27,8 @@ copy_setports ns6/named.conf.in ns6/named.conf ...@@ -27,6 +27,8 @@ copy_setports ns6/named.conf.in ns6/named.conf
copy_setports ns7/named.conf.in ns7/named.conf copy_setports ns7/named.conf.in ns7/named.conf
copy_setports ns8/named.conf.in ns8/named.conf copy_setports ns8/named.conf.in ns8/named.conf
copy_setports ns9/named.conf.in ns9/named.conf
( (
cd ns1 cd ns1
$SHELL sign.sh $SHELL sign.sh
......
...@@ -2300,9 +2300,31 @@ fi ...@@ -2300,9 +2300,31 @@ fi
# cleanup # cleanup
rndccmd 10.53.0.4 nta -remove secure.example > rndc.out.ns4.test$n.3 2>/dev/null 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 if [ "$ret" -ne 0 ]; then echo_i "failed - NTA lifetime clamping failed"; fi
status=$((status+ret)) 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" echo_i "completed NTA tests"
......
...@@ -1189,14 +1189,16 @@ dns_view_getsecroots(dns_view_t *view, dns_keytable_t **ktp); ...@@ -1189,14 +1189,16 @@ dns_view_getsecroots(dns_view_t *view, dns_keytable_t **ktp);
isc_result_t isc_result_t
dns_view_issecuredomain(dns_view_t *view, const dns_name_t *name, 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); bool *secure_domain);
/*%< /*%<
* Is 'name' at or beneath a trusted key, and not covered by a valid * Is 'name' at or beneath a trusted key, and not covered by a valid
* negative trust anchor? Put answer in '*secure_domain'. * negative trust anchor? Put answer in '*secure_domain'.
* *
* If 'checknta' is false, ignore the NTA table in determining * 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: * Requires:
* \li 'view' is valid. * \li 'view' is valid.
......
...@@ -2326,8 +2326,7 @@ compute_cc(resquery_t *query, unsigned char *cookie, size_t len) { ...@@ -2326,8 +2326,7 @@ compute_cc(resquery_t *query, unsigned char *cookie, size_t len) {
static isc_result_t static isc_result_t
issecuredomain(dns_view_t *view, const dns_name_t *name, dns_rdatatype_t type, issecuredomain(dns_view_t *view, const dns_name_t *name, dns_rdatatype_t type,
isc_stdtime_t now, bool checknta, isc_stdtime_t now, bool checknta, bool *ntap, bool *issecure)
bool *issecure)
{ {
dns_name_t suffix; dns_name_t suffix;
unsigned int labels; unsigned int labels;
...@@ -2345,7 +2344,8 @@ issecuredomain(dns_view_t *view, const dns_name_t *name, dns_rdatatype_t type, ...@@ -2345,7 +2344,8 @@ issecuredomain(dns_view_t *view, const dns_name_t *name, dns_rdatatype_t type,
name = &suffix; 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 static isc_result_t
...@@ -2449,25 +2449,31 @@ resquery_send(resquery_t *query) { ...@@ -2449,25 +2449,31 @@ resquery_send(resquery_t *query) {
* question is under a secure entry point and this is a * question is under a secure entry point and this is a
* recursive/forward query -- unless the client said not to. * 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 */ /* Do nothing */
; } else if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) {
else if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0)
fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
else if (res->view->enablevalidation && } else if (res->view->enablevalidation &&
((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0)) ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0))
{ {
bool checknta = ((query->options & DNS_FETCHOPT_NONTA) == 0); bool checknta = ((query->options & DNS_FETCHOPT_NONTA) == 0);
bool ntacovered = false;
result = issecuredomain(res->view, &fctx->name, fctx->type, result = issecuredomain(res->view, &fctx->name, fctx->type,
isc_time_seconds(&query->start), isc_time_seconds(&query->start),
checknta, &secure_domain); checknta, &ntacovered, &secure_domain);
if (result != ISC_R_SUCCESS) if (result != ISC_R_SUCCESS) {
secure_domain = false; secure_domain = false;
if (res->view->dlv != NULL) }
if (res->view->dlv != NULL) {
secure_domain = true; secure_domain = true;
if (secure_domain) }
if (secure_domain ||
(ISFORWARDER(query->addrinfo) && ntacovered))
{
fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
} }
}
/* /*
* We don't have to set opcode because it defaults to query. * We don't have to set opcode because it defaults to query.
...@@ -5921,7 +5927,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, ...@@ -5921,7 +5927,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
if (res->view->enablevalidation) { if (res->view->enablevalidation) {
result = issecuredomain(res->view, name, fctx->type, result = issecuredomain(res->view, name, fctx->type,
now, checknta, &secure_domain); now, checknta, NULL, &secure_domain);
if (result != ISC_R_SUCCESS) { if (result != ISC_R_SUCCESS) {
return (result); return (result);
} }
...@@ -6516,7 +6522,7 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, ...@@ -6516,7 +6522,7 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
if (fctx->res->view->enablevalidation) { if (fctx->res->view->enablevalidation) {
result = issecuredomain(res->view, name, fctx->type, result = issecuredomain(res->view, name, fctx->type,
now, checknta, &secure_domain); now, checknta, NULL, &secure_domain);
if (result != ISC_R_SUCCESS) if (result != ISC_R_SUCCESS)
return (result); return (result);
......
...@@ -674,15 +674,17 @@ nta_test(void **state) { ...@@ -674,15 +674,17 @@ nta_test(void **state) {
/* Should be secure */ /* Should be secure */
result = dns_view_issecuredomain(myview, result = dns_view_issecuredomain(myview,
str2name("test.secure.example"), str2name("test.secure.example"),
now, true, &issecure); now, true, &covered, &issecure);
assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(result, ISC_R_SUCCESS);
assert_false(covered);
assert_true(issecure); assert_true(issecure);
/* Should not be secure */ /* Should not be secure */
result = dns_view_issecuredomain(myview, result = dns_view_issecuredomain(myview,
str2name("test.insecure.example"), str2name("test.insecure.example"),
now, true, &issecure); now, true, &covered, &issecure);
assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(result, ISC_R_SUCCESS);
assert_true(covered);
assert_false(issecure); assert_false(issecure);
/* NTA covered */ /* NTA covered */
...@@ -698,14 +700,16 @@ nta_test(void **state) { ...@@ -698,14 +700,16 @@ nta_test(void **state) {
/* As of now + 2, the NTA should be clear */ /* As of now + 2, the NTA should be clear */
result = dns_view_issecuredomain(myview, result = dns_view_issecuredomain(myview,
str2name("test.insecure.example"), str2name("test.insecure.example"),
now + 2, true, &issecure); now + 2, true, &covered, &issecure);
assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(result, ISC_R_SUCCESS);
assert_false(covered);
assert_true(issecure); assert_true(issecure);
/* Now check deletion */ /* Now check deletion */
result = dns_view_issecuredomain(myview, str2name("test.new.example"), result = dns_view_issecuredomain(myview, str2name("test.new.example"),
now, true, &issecure); now, true, &covered, &issecure);
assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(result, ISC_R_SUCCESS);
assert_false(covered);
assert_true(issecure); assert_true(issecure);
result = dns_ntatable_add(ntatable, str2name("new.example"), result = dns_ntatable_add(ntatable, str2name("new.example"),
...@@ -713,16 +717,18 @@ nta_test(void **state) { ...@@ -713,16 +717,18 @@ nta_test(void **state) {
assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(result, ISC_R_SUCCESS);
result = dns_view_issecuredomain(myview, str2name("test.new.example"), result = dns_view_issecuredomain(myview, str2name("test.new.example"),
now, true, &issecure); now, true, &covered, &issecure);
assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(result, ISC_R_SUCCESS);
assert_true(covered);
assert_false(issecure); assert_false(issecure);
result = dns_ntatable_delete(ntatable, str2name("new.example")); result = dns_ntatable_delete(ntatable, str2name("new.example"));
assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(result, ISC_R_SUCCESS);
result = dns_view_issecuredomain(myview, str2name("test.new.example"), result = dns_view_issecuredomain(myview, str2name("test.new.example"),
now, true, &issecure); now, true, &covered, &issecure);
assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(result, ISC_R_SUCCESS);
assert_false(covered);
assert_true(issecure); assert_true(issecure);
/* Clean up */ /* Clean up */
......
...@@ -1908,7 +1908,7 @@ dns_view_ntacovers(dns_view_t *view, isc_stdtime_t now, ...@@ -1908,7 +1908,7 @@ dns_view_ntacovers(dns_view_t *view, isc_stdtime_t now,
isc_result_t isc_result_t
dns_view_issecuredomain(dns_view_t *view, const dns_name_t *name, 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) bool *secure_domain)
{ {
isc_result_t result; isc_result_t result;
...@@ -1918,19 +1918,29 @@ dns_view_issecuredomain(dns_view_t *view, const dns_name_t *name, ...@@ -1918,19 +1918,29 @@ dns_view_issecuredomain(dns_view_t *view, const dns_name_t *name,
REQUIRE(DNS_VIEW_VALID(view)); REQUIRE(DNS_VIEW_VALID(view));
if (view->secroots_priv == NULL) if (view->secroots_priv == NULL) {
return (ISC_R_NOTFOUND); return (ISC_R_NOTFOUND);
}
anchor = dns_fixedname_initname(&fn); anchor = dns_fixedname_initname(&fn);
result = dns_keytable_issecuredomain(view->secroots_priv, name, result = dns_keytable_issecuredomain(view->secroots_priv, name,
anchor, &secure); anchor, &secure);
if (result != ISC_R_SUCCESS) if (result != ISC_R_SUCCESS) {
return (result); return (result);
}
if (ntap != NULL) {
*ntap = false;
}
if (checknta && secure && view->ntatable_priv != NULL && if (checknta && secure && view->ntatable_priv != NULL &&
dns_ntatable_covered(view->ntatable_priv, now, name, anchor)) dns_ntatable_covered(view->ntatable_priv, now, name, anchor))
{
if (ntap != NULL) {
*ntap = true;
}
secure = false; secure = false;
}
*secure_domain = secure; *secure_domain = secure;
return (ISC_R_SUCCESS); return (ISC_R_SUCCESS);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment