Commit 0cae6657 authored by Mark Andrews's avatar Mark Andrews

2852. [bug] Handle broken DNSSEC trust chains better. [RT #15619]

parent dea0471d
2852. [bug] Handle broken DNSSEC trust chains better. [RT #15619]
2851. [doc] nslookup.1, removed <informalexample> from the docbook
source as it produced bad nroff. [RT #21007]
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: server.c,v 1.562 2010/01/13 23:48:59 tbox Exp $ */
/* $Id: server.c,v 1.563 2010/02/25 04:39:12 marka Exp $ */
/*! \file */
......@@ -5528,6 +5528,8 @@ dumpdone(void *arg, isc_result_t result) {
}
if (dctx->cache != NULL) {
dns_adb_dump(dctx->view->view->adb, dctx->fp);
dns_resolver_printbadcache(dctx->view->view->resolver,
dctx->fp);
dns_db_detach(&dctx->cache);
}
if (dctx->dumpzones) {
......
......@@ -18,7 +18,7 @@
- PERFORMANCE OF THIS SOFTWARE.
-->
<!-- File: $Id: Bv9ARM-book.xml,v 1.455 2010/02/03 01:31:48 each Exp $ -->
<!-- File: $Id: Bv9ARM-book.xml,v 1.456 2010/02/25 04:39:12 marka Exp $ -->
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<title>BIND 9 Administrator Reference Manual</title>
......@@ -8176,6 +8176,13 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
<literal>1800</literal> (30 minutes).
</para>
<para>
Lame-ttl also controls the amount of time DNSSEC
validation failures are cached. There is a minimum
of 30 seconds applied to bad cache entries if the
lame-ttl is set to less than 30 seconds.
</para>
</listitem>
</varlistentry>
......
......@@ -14,7 +14,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: ecdb.c,v 1.4 2009/11/06 04:19:28 marka Exp $ */
/* $Id: ecdb.c,v 1.5 2010/02/25 04:39:13 marka Exp $ */
#include "config.h"
......@@ -99,6 +99,7 @@ static isc_result_t rdataset_next(dns_rdataset_t *rdataset);
static void rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata);
static void rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target);
static unsigned int rdataset_count(dns_rdataset_t *rdataset);
static void rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
static dns_rdatasetmethods_t rdataset_methods = {
rdataset_disassociate,
......@@ -113,7 +114,9 @@ static dns_rdatasetmethods_t rdataset_methods = {
NULL, /* getclosest */
NULL, /* getadditional */
NULL, /* setadditional */
NULL /* putadditional */
NULL, /* putadditional */
rdataset_settrust, /* settrust */
NULL /* expire */
};
typedef struct ecdb_rdatasetiter {
......@@ -736,6 +739,14 @@ rdataset_count(dns_rdataset_t *rdataset) {
return (count);
}
static void
rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
rdatasetheader_t *header = rdataset->private3;
header--;
header->trust = rdataset->trust = trust;
}
/*
* Rdataset Iterator Methods
*/
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: rdataset.h,v 1.67 2009/01/17 23:47:43 tbox Exp $ */
/* $Id: rdataset.h,v 1.68 2010/02/25 04:39:13 marka Exp $ */
#ifndef DNS_RDATASET_H
#define DNS_RDATASET_H 1
......@@ -110,6 +110,9 @@ typedef struct dns_rdatasetmethods {
dns_rdataset_t *rdataset,
dns_rdatasetadditional_t type,
dns_rdatatype_t qtype);
void (*settrust)(dns_rdataset_t *rdataset,
dns_trust_t trust);
void (*expire)(dns_rdataset_t *rdataset);
} dns_rdatasetmethods_t;
#define DNS_RDATASET_MAGIC ISC_MAGIC('D','N','S','R')
......@@ -634,6 +637,19 @@ dns_rdataset_putadditional(dns_acache_t *acache,
* information for 'rdataset.'
*/
void
dns_rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
/*%<
* Set the trust of the 'rdataset' to trust in any in the backing database.
* The local trust level of 'rdataset' is also set.
*/
void
dns_rdataset_expire(dns_rdataset_t *rdataset);
/*%<
* Mark the rdataset to be expired in the backing database.
*/
ISC_LANG_ENDDECLS
#endif /* DNS_RDATASET_H */
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: resolver.h,v 1.64 2009/09/01 00:22:26 jinmei Exp $ */
/* $Id: resolver.h,v 1.65 2010/02/25 04:39:13 marka Exp $ */
#ifndef DNS_RESOLVER_H
#define DNS_RESOLVER_H 1
......@@ -509,6 +509,48 @@ dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state);
unsigned int
dns_resolver_getoptions(dns_resolver_t *resolver);
void
dns_resolver_addbadcache(dns_resolver_t *resolver, dns_name_t *name,
dns_rdatatype_t type, isc_time_t *expire);
/*%<
* Add a entry to the bad cache for <name,type> that will expire at 'expire'.
*
* Requires:
* \li resolver to be valid.
* \li name to be valid.
*/
isc_boolean_t
dns_resolver_getbadcache(dns_resolver_t *resolver, dns_name_t *name,
dns_rdatatype_t type, isc_time_t *now);
/*%<
* Check to see if there is a unexpired entry in the bad cache for
* <name,type>.
*
* Requires:
* \li resolver to be valid.
* \li name to be valid.
*/
void
dns_resolver_flushbadcache(dns_resolver_t *resolver, dns_name_t *name);
/*%<
* Flush the bad cache of all entries at 'name' if 'name' is non NULL.
* Flush the entire bad cache if 'name' is NULL.
*
* Requires:
* \li resolver to be valid.
*/
void
dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp);
/*%
* Print out the contents of the bad cache to 'fp'.
*
* Requires:
* \li resolver to be valid.
*/
ISC_LANG_ENDDECLS
#endif /* DNS_RESOLVER_H */
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: result.h,v 1.118 2009/10/12 23:48:02 tbox Exp $ */
/* $Id: result.h,v 1.119 2010/02/25 04:39:13 marka Exp $ */
#ifndef DNS_RESULT_H
#define DNS_RESULT_H 1
......@@ -149,8 +149,9 @@
#define DNS_R_DUPLICATE (ISC_RESULTCLASS_DNS + 103)
#define DNS_R_INVALIDNSEC3 (ISC_RESULTCLASS_DNS + 104)
#define DNS_R_NOTMASTER (ISC_RESULTCLASS_DNS + 105)
#define DNS_R_BROKENCHAIN (ISC_RESULTCLASS_DNS + 106)
#define DNS_R_NRESULTS 106 /*%< Number of results */
#define DNS_R_NRESULTS 107 /*%< Number of results */
/*
* DNS wire format rcodes.
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: validator.h,v 1.44 2009/01/17 13:20:45 fdupont Exp $ */
/* $Id: validator.h,v 1.45 2010/02/25 04:39:13 marka Exp $ */
#ifndef DNS_VALIDATOR_H
#define DNS_VALIDATOR_H 1
......@@ -159,6 +159,8 @@ struct dns_validator {
isc_boolean_t mustbesecure;
unsigned int dlvlabels;
unsigned int depth;
unsigned int authcount;
unsigned int authfail;
};
/*%
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: ncache.c,v 1.43 2008/09/25 04:02:38 tbox Exp $ */
/* $Id: ncache.c,v 1.44 2010/02/25 04:39:13 marka Exp $ */
/*! \file */
......@@ -519,6 +519,8 @@ static dns_rdatasetmethods_t rdataset_methods = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: rbtdb.c,v 1.298 2010/01/04 23:48:51 tbox Exp $ */
/* $Id: rbtdb.c,v 1.299 2010/02/25 04:39:13 marka Exp $ */
/*! \file */
......@@ -522,6 +522,8 @@ static void overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
static isc_result_t resign_insert(dns_rbtdb_t *rbtdb, int idx,
rdatasetheader_t *newheader);
static void prune_tree(isc_task_t *task, isc_event_t *event);
static void rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
static void rdataset_expire(dns_rdataset_t *rdataset);
static dns_rdatasetmethods_t rdataset_methods = {
rdataset_disassociate,
......@@ -536,7 +538,9 @@ static dns_rdatasetmethods_t rdataset_methods = {
rdataset_getclosest,
rdataset_getadditional,
rdataset_setadditional,
rdataset_putadditional
rdataset_putadditional,
rdataset_settrust,
rdataset_expire
};
static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);
......@@ -7662,6 +7666,28 @@ rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
return (ISC_R_SUCCESS);
}
static void
rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
rdatasetheader_t *header = rdataset->private3;
header--;
header->trust = rdataset->trust = trust;
}
static void
rdataset_expire(dns_rdataset_t *rdataset) {
dns_rbtdb_t *rbtdb = rdataset->private1;
dns_rbtnode_t *rbtnode = rdataset->private2;
rdatasetheader_t *header = rdataset->private3;
header--;
NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
isc_rwlocktype_write);
expire_header(rbtdb, header, ISC_FALSE);
NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
isc_rwlocktype_write);
}
/*
* Rdataset Iterator Methods
*/
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: rdatalist.c,v 1.36 2008/09/24 02:46:22 marka Exp $ */
/* $Id: rdatalist.c,v 1.37 2010/02/25 04:39:13 marka Exp $ */
/*! \file */
......@@ -46,6 +46,8 @@ static dns_rdatasetmethods_t methods = {
isc__rdatalist_getclosest,
NULL,
NULL,
NULL,
NULL,
NULL
};
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: rdataset.c,v 1.84 2009/01/17 23:47:43 tbox Exp $ */
/* $Id: rdataset.c,v 1.85 2010/02/25 04:39:13 marka Exp $ */
/*! \file */
......@@ -182,6 +182,8 @@ static dns_rdatasetmethods_t question_methods = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
......@@ -732,3 +734,22 @@ dns_rdataset_putadditional(dns_acache_t *acache,
return (ISC_R_FAILURE);
}
void
dns_rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
REQUIRE(DNS_RDATASET_VALID(rdataset));
REQUIRE(rdataset->methods != NULL);
if (rdataset->methods->settrust != NULL)
(rdataset->methods->settrust)(rdataset, trust);
else
rdataset->trust = trust;
}
void
dns_rdataset_expire(dns_rdataset_t *rdataset) {
REQUIRE(DNS_RDATASET_VALID(rdataset));
REQUIRE(rdataset->methods != NULL);
if (rdataset->methods->expire != NULL)
(rdataset->methods->expire)(rdataset);
}
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: rdataslab.c,v 1.50 2009/01/17 23:47:43 tbox Exp $ */
/* $Id: rdataslab.c,v 1.51 2010/02/25 04:39:13 marka Exp $ */
/*! \file */
......@@ -436,6 +436,8 @@ static dns_rdatasetmethods_t rdataset_methods = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: resolver.c,v 1.415 2010/01/07 23:48:54 tbox Exp $ */
/* $Id: resolver.c,v 1.416 2010/02/25 04:39:13 marka Exp $ */
/*! \file */
......@@ -336,6 +336,18 @@ typedef struct alternate {
ISC_LINK(struct alternate) link;
} alternate_t;
typedef struct dns_badcache dns_badcache_t;
struct dns_badcache {
dns_badcache_t * next;
dns_rdatatype_t type;
isc_time_t expire;
unsigned int hashval;
dns_name_t name;
};
#define DNS_BADCACHE_SIZE 1021
#define DNS_BADCACHE_TTL(fctx) \
(((fctx)->res->lame_ttl > 30 ) ? (fctx)->res->lame_ttl : 30)
struct dns_resolver {
/* Unlocked. */
unsigned int magic;
......@@ -382,6 +394,13 @@ struct dns_resolver {
isc_boolean_t priming;
unsigned int spillat; /* clients-per-query */
unsigned int nextdisp;
/* Bad cache. */
dns_badcache_t ** badcache;
unsigned int badcount;
unsigned int badhash;
unsigned int badsweep;
/* Locked by primelock. */
dns_fetch_t * primefetch;
/* Locked by nlock. */
......@@ -412,7 +431,8 @@ static void empty_bucket(dns_resolver_t *res);
static isc_result_t resquery_send(resquery_t *query);
static void resquery_response(isc_task_t *task, isc_event_t *event);
static void resquery_connected(isc_task_t *task, isc_event_t *event);
static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying);
static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying,
isc_boolean_t badcache);
static isc_boolean_t fctx_destroy(fetchctx_t *fctx);
static isc_result_t ncache_adderesult(dns_message_t *message,
dns_db_t *cache, dns_dbnode_t *node,
......@@ -1172,7 +1192,7 @@ process_sendevent(resquery_t *query, isc_event_t *event) {
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result, __LINE__);
else
fctx_try(fctx, ISC_TRUE);
fctx_try(fctx, ISC_TRUE, ISC_FALSE);
}
}
......@@ -2073,7 +2093,7 @@ resquery_connected(isc_task_t *task, isc_event_t *event) {
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result, __LINE__);
else
fctx_try(fctx, ISC_TRUE);
fctx_try(fctx, ISC_TRUE, ISC_FALSE);
}
}
......@@ -2135,7 +2155,7 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) {
dns_adb_destroyfind(&find);
if (want_try)
fctx_try(fctx, ISC_TRUE);
fctx_try(fctx, ISC_TRUE, ISC_FALSE);
else if (want_done)
fctx_done(fctx, ISC_R_FAILURE, __LINE__);
else if (bucket_empty)
......@@ -2543,7 +2563,7 @@ isstrictsubdomain(dns_name_t *name1, dns_name_t *name2) {
}
static isc_result_t
fctx_getaddresses(fetchctx_t *fctx) {
fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_result_t result;
dns_resolver_t *res;
......@@ -2762,12 +2782,24 @@ fctx_getaddresses(fetchctx_t *fctx) {
*/
result = DNS_R_WAIT;
} else {
isc_time_t expire;
isc_interval_t i;
/*
* We've lost completely. We don't know any
* addresses, and the ADB has told us it can't get
* them.
*/
FCTXTRACE("no addresses");
isc_interval_set(&i, DNS_BADCACHE_TTL(fctx), 0);
result = isc_time_nowplusinterval(&expire, &i);
if (badcache &&
(fctx->type == dns_rdatatype_dnskey ||
fctx->type == dns_rdatatype_dlv ||
fctx->type == dns_rdatatype_ds) &&
result == ISC_R_SUCCESS)
dns_resolver_addbadcache(fctx->res,
&fctx->name,
fctx->type, &expire);
result = ISC_R_FAILURE;
}
} else {
......@@ -2990,7 +3022,7 @@ fctx_nextaddress(fetchctx_t *fctx) {
}
static void
fctx_try(fetchctx_t *fctx, isc_boolean_t retrying) {
fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
isc_result_t result;
dns_adbaddrinfo_t *addrinfo;
......@@ -3008,7 +3040,7 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying) {
fctx_cleanupaltfinds(fctx);
fctx_cleanupforwaddrs(fctx);
fctx_cleanupaltaddrs(fctx);
result = fctx_getaddresses(fctx);
result = fctx_getaddresses(fctx, badcache);
if (result == DNS_R_WAIT) {
/*
* Sleep waiting for addresses.
......@@ -3173,7 +3205,7 @@ fctx_timeout(isc_task_t *task, isc_event_t *event) {
/*
* Keep trying.
*/
fctx_try(fctx, ISC_TRUE);
fctx_try(fctx, ISC_TRUE, ISC_FALSE);
}
isc_event_free(&event);
......@@ -3343,7 +3375,7 @@ fctx_start(isc_task_t *task, isc_event_t *event) {
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result, __LINE__);
else
fctx_try(fctx, ISC_FALSE);
fctx_try(fctx, ISC_FALSE, ISC_FALSE);
} else if (bucket_empty)
empty_bucket(res);
}
......@@ -3965,6 +3997,8 @@ validated(isc_task_t *task, isc_event_t *event) {
LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
isc_stdtime_get(&now);
/*
* If chaining, we need to make sure that the right result code is
* returned, and that the rdatasets are bound.
......@@ -4011,35 +4045,80 @@ validated(isc_task_t *task, isc_event_t *event) {
inc_stats(fctx->res, dns_resstatscounter_valfail);
fctx->valfail++;
fctx->vresult = vevent->result;
result = ISC_R_NOTFOUND;
if (vevent->rdataset != NULL)
result = dns_db_findnode(fctx->cache, vevent->name,
ISC_TRUE, &node);
if (result == ISC_R_SUCCESS)
(void)dns_db_deleterdataset(fctx->cache, node, NULL,
vevent->type, 0);
if (result == ISC_R_SUCCESS && vevent->sigrdataset != NULL)
(void)dns_db_deleterdataset(fctx->cache, node, NULL,
dns_rdatatype_rrsig,
vevent->type);
if (result == ISC_R_SUCCESS)
dns_db_detachnode(fctx->cache, &node);
result = vevent->result;
if (fctx->vresult != DNS_R_BROKENCHAIN) {
result = ISC_R_NOTFOUND;
if (vevent->rdataset != NULL)
result = dns_db_findnode(fctx->cache,
vevent->name,
ISC_TRUE, &node);
if (result == ISC_R_SUCCESS)
(void)dns_db_deleterdataset(fctx->cache, node,
NULL,
vevent->type, 0);
if (result == ISC_R_SUCCESS &&
vevent->sigrdataset != NULL)
(void)dns_db_deleterdataset(fctx->cache, node,
NULL,
dns_rdatatype_rrsig,
vevent->type);
if (result == ISC_R_SUCCESS)
dns_db_detachnode(fctx->cache, &node);
}
if (fctx->vresult == DNS_R_BROKENCHAIN && !negative) {
/*
* Cache the data as pending for later validation.
*/
result = ISC_R_NOTFOUND;
if (vevent->rdataset != NULL)
result = dns_db_findnode(fctx->cache,
vevent->name,
ISC_TRUE, &node);
if (result == ISC_R_SUCCESS) {
(void)dns_db_addrdataset(fctx->cache, node,
NULL, now,
vevent->rdataset, 0,
NULL);
}
if (result == ISC_R_SUCCESS &&
vevent->sigrdataset != NULL)
(void)dns_db_addrdataset(fctx->cache, node,
NULL, now,
vevent->sigrdataset,
0, NULL);
if (result == ISC_R_SUCCESS)
dns_db_detachnode(fctx->cache, &node);
}
result = fctx->vresult;
add_bad(fctx, addrinfo, result, badns_validation);
isc_event_free(&event);
UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
INSIST(fctx->validator == NULL);
fctx->validator = ISC_LIST_HEAD(fctx->validators);
if (fctx->validator != NULL) {
if (fctx->validator != NULL)
dns_validator_send(fctx->validator);
} else if (sentresponse)
else if (sentresponse)
fctx_done(fctx, result, __LINE__); /* Locks bucket. */
else
fctx_try(fctx, ISC_TRUE); /* Locks bucket. */
else if (result == DNS_R_BROKENCHAIN) {
isc_result_t tresult;
isc_time_t expire;
isc_interval_t i;
isc_interval_set(&i, DNS_BADCACHE_TTL(fctx), 0);
tresult = isc_time_nowplusinterval(&expire, &i);
if (negative &&
(fctx->type == dns_rdatatype_dnskey ||
fctx->type == dns_rdatatype_dlv ||
fctx->type == dns_rdatatype_ds) &&
tresult == ISC_R_SUCCESS)
dns_resolver_addbadcache(fctx->res,
&fctx->name,
fctx->type, &expire);
fctx_done(fctx, result, __LINE__); /* Locks bucket. */
} else
fctx_try(fctx, ISC_TRUE, ISC_TRUE); /* Locks bucket. */
return;
}
isc_stdtime_get(&now);
if (negative) {
dns_rdatatype_t covers;
......@@ -6102,7 +6181,7 @@ resume_dslookup(isc_task_t *task, isc_event_t *event) {
/*
* Try again.
*/
fctx_try(fctx, ISC_TRUE);
fctx_try(fctx, ISC_TRUE, ISC_FALSE);