Commit 0cfb2473 authored by Evan Hunt's avatar Evan Hunt
Browse files

[master] rndc nta

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]
parent fa6308bd
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]
......
......@@ -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,
......
......@@ -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);
......
......@@ -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 */
......@@ -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);
}
......@@ -596,6 +596,38 @@
</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>
......
......@@ -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
......
......@@ -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`
......
......@@ -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>
......
......@@ -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 \
......
......@@ -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);
......
......@@ -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:
*
......
......@@ -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
......
/*
* 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 */
......@@ -153,8 +153,9 @@
#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.
......
......@@ -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;
......
......@@ -165,6 +165,7 @@ struct dns_validator {
unsigned int depth;
unsigned int authcount;
unsigned int authfail;
isc_stdtime_t start;
};
/*%
......
......@@ -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.
*