2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-09-04 00:25:29 +00:00

1939. [bug] The resolver could dereference a null pointer after

validation if all the queries have timed out.
                        [RT #15528]

1938.   [bug]           The validator was not correctly handling unsecure
                        negative responses at or below a SEP. [RT #15528]
This commit is contained in:
Mark Andrews
2005-11-03 00:51:55 +00:00
parent e6d667397a
commit 60ab03125c
5 changed files with 334 additions and 140 deletions

View File

@@ -1,3 +1,10 @@
1939. [bug] The resolver could dereference a null pointer after
validation if all the queries have timed out.
[RT #15528]
1938. [bug] The validator was not correctly handling unsecure
negative responses at or below a SEP. [RT #15528]
1937. [bug] sdlz doesn't handle RRSIG records. [RT #15564] 1937. [bug] sdlz doesn't handle RRSIG records. [RT #15564]
1936. [bug] The validator could leak memory. [RT #15544] 1936. [bug] The validator could leak memory. [RT #15544]

View File

@@ -18,7 +18,7 @@
- PERFORMANCE OF THIS SOFTWARE. - PERFORMANCE OF THIS SOFTWARE.
--> -->
<!-- File: $Id: Bv9ARM-book.xml,v 1.281 2005/11/02 22:26:48 marka Exp $ --> <!-- File: $Id: Bv9ARM-book.xml,v 1.282 2005/11/03 00:51:54 marka Exp $ -->
<book xmlns:xi="http://www.w3.org/2001/XInclude"> <book xmlns:xi="http://www.w3.org/2001/XInclude">
<title>BIND 9 Administrator Reference Manual</title> <title>BIND 9 Administrator Reference Manual</title>
@@ -7581,19 +7581,22 @@ query-source-v6 address * port *;
<title><command>trusted-keys</command> Statement Definition <title><command>trusted-keys</command> Statement Definition
and Usage</title> and Usage</title>
<para> <para>
The <command>trusted-keys</command> statement defines The <command>trusted-keys</command> statement defines DNSSEC
DNSSEC
security roots. DNSSEC is described in <xref linkend="DNSSEC"/>. A security roots. DNSSEC is described in <xref linkend="DNSSEC"/>. A
security root is defined when the public key for a security root is defined when the public key for a
non-authoritative non-authoritative
zone is known, but cannot be securely obtained through DNS, either zone is known, but cannot be securely obtained through DNS, either
because it is the DNS root zone or because its parent zone is because it is the DNS root zone or because its parent zone is
unsigned. unsigned.
Once a key has been configured as a trusted key, it is treated as Once a key has been configured as a trusted key, it is treated as
if it had been validated and proven secure. The resolver attempts if it had been validated and proven secure. The resolver attempts
DNSSEC validation on all DNS data in subdomains of a security DNSSEC validation on all DNS data in subdomains of a security
root. root.
</para> </para>
<para>
All zones listed in <command>trusted-keys</command> are deemed
to exist regardless of what parent zones say.
</para>
<para> <para>
The <command>trusted-keys</command> statement can The <command>trusted-keys</command> statement can
contain contain

View File

@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: validator.h,v 1.32 2005/08/25 00:56:08 marka Exp $ */ /* $Id: validator.h,v 1.33 2005/11/03 00:51:55 marka Exp $ */
#ifndef DNS_VALIDATOR_H #ifndef DNS_VALIDATOR_H
#define DNS_VALIDATOR_H 1 #define DNS_VALIDATOR_H 1
@@ -25,10 +25,18 @@
*****/ *****/
/*! \file /*! \file
*
* \brief * \brief
* DNS Validator * DNS Validator
* This is the BIND 9 validator, the module responsible for validating the
* rdatasets and negative responses (messages). It makes use of zones in
* the view and may fetch RRset to complete trust chains. It implements
* DNSSEC as specified in RFC 4033, 4034 and 4035.
* *
* XXX TBS XXX * It can also optionally implement ISC's DNSSEC look-aside validation.
*
* Correct operation is critical to preventing spoofed answers from secure
* zones being accepted.
* *
* MP: * MP:
*\li The module ensures appropriate synchronization of data structures it *\li The module ensures appropriate synchronization of data structures it
@@ -44,8 +52,7 @@
*\li No anticipated impact. *\li No anticipated impact.
* *
* Standards: * Standards:
*\li RFCs: 1034, 1035, 2181, 2535, TBS *\li RFCs: 1034, 1035, 2181, 4033, 4034, 4035.
*\li Drafts: TBS
*/ */
#include <isc/lang.h> #include <isc/lang.h>
@@ -65,6 +72,10 @@
* 'name', 'rdataset', 'sigrdataset', and 'message' are the values that were * 'name', 'rdataset', 'sigrdataset', and 'message' are the values that were
* supplied when dns_validator_create() was called. They are returned to the * supplied when dns_validator_create() was called. They are returned to the
* caller so that they may be freed. * caller so that they may be freed.
*
* If the RESULT is ISC_R_SUCCESS and the answer is secure then
* proofs[] will contain the the names of the NSEC records that hold the
* various proofs. Note the same name may appear multiple times.
*/ */
typedef struct dns_validatorevent { typedef struct dns_validatorevent {
ISC_EVENT_COMMON(struct dns_validatorevent); ISC_EVENT_COMMON(struct dns_validatorevent);
@@ -129,7 +140,10 @@ struct dns_validator {
unsigned int depth; unsigned int depth;
}; };
#define DNS_VALIDATOR_DLV 1 /*%
* dns_validator_create() options.
*/
#define DNS_VALIDATOR_DLV 1U
ISC_LANG_BEGINDECLS ISC_LANG_BEGINDECLS
@@ -164,13 +178,17 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
* arguments must be provided. * arguments must be provided.
* *
* The validation is performed in the context of 'view'. * The validation is performed in the context of 'view'.
* 'options' must be zero.
* *
* When the validation finishes, a dns_validatorevent_t with * When the validation finishes, a dns_validatorevent_t with
* the given 'action' and 'arg' are sent to 'task'. * the given 'action' and 'arg' are sent to 'task'.
* Its 'result' field will be ISC_R_SUCCESS iff the * Its 'result' field will be ISC_R_SUCCESS iff the
* response was successfully proven to be either secure or * response was successfully proven to be either secure or
* part of a known insecure domain. * part of a known insecure domain.
*
* options:
* If DNS_VALIDATOR_DLV is set the caller knows there is not a
* trusted key and the validator should immediately attempt to validate
* the answer by looking for a appopriate DLV RRset.
*/ */
void void

View File

@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: resolver.c,v 1.318 2005/10/14 01:14:09 marka Exp $ */ /* $Id: resolver.c,v 1.319 2005/11/03 00:51:54 marka Exp $ */
/*! \file */ /*! \file */
@@ -3809,21 +3809,20 @@ ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
isc_result_t result; isc_result_t result;
result = dns_ncache_add(message, cache, node, covers, now, result = dns_ncache_add(message, cache, node, covers, now,
maxttl, ardataset); maxttl, ardataset);
if (result == DNS_R_UNCHANGED) { if (result == DNS_R_UNCHANGED || result == ISC_R_SUCCESS) {
/* /*
* The data in the cache are better than the negative cache * If the cache now contains a negative entry and we
* entry we're trying to add. * care about whether it is DNS_R_NCACHENXDOMAIN or
* DNS_R_NCACHENXRRSET then extract it.
*/ */
if (ardataset != NULL && ardataset->type == 0) { if (ardataset != NULL && ardataset->type == 0) {
/* /*
* The cache data is also a negative cache * The cache data is a negative cache entry.
* entry.
*/ */
if (NXDOMAIN(ardataset)) if (NXDOMAIN(ardataset))
*eresultp = DNS_R_NCACHENXDOMAIN; *eresultp = DNS_R_NCACHENXDOMAIN;
else else
*eresultp = DNS_R_NCACHENXRRSET; *eresultp = DNS_R_NCACHENXRRSET;
result = ISC_R_SUCCESS;
} else { } else {
/* /*
* Either we don't care about the nature of the * Either we don't care about the nature of the
@@ -3835,13 +3834,8 @@ ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
* XXXRTH There's a CNAME/DNAME problem here. * XXXRTH There's a CNAME/DNAME problem here.
*/ */
*eresultp = ISC_R_SUCCESS; *eresultp = ISC_R_SUCCESS;
result = ISC_R_SUCCESS;
} }
} else if (result == ISC_R_SUCCESS) { result = ISC_R_SUCCESS;
if (NXDOMAIN(ardataset))
*eresultp = DNS_R_NCACHENXDOMAIN;
else
*eresultp = DNS_R_NCACHENXRRSET;
} }
return (result); return (result);

View File

@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/* $Id: validator.c,v 1.134 2005/11/02 01:46:31 marka Exp $ */ /* $Id: validator.c,v 1.135 2005/11/03 00:51:55 marka Exp $ */
/*! \file */ /*! \file */
@@ -45,20 +45,65 @@
#include <dns/validator.h> #include <dns/validator.h>
#include <dns/view.h> #include <dns/view.h>
/*! \file
* \brief
* Basic processing sequences.
*
* \li When called with rdataset and sigrdataset:
* validator_start -> validate -> proveunsecure -> startfinddlvsep ->
* dlv_validator_start -> validator_start -> validate -> proveunsecure
*
* validator_start -> validate -> nsecvalidate (secure wildcard answer)
*
* \li When called with rdataset, sigrdataset and with DNS_VALIDATOR_DLV:
* validator_start -> startfinddlvsep -> dlv_validator_start ->
* validator_start -> validate -> proveunsecure
*
* \li When called with rdataset:
* validator_start -> proveunsecure -> startfinddlvsep ->
* dlv_validator_start -> validator_start -> proveunsecure
*
* \li When called with rdataset and with DNS_VALIDATOR_DLV:
* validator_start -> startfinddlvsep -> dlv_validator_start ->
* validator_start -> proveunsecure
*
* \li When called without a rdataset:
* validator_start -> nsecvalidate -> proveunsecure -> startfinddlvsep ->
* dlv_validator_start -> validator_start -> nsecvalidate -> proveunsecure
*
* \li When called without a rdataset and with DNS_VALIDATOR_DLV:
* validator_start -> startfinddlvsep -> dlv_validator_start ->
* validator_start -> nsecvalidate -> proveunsecure
*
* validator_start: determines what type of validation to do.
* validate: attempts to perform a positive validation.
* proveunsecure: attempts to prove the answer comes from a unsecure zone.
* nsecvalidate: attempts to prove a negative response.
* startfinddlvsep: starts the DLV record lookup.
* dlv_validator_start: resets state and restarts the lookup using the
* DLV RRset found by startfinddlvsep.
*/
#define VALIDATOR_MAGIC ISC_MAGIC('V', 'a', 'l', '?') #define VALIDATOR_MAGIC ISC_MAGIC('V', 'a', 'l', '?')
#define VALID_VALIDATOR(v) ISC_MAGIC_VALID(v, VALIDATOR_MAGIC) #define VALID_VALIDATOR(v) ISC_MAGIC_VALID(v, VALIDATOR_MAGIC)
#define VALATTR_SHUTDOWN 0x0001 #define VALATTR_SHUTDOWN 0x0001 /*%< Shutting down. */
#define VALATTR_FOUNDNONEXISTENCE 0x0002 #define VALATTR_TRIEDVERIFY 0x0004 /*%< We have found a key and
#define VALATTR_TRIEDVERIFY 0x0004 * have attempted a verify. */
#define VALATTR_NEGATIVE 0x0008 #define VALATTR_INSECURITY 0x0010 /*%< Attempting proveunsecure. */
#define VALATTR_INSECURITY 0x0010 #define VALATTR_DLVTRIED 0x0020 /*%< Looked for a DLV record. */
#define VALATTR_DLVTRIED 0x0020 #define VALATTR_AUTHNONPENDING 0x0040 /*%< Tidy up pending auth. */
/*!
* NSEC proofs to be looked for.
*/
#define VALATTR_NEEDNOQNAME 0x0100 #define VALATTR_NEEDNOQNAME 0x0100
#define VALATTR_NEEDNOWILDCARD 0x0200 #define VALATTR_NEEDNOWILDCARD 0x0200
#define VALATTR_NEEDNODATA 0x0400 #define VALATTR_NEEDNODATA 0x0400
/*!
* NSEC proofs that have been found.
*/
#define VALATTR_FOUNDNOQNAME 0x1000 #define VALATTR_FOUNDNOQNAME 0x1000
#define VALATTR_FOUNDNOWILDCARD 0x2000 #define VALATTR_FOUNDNOWILDCARD 0x2000
#define VALATTR_FOUNDNODATA 0x4000 #define VALATTR_FOUNDNODATA 0x4000
@@ -106,19 +151,35 @@ validator_logcreate(dns_validator_t *val,
static isc_result_t static isc_result_t
dlv_validatezonekey(dns_validator_t *val); dlv_validatezonekey(dns_validator_t *val);
static isc_result_t static void
dlv_validator_start(dns_validator_t *val); dlv_validator_start(dns_validator_t *val);
static isc_result_t static isc_result_t
finddlvsep(dns_validator_t *val, isc_boolean_t resume); finddlvsep(dns_validator_t *val, isc_boolean_t resume);
static void
auth_nonpending(dns_message_t *message);
static isc_result_t
startfinddlvsep(dns_validator_t *val, dns_name_t *unsecure);
/*%
* Mark the RRsets as a answer.
*
* If VALATTR_AUTHNONPENDING is set then this is a negative answer
* in a insecure zone. We need to mark any pending RRsets as
* dns_trust_authauthority answers (this is deferred from resolver.c).
*/
static inline void static inline void
markanswer(dns_validator_t *val) { markanswer(dns_validator_t *val) {
validator_log(val, ISC_LOG_DEBUG(3), "marking as answer"); validator_log(val, ISC_LOG_DEBUG(3), "marking as answer");
if (val->event->rdataset) if (val->event->rdataset != NULL)
val->event->rdataset->trust = dns_trust_answer; val->event->rdataset->trust = dns_trust_answer;
if (val->event->sigrdataset) if (val->event->sigrdataset != NULL)
val->event->sigrdataset->trust = dns_trust_answer; val->event->sigrdataset->trust = dns_trust_answer;
if (val->event->message != NULL &&
(val->attributes & VALATTR_AUTHNONPENDING) != 0)
auth_nonpending(val->event->message);
} }
static void static void
@@ -157,6 +218,9 @@ exit_check(dns_validator_t *val) {
return (ISC_TRUE); return (ISC_TRUE);
} }
/*%
* Mark pending answers in the authority section as dns_trust_authauthority.
*/
static void static void
auth_nonpending(dns_message_t *message) { auth_nonpending(dns_message_t *message) {
isc_result_t result; isc_result_t result;
@@ -179,6 +243,10 @@ auth_nonpending(dns_message_t *message) {
} }
} }
/*%
* Look in the NSEC record returned from a DS query to see if there is
* a NS RRset at this name. If it is found we are at a delegation point.
*/
static isc_boolean_t static isc_boolean_t
isdelegation(dns_name_t *name, dns_rdataset_t *rdataset, isdelegation(dns_name_t *name, dns_rdataset_t *rdataset,
isc_result_t dbresult) isc_result_t dbresult)
@@ -212,6 +280,11 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset,
return (found); return (found);
} }
/*%
* We have been asked to to look for a key.
* If found resume the validation process.
* If not found fail the validation process.
*/
static void static void
fetch_callback_validator(isc_task_t *task, isc_event_t *event) { fetch_callback_validator(isc_task_t *task, isc_event_t *event) {
dns_fetchevent_t *devent; dns_fetchevent_t *devent;
@@ -271,6 +344,11 @@ fetch_callback_validator(isc_task_t *task, isc_event_t *event) {
destroy(val); destroy(val);
} }
/*%
* We were asked to look for a DS record as part of following a key chain
* upwards. If found resume the validation process. If not found fail the
* validation process.
*/
static void static void
dsfetched(isc_task_t *task, isc_event_t *event) { dsfetched(isc_task_t *task, isc_event_t *event) {
dns_fetchevent_t *devent; dns_fetchevent_t *devent;
@@ -332,8 +410,16 @@ dsfetched(isc_task_t *task, isc_event_t *event) {
destroy(val); destroy(val);
} }
/* /*%
* XXX there's too much duplicated code here. * We were asked to look for the DS record as part of proving that a
* name is unsecure.
*
* If the DS record doesn't exist and the query name corresponds to
* a delegation point we are transitioning from a secure zone to a
* unsecure zone.
*
* If the DS record exists it will be secure. We can continue looking
* for the break point in the chain of trust.
*/ */
static void static void
dsfetched2(isc_task_t *task, isc_event_t *event) { dsfetched2(isc_task_t *task, isc_event_t *event) {
@@ -361,7 +447,8 @@ dsfetched2(isc_task_t *task, isc_event_t *event) {
INSIST(val->event != NULL); INSIST(val->event != NULL);
validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched2"); validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched2: %s",
dns_result_totext(eresult));
LOCK(&val->lock); LOCK(&val->lock);
if (eresult == DNS_R_NXRRSET || eresult == DNS_R_NCACHENXRRSET) { if (eresult == DNS_R_NXRRSET || eresult == DNS_R_NCACHENXRRSET) {
/* /*
@@ -373,9 +460,13 @@ dsfetched2(isc_task_t *task, isc_event_t *event) {
validator_log(val, ISC_LOG_WARNING, validator_log(val, ISC_LOG_WARNING,
"must be secure failure"); "must be secure failure");
validator_done(val, DNS_R_MUSTBESECURE); validator_done(val, DNS_R_MUSTBESECURE);
} else { } else if (val->view->dlv == NULL || DLVTRIED(val)) {
markanswer(val); markanswer(val);
validator_done(val, ISC_R_SUCCESS); validator_done(val, ISC_R_SUCCESS);
} else {
result = startfinddlvsep(val, tname);
if (result != DNS_R_WAIT)
validator_done(val, result);
} }
} else { } else {
result = proveunsecure(val, ISC_TRUE); result = proveunsecure(val, ISC_TRUE);
@@ -387,7 +478,9 @@ dsfetched2(isc_task_t *task, isc_event_t *event) {
eresult == DNS_R_NCACHENXDOMAIN) eresult == DNS_R_NCACHENXDOMAIN)
{ {
/* /*
* Either there is a DS or this is not a zone cut. Continue. * There is a DS which may or may not be a zone cut.
* In either case we are still in a secure zone resume
* validation.
*/ */
result = proveunsecure(val, ISC_TRUE); result = proveunsecure(val, ISC_TRUE);
if (result != DNS_R_WAIT) if (result != DNS_R_WAIT)
@@ -405,6 +498,11 @@ dsfetched2(isc_task_t *task, isc_event_t *event) {
destroy(val); destroy(val);
} }
/*%
* Callback from when a DNSKEY RRset has been validated.
*
* Resumes the stalled validation process.
*/
static void static void
keyvalidated(isc_task_t *task, isc_event_t *event) { keyvalidated(isc_task_t *task, isc_event_t *event) {
dns_validatorevent_t *devent; dns_validatorevent_t *devent;
@@ -450,6 +548,11 @@ keyvalidated(isc_task_t *task, isc_event_t *event) {
destroy(val); destroy(val);
} }
/*%
* Callback when the DS record has been validated.
*
* Resumes validation of the zone key or the unsecure zone proof.
*/
static void static void
dsvalidated(isc_task_t *task, isc_event_t *event) { dsvalidated(isc_task_t *task, isc_event_t *event) {
dns_validatorevent_t *devent; dns_validatorevent_t *devent;
@@ -493,10 +596,12 @@ dsvalidated(isc_task_t *task, isc_event_t *event) {
destroy(val); destroy(val);
} }
/* /*%
* Return ISC_R_SUCCESS if we can determine that the name doesn't exist * Return ISC_R_SUCCESS if we can determine that the name doesn't exist
* or we can determine whether there is data or not at the name. * or we can determine whether there is data or not at the name.
* If the name does not exist return the wildcard name. * If the name does not exist return the wildcard name.
*
* Return ISC_R_IGNORE when the NSEC is not the appropriate one.
*/ */
static isc_result_t static isc_result_t
nsecnoexistnodata(dns_validator_t *val, dns_name_t* name, dns_name_t *nsecname, nsecnoexistnodata(dns_validator_t *val, dns_name_t* name, dns_name_t *nsecname,
@@ -639,6 +744,13 @@ nsecnoexistnodata(dns_validator_t *val, dns_name_t* name, dns_name_t *nsecname,
return (ISC_R_SUCCESS); return (ISC_R_SUCCESS);
} }
/*%
* Callback for when NSEC records have been validated.
*
* Looks for NOQNAME and NODATA proofs.
*
* Resumes nsecvalidate.
*/
static void static void
authvalidated(isc_task_t *task, isc_event_t *event) { authvalidated(isc_task_t *task, isc_event_t *event) {
dns_validatorevent_t *devent; dns_validatorevent_t *devent;
@@ -717,44 +829,20 @@ authvalidated(isc_task_t *task, isc_event_t *event) {
isc_event_free(&event); isc_event_free(&event);
} }
static void /*%
negauthvalidated(isc_task_t *task, isc_event_t *event) { * Looks for the requested name and type in the view (zones and cache).
dns_validatorevent_t *devent; *
dns_validator_t *val; * When looking for a DLV record also checks to make sure the NSEC record
isc_boolean_t want_destroy; * returns covers the query name as part of aggressive negative caching.
isc_result_t eresult; *
* Returns:
UNUSED(task); * \li ISC_R_SUCCESS
INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE); * \li ISC_R_NOTFOUND
* \li DNS_R_NCACHENXDOMAIN
devent = (dns_validatorevent_t *)event; * \li DNS_R_NCACHENXRRSET
val = devent->ev_arg; * \li DNS_R_NXRRSET
eresult = devent->result; * \li DNS_R_NXDOMAIN
isc_event_free(&event); */
dns_validator_destroy(&val->subvalidator);
INSIST(val->event != NULL);
validator_log(val, ISC_LOG_DEBUG(3), "in negauthvalidated");
LOCK(&val->lock);
if (eresult == ISC_R_SUCCESS) {
val->attributes |= VALATTR_FOUNDNONEXISTENCE;
validator_log(val, ISC_LOG_DEBUG(3),
"nonexistence proof found");
auth_nonpending(val->event->message);
validator_done(val, ISC_R_SUCCESS);
} else {
validator_log(val, ISC_LOG_DEBUG(3),
"negauthvalidated: got %s",
isc_result_totext(eresult));
validator_done(val, eresult);
}
want_destroy = exit_check(val);
UNLOCK(&val->lock);
if (want_destroy)
destroy(val);
}
static inline isc_result_t static inline isc_result_t
view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) { view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
dns_fixedname_t fixedname; dns_fixedname_t fixedname;
@@ -857,12 +945,9 @@ view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
dns_rdata_freestruct(&nsec); dns_rdata_freestruct(&nsec);
result = DNS_R_NCACHENXDOMAIN; result = DNS_R_NCACHENXDOMAIN;
} else if (result != ISC_R_SUCCESS && } else if (result != ISC_R_SUCCESS &&
result != DNS_R_GLUE &&
result != DNS_R_HINT &&
result != DNS_R_NCACHENXDOMAIN && result != DNS_R_NCACHENXDOMAIN &&
result != DNS_R_NCACHENXRRSET && result != DNS_R_NCACHENXRRSET &&
result != DNS_R_NXRRSET && result != DNS_R_NXRRSET &&
result != DNS_R_HINTNXRRSET &&
result != ISC_R_NOTFOUND) { result != ISC_R_NOTFOUND) {
goto notfound; goto notfound;
} }
@@ -876,11 +961,15 @@ view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
return (ISC_R_NOTFOUND); return (ISC_R_NOTFOUND);
} }
/*%
* Checks to make sure we are not going to loop. As we use a SHARED fetch
* the validation process will stall if looping was to occur.
*/
static inline isc_boolean_t static inline isc_boolean_t
check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) { check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
dns_validator_t *parent; dns_validator_t *parent;
for (parent = val->parent; parent != NULL; parent = parent->parent) { for (parent = val; parent != NULL; parent = parent->parent) {
if (parent->event != NULL && if (parent->event != NULL &&
parent->event->type == type && parent->event->type == type &&
dns_name_equal(parent->event->name, name)) dns_name_equal(parent->event->name, name))
@@ -894,6 +983,9 @@ check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
return (ISC_FALSE); return (ISC_FALSE);
} }
/*%
* Start a fetch for the requested name and type.
*/
static inline isc_result_t static inline isc_result_t
create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
isc_taskaction_t callback, const char *caller) isc_taskaction_t callback, const char *caller)
@@ -916,6 +1008,9 @@ create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
&val->fetch)); &val->fetch));
} }
/*%
* Start a subvalidation process.
*/
static inline isc_result_t static inline isc_result_t
create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
@@ -938,7 +1033,7 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
return (result); return (result);
} }
/* /*%
* Try to find a key that could have signed 'siginfo' among those * Try to find a key that could have signed 'siginfo' among those
* in 'rdataset'. If found, build a dst_key_t for it and point * in 'rdataset'. If found, build a dst_key_t for it and point
* val->key at it. * val->key at it.
@@ -1006,6 +1101,9 @@ get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo,
return (result); return (result);
} }
/*%
* Get the key that genertated this signature.
*/
static isc_result_t static isc_result_t
get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) { get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) {
isc_result_t result; isc_result_t result;
@@ -1132,7 +1230,7 @@ compute_keytag(dns_rdata_t *rdata, dns_rdata_dnskey_t *key) {
return (dst_region_computeid(&r, key->algorithm)); return (dst_region_computeid(&r, key->algorithm));
} }
/* /*%
* Is this keyset self-signed? * Is this keyset self-signed?
*/ */
static isc_boolean_t static isc_boolean_t
@@ -1174,8 +1272,19 @@ isselfsigned(dns_validator_t *val) {
return (ISC_FALSE); return (ISC_FALSE);
} }
/*%
* Attempt to verify the rdataset using the given key and rdata (RRSIG).
* The signature was good and from a wildcard record and the QNAME does
* not match the wildcard we need to look for a NOQNAME proof.
*
* Returns:
* \li ISC_R_SUCCESS if the verification succeeds.
* \li Others if the verification fails.
*/
static isc_result_t static isc_result_t
verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata) { verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata,
isc_uint16_t keyid)
{
isc_result_t result; isc_result_t result;
dns_fixedname_t fixed; dns_fixedname_t fixed;
@@ -1185,8 +1294,8 @@ verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata) {
key, ISC_FALSE, val->view->mctx, rdata, key, ISC_FALSE, val->view->mctx, rdata,
dns_fixedname_name(&fixed)); dns_fixedname_name(&fixed));
validator_log(val, ISC_LOG_DEBUG(3), validator_log(val, ISC_LOG_DEBUG(3),
"verify rdataset: %s", "verify rdataset (keyid=%u): %s",
isc_result_totext(result)); keyid, isc_result_totext(result));
if (result == DNS_R_FROMWILDCARD) { if (result == DNS_R_FROMWILDCARD) {
if (!dns_name_equal(val->event->name, if (!dns_name_equal(val->event->name,
dns_fixedname_name(&fixed))) dns_fixedname_name(&fixed)))
@@ -1196,14 +1305,14 @@ verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata) {
return (result); return (result);
} }
/* /*%
* Attempts positive response validation of a normal RRset. * Attempts positive response validation of a normal RRset.
* *
* Returns: * Returns:
* ISC_R_SUCCESS Validation completed successfully * \li ISC_R_SUCCESS Validation completed successfully
* DNS_R_WAIT Validation has started but is waiting * \li DNS_R_WAIT Validation has started but is waiting
* for an event. * for an event.
* Other return codes are possible and all indicate failure. * \li Other return codes are possible and all indicate failure.
*/ */
static isc_result_t static isc_result_t
validate(dns_validator_t *val, isc_boolean_t resume) { validate(dns_validator_t *val, isc_boolean_t resume) {
@@ -1274,7 +1383,8 @@ validate(dns_validator_t *val, isc_boolean_t resume) {
} }
do { do {
result = verify(val, val->key, &rdata); result = verify(val, val->key, &rdata,
val->siginfo->keyid);
if (result == ISC_R_SUCCESS) if (result == ISC_R_SUCCESS)
break; break;
if (val->keynode != NULL) { if (val->keynode != NULL) {
@@ -1358,6 +1468,10 @@ validate(dns_validator_t *val, isc_boolean_t resume) {
return (DNS_R_NOVALIDSIG); return (DNS_R_NOVALIDSIG);
} }
/*%
* Validate the DNSKEY RRset by looking for a DNSKEY that matches a
* DLV record and that also verifies the DNSKEY RRset.
*/
static isc_result_t static isc_result_t
dlv_validatezonekey(dns_validator_t *val) { dlv_validatezonekey(dns_validator_t *val) {
dns_keytag_t keytag; dns_keytag_t keytag;
@@ -1375,12 +1489,12 @@ dlv_validatezonekey(dns_validator_t *val) {
unsigned char dsbuf[DNS_DS_BUFFERSIZE]; unsigned char dsbuf[DNS_DS_BUFFERSIZE];
validator_log(val, ISC_LOG_DEBUG(3), "dlv_validatezonekey"); validator_log(val, ISC_LOG_DEBUG(3), "dlv_validatezonekey");
/* /*
* Look through the DLV record and find the keys that can sign the * Look through the DLV record and find the keys that can sign the
* key set and the matching signature. For each such key, attempt * key set and the matching signature. For each such key, attempt
* verification. * verification.
*/ */
supported_algorithm = ISC_FALSE; supported_algorithm = ISC_FALSE;
for (result = dns_rdataset_first(&val->dlv); for (result = dns_rdataset_first(&val->dlv);
@@ -1460,7 +1574,7 @@ dlv_validatezonekey(dns_validator_t *val) {
*/ */
continue; continue;
result = verify(val, dstkey, &sigrdata); result = verify(val, dstkey, &sigrdata, sig.keyid);
dst_key_free(&dstkey); dst_key_free(&dstkey);
if (result == ISC_R_SUCCESS) if (result == ISC_R_SUCCESS)
break; break;
@@ -1490,14 +1604,14 @@ dlv_validatezonekey(dns_validator_t *val) {
return (DNS_R_NOVALIDSIG); return (DNS_R_NOVALIDSIG);
} }
/* /*%
* Attempts positive response validation of an RRset containing zone keys. * Attempts positive response validation of an RRset containing zone keys.
* *
* Returns: * Returns:
* ISC_R_SUCCESS Validation completed successfully * \li ISC_R_SUCCESS Validation completed successfully
* DNS_R_WAIT Validation has started but is waiting * \li DNS_R_WAIT Validation has started but is waiting
* for an event. * for an event.
* Other return codes are possible and all indicate failure. * \li Other return codes are possible and all indicate failure.
*/ */
static isc_result_t static isc_result_t
validatezonekey(dns_validator_t *val) { validatezonekey(dns_validator_t *val) {
@@ -1547,7 +1661,8 @@ validatezonekey(dns_validator_t *val) {
&keynode); &keynode);
while (result == ISC_R_SUCCESS) { while (result == ISC_R_SUCCESS) {
dstkey = dns_keynode_key(keynode); dstkey = dns_keynode_key(keynode);
result = verify(val, dstkey, &sigrdata); result = verify(val, dstkey, &sigrdata,
sig.keyid);
if (result == ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) {
dns_keytable_detachkeynode(val->keytable, dns_keytable_detachkeynode(val->keytable,
&keynode); &keynode);
@@ -1686,6 +1801,9 @@ validatezonekey(dns_validator_t *val) {
dns_rdataset_init(&trdataset); dns_rdataset_init(&trdataset);
dns_rdataset_clone(val->event->rdataset, &trdataset); dns_rdataset_clone(val->event->rdataset, &trdataset);
/*
* Look for the KEY that matches the DS record.
*/
for (result = dns_rdataset_first(&trdataset); for (result = dns_rdataset_first(&trdataset);
result == ISC_R_SUCCESS; result == ISC_R_SUCCESS;
result = dns_rdataset_next(&trdataset)) result = dns_rdataset_next(&trdataset))
@@ -1720,7 +1838,7 @@ validatezonekey(dns_validator_t *val) {
dns_rdataset_current(val->event->sigrdataset, dns_rdataset_current(val->event->sigrdataset,
&sigrdata); &sigrdata);
(void)dns_rdata_tostruct(&sigrdata, &sig, NULL); (void)dns_rdata_tostruct(&sigrdata, &sig, NULL);
if (ds.key_tag != sig.keyid && if (ds.key_tag != sig.keyid ||
ds.algorithm != sig.algorithm) ds.algorithm != sig.algorithm)
continue; continue;
@@ -1734,8 +1852,7 @@ validatezonekey(dns_validator_t *val) {
* This really shouldn't happen, but... * This really shouldn't happen, but...
*/ */
continue; continue;
result = verify(val, dstkey, &sigrdata, sig.keyid);
result = verify(val, dstkey, &sigrdata);
dst_key_free(&dstkey); dst_key_free(&dstkey);
if (result == ISC_R_SUCCESS) if (result == ISC_R_SUCCESS)
break; break;
@@ -1765,14 +1882,14 @@ validatezonekey(dns_validator_t *val) {
return (DNS_R_NOVALIDSIG); return (DNS_R_NOVALIDSIG);
} }
/* /*%
* Starts a positive response validation. * Starts a positive response validation.
* *
* Returns: * Returns:
* ISC_R_SUCCESS Validation completed successfully * \li ISC_R_SUCCESS Validation completed successfully
* DNS_R_WAIT Validation has started but is waiting * \li DNS_R_WAIT Validation has started but is waiting
* for an event. * for an event.
* Other return codes are possible and all indicate failure. * \li Other return codes are possible and all indicate failure.
*/ */
static isc_result_t static isc_result_t
start_positive_validation(dns_validator_t *val) { start_positive_validation(dns_validator_t *val) {
@@ -1785,6 +1902,14 @@ start_positive_validation(dns_validator_t *val) {
return (validatezonekey(val)); return (validatezonekey(val));
} }
/*%
* Look for NODATA at the wildcard and NOWILDCARD proofs in the
* previously validated NSEC records. As these proofs are mutually
* exclusive we stop when one is found.
*
* Returns
* \li ISC_R_SUCCESS
*/
static isc_result_t static isc_result_t
checkwildcard(dns_validator_t *val) { checkwildcard(dns_validator_t *val) {
dns_name_t *name, *wild; dns_name_t *name, *wild;
@@ -1857,6 +1982,18 @@ checkwildcard(dns_validator_t *val) {
return (result); return (result);
} }
/*%
* Prove a negative answer is good or that there is a NOQNAME when the
* answer is from a wildcard.
*
* Loop through the authority section looking for NODATA, NOWILDCARD
* and NOQNAME proofs in the NSEC records by calling authvalidated().
*
* If the required proofs are found we are done.
*
* If the proofs are not found attempt to prove this is a unsecure
* response.
*/
static isc_result_t static isc_result_t
nsecvalidate(dns_validator_t *val, isc_boolean_t resume) { nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
dns_name_t *name; dns_name_t *name;
@@ -1952,7 +2089,8 @@ nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
return (result); return (result);
/* /*
* Do we only need to check for NOQNAME? * Do we only need to check for NOQNAME? To get here we must have
* had a secure wildcard answer.
*/ */
if ((val->attributes & VALATTR_NEEDNODATA) == 0 && if ((val->attributes & VALATTR_NEEDNODATA) == 0 &&
(val->attributes & VALATTR_NEEDNOWILDCARD) == 0 && (val->attributes & VALATTR_NEEDNOWILDCARD) == 0 &&
@@ -1988,28 +2126,17 @@ nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
((val->attributes & VALATTR_NEEDNOQNAME) != 0 && ((val->attributes & VALATTR_NEEDNOQNAME) != 0 &&
(val->attributes & VALATTR_FOUNDNOQNAME) != 0 && (val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
(val->attributes & VALATTR_NEEDNOWILDCARD) != 0 && (val->attributes & VALATTR_NEEDNOWILDCARD) != 0 &&
(val->attributes & VALATTR_FOUNDNOWILDCARD) != 0)) (val->attributes & VALATTR_FOUNDNOWILDCARD) != 0)) {
val->attributes |= VALATTR_FOUNDNONEXISTENCE;
if ((val->attributes & VALATTR_FOUNDNONEXISTENCE) == 0) {
if (!val->seensig && val->soaset != NULL) {
result = create_validator(val, val->soaname,
dns_rdatatype_soa,
val->soaset, NULL,
negauthvalidated,
"nsecvalidate");
if (result != ISC_R_SUCCESS)
return (result);
return (DNS_R_WAIT);
}
validator_log(val, ISC_LOG_DEBUG(3), validator_log(val, ISC_LOG_DEBUG(3),
"nonexistence proof not found"); "nonexistence proof(s) found");
return (DNS_R_NOVALIDNSEC);
} else {
validator_log(val, ISC_LOG_DEBUG(3),
"nonexistence proof found");
return (ISC_R_SUCCESS); return (ISC_R_SUCCESS);
} }
validator_log(val, ISC_LOG_DEBUG(3),
"nonexistence proof(s) not found");
val->attributes |= VALATTR_AUTHNONPENDING;
val->attributes |= VALATTR_INSECURITY;
return (proveunsecure(val, ISC_FALSE));
} }
static isc_boolean_t static isc_boolean_t
@@ -2036,6 +2163,11 @@ check_ds(dns_validator_t *val, dns_name_t *name, dns_rdataset_t *rdataset) {
return (ISC_FALSE); return (ISC_FALSE);
} }
/*%
* Callback from fetching a DLV record.
*
* Resumes the DLV lookup process.
*/
static void static void
dlvfetched(isc_task_t *task, isc_event_t *event) { dlvfetched(isc_task_t *task, isc_event_t *event) {
char namebuf[DNS_NAME_FORMATSIZE]; char namebuf[DNS_NAME_FORMATSIZE];
@@ -2072,9 +2204,7 @@ dlvfetched(isc_task_t *task, isc_event_t *event) {
dns_rdataset_clone(&val->frdataset, &val->dlv); dns_rdataset_clone(&val->frdataset, &val->dlv);
val->havedlvsep = ISC_TRUE; val->havedlvsep = ISC_TRUE;
validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", namebuf); validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", namebuf);
result = dlv_validator_start(val); dlv_validator_start(val);
if (result != DNS_R_WAIT)
validator_done(val, result);
} else if (eresult == DNS_R_NXRRSET || } else if (eresult == DNS_R_NXRRSET ||
eresult == DNS_R_NXDOMAIN || eresult == DNS_R_NXDOMAIN ||
eresult == DNS_R_NCACHENXRRSET || eresult == DNS_R_NCACHENXRRSET ||
@@ -2085,9 +2215,7 @@ dlvfetched(isc_task_t *task, isc_event_t *event) {
namebuf, sizeof(namebuf)); namebuf, sizeof(namebuf));
validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found",
namebuf); namebuf);
result = dlv_validator_start(val); dlv_validator_start(val);
if (result != DNS_R_WAIT)
validator_done(val, result);
} else if (result == ISC_R_NOTFOUND) { } else if (result == ISC_R_NOTFOUND) {
validator_log(val, ISC_LOG_DEBUG(3), "DLV not found"); validator_log(val, ISC_LOG_DEBUG(3), "DLV not found");
markanswer(val); markanswer(val);
@@ -2101,6 +2229,7 @@ dlvfetched(isc_task_t *task, isc_event_t *event) {
} else { } else {
validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s", validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s",
dns_result_totext(eresult)); dns_result_totext(eresult));
validator_done(val, eresult);
} }
want_destroy = exit_check(val); want_destroy = exit_check(val);
UNLOCK(&val->lock); UNLOCK(&val->lock);
@@ -2108,6 +2237,14 @@ dlvfetched(isc_task_t *task, isc_event_t *event) {
destroy(val); destroy(val);
} }
/*%
* Start the DLV lookup proccess.
*
* Returns
* \li ISC_R_SUCCESS
* \li DNS_R_WAIT
* \li Others on validation failures.
*/
static isc_result_t static isc_result_t
startfinddlvsep(dns_validator_t *val, dns_name_t *unsecure) { startfinddlvsep(dns_validator_t *val, dns_name_t *unsecure) {
char namebuf[DNS_NAME_FORMATSIZE]; char namebuf[DNS_NAME_FORMATSIZE];
@@ -2142,9 +2279,19 @@ startfinddlvsep(dns_validator_t *val, dns_name_t *unsecure) {
dns_name_format(dns_fixedname_name(&val->dlvsep), namebuf, dns_name_format(dns_fixedname_name(&val->dlvsep), namebuf,
sizeof(namebuf)); sizeof(namebuf));
validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", namebuf); validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", namebuf);
return (dlv_validator_start(val)); dlv_validator_start(val);
return (DNS_R_WAIT);
} }
/*%
* Continue the DLV lookup process.
*
* Returns
* \li ISC_R_SUCCESS
* \li ISC_R_NOTFOUND
* \li DNS_R_WAIT
* \li Others on validation failure.
*/
static isc_result_t static isc_result_t
finddlvsep(dns_validator_t *val, isc_boolean_t resume) { finddlvsep(dns_validator_t *val, isc_boolean_t resume) {
char namebuf[DNS_NAME_FORMATSIZE]; char namebuf[DNS_NAME_FORMATSIZE];
@@ -2238,11 +2385,24 @@ finddlvsep(dns_validator_t *val, isc_boolean_t resume) {
return (ISC_R_NOTFOUND); return (ISC_R_NOTFOUND);
} }
/* /*%
* proveunsecure walks down from the SEP looking for a break in the * proveunsecure walks down from the SEP looking for a break in the
* chain of trust. That occurs when we can prove the DS record does * chain of trust. That occurs when we can prove the DS record does
* not exist at a delegation point or the DS exists at a delegation * not exist at a delegation point or the DS exists at a delegation
* but we don't support the algorithm/digest. * but we don't support the algorithm/digest.
*
* If DLV is active and we look for a DLV record at or below the
* point we go insecure. If found we restart the validation process.
* If not found or DLV isn't active we mark the response as a answer.
*
* Returns:
* \li ISC_R_SUCCESS val->event->name is in a unsecure zone
* \li DNS_R_WAIT validation is in progress.
* \li DNS_R_MUSTBESECURE val->event->name is supposed to be secure
* (policy) but we proved that it is unsecure.
* \li DNS_R_NOVALIDSIG
* \li DNS_R_NOVALIDNSEC
* \li DNS_R_NOTINSECURE
*/ */
static isc_result_t static isc_result_t
proveunsecure(dns_validator_t *val, isc_boolean_t resume) { proveunsecure(dns_validator_t *val, isc_boolean_t resume) {
@@ -2402,8 +2562,7 @@ proveunsecure(dns_validator_t *val, isc_boolean_t resume) {
goto out; goto out;
return (DNS_R_WAIT); return (DNS_R_WAIT);
} else if (result == DNS_R_NXDOMAIN || } else if (result == DNS_R_NXDOMAIN ||
result == DNS_R_NCACHENXDOMAIN) result == DNS_R_NCACHENXDOMAIN) {
{
/* /*
* This is not a zone cut. Assuming things are * This is not a zone cut. Assuming things are
* as expected, continue. * as expected, continue.
@@ -2448,7 +2607,10 @@ proveunsecure(dns_validator_t *val, isc_boolean_t resume) {
return (result); return (result);
} }
static isc_result_t /*%
* Reset state and revalidate the answer using DLV.
*/
static void
dlv_validator_start(dns_validator_t *val) { dlv_validator_start(dns_validator_t *val) {
isc_event_t *event; isc_event_t *event;
@@ -2462,9 +2624,20 @@ dlv_validator_start(dns_validator_t *val) {
event = (isc_event_t *)val->event; event = (isc_event_t *)val->event;
isc_task_send(val->task, &event); isc_task_send(val->task, &event);
return (DNS_R_WAIT);
} }
/*%
* Start the validation process.
*
* Attempt to valididate the answer based on the category it appears to
* fall in.
* \li 1. secure positive answer.
* \li 2. unsecure positive answer.
* \li 3. a negative answer (secure or unsecure).
*
* Note a answer that appears to be a secure positive answer may actually
* be a unsecure positive answer.
*/
static void static void
validator_start(isc_task_t *task, isc_event_t *event) { validator_start(isc_task_t *task, isc_event_t *event) {
dns_validator_t *val; dns_validator_t *val;
@@ -2536,7 +2709,6 @@ validator_start(isc_task_t *task, isc_event_t *event) {
validator_log(val, ISC_LOG_DEBUG(3), validator_log(val, ISC_LOG_DEBUG(3),
"attempting negative response validation"); "attempting negative response validation");
val->attributes |= VALATTR_NEGATIVE;
if (val->event->message->rcode == dns_rcode_nxdomain) { if (val->event->message->rcode == dns_rcode_nxdomain) {
val->attributes |= VALATTR_NEEDNOQNAME; val->attributes |= VALATTR_NEEDNOQNAME;
val->attributes |= VALATTR_NEEDNOWILDCARD; val->attributes |= VALATTR_NEEDNOWILDCARD;