diff --git a/CHANGES b/CHANGES index 49802fcf80..61cdf9490c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +5318. [cleanup] The DNSSEC validation code has been refactored + for clarity and to reduce code duplication. + [GL #622] + 5317. [func] A new asynchronous network communications system based on libuv is now used for listening for incoming requests and responding to them. (The diff --git a/doc/arm/notes-9.15.6.xml b/doc/arm/notes-9.15.6.xml index c368fdae41..fca186f62a 100644 --- a/doc/arm/notes-9.15.6.xml +++ b/doc/arm/notes-9.15.6.xml @@ -46,6 +46,12 @@ in the future releases. [GL #1265] + + + The DNSSEC validation code has been refactored for clarity and to + reduce code duplication. [GL #622] + + diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h index 51c62239d1..0057e1d5b5 100644 --- a/lib/dns/include/dns/validator.h +++ b/lib/dns/include/dns/validator.h @@ -128,7 +128,6 @@ struct dns_validator { dns_validator_t * subvalidator; dns_validator_t * parent; dns_keytable_t * keytable; - dns_keynode_t * keynode; dst_key_t * key; dns_rdata_rrsig_t * siginfo; isc_task_t * task; @@ -136,18 +135,12 @@ struct dns_validator { void * arg; unsigned int labels; dns_rdataset_t * currentset; - bool seensig; dns_rdataset_t * keyset; dns_rdataset_t * dsset; - dns_rdataset_t * soaset; - dns_rdataset_t * nsecset; - dns_rdataset_t * nsec3set; - dns_name_t * soaname; dns_rdataset_t frdataset; dns_rdataset_t fsigrdataset; dns_fixedname_t fname; dns_fixedname_t wild; - dns_fixedname_t nearest; dns_fixedname_t closest; ISC_LINK(dns_validator_t) link; bool mustbesecure; diff --git a/lib/dns/keytable.c b/lib/dns/keytable.c index 73e6cec89b..a154699d45 100644 --- a/lib/dns/keytable.c +++ b/lib/dns/keytable.c @@ -47,8 +47,8 @@ struct dns_keynode { unsigned int magic; isc_refcount_t refcount; dst_key_t * key; - bool managed; - bool initial; + bool managed; + bool initial; struct dns_keynode * next; }; diff --git a/lib/dns/nta.c b/lib/dns/nta.c index 8ea495cde5..3058ffcaa8 100644 --- a/lib/dns/nta.c +++ b/lib/dns/nta.c @@ -320,8 +320,7 @@ nta_create(dns_ntatable_t *ntatable, const dns_name_t *name, isc_result_t dns_ntatable_add(dns_ntatable_t *ntatable, const dns_name_t *name, - bool force, isc_stdtime_t now, - uint32_t lifetime) + bool force, isc_stdtime_t now, uint32_t lifetime) { isc_result_t result; dns_nta_t *nta = NULL; diff --git a/lib/dns/validator.c b/lib/dns/validator.c index ceb45260ed..11bafb567e 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -42,97 +42,90 @@ /*! \file * \brief - * Basic processing sequences. + * Basic processing sequences: * * \li When called with rdataset and sigrdataset: - * validator_start -> validate -> proveunsecure + * validator_start -> validate_answer -> proveunsecure + * validator_start -> validate_answer -> validate_nx (if secure wildcard) * - * validator_start -> validate -> nsecvalidate (secure wildcard answer) + * \li When called with rdataset but no sigrdataset: + * validator_start -> proveunsecure * - * \li When called with rdataset: - * validator_start -> proveunsecure + * \li When called with no rdataset or sigrdataset: + * validator_start -> validate_nx-> proveunsecure * - * \li When called without a rdataset: - * 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. + * validator_start: determine what type of validation to do. + * validate_answer: attempt to perform a positive validation. + * proveunsecure: attempt to prove the answer comes from an unsecure zone. + * validate_nx: attempt to prove a negative response. */ -#define VALIDATOR_MAGIC ISC_MAGIC('V', 'a', 'l', '?') -#define VALID_VALIDATOR(v) ISC_MAGIC_VALID(v, VALIDATOR_MAGIC) +#define VALIDATOR_MAGIC ISC_MAGIC('V', 'a', 'l', '?') +#define VALID_VALIDATOR(v) ISC_MAGIC_VALID(v, VALIDATOR_MAGIC) -#define VALATTR_SHUTDOWN 0x0001 /*%< Shutting down. */ -#define VALATTR_CANCELED 0x0002 /*%< Canceled. */ -#define VALATTR_TRIEDVERIFY 0x0004 /*%< We have found a key and +#define VALATTR_SHUTDOWN 0x0001 /*%< Shutting down. */ +#define VALATTR_CANCELED 0x0002 /*%< Canceled. */ +#define VALATTR_TRIEDVERIFY 0x0004 /*%< We have found a key and * have attempted a verify. */ -#define VALATTR_INSECURITY 0x0010 /*%< Attempting proveunsecure. */ +#define VALATTR_INSECURITY 0x0010 /*%< Attempting proveunsecure. */ /*! * NSEC proofs to be looked for. */ -#define VALATTR_NEEDNOQNAME 0x00000100 -#define VALATTR_NEEDNOWILDCARD 0x00000200 -#define VALATTR_NEEDNODATA 0x00000400 +#define VALATTR_NEEDNOQNAME 0x00000100 +#define VALATTR_NEEDNOWILDCARD 0x00000200 +#define VALATTR_NEEDNODATA 0x00000400 /*! * NSEC proofs that have been found. */ -#define VALATTR_FOUNDNOQNAME 0x00001000 -#define VALATTR_FOUNDNOWILDCARD 0x00002000 -#define VALATTR_FOUNDNODATA 0x00004000 -#define VALATTR_FOUNDCLOSEST 0x00008000 +#define VALATTR_FOUNDNOQNAME 0x00001000 +#define VALATTR_FOUNDNOWILDCARD 0x00002000 +#define VALATTR_FOUNDNODATA 0x00004000 +#define VALATTR_FOUNDCLOSEST 0x00008000 +#define VALATTR_FOUNDOPTOUT 0x00010000 +#define VALATTR_FOUNDUNKNOWN 0x00020000 -/* - * - */ -#define VALATTR_FOUNDOPTOUT 0x00010000 -#define VALATTR_FOUNDUNKNOWN 0x00020000 - -#define NEEDNODATA(val) ((val->attributes & VALATTR_NEEDNODATA) != 0) -#define NEEDNOQNAME(val) ((val->attributes & VALATTR_NEEDNOQNAME) != 0) -#define NEEDNOWILDCARD(val) ((val->attributes & VALATTR_NEEDNOWILDCARD) != 0) -#define FOUNDNODATA(val) ((val->attributes & VALATTR_FOUNDNODATA) != 0) -#define FOUNDNOQNAME(val) ((val->attributes & VALATTR_FOUNDNOQNAME) != 0) +#define NEEDNODATA(val) ((val->attributes & VALATTR_NEEDNODATA) != 0) +#define NEEDNOQNAME(val) ((val->attributes & VALATTR_NEEDNOQNAME) != 0) +#define NEEDNOWILDCARD(val) ((val->attributes & VALATTR_NEEDNOWILDCARD) != 0) +#define FOUNDNODATA(val) ((val->attributes & VALATTR_FOUNDNODATA) != 0) +#define FOUNDNOQNAME(val) ((val->attributes & VALATTR_FOUNDNOQNAME) != 0) #define FOUNDNOWILDCARD(val) ((val->attributes & VALATTR_FOUNDNOWILDCARD) != 0) -#define FOUNDCLOSEST(val) ((val->attributes & VALATTR_FOUNDCLOSEST) != 0) -#define FOUNDOPTOUT(val) ((val->attributes & VALATTR_FOUNDOPTOUT) != 0) +#define FOUNDCLOSEST(val) ((val->attributes & VALATTR_FOUNDCLOSEST) != 0) +#define FOUNDOPTOUT(val) ((val->attributes & VALATTR_FOUNDOPTOUT) != 0) -#define SHUTDOWN(v) (((v)->attributes & VALATTR_SHUTDOWN) != 0) -#define CANCELED(v) (((v)->attributes & VALATTR_CANCELED) != 0) +#define SHUTDOWN(v) (((v)->attributes & VALATTR_SHUTDOWN) != 0) +#define CANCELED(v) (((v)->attributes & VALATTR_CANCELED) != 0) -#define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) +#define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) static void destroy(dns_validator_t *val); static isc_result_t -get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo, - dns_rdataset_t *rdataset); +select_signing_key(dns_validator_t *val, dns_rdataset_t *rdataset); static isc_result_t -validate(dns_validator_t *val, bool resume); +validate_answer(dns_validator_t *val, bool resume); static isc_result_t -validatezonekey(dns_validator_t *val); +validate_dnskey(dns_validator_t *val); static isc_result_t -nsecvalidate(dns_validator_t *val, bool resume); +validate_nx(dns_validator_t *val, bool resume); static isc_result_t -proveunsecure(dns_validator_t *val, bool have_ds, - bool resume); +proveunsecure(dns_validator_t *val, bool have_ds, bool resume); static void validator_logv(dns_validator_t *val, isc_logcategory_t *category, isc_logmodule_t *module, int level, const char *fmt, va_list ap) - ISC_FORMAT_PRINTF(5, 0); + ISC_FORMAT_PRINTF(5, 0); static void validator_log(void *val, int level, const char *fmt, ...) - ISC_FORMAT_PRINTF(3, 4); + ISC_FORMAT_PRINTF(3, 4); static void validator_logcreate(dns_validator_t *val, @@ -140,32 +133,81 @@ validator_logcreate(dns_validator_t *val, const char *caller, const char *operation); /*% - * Mark the RRsets as a answer. + * Ensure the validator's rdatasets are marked as expired. */ -static inline void -markanswer(dns_validator_t *val, const char *where) { - validator_log(val, ISC_LOG_DEBUG(3), "marking as answer (%s)", where); - if (val->event->rdataset != NULL) - dns_rdataset_settrust(val->event->rdataset, dns_trust_answer); - if (val->event->sigrdataset != NULL) - dns_rdataset_settrust(val->event->sigrdataset, - dns_trust_answer); +static void +expire_rdatasets(dns_validator_t *val) { + if (dns_rdataset_isassociated(&val->frdataset)) { + dns_rdataset_expire(&val->frdataset); + } + if (dns_rdataset_isassociated(&val->fsigrdataset)) { + dns_rdataset_expire(&val->fsigrdataset); + } } +/*% + * Ensure the validator's rdatasets are disassociated. + */ +static void +disassociate_rdatasets(dns_validator_t *val) { + if (dns_rdataset_isassociated(&val->frdataset)) { + dns_rdataset_disassociate(&val->frdataset); + } + if (dns_rdataset_isassociated(&val->fsigrdataset)) { + dns_rdataset_disassociate(&val->fsigrdataset); + } +} + +/*% + * Mark the rdatasets in val->event with trust level "answer", + * indicating that they did not validate, but could be cached as insecure. + * + * If we are validating a name that is marked as "must be secure", log a + * warning and return DNS_R_MUSTBESECURE instead. + */ +static inline isc_result_t +markanswer(dns_validator_t *val, const char *where, const char *mbstext) { + if (val->mustbesecure && mbstext != NULL) { + validator_log(val, ISC_LOG_WARNING, + "must be secure failure, %s", mbstext); + return (DNS_R_MUSTBESECURE); + } + + validator_log(val, ISC_LOG_DEBUG(3), "marking as answer (%s)", where); + if (val->event->rdataset != NULL) { + dns_rdataset_settrust(val->event->rdataset, dns_trust_answer); + } + if (val->event->sigrdataset != NULL) { + dns_rdataset_settrust(val->event->sigrdataset, + dns_trust_answer); + } + + return (ISC_R_SUCCESS); +} + +/*% + * Mark the RRsets in val->event with trust level secure. + */ static inline void marksecure(dns_validatorevent_t *event) { dns_rdataset_settrust(event->rdataset, dns_trust_secure); - if (event->sigrdataset != NULL) + if (event->sigrdataset != NULL) { dns_rdataset_settrust(event->sigrdataset, dns_trust_secure); + } event->secure = true; } +/* + * Validator 'val' is finished; send the completion event to the task + * that called dns_validator_create(), with result `result`. + */ static void validator_done(dns_validator_t *val, isc_result_t result) { isc_task_t *task; - if (val->event == NULL) + if (val->event == NULL) { return; + } /* * Caller must be holding the lock. @@ -180,18 +222,23 @@ validator_done(dns_validator_t *val, isc_result_t result) { isc_task_sendanddetach(&task, (isc_event_t **)&val->event); } +/* + * Called when deciding whether to destroy validator 'val'. + */ static inline bool exit_check(dns_validator_t *val) { /* * Caller must be holding the lock. */ - if (!SHUTDOWN(val)) + if (!SHUTDOWN(val)) { return (false); + } INSIST(val->event == NULL); - if (val->fetch != NULL || val->subvalidator != NULL) + if (val->fetch != NULL || val->subvalidator != NULL) { return (false); + } return (true); } @@ -222,15 +269,17 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset, REQUIRE(dbresult == DNS_R_NXRRSET || dbresult == DNS_R_NCACHENXRRSET); dns_rdataset_init(&set); - if (dbresult == DNS_R_NXRRSET) + if (dbresult == DNS_R_NXRRSET) { dns_rdataset_clone(rdataset, &set); - else { + } else { result = dns_ncache_getrdataset(rdataset, name, dns_rdatatype_nsec, &set); - if (result == ISC_R_NOTFOUND) + if (result == ISC_R_NOTFOUND) { goto trynsec3; - if (result != ISC_R_SUCCESS) + } + if (result != ISC_R_SUCCESS) { return (false); + } } INSIST(set.type == dns_rdatatype_nsec); @@ -278,14 +327,16 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset, dns_rdata_reset(&rdata); dns_rdataset_current(&set, &rdata); (void)dns_rdata_tostruct(&rdata, &nsec3, NULL); - if (nsec3.hash != 1) + if (nsec3.hash != 1) { continue; + } length = isc_iterated_hash(hash, nsec3.hash, nsec3.iterations, nsec3.salt, nsec3.salt_length, name->ndata, name->length); - if (length != isc_buffer_usedlength(&buffer)) + if (length != isc_buffer_usedlength(&buffer)) { continue; + } order = memcmp(hash, owner, length); if (order == 0) { found = dns_nsec3_typepresent(&rdata, @@ -293,8 +344,9 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset, dns_rdataset_disassociate(&set); return (found); } - if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) == 0) + if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) == 0) { continue; + } /* * Does this optout span cover the name? */ @@ -302,7 +354,8 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset, if ((scope < 0 && order > 0 && memcmp(hash, nsec3.next, length) < 0) || (scope >= 0 && (order > 0 || - memcmp(hash, nsec3.next, length) < 0))) + memcmp(hash, nsec3.next, + length) < 0))) { dns_rdataset_disassociate(&set); return (true); @@ -315,11 +368,11 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset, /*% * We have been asked to look for a key. - * If found resume the validation process. - * If not found fail the validation process. + * If found, resume the validation process. + * If not found, fail the validation process. */ static void -fetch_callback_validator(isc_task_t *task, isc_event_t *event) { +fetch_callback_dnskey(isc_task_t *task, isc_event_t *event) { dns_fetchevent_t *devent; dns_validator_t *val; dns_rdataset_t *rdataset; @@ -337,17 +390,20 @@ fetch_callback_validator(isc_task_t *task, isc_event_t *event) { eresult = devent->result; /* Free resources which are not of interest. */ - if (devent->node != NULL) + if (devent->node != NULL) { dns_db_detachnode(devent->db, &devent->node); - if (devent->db != NULL) + } + if (devent->db != NULL) { dns_db_detach(&devent->db); - if (dns_rdataset_isassociated(&val->fsigrdataset)) + } + if (dns_rdataset_isassociated(&val->fsigrdataset)) { dns_rdataset_disassociate(&val->fsigrdataset); + } isc_event_free(&event); INSIST(val->event != NULL); - validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_validator"); + validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_dnskey"); LOCK(&val->lock); fetch = val->fetch; val->fetch = NULL; @@ -361,52 +417,59 @@ fetch_callback_validator(isc_task_t *task, isc_event_t *event) { * Only extract the dst key if the keyset is secure. */ if (rdataset->trust >= dns_trust_secure) { - result = get_dst_key(val, val->siginfo, rdataset); - if (result == ISC_R_SUCCESS) + result = select_signing_key(val, rdataset); + if (result == ISC_R_SUCCESS) { val->keyset = &val->frdataset; + } } - result = validate(val, true); + result = validate_answer(val, true); if (result == DNS_R_NOVALIDSIG && (val->attributes & VALATTR_TRIEDVERIFY) == 0) { saved_result = result; validator_log(val, ISC_LOG_DEBUG(3), "falling back to insecurity proof"); - val->attributes |= VALATTR_INSECURITY; result = proveunsecure(val, false, false); - if (result == DNS_R_NOTINSECURE) + if (result == DNS_R_NOTINSECURE) { result = saved_result; + } } - if (result != DNS_R_WAIT) + if (result != DNS_R_WAIT) { validator_done(val, result); + } } else { validator_log(val, ISC_LOG_DEBUG(3), - "fetch_callback_validator: got %s", + "fetch_callback_dnskey: got %s", isc_result_totext(eresult)); - if (eresult == ISC_R_CANCELED) + if (eresult == ISC_R_CANCELED) { validator_done(val, eresult); - else + } else { validator_done(val, DNS_R_BROKENCHAIN); + } } + want_destroy = exit_check(val); UNLOCK(&val->lock); - if (fetch != NULL) + + if (fetch != NULL) { dns_resolver_destroyfetch(&fetch); - if (want_destroy) + } + + if (want_destroy) { 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. + * We have been asked to look for a DS. This may be part of + * walking a trust chain, or an insecurity proof. */ static void -dsfetched(isc_task_t *task, isc_event_t *event) { +fetch_callback_ds(isc_task_t *task, isc_event_t *event) { dns_fetchevent_t *devent; dns_validator_t *val; dns_rdataset_t *rdataset; - bool want_destroy; + bool want_destroy, trustchain; isc_result_t result; isc_result_t eresult; dns_fetch_t *fetch; @@ -418,154 +481,142 @@ dsfetched(isc_task_t *task, isc_event_t *event) { rdataset = &val->frdataset; eresult = devent->result; + /* + * Set 'trustchain' to true if we're walking a chain of + * trust; false if we're attempting to prove insecurity. + */ + trustchain = ((val->attributes & VALATTR_INSECURITY) == 0); + /* Free resources which are not of interest. */ - if (devent->node != NULL) + if (devent->node != NULL) { dns_db_detachnode(devent->db, &devent->node); - if (devent->db != NULL) - dns_db_detach(&devent->db); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); - isc_event_free(&event); - - INSIST(val->event != NULL); - - validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched"); - LOCK(&val->lock); - fetch = val->fetch; - val->fetch = NULL; - if (CANCELED(val)) { - validator_done(val, ISC_R_CANCELED); - } else if (eresult == ISC_R_SUCCESS) { - validator_log(val, ISC_LOG_DEBUG(3), - "dsset with trust %s", - dns_trust_totext(rdataset->trust)); - val->dsset = &val->frdataset; - result = validatezonekey(val); - if (result != DNS_R_WAIT) - validator_done(val, result); - } else if (eresult == DNS_R_CNAME || - eresult == DNS_R_NXRRSET || - eresult == DNS_R_NCACHENXRRSET || - eresult == DNS_R_SERVFAIL) /* RFC 1034 parent? */ - { - validator_log(val, ISC_LOG_DEBUG(3), - "falling back to insecurity proof (%s)", - dns_result_totext(eresult)); - val->attributes |= VALATTR_INSECURITY; - result = proveunsecure(val, false, false); - if (result != DNS_R_WAIT) - validator_done(val, result); - } else { - validator_log(val, ISC_LOG_DEBUG(3), - "dsfetched: got %s", - isc_result_totext(eresult)); - if (eresult == ISC_R_CANCELED) - validator_done(val, eresult); - else - validator_done(val, DNS_R_BROKENCHAIN); } - want_destroy = exit_check(val); - UNLOCK(&val->lock); - if (fetch != NULL) - dns_resolver_destroyfetch(&fetch); - if (want_destroy) - destroy(val); -} - -/*% - * 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 -dsfetched2(isc_task_t *task, isc_event_t *event) { - dns_fetchevent_t *devent; - dns_validator_t *val; - dns_name_t *tname; - bool want_destroy; - isc_result_t result; - isc_result_t eresult; - dns_fetch_t *fetch; - - UNUSED(task); - INSIST(event->ev_type == DNS_EVENT_FETCHDONE); - devent = (dns_fetchevent_t *)event; - val = devent->ev_arg; - eresult = devent->result; - - /* Free resources which are not of interest. */ - if (devent->node != NULL) - dns_db_detachnode(devent->db, &devent->node); - if (devent->db != NULL) + if (devent->db != NULL) { dns_db_detach(&devent->db); - if (dns_rdataset_isassociated(&val->fsigrdataset)) + } + if (dns_rdataset_isassociated(&val->fsigrdataset)) { dns_rdataset_disassociate(&val->fsigrdataset); + } INSIST(val->event != NULL); - validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched2: %s", - dns_result_totext(eresult)); + validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_ds"); LOCK(&val->lock); fetch = val->fetch; val->fetch = NULL; + if (CANCELED(val)) { validator_done(val, ISC_R_CANCELED); - } else if (eresult == DNS_R_CNAME || - eresult == DNS_R_NXRRSET || - eresult == DNS_R_NCACHENXRRSET) - { + goto done; + } + + switch (eresult) { + case DNS_R_NXDOMAIN: + case DNS_R_NCACHENXDOMAIN: /* - * There is no DS. If this is a delegation, we're done. + * These results only make sense if we're attempting + * an insecurity proof, not when walking a chain of trust. */ - tname = dns_fixedname_name(&devent->foundname); - if (eresult != DNS_R_CNAME && - isdelegation(tname, &val->frdataset, eresult)) { - if (val->mustbesecure) { - validator_log(val, ISC_LOG_WARNING, - "must be secure failure, no DS" - " and this is a delegation"); - validator_done(val, DNS_R_MUSTBESECURE); - } else { - markanswer(val, "dsfetched2"); - validator_done(val, ISC_R_SUCCESS); + if (trustchain) { + goto unexpected; + } + + /* FALLTHROUGH */ + case ISC_R_SUCCESS: + if (trustchain) { + /* + * We looked for a DS record as part of + * following a key chain upwards; resume following + * the chain. + */ + validator_log(val, ISC_LOG_DEBUG(3), + "dsset with trust %s", + dns_trust_totext(rdataset->trust)); + val->dsset = &val->frdataset; + result = validate_dnskey(val); + if (result != DNS_R_WAIT) { + validator_done(val, result); } } else { - result = proveunsecure(val, false, true); - if (result != DNS_R_WAIT) + /* + * There is a DS which may or may not be a zone cut. + * In either case we are still in a secure zone, + * so keep looking for the break in the chain + * of trust. + */ + result = proveunsecure(val, (eresult == ISC_R_SUCCESS), + true); + if (result != DNS_R_WAIT) { validator_done(val, result); + } } - } else if (eresult == ISC_R_SUCCESS || - eresult == DNS_R_NXDOMAIN || - eresult == DNS_R_NCACHENXDOMAIN) - { - /* - * 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, (eresult == ISC_R_SUCCESS), - true); - if (result != DNS_R_WAIT) + break; + case DNS_R_CNAME: + case DNS_R_NXRRSET: + case DNS_R_NCACHENXRRSET: + case DNS_R_SERVFAIL: /* RFC 1034 parent? */ + if (trustchain) { + /* + * Failed to find a DS while following the + * chain of trust; now we need to prove insecurity. + */ + validator_log(val, ISC_LOG_DEBUG(3), + "falling back to insecurity proof (%s)", + dns_result_totext(eresult)); + result = proveunsecure(val, false, false); + if (result != DNS_R_WAIT) { + validator_done(val, result); + } + } else if (eresult == DNS_R_SERVFAIL) { + goto unexpected; + } else if (eresult != DNS_R_CNAME && + isdelegation(dns_fixedname_name(&devent->foundname), + &val->frdataset, eresult)) + { + /* + * Failed to find a DS while trying to prove + * insecurity. If this is a zone cut, that + * means we're insecure. + */ + result = markanswer(val, "fetch_callback_ds", + "no DS and this is a delegation"); validator_done(val, result); - } else { - if (eresult == ISC_R_CANCELED) + } else { + /* + * Not a zone cut, so we have to keep looking for + * the break point in the chain of trust. + */ + result = proveunsecure(val, false, true); + if (result != DNS_R_WAIT) { + validator_done(val, result); + } + } + break; + + default: + unexpected: + validator_log(val, ISC_LOG_DEBUG(3), + "fetch_callback_ds: got %s", + isc_result_totext(eresult)); + if (eresult == ISC_R_CANCELED) { validator_done(val, eresult); - else - validator_done(val, DNS_R_NOVALIDDS); + } else { + validator_done(val, DNS_R_BROKENCHAIN); + } } + done: + isc_event_free(&event); want_destroy = exit_check(val); UNLOCK(&val->lock); - if (fetch != NULL) + + if (fetch != NULL) { dns_resolver_destroyfetch(&fetch); - if (want_destroy) + } + + if (want_destroy) { destroy(val); + } } /*% @@ -574,7 +625,7 @@ dsfetched2(isc_task_t *task, isc_event_t *event) { * Resumes the stalled validation process. */ static void -keyvalidated(isc_task_t *task, isc_event_t *event) { +validator_callback_dnskey(isc_task_t *task, isc_event_t *event) { dns_validatorevent_t *devent; dns_validator_t *val; bool want_destroy; @@ -594,7 +645,7 @@ keyvalidated(isc_task_t *task, isc_event_t *event) { INSIST(val->event != NULL); - validator_log(val, ISC_LOG_DEBUG(3), "in keyvalidated"); + validator_log(val, ISC_LOG_DEBUG(3), "in validator_callback_dnskey"); LOCK(&val->lock); if (CANCELED(val)) { validator_done(val, ISC_R_CANCELED); @@ -605,38 +656,39 @@ keyvalidated(isc_task_t *task, isc_event_t *event) { /* * Only extract the dst key if the keyset is secure. */ - if (val->frdataset.trust >= dns_trust_secure) - (void) get_dst_key(val, val->siginfo, &val->frdataset); - result = validate(val, true); + if (val->frdataset.trust >= dns_trust_secure) { + (void) select_signing_key(val, &val->frdataset); + } + result = validate_answer(val, true); if (result == DNS_R_NOVALIDSIG && (val->attributes & VALATTR_TRIEDVERIFY) == 0) { saved_result = result; validator_log(val, ISC_LOG_DEBUG(3), "falling back to insecurity proof"); - val->attributes |= VALATTR_INSECURITY; result = proveunsecure(val, false, false); - if (result == DNS_R_NOTINSECURE) + if (result == DNS_R_NOTINSECURE) { result = saved_result; + } } - if (result != DNS_R_WAIT) + if (result != DNS_R_WAIT) { validator_done(val, result); + } } else { if (eresult != DNS_R_BROKENCHAIN) { - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_expire(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_expire(&val->fsigrdataset); + expire_rdatasets(val); } validator_log(val, ISC_LOG_DEBUG(3), - "keyvalidated: got %s", + "validator_callback_dnskey: got %s", isc_result_totext(eresult)); validator_done(val, DNS_R_BROKENCHAIN); } + want_destroy = exit_check(val); UNLOCK(&val->lock); - if (want_destroy) + if (want_destroy) { destroy(val); + } } /*% @@ -645,7 +697,7 @@ keyvalidated(isc_task_t *task, isc_event_t *event) { * Resumes validation of the zone key or the unsecure zone proof. */ static void -dsvalidated(isc_task_t *task, isc_event_t *event) { +validator_callback_ds(isc_task_t *task, isc_event_t *event) { dns_validatorevent_t *devent; dns_validator_t *val; bool want_destroy; @@ -664,7 +716,7 @@ dsvalidated(isc_task_t *task, isc_event_t *event) { INSIST(val->event != NULL); - validator_log(val, ISC_LOG_DEBUG(3), "in dsvalidated"); + validator_log(val, ISC_LOG_DEBUG(3), "in validator_callback_ds"); LOCK(&val->lock); if (CANCELED(val)) { validator_done(val, ISC_R_CANCELED); @@ -674,45 +726,40 @@ dsvalidated(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "%s with trust %s", val->frdataset.type == dns_rdatatype_ds ? - "dsset" : "ds non-existance", + "dsset" : "ds non-existence", dns_trust_totext(val->frdataset.trust)); have_dsset = (val->frdataset.type == dns_rdatatype_ds); name = dns_fixedname_name(&val->fname); if ((val->attributes & VALATTR_INSECURITY) != 0 && val->frdataset.covers == dns_rdatatype_ds && NEGATIVE(&val->frdataset) && - isdelegation(name, &val->frdataset, DNS_R_NCACHENXRRSET)) { - if (val->mustbesecure) { - validator_log(val, ISC_LOG_WARNING, - "must be secure failure, no DS " - "and this is a delegation"); - result = DNS_R_MUSTBESECURE; - } else { - markanswer(val, "dsvalidated"); - result = ISC_R_SUCCESS;; - } + isdelegation(name, &val->frdataset, DNS_R_NCACHENXRRSET)) + { + result = markanswer(val, "validator_callback_ds", + "no DS and this is a delegation"); } else if ((val->attributes & VALATTR_INSECURITY) != 0) { result = proveunsecure(val, have_dsset, true); - } else - result = validatezonekey(val); - if (result != DNS_R_WAIT) + } else { + result = validate_dnskey(val); + } + if (result != DNS_R_WAIT) { validator_done(val, result); + } } else { if (eresult != DNS_R_BROKENCHAIN) { - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_expire(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_expire(&val->fsigrdataset); + expire_rdatasets(val); } validator_log(val, ISC_LOG_DEBUG(3), - "dsvalidated: got %s", + "validator_callback_ds: got %s", isc_result_totext(eresult)); validator_done(val, DNS_R_BROKENCHAIN); } + want_destroy = exit_check(val); UNLOCK(&val->lock); - if (want_destroy) + if (want_destroy) { destroy(val); + } } /*% @@ -721,7 +768,7 @@ dsvalidated(isc_task_t *task, isc_event_t *event) { * Resumes validation of the unsecure zone proof. */ static void -cnamevalidated(isc_task_t *task, isc_event_t *event) { +validator_callback_cname(isc_task_t *task, isc_event_t *event) { dns_validatorevent_t *devent; dns_validator_t *val; bool want_destroy; @@ -741,7 +788,7 @@ cnamevalidated(isc_task_t *task, isc_event_t *event) { INSIST(val->event != NULL); INSIST((val->attributes & VALATTR_INSECURITY) != 0); - validator_log(val, ISC_LOG_DEBUG(3), "in cnamevalidated"); + validator_log(val, ISC_LOG_DEBUG(3), "in validator_callback_cname"); LOCK(&val->lock); if (CANCELED(val)) { validator_done(val, ISC_R_CANCELED); @@ -749,24 +796,24 @@ cnamevalidated(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "cname with trust %s", dns_trust_totext(val->frdataset.trust)); result = proveunsecure(val, false, true); - if (result != DNS_R_WAIT) + if (result != DNS_R_WAIT) { validator_done(val, result); + } } else { if (eresult != DNS_R_BROKENCHAIN) { - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_expire(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_expire(&val->fsigrdataset); + expire_rdatasets(val); } validator_log(val, ISC_LOG_DEBUG(3), - "cnamevalidated: got %s", + "validator_callback_cname: got %s", isc_result_totext(eresult)); validator_done(val, DNS_R_BROKENCHAIN); } + want_destroy = exit_check(val); UNLOCK(&val->lock); - if (want_destroy) + if (want_destroy) { destroy(val); + } } /*% @@ -774,10 +821,10 @@ cnamevalidated(isc_task_t *task, isc_event_t *event) { * * Looks for NOQNAME, NODATA and OPTOUT proofs. * - * Resumes nsecvalidate. + * Resumes the negative response validation by calling validate_nx(). */ static void -authvalidated(isc_task_t *task, isc_event_t *event) { +validator_callback_nsec(isc_task_t *task, isc_event_t *event) { dns_validatorevent_t *devent; dns_validator_t *val; dns_rdataset_t *rdataset; @@ -796,30 +843,29 @@ authvalidated(isc_task_t *task, isc_event_t *event) { INSIST(val->event != NULL); - validator_log(val, ISC_LOG_DEBUG(3), "in authvalidated"); + validator_log(val, ISC_LOG_DEBUG(3), "in validator_callback_nsec"); LOCK(&val->lock); if (CANCELED(val)) { validator_done(val, ISC_R_CANCELED); } else if (result != ISC_R_SUCCESS) { validator_log(val, ISC_LOG_DEBUG(3), - "authvalidated: got %s", + "validator_callback_nsec: got %s", isc_result_totext(result)); - if (result == DNS_R_BROKENCHAIN) + if (result == DNS_R_BROKENCHAIN) { val->authfail++; - if (result == ISC_R_CANCELED) + } + if (result == ISC_R_CANCELED) { validator_done(val, result); - else { - result = nsecvalidate(val, true); - if (result != DNS_R_WAIT) + } else { + result = validate_nx(val, true); + if (result != DNS_R_WAIT) { validator_done(val, result); + } } } else { dns_name_t **proofs = val->event->proofs; dns_name_t *wild = dns_fixedname_name(&val->wild); - if (rdataset->trust == dns_trust_secure) - val->seensig = true; - if (rdataset->type == dns_rdatatype_nsec && rdataset->trust == dns_trust_secure && (NEEDNODATA(val) || NEEDNOQNAME(val)) && @@ -827,13 +873,14 @@ authvalidated(isc_task_t *task, isc_event_t *event) { dns_nsec_noexistnodata(val->event->type, val->event->name, devent->name, rdataset, &exists, &data, wild, validator_log, val) - == ISC_R_SUCCESS) + == ISC_R_SUCCESS) { if (exists && !data) { val->attributes |= VALATTR_FOUNDNODATA; - if (NEEDNODATA(val)) + if (NEEDNODATA(val)) { proofs[DNS_VALIDATOR_NODATAPROOF] = devent->name; + } } if (!exists) { dns_name_t *closest; @@ -852,25 +899,31 @@ authvalidated(isc_task_t *task, isc_event_t *event) { */ if (clabels == 0 || dns_name_countlabels(wild) == clabels + 1) + { val->attributes |= VALATTR_FOUNDCLOSEST; + } /* * The NSEC noqname proof also contains * the closest encloser. */ - if (NEEDNOQNAME(val)) + if (NEEDNOQNAME(val)) { proofs[DNS_VALIDATOR_NOQNAMEPROOF] = devent->name; + } } } - result = nsecvalidate(val, true); - if (result != DNS_R_WAIT) + result = validate_nx(val, true); + if (result != DNS_R_WAIT) { validator_done(val, result); + } } + want_destroy = exit_check(val); UNLOCK(&val->lock); - if (want_destroy) + if (want_destroy) { destroy(val); + } /* * Free stuff from the event. @@ -900,14 +953,11 @@ view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); + disassociate_rdatasets(val); if (isc_time_now(&now) == ISC_R_SUCCESS && - dns_resolver_getbadcache(val->view->resolver, name, type, &now)) { - + dns_resolver_getbadcache(val->view->resolver, name, type, &now)) + { dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(type, typebuf, sizeof(typebuf)); validator_log(val, ISC_LOG_INFO, "bad cache hit (%s/%s)", @@ -922,35 +972,33 @@ view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) { &val->frdataset, &val->fsigrdataset); if (result == DNS_R_NXDOMAIN) { - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); + goto notfound; } else if (result != ISC_R_SUCCESS && result != DNS_R_NCACHENXDOMAIN && result != DNS_R_NCACHENXRRSET && result != DNS_R_EMPTYNAME && result != DNS_R_NXRRSET && - result != ISC_R_NOTFOUND) { - goto notfound; + result != ISC_R_NOTFOUND) + { + result = ISC_R_NOTFOUND; + goto notfound; } + return (result); notfound: - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); - return (ISC_R_NOTFOUND); + disassociate_rdatasets(val); + + return (result); } /*% - * 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. - */ +* 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 bool check_deadlock(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) { dns_validator_t *parent; @@ -987,10 +1035,7 @@ create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, { unsigned int fopts = 0; - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); + disassociate_rdatasets(val); if (check_deadlock(val, name, type, NULL, NULL)) { validator_log(val, ISC_LOG_DEBUG(3), @@ -998,11 +1043,13 @@ create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, return (DNS_R_NOVALIDSIG); } - if ((val->options & DNS_VALIDATOR_NOCDFLAG) != 0) + if ((val->options & DNS_VALIDATOR_NOCDFLAG) != 0) { fopts |= DNS_FETCHOPT_NOCDFLAG; + } - if ((val->options & DNS_VALIDATOR_NONTA) != 0) + if ((val->options & DNS_VALIDATOR_NONTA) != 0) { fopts |= DNS_FETCHOPT_NONTA; + } validator_logcreate(val, name, type, caller, "fetch"); return (dns_resolver_createfetch(val->view->resolver, name, type, @@ -1024,20 +1071,26 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, { isc_result_t result; unsigned int vopts = 0; + dns_rdataset_t *sig = NULL; - if (check_deadlock(val, name, type, rdataset, sigrdataset)) { + if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { + sig = sigrdataset; + } + + if (check_deadlock(val, name, type, rdataset, sig)) { validator_log(val, ISC_LOG_DEBUG(3), "deadlock found (create_validator)"); return (DNS_R_NOVALIDSIG); } /* OK to clear other options, but preserve NOCDFLAG and NONTA. */ - vopts |= (val->options & (DNS_VALIDATOR_NOCDFLAG|DNS_VALIDATOR_NONTA)); + vopts |= (val->options & (DNS_VALIDATOR_NOCDFLAG | + DNS_VALIDATOR_NONTA)); validator_logcreate(val, name, type, caller, "validator"); result = dns_validator_create(val->view, name, type, - rdataset, sigrdataset, NULL, vopts, - val->task, action, val, + rdataset, sig, NULL, + vopts, val->task, action, val, &val->subvalidator); if (result == ISC_R_SUCCESS) { val->subvalidator->parent = val; @@ -1047,32 +1100,37 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, } /*% - * 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 - * val->key at it. + * Try to find a key that could have signed val->siginfo among those in + * 'rdataset'. If found, build a dst_key_t for it and point val->key at + * it. * - * If val->key is non-NULL, this returns the next matching key. + * If val->key is already non-NULL, locate it in the rdataset and then + * search past it for the *next* key that could have signed 'siginfo', then + * set val->key to that. + * + * Returns ISC_R_SUCCESS if a possible matching key has been found, + * ISC_R_NOTFOUND if not. Any other value indicates error. */ static isc_result_t -get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo, - dns_rdataset_t *rdataset) -{ +select_signing_key(dns_validator_t *val, dns_rdataset_t *rdataset) { isc_result_t result; + dns_rdata_rrsig_t *siginfo = val->siginfo; isc_buffer_t b; dns_rdata_t rdata = DNS_RDATA_INIT; dst_key_t *oldkey = val->key; bool foundold; - if (oldkey == NULL) + if (oldkey == NULL) { foundold = true; - else { + } else { foundold = false; val->key = NULL; } result = dns_rdataset_first(rdataset); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { goto failure; + } do { dns_rdataset_current(rdataset, &rdata); @@ -1081,21 +1139,21 @@ get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo, INSIST(val->key == NULL); result = dst_key_fromdns(&siginfo->signer, rdata.rdclass, &b, val->view->mctx, &val->key); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { goto failure; + } if (siginfo->algorithm == (dns_secalg_t)dst_key_alg(val->key) && siginfo->keyid == (dns_keytag_t)dst_key_id(val->key) && dst_key_iszonekey(val->key)) { - if (foundold) + if (foundold) { /* * This is the key we're looking for. */ return (ISC_R_SUCCESS); - else if (dst_key_compare(oldkey, val->key) == true) - { + } else if (dst_key_compare(oldkey, val->key)) { foundold = true; dst_key_free(&oldkey); } @@ -1104,22 +1162,26 @@ get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo, dns_rdata_reset(&rdata); result = dns_rdataset_next(rdataset); } while (result == ISC_R_SUCCESS); - if (result == ISC_R_NOMORE) + + if (result == ISC_R_NOMORE) { result = ISC_R_NOTFOUND; + } failure: - if (oldkey != NULL) + if (oldkey != NULL) { dst_key_free(&oldkey); + } return (result); } /*% - * Get the key that generated this signature. + * Get the key that generated the signature in val->siginfo. */ static isc_result_t -get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) { +seek_dnskey(dns_validator_t *val) { isc_result_t result; + dns_rdata_rrsig_t *siginfo = val->siginfo; unsigned int nlabels; int order; dns_namereln_t namereln; @@ -1134,22 +1196,26 @@ get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) { &order, &nlabels); if (namereln != dns_namereln_subdomain && namereln != dns_namereln_equal) + { return (DNS_R_CONTINUE); + } if (namereln == dns_namereln_equal) { /* * If this is a self-signed keyset, it must not be a zone key - * (since get_key is not called from validatezonekey). + * (since seek_dnskey is not called from validate_dnskey). */ - if (val->event->rdataset->type == dns_rdatatype_dnskey) + if (val->event->rdataset->type == dns_rdatatype_dnskey) { return (DNS_R_CONTINUE); + } /* * Records appearing in the parent zone at delegation * points cannot be self-signed. */ - if (dns_rdatatype_atparent(val->event->rdataset->type)) + if (dns_rdatatype_atparent(val->event->rdataset->type)) { return (DNS_R_CONTINUE); + } } else { /* * SOA and NS RRsets can only be signed by a key with @@ -1160,10 +1226,11 @@ get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) { { const char *type; - if (val->event->rdataset->type == dns_rdatatype_soa) + if (val->event->rdataset->type == dns_rdatatype_soa) { type = "SOA"; - else + } else { type = "NS"; + } validator_log(val, ISC_LOG_DEBUG(3), "%s signer mismatch", type); return (DNS_R_CONTINUE); @@ -1174,7 +1241,8 @@ get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) { * Do we know about this key? */ result = view_find(val, &siginfo->signer, dns_rdatatype_dnskey); - if (result == ISC_R_SUCCESS) { + switch (result) { + case ISC_R_SUCCESS: /* * We have an rrset for the given keyname. */ @@ -1192,10 +1260,11 @@ get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) { dns_rdatatype_dnskey, &val->frdataset, &val->fsigrdataset, - keyvalidated, - "get_key"); - if (result != ISC_R_SUCCESS) + validator_callback_dnskey, + "seek_dnskey"); + if (result != ISC_R_SUCCESS) { return (result); + } return (DNS_R_WAIT); } else if (DNS_TRUST_PENDING(val->frdataset.trust)) { /* @@ -1217,7 +1286,7 @@ get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) { validator_log(val, ISC_LOG_DEBUG(3), "keyset with trust %s", dns_trust_totext(val->frdataset.trust)); - result = get_dst_key(val, siginfo, val->keyset); + result = select_signing_key(val, val->keyset); if (result != ISC_R_SUCCESS) { /* * Either the key we're looking for is not @@ -1227,38 +1296,53 @@ get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) { result = DNS_R_CONTINUE; } } - } else if (result == ISC_R_NOTFOUND) { + break; + + case ISC_R_NOTFOUND: /* * We don't know anything about this key. */ result = create_fetch(val, &siginfo->signer, dns_rdatatype_dnskey, - fetch_callback_validator, "get_key"); - if (result != ISC_R_SUCCESS) + fetch_callback_dnskey, "seek_dnskey"); + if (result != ISC_R_SUCCESS) { return (result); + } return (DNS_R_WAIT); - } else if (result == DNS_R_NCACHENXDOMAIN || - result == DNS_R_NCACHENXRRSET || - result == DNS_R_EMPTYNAME || - result == DNS_R_NXDOMAIN || - result == DNS_R_NXRRSET) - { + + case DNS_R_NCACHENXDOMAIN: + case DNS_R_NCACHENXRRSET: + case DNS_R_EMPTYNAME: + case DNS_R_NXDOMAIN: + case DNS_R_NXRRSET: /* * This key doesn't exist. */ result = DNS_R_CONTINUE; - } else if (result == DNS_R_BROKENCHAIN) + break; + + case DNS_R_BROKENCHAIN: return (result); + default: + break; + } + if (dns_rdataset_isassociated(&val->frdataset) && val->keyset != &val->frdataset) + { dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) + } + if (dns_rdataset_isassociated(&val->fsigrdataset)) { dns_rdataset_disassociate(&val->fsigrdataset); + } return (result); } +/* + * Compute the tag for a key represented in a DNSKEY rdata. + */ static dns_keytag_t compute_keytag(dns_rdata_t *rdata) { isc_region_t r; @@ -1268,10 +1352,10 @@ compute_keytag(dns_rdata_t *rdata) { } /*% - * Is this keyset self-signed? + * Is the DNSKEY rrset in val->event->rdataset self-signed? */ static bool -isselfsigned(dns_validator_t *val) { +selfsigned_dnskey(dns_validator_t *val) { dns_rdataset_t *rdataset, *sigrdataset; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_t sigrdata = DNS_RDATA_INIT; @@ -1289,11 +1373,9 @@ isselfsigned(dns_validator_t *val) { name = val->event->name; mctx = val->view->mctx; - if (rdataset->type == dns_rdatatype_cname || - rdataset->type == dns_rdatatype_dname) - return (answer); - - INSIST(rdataset->type == dns_rdatatype_dnskey); + if (rdataset->type != dns_rdatatype_dnskey) { + return (false); + } for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; @@ -1316,21 +1398,25 @@ isselfsigned(dns_validator_t *val) { if (sig.algorithm != key.algorithm || sig.keyid != keytag || !dns_name_equal(name, &sig.signer)) + { continue; + } dstkey = NULL; result = dns_dnssec_keyfromrdata(name, &rdata, mctx, &dstkey); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { continue; + } result = dns_dnssec_verify(name, rdataset, dstkey, true, val->view->maxbits, mctx, &sigrdata, NULL); dst_key_free(&dstkey); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { continue; + } if ((key.flags & DNS_KEYFLAG_REVOKE) == 0) { answer = true; continue; @@ -1338,6 +1424,7 @@ isselfsigned(dns_validator_t *val) { dns_view_untrust(val->view, name, &key, mctx); } } + return (answer); } @@ -1371,19 +1458,23 @@ verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata, ignore = true; goto again; } - if (ignore && (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD)) + + if (ignore && + (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD)) + { validator_log(val, ISC_LOG_INFO, "accepted expired %sRRSIG (keyid=%u)", (result == DNS_R_FROMWILDCARD) ? "wildcard " : "", keyid); - else if (result == DNS_R_SIGEXPIRED || result == DNS_R_SIGFUTURE) + } else if (result == DNS_R_SIGEXPIRED || result == DNS_R_SIGFUTURE) { validator_log(val, ISC_LOG_INFO, "verify failed due to bad signature (keyid=%u): " "%s", keyid, isc_result_totext(result)); - else + } else { validator_log(val, ISC_LOG_DEBUG(3), "verify rdataset (keyid=%u): %s", keyid, isc_result_totext(result)); + } if (result == DNS_R_FROMWILDCARD) { if (!dns_name_equal(val->event->name, wild)) { dns_name_t *closest; @@ -1414,7 +1505,7 @@ verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata, * \li Other return codes are possible and all indicate failure. */ static isc_result_t -validate(dns_validator_t *val, bool resume) { +validate_answer(dns_validator_t *val, bool resume) { isc_result_t result, vresult = DNS_R_NOVALIDSIG; dns_validatorevent_t *event; dns_rdata_t rdata = DNS_RDATA_INIT; @@ -1446,26 +1537,30 @@ validate(dns_validator_t *val, bool resume) { sizeof(*val->siginfo)); } result = dns_rdata_tostruct(&rdata, val->siginfo, NULL); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (result); + } /* * At this point we could check that the signature algorithm * was known and "sufficiently good". */ if (!dns_resolver_algorithm_supported(val->view->resolver, - event->name, - val->siginfo->algorithm)) { + event->name, + val->siginfo->algorithm)) + { resume = false; continue; } if (!resume) { - result = get_key(val, val->siginfo); - if (result == DNS_R_CONTINUE) + result = seek_dnskey(val); + if (result == DNS_R_CONTINUE) { continue; /* Try the next SIG RR. */ - if (result != ISC_R_SUCCESS) + } + if (result != ISC_R_SUCCESS) { return (result); + } } /* @@ -1478,63 +1573,46 @@ validate(dns_validator_t *val, bool resume) { } do { + isc_result_t tresult; vresult = verify(val, val->key, &rdata, - val->siginfo->keyid); - if (vresult == ISC_R_SUCCESS) + val->siginfo->keyid); + if (vresult == ISC_R_SUCCESS) { + break; + } + + tresult = select_signing_key(val, val->keyset); + if (tresult != ISC_R_SUCCESS) { break; - if (val->keynode != NULL) { - dns_keynode_t *nextnode = NULL; - result = dns_keytable_findnextkeynode( - val->keytable, - val->keynode, - &nextnode); - dns_keytable_detachkeynode(val->keytable, - &val->keynode); - val->keynode = nextnode; - if (result != ISC_R_SUCCESS) { - val->key = NULL; - break; - } - val->key = dns_keynode_key(val->keynode); - if (val->key == NULL) - break; - } else { - if (get_dst_key(val, val->siginfo, val->keyset) - != ISC_R_SUCCESS) - break; } } while (1); - if (vresult != ISC_R_SUCCESS) + if (vresult != ISC_R_SUCCESS) { validator_log(val, ISC_LOG_DEBUG(3), "failed to verify rdataset"); - else { + } else { dns_rdataset_trimttl(event->rdataset, event->sigrdataset, val->siginfo, val->start, val->view->acceptexpired); } - if (val->keynode != NULL) - dns_keytable_detachkeynode(val->keytable, - &val->keynode); - else { - if (val->key != NULL) - dst_key_free(&val->key); - if (val->keyset != NULL) { - dns_rdataset_disassociate(val->keyset); - val->keyset = NULL; - } + if (val->key != NULL) { + dst_key_free(&val->key); + } + if (val->keyset != NULL) { + dns_rdataset_disassociate(val->keyset); + val->keyset = NULL; } val->key = NULL; if (NEEDNOQNAME(val)) { if (val->event->message == NULL) { validator_log(val, ISC_LOG_DEBUG(3), - "no message available for noqname proof"); + "no message available " + "for noqname proof"); return (DNS_R_NOVALIDSIG); } validator_log(val, ISC_LOG_DEBUG(3), "looking for noqname proof"); - return (nsecvalidate(val, false)); + return (validate_nx(val, false)); } else if (vresult == ISC_R_SUCCESS) { marksecure(event); validator_log(val, ISC_LOG_DEBUG(3), @@ -1564,8 +1642,8 @@ validate(dns_validator_t *val, bool resume) { * (val->event->rdataset). */ static isc_result_t -checkkey(dns_validator_t *val, dns_rdata_t *keyrdata, uint16_t keyid, - dns_secalg_t algorithm) +check_signer(dns_validator_t *val, dns_rdata_t *keyrdata, + uint16_t keyid, dns_secalg_t algorithm) { dns_rdata_rrsig_t sig; dst_key_t *dstkey = NULL; @@ -1580,25 +1658,29 @@ checkkey(dns_validator_t *val, dns_rdata_t *keyrdata, uint16_t keyid, dns_rdataset_current(val->event->sigrdataset, &rdata); result = dns_rdata_tostruct(&rdata, &sig, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if (keyid != sig.keyid || algorithm != sig.algorithm) + if (keyid != sig.keyid || algorithm != sig.algorithm) { continue; + } if (dstkey == NULL) { result = dns_dnssec_keyfromrdata(val->event->name, keyrdata, val->view->mctx, &dstkey); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { /* * This really shouldn't happen, but... */ continue; + } } result = verify(val, dstkey, &rdata, sig.keyid); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { break; + } } - if (dstkey != NULL) + if (dstkey != NULL) { dst_key_free(&dstkey); + } return (result); } @@ -1606,9 +1688,8 @@ checkkey(dns_validator_t *val, dns_rdata_t *keyrdata, uint16_t keyid, * Find the DNSKEY that corresponds to the DS. */ static isc_result_t -keyfromds(dns_validator_t *val, dns_rdataset_t *rdataset, dns_rdata_t *dsrdata, - dns_dsdigest_t digest, uint16_t keyid, dns_secalg_t algorithm, - dns_rdata_t *keyrdata) +dnskey_for_ds(dns_validator_t *val, dns_rdataset_t *rdataset, + dns_rdata_t *dsrdata, dns_rdata_ds_t *ds, dns_rdata_t *keyrdata) { dns_keytag_t keytag; dns_rdata_dnskey_t key; @@ -1626,10 +1707,12 @@ keyfromds(dns_validator_t *val, dns_rdataset_t *rdataset, dns_rdata_t *dsrdata, result = dns_rdata_tostruct(keyrdata, &key, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); keytag = compute_keytag(keyrdata); - if (keyid != keytag || algorithm != key.algorithm) + if (ds->key_tag != keytag || ds->algorithm != key.algorithm) { continue; + } dns_rdata_reset(&newdsrdata); - result = dns_ds_buildrdata(val->event->name, keyrdata, digest, + result = dns_ds_buildrdata(val->event->name, keyrdata, + ds->digest_type, dsbuf, &newdsrdata); if (result != ISC_R_SUCCESS) { validator_log(val, ISC_LOG_DEBUG(3), @@ -1637,12 +1720,208 @@ keyfromds(dns_validator_t *val, dns_rdataset_t *rdataset, dns_rdata_t *dsrdata, dns_result_totext(result)); continue; } - if (dns_rdata_compare(dsrdata, &newdsrdata) == 0) + if (dns_rdata_compare(dsrdata, &newdsrdata) == 0) { break; + } } return (result); } +static isc_result_t +anchor_signed(dns_validator_t *val, isc_result_t *resp) { + isc_result_t result; + bool atsep = false; + + /* + * First, see if this key was signed by a trust anchor. + */ + for (result = dns_rdataset_first(val->event->sigrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(val->event->sigrdataset)) + { + dns_keynode_t *keynode = NULL; + dns_rdata_t sigrdata = DNS_RDATA_INIT; + dns_fixedname_t fixed; + dns_name_t *found; + dst_key_t *dstkey; + dns_rdata_rrsig_t sig; + + found = dns_fixedname_initname(&fixed); + dns_rdata_reset(&sigrdata); + dns_rdataset_current(val->event->sigrdataset, &sigrdata); + result = dns_rdata_tostruct(&sigrdata, &sig, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + if (!dns_name_equal(val->event->name, &sig.signer)) { + continue; + } + + result = dns_keytable_findkeynode(val->keytable, + val->event->name, + sig.algorithm, sig.keyid, + &keynode); + if (result == ISC_R_NOTFOUND) { + result = dns_keytable_finddeepestmatch(val->keytable, + val->event->name, + found); + if (result != ISC_R_SUCCESS) { + validator_log(val, ISC_LOG_DEBUG(3), + "not beneath secure root"); + *resp = markanswer(val, "validate_dnskey (1)", + "not beneath secure root"); + return (ISC_R_COMPLETE); + } + continue; + } + + if (result == DNS_R_PARTIALMATCH || result == ISC_R_SUCCESS) { + atsep = true; + } + + while (result == ISC_R_SUCCESS) { + dns_keynode_t *nextnode = NULL; + dstkey = dns_keynode_key(keynode); + if (dstkey == NULL) { + dns_keytable_detachkeynode(val->keytable, + &keynode); + break; + } + + result = verify(val, dstkey, &sigrdata, sig.keyid); + if (result == ISC_R_SUCCESS) { + dns_keytable_detachkeynode(val->keytable, + &keynode); + break; + } + + result = dns_keytable_findnextkeynode(val->keytable, + keynode, + &nextnode); + dns_keytable_detachkeynode(val->keytable, &keynode); + keynode = nextnode; + } + + if (result == ISC_R_SUCCESS) { + marksecure(val->event); + validator_log(val, ISC_LOG_DEBUG(3), + "signed by trusted key; " + "marking as secure"); + *resp = result; + return (ISC_R_COMPLETE); + } + } + + if (atsep) { + /* + * We have not found a key to verify this DNSKEY + * RRset, but there is a trust anchor defined for this + * name, so we have to assume that the RRset is invalid. + */ + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(val->event->name, namebuf, sizeof(namebuf)); + validator_log(val, ISC_LOG_NOTICE, + "unable to find a DNSKEY which verifies " + "the DNSKEY RRset and also matches a " + "trusted key for '%s'", namebuf); + *resp = DNS_R_NOVALIDKEY; + return (ISC_R_COMPLETE); + } + + return (result); +} + +/* + * get_dsset is called to look up a DS RRset corresponding to the name + * of a DNSKEY record, either in the cache or, if necessary, by starting a + * fetch. This is done in the context of validating a zone key to build a + * trust chain. + * + * Returns: + * \li ISC_R_COMPLETE a DS has not been found; the caller should + * stop trying to validate the zone key and + * return the result code in '*resp'. + * \li DNS_R_CONTINUE a DS has been found and the caller may + * continue the zone key validation. + */ +static isc_result_t +get_dsset(dns_validator_t *val, dns_name_t *tname, isc_result_t *resp) { + isc_result_t result; + + result = view_find(val, tname, dns_rdatatype_ds); + switch (result) { + case ISC_R_SUCCESS: + /* + * We have a DS RRset. + */ + val->dsset = &val->frdataset; + if ((DNS_TRUST_PENDING(val->frdataset.trust) || + DNS_TRUST_ANSWER(val->frdataset.trust)) && + dns_rdataset_isassociated(&val->fsigrdataset)) + { + /* + * ... which is signed but not yet validated. + */ + result = create_validator(val, tname, + dns_rdatatype_ds, + &val->frdataset, + &val->fsigrdataset, + validator_callback_ds, + "validate_dnskey"); + *resp = DNS_R_WAIT; + if (result != ISC_R_SUCCESS) { + *resp = result; + } + return (ISC_R_COMPLETE); + } else if (DNS_TRUST_PENDING(val->frdataset.trust)) { + /* + * There should never be an unsigned DS. + */ + disassociate_rdatasets(val); + validator_log(val, ISC_LOG_DEBUG(2), + "unsigned DS record"); + *resp = DNS_R_NOVALIDSIG; + return (ISC_R_COMPLETE); + } + break; + + case ISC_R_NOTFOUND: + /* + * We don't have the DS. Find it. + */ + result = create_fetch(val, tname, dns_rdatatype_ds, + fetch_callback_ds, "validate_dnskey"); + *resp = DNS_R_WAIT; + if (result != ISC_R_SUCCESS) { + *resp = result; + } + return (ISC_R_COMPLETE); + + case DNS_R_NCACHENXDOMAIN: + case DNS_R_NCACHENXRRSET: + case DNS_R_EMPTYNAME: + case DNS_R_NXDOMAIN: + case DNS_R_NXRRSET: + case DNS_R_CNAME: + /* + * The DS does not exist. + */ + disassociate_rdatasets(val); + validator_log(val, ISC_LOG_DEBUG(2), "no DS record"); + *resp = DNS_R_NOVALIDSIG; + return (ISC_R_COMPLETE); + + case DNS_R_BROKENCHAIN: + *resp = result; + return (ISC_R_COMPLETE); + + default: + break; + } + + return (DNS_R_CONTINUE); +} + /*% * Attempts positive response validation of an RRset containing zone keys * (i.e. a DNSKEY rrset). @@ -1654,125 +1933,36 @@ keyfromds(dns_validator_t *val, dns_rdataset_t *rdataset, dns_rdata_t *dsrdata, * \li Other return codes are possible and all indicate failure. */ static isc_result_t -validatezonekey(dns_validator_t *val) { +validate_dnskey(dns_validator_t *val) { isc_result_t result; - dns_validatorevent_t *event; dns_rdataset_t trdataset; dns_rdata_t dsrdata = DNS_RDATA_INIT; dns_rdata_t keyrdata = DNS_RDATA_INIT; - dns_rdata_t sigrdata = DNS_RDATA_INIT; - char namebuf[DNS_NAME_FORMATSIZE]; dns_rdata_ds_t ds; - dns_rdata_rrsig_t sig; - dst_key_t *dstkey; bool supported_algorithm; - bool atsep = false; char digest_types[256]; /* * Caller must be holding the validator lock. */ - event = val->event; - if (val->dsset == NULL) { + isc_result_t tresult = ISC_R_SUCCESS; + /* - * First, see if this key was signed by a trusted key. + * First, check whether the key to be validated was + * signed by a trust anchor. */ - for (result = dns_rdataset_first(val->event->sigrdataset); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(val->event->sigrdataset)) - { - dns_keynode_t *keynode = NULL; - dns_fixedname_t fixed; - dns_name_t *found; - - found = dns_fixedname_initname(&fixed); - dns_rdata_reset(&sigrdata); - dns_rdataset_current(val->event->sigrdataset, - &sigrdata); - result = dns_rdata_tostruct(&sigrdata, &sig, NULL); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - if (!dns_name_equal(val->event->name, &sig.signer)) - continue; - - result = dns_keytable_findkeynode(val->keytable, - val->event->name, - sig.algorithm, - sig.keyid, &keynode); - if (result == ISC_R_NOTFOUND && - dns_keytable_finddeepestmatch(val->keytable, - val->event->name, found) != ISC_R_SUCCESS) { - if (val->mustbesecure) { - validator_log(val, ISC_LOG_WARNING, - "must be secure failure, " - "not beneath secure root"); - return (DNS_R_MUSTBESECURE); - } - validator_log(val, ISC_LOG_DEBUG(3), - "not beneath secure root"); - markanswer(val, "validatezonekey (1)"); - return (ISC_R_SUCCESS); - } - if (result == DNS_R_PARTIALMATCH || - result == ISC_R_SUCCESS) - atsep = true; - while (result == ISC_R_SUCCESS) { - dns_keynode_t *nextnode = NULL; - dstkey = dns_keynode_key(keynode); - if (dstkey == NULL) { - dns_keytable_detachkeynode( - val->keytable, - &keynode); - break; - } - result = verify(val, dstkey, &sigrdata, - sig.keyid); - if (result == ISC_R_SUCCESS) { - dns_keytable_detachkeynode( - val->keytable, - &keynode); - break; - } - result = dns_keytable_findnextkeynode( - val->keytable, - keynode, - &nextnode); - dns_keytable_detachkeynode(val->keytable, - &keynode); - keynode = nextnode; - } - if (result == ISC_R_SUCCESS) { - marksecure(event); - validator_log(val, ISC_LOG_DEBUG(3), - "signed by trusted key; " - "marking as secure"); - return (result); - } - } - - if (atsep) { - /* - * We have not found a key to verify this DNSKEY - * RRset. As this is a SEP we have to assume that - * the RRset is invalid. - */ - dns_name_format(val->event->name, namebuf, - sizeof(namebuf)); - validator_log(val, ISC_LOG_NOTICE, - "unable to find a DNSKEY which verifies " - "the DNSKEY RRset and also matches a " - "trusted key for '%s'", - namebuf); - return (DNS_R_NOVALIDKEY); + result = anchor_signed(val, &tresult); + if (result == ISC_R_COMPLETE) { + return (tresult); } /* * If this is the root name and there was no trusted key, - * give up, since there's no DS at the root. + * we can give up now, since there's no DS at the root. */ - if (dns_name_equal(event->name, dns_rootname)) { + if (dns_name_equal(val->event->name, dns_rootname)) { if ((val->attributes & VALATTR_TRIEDVERIFY) != 0) { validator_log(val, ISC_LOG_DEBUG(3), "root key failed to validate"); @@ -1785,68 +1975,12 @@ validatezonekey(dns_validator_t *val) { } /* - * Otherwise, try to find the DS record. + * Otherwise, look up the DS RRset for this name. */ - result = view_find(val, val->event->name, dns_rdatatype_ds); - if (result == ISC_R_SUCCESS) { - /* - * We have DS records. - */ - val->dsset = &val->frdataset; - if ((DNS_TRUST_PENDING(val->frdataset.trust) || - DNS_TRUST_ANSWER(val->frdataset.trust)) && - dns_rdataset_isassociated(&val->fsigrdataset)) - { - result = create_validator(val, - val->event->name, - dns_rdatatype_ds, - &val->frdataset, - &val->fsigrdataset, - dsvalidated, - "validatezonekey"); - if (result != ISC_R_SUCCESS) - return (result); - return (DNS_R_WAIT); - } else if (DNS_TRUST_PENDING(val->frdataset.trust)) { - /* - * There should never be an unsigned DS. - */ - dns_rdataset_disassociate(&val->frdataset); - validator_log(val, ISC_LOG_DEBUG(2), - "unsigned DS record"); - return (DNS_R_NOVALIDSIG); - } else { - result = ISC_R_SUCCESS; - POST(result); - } - } else if (result == ISC_R_NOTFOUND) { - /* - * We don't have the DS. Find it. - */ - result = create_fetch(val, val->event->name, - dns_rdatatype_ds, dsfetched, - "validatezonekey"); - if (result != ISC_R_SUCCESS) - return (result); - return (DNS_R_WAIT); - } else if (result == DNS_R_NCACHENXDOMAIN || - result == DNS_R_NCACHENXRRSET || - result == DNS_R_EMPTYNAME || - result == DNS_R_NXDOMAIN || - result == DNS_R_NXRRSET || - result == DNS_R_CNAME) - { - /* - * The DS does not exist. - */ - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); - validator_log(val, ISC_LOG_DEBUG(2), "no DS record"); - return (DNS_R_NOVALIDSIG); - } else if (result == DNS_R_BROKENCHAIN) - return (result); + result = get_dsset(val, val->event->name, &tresult); + if (result == ISC_R_COMPLETE) { + return (tresult); + } } /* @@ -1855,14 +1989,7 @@ validatezonekey(dns_validator_t *val) { INSIST(val->dsset != NULL); if (val->dsset->trust < dns_trust_secure) { - if (val->mustbesecure) { - validator_log(val, ISC_LOG_WARNING, - "must be secure failure," - " insecure DS"); - return (DNS_R_MUSTBESECURE); - } - markanswer(val, "validatezonekey (2)"); - return (ISC_R_SUCCESS); + return (markanswer(val, "validate_dnskey (2)", "insecure DS")); } /* @@ -1882,7 +2009,8 @@ validatezonekey(dns_validator_t *val) { memset(digest_types, 1, sizeof(digest_types)); for (result = dns_rdataset_first(val->dsset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(val->dsset)) { + result = dns_rdataset_next(val->dsset)) + { dns_rdata_reset(&dsrdata); dns_rdataset_current(val->dsset, &dsrdata); result = dns_rdata_tostruct(&dsrdata, &ds, NULL); @@ -1891,12 +2019,16 @@ validatezonekey(dns_validator_t *val) { if (!dns_resolver_ds_digest_supported(val->view->resolver, val->event->name, ds.digest_type)) + { continue; + } if (!dns_resolver_algorithm_supported(val->view->resolver, val->event->name, ds.algorithm)) + { continue; + } if ((ds.digest_type == DNS_DSDIGEST_SHA256 && ds.length == ISC_SHA256_DIGESTLENGTH) || @@ -1917,18 +2049,23 @@ validatezonekey(dns_validator_t *val) { result = dns_rdata_tostruct(&dsrdata, &ds, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if (digest_types[ds.digest_type] == 0) + if (digest_types[ds.digest_type] == 0) { continue; + } if (!dns_resolver_ds_digest_supported(val->view->resolver, val->event->name, ds.digest_type)) + { continue; + } if (!dns_resolver_algorithm_supported(val->view->resolver, val->event->name, ds.algorithm)) + { continue; + } supported_algorithm = true; @@ -1936,10 +2073,10 @@ validatezonekey(dns_validator_t *val) { dns_rdataset_clone(val->event->rdataset, &trdataset); /* - * Find matching DNSKEY from DS. + * Find the DNSKEY matching the DS... */ - result = keyfromds(val, &trdataset, &dsrdata, ds.digest_type, - ds.key_tag, ds.algorithm, &keyrdata); + result = dnskey_for_ds(val, &trdataset, &dsrdata, + &ds, &keyrdata); if (result != ISC_R_SUCCESS) { dns_rdataset_disassociate(&trdataset); validator_log(val, ISC_LOG_DEBUG(3), @@ -1948,31 +2085,25 @@ validatezonekey(dns_validator_t *val) { } /* - * Check that this DNSKEY signed the DNSKEY rrset. + * ... and check that it signed the DNSKEY RRset. */ - result = checkkey(val, &keyrdata, ds.key_tag, ds.algorithm); - + result = check_signer(val, &keyrdata, ds.key_tag, ds.algorithm); dns_rdataset_disassociate(&trdataset); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { break; + } validator_log(val, ISC_LOG_DEBUG(3), "no RRSIG matching DS key"); } if (result == ISC_R_SUCCESS) { - marksecure(event); + marksecure(val->event); validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (DS)"); return (result); } else if (result == ISC_R_NOMORE && !supported_algorithm) { - if (val->mustbesecure) { - validator_log(val, ISC_LOG_WARNING, - "must be secure failure, " - "no supported algorithm/digest (DS)"); - return (DNS_R_MUSTBESECURE); - } validator_log(val, ISC_LOG_DEBUG(3), "no supported algorithm/digest (DS)"); - markanswer(val, "validatezonekey (3)"); - return (ISC_R_SUCCESS); + return (markanswer(val, "validate_dnskey (3)", + "no supported algorithm/digest (DS)")); } else { validator_log(val, ISC_LOG_INFO, "no valid signature found (DS)"); @@ -1980,30 +2111,10 @@ validatezonekey(dns_validator_t *val) { } } -/*% - * Starts a positive response validation. - * - * Returns: - * \li ISC_R_SUCCESS Validation completed successfully - * \li DNS_R_WAIT Validation has started but is waiting - * for an event. - * \li Other return codes are possible and all indicate failure. - */ -static isc_result_t -start_positive_validation(dns_validator_t *val) { - /* - * If this is not a key, go straight into validate(). - */ - if (val->event->type != dns_rdatatype_dnskey || !isselfsigned(val)) - return (validate(val, false)); - - return (validatezonekey(val)); -} - /*% * val_rdataset_first and val_rdataset_next provide iteration methods - * that hide whether we are iterating across a message or a negative - * cache rdataset. + * that hide whether we are iterating across the AUTHORITY section of + * a message, or a negative cache rdataset. */ static isc_result_t val_rdataset_first(dns_validator_t *val, dns_name_t **namep, @@ -2024,16 +2135,18 @@ val_rdataset_first(dns_validator_t *val, dns_name_t **namep, if (message != NULL) { result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (result); + } dns_message_currentname(message, DNS_SECTION_AUTHORITY, namep); *rdatasetp = ISC_LIST_HEAD((*namep)->list); INSIST(*rdatasetp != NULL); } else { result = dns_rdataset_first(val->event->rdataset); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { dns_ncache_current(val->event->rdataset, *namep, *rdatasetp); + } } return (result); } @@ -2067,9 +2180,10 @@ val_rdataset_next(dns_validator_t *val, dns_name_t **namep, } else { dns_rdataset_disassociate(*rdatasetp); result = dns_rdataset_next(val->event->rdataset); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { dns_ncache_current(val->event->rdataset, *namep, *rdatasetp); + } } return (result); } @@ -2083,7 +2197,8 @@ val_rdataset_next(dns_validator_t *val, dns_name_t **namep, * \li ISC_R_SUCCESS */ static isc_result_t -checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename) +checkwildcard(dns_validator_t *val, dns_rdatatype_t type, + dns_name_t *zonename) { dns_name_t *name, *wild, tname; isc_result_t result; @@ -2118,7 +2233,9 @@ checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename) { if (rdataset->type != type || rdataset->trust != dns_trust_secure) + { continue; + } if (rdataset->type == dns_rdatatype_nsec && (NEEDNODATA(val) || NEEDNOWILDCARD(val)) && @@ -2126,22 +2243,26 @@ checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename) dns_nsec_noexistnodata(val->event->type, wild, name, rdataset, &exists, &data, NULL, validator_log, val) - == ISC_R_SUCCESS) + == ISC_R_SUCCESS) { dns_name_t **proofs = val->event->proofs; - if (exists && !data) + if (exists && !data) { val->attributes |= VALATTR_FOUNDNODATA; - if (exists && !data && NEEDNODATA(val)) + } + if (exists && !data && NEEDNODATA(val)) { proofs[DNS_VALIDATOR_NODATAPROOF] = - name; - if (!exists) + name; + } + if (!exists) { val->attributes |= - VALATTR_FOUNDNOWILDCARD; - if (!exists && NEEDNOQNAME(val)) - proofs[DNS_VALIDATOR_NOWILDCARDPROOF] = - name; - if (dns_rdataset_isassociated(&trdataset)) + VALATTR_FOUNDNOWILDCARD; + } + if (!exists && NEEDNOQNAME(val)) { + proofs[DNS_VALIDATOR_NOWILDCARDPROOF] = name; + } + if (dns_rdataset_isassociated(&trdataset)) { dns_rdataset_disassociate(&trdataset); + } return (ISC_R_SUCCESS); } @@ -2152,32 +2273,41 @@ checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename) rdataset, zonename, &exists, &data, NULL, NULL, NULL, NULL, NULL, NULL, validator_log, val) - == ISC_R_SUCCESS) + == ISC_R_SUCCESS) { dns_name_t **proofs = val->event->proofs; - if (exists && !data) + if (exists && !data) { val->attributes |= VALATTR_FOUNDNODATA; - if (exists && !data && NEEDNODATA(val)) - proofs[DNS_VALIDATOR_NODATAPROOF] = - name; - if (!exists) - val->attributes |= - VALATTR_FOUNDNOWILDCARD; - if (!exists && NEEDNOQNAME(val)) - proofs[DNS_VALIDATOR_NOWILDCARDPROOF] = - name; - if (dns_rdataset_isassociated(&trdataset)) + } + if (exists && !data && NEEDNODATA(val)) { + proofs[DNS_VALIDATOR_NODATAPROOF] = name; + } + if (!exists) { + val->attributes |= VALATTR_FOUNDNOWILDCARD; + } + if (!exists && NEEDNOQNAME(val)) { + proofs[DNS_VALIDATOR_NOWILDCARDPROOF] = name; + } + if (dns_rdataset_isassociated(&trdataset)) { dns_rdataset_disassociate(&trdataset); + } return (ISC_R_SUCCESS); } } - if (result == ISC_R_NOMORE) + if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; - if (dns_rdataset_isassociated(&trdataset)) + } + if (dns_rdataset_isassociated(&trdataset)) { dns_rdataset_disassociate(&trdataset); + } return (result); } +/* + * Look for the needed proofs for a negative or wildcard response + * from a zone using NSEC3, and set flags in the validator as they + * are found. + */ static isc_result_t findnsec3proofs(dns_validator_t *val) { dns_name_t *name, tname; @@ -2209,7 +2339,9 @@ findnsec3proofs(dns_validator_t *val) { { if (rdataset->type != dns_rdatatype_nsec3 || rdataset->trust != dns_trust_secure) + { continue; + } result = dns_nsec3_noexistnodata(val->event->type, val->event->name, name, @@ -2218,17 +2350,20 @@ findnsec3proofs(dns_validator_t *val) { NULL, NULL, validator_log, val); if (result != ISC_R_IGNORE && result != ISC_R_SUCCESS) { - if (dns_rdataset_isassociated(&trdataset)) + if (dns_rdataset_isassociated(&trdataset)) { dns_rdataset_disassociate(&trdataset); + } return (result); } } - if (result != ISC_R_NOMORE) + if (result != ISC_R_NOMORE) { result = ISC_R_SUCCESS; + } POST(result); - if (dns_name_countlabels(zonename) == 0) + if (dns_name_countlabels(zonename) == 0) { return (ISC_R_SUCCESS); + } /* * If the val->closest is set then we want to use it otherwise @@ -2238,9 +2373,10 @@ findnsec3proofs(dns_validator_t *val) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(dns_fixedname_name(&val->closest), - namebuf, sizeof(namebuf)); - validator_log(val, ISC_LOG_DEBUG(3), "closest encloser from " - "wildcard signature '%s'", namebuf); + namebuf, sizeof(namebuf)); + validator_log(val, ISC_LOG_DEBUG(3), + "closest encloser from wildcard signature '%s'", + namebuf); dns_name_copynf(dns_fixedname_name(&val->closest), closest); closestp = NULL; setclosestp = NULL; @@ -2255,7 +2391,9 @@ findnsec3proofs(dns_validator_t *val) { { if (rdataset->type != dns_rdatatype_nsec3 || rdataset->trust != dns_trust_secure) + { continue; + } /* * We process all NSEC3 records to find the closest @@ -2271,12 +2409,15 @@ findnsec3proofs(dns_validator_t *val) { &unknown, setclosestp, &setnearest, closestp, nearest, validator_log, val); - if (unknown) + if (unknown) { val->attributes |= VALATTR_FOUNDUNKNOWN; - if (result != ISC_R_SUCCESS) + } + if (result != ISC_R_SUCCESS) { continue; - if (setclosest) + } + if (setclosest) { proofs[DNS_VALIDATOR_CLOSESTENCLOSER] = name; + } if (exists && !data && NEEDNODATA(val)) { val->attributes |= VALATTR_FOUNDNODATA; proofs[DNS_VALIDATOR_NODATAPROOF] = name; @@ -2284,12 +2425,14 @@ findnsec3proofs(dns_validator_t *val) { if (!exists && setnearest) { val->attributes |= VALATTR_FOUNDNOQNAME; proofs[DNS_VALIDATOR_NOQNAMEPROOF] = name; - if (optout) + if (optout) { val->attributes |= VALATTR_FOUNDOPTOUT; + } } } - if (result == ISC_R_NOMORE) + if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; + } /* * To know we have a valid noqname and optout proofs we need to also @@ -2316,14 +2459,70 @@ findnsec3proofs(dns_validator_t *val) { * Do we need to check for the wildcard? */ if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) && - ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) { + ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) + { result = checkwildcard(val, dns_rdatatype_nsec3, zonename); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (result); + } } return (result); } +/* + * Start a validator for negative response data. + * + * Returns: + * \li DNS_R_CONTINUE Validation skipped, continue + * \li DNS_R_WAIT Validation is in progress + * + * \li Other return codes indicate failure. + */ +static isc_result_t +validate_neg_rrset(dns_validator_t *val, dns_name_t *name, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +{ + isc_result_t result; + + /* + * If a signed zone is missing the zone key, bad + * things could happen. A query for data in the zone + * would lead to a query for the zone key, which + * would return a negative answer, which would contain + * an SOA and an NSEC signed by the missing key, which + * would trigger another query for the DNSKEY (since + * the first one is still in progress), and go into an + * infinite loop. Avoid that. + */ + if (val->event->type == dns_rdatatype_dnskey && + rdataset->type == dns_rdatatype_nsec && + dns_name_equal(name, val->event->name)) + { + dns_rdata_t nsec = DNS_RDATA_INIT; + + result = dns_rdataset_first(rdataset); + if (result != ISC_R_SUCCESS) { + return (result); + } + dns_rdataset_current(rdataset, &nsec); + if (dns_nsec_typepresent(&nsec, dns_rdatatype_soa)) { + return (DNS_R_CONTINUE); + } + } + + val->currentset = rdataset; + result = create_validator(val, name, rdataset->type, + rdataset, sigrdataset, + validator_callback_nsec, + "validate_neg_rrset"); + if (result != ISC_R_SUCCESS) { + return (result); + } + + val->authcount++; + return (DNS_R_WAIT); +} + /*% * Validate the authority section records. */ @@ -2333,10 +2532,11 @@ validate_authority(dns_validator_t *val, bool resume) { dns_message_t *message = val->event->message; isc_result_t result; - if (!resume) + if (!resume) { result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); - else + } else { result = ISC_R_SUCCESS; + } for (; result == ISC_R_SUCCESS; @@ -2350,77 +2550,55 @@ validate_authority(dns_validator_t *val, bool resume) { rdataset = ISC_LIST_NEXT(val->currentset, link); val->currentset = NULL; resume = false; - } else + } else { rdataset = ISC_LIST_HEAD(name->list); + } for (; rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { - if (rdataset->type == dns_rdatatype_rrsig) + if (rdataset->type == dns_rdatatype_rrsig) { continue; + } for (sigrdataset = ISC_LIST_HEAD(name->list); sigrdataset != NULL; - sigrdataset = ISC_LIST_NEXT(sigrdataset, - link)) + sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { if (sigrdataset->type == dns_rdatatype_rrsig && sigrdataset->covers == rdataset->type) + { break; + } } - /* - * If a signed zone is missing the zone key, bad - * things could happen. A query for data in the zone - * would lead to a query for the zone key, which - * would return a negative answer, which would contain - * an SOA and an NSEC signed by the missing key, which - * would trigger another query for the DNSKEY (since - * the first one is still in progress), and go into an - * infinite loop. Avoid that. - */ - if (val->event->type == dns_rdatatype_dnskey && - rdataset->type == dns_rdatatype_nsec && - dns_name_equal(name, val->event->name)) - { - dns_rdata_t nsec = DNS_RDATA_INIT; - result = dns_rdataset_first(rdataset); - if (result != ISC_R_SUCCESS) - return (result); - dns_rdataset_current(rdataset, &nsec); - if (dns_nsec_typepresent(&nsec, - dns_rdatatype_soa)) - continue; - } - val->currentset = rdataset; - result = create_validator(val, name, rdataset->type, - rdataset, sigrdataset, - authvalidated, - "validate_authority"); - if (result != ISC_R_SUCCESS) + result = validate_neg_rrset(val, name, rdataset, + sigrdataset); + if (result != DNS_R_CONTINUE) { return (result); - val->authcount++; - return (DNS_R_WAIT); + } } } - if (result == ISC_R_NOMORE) + if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; + } return (result); } /*% - * Validate the ncache elements. + * Validate negative cache elements. */ static isc_result_t validate_ncache(dns_validator_t *val, bool resume) { dns_name_t *name; isc_result_t result; - if (!resume) + if (!resume) { result = dns_rdataset_first(val->event->rdataset); - else + } else { result = dns_rdataset_next(val->event->rdataset); + } for (; result == ISC_R_SUCCESS; @@ -2428,60 +2606,34 @@ validate_ncache(dns_validator_t *val, bool resume) { { dns_rdataset_t *rdataset, *sigrdataset = NULL; - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); + disassociate_rdatasets(val); name = dns_fixedname_initname(&val->fname); rdataset = &val->frdataset; dns_ncache_current(val->event->rdataset, name, rdataset); - if (val->frdataset.type == dns_rdatatype_rrsig) + if (val->frdataset.type == dns_rdatatype_rrsig) { continue; + } result = dns_ncache_getsigrdataset(val->event->rdataset, name, rdataset->type, &val->fsigrdataset); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { sigrdataset = &val->fsigrdataset; - - /* - * If a signed zone is missing the zone key, bad - * things could happen. A query for data in the zone - * would lead to a query for the zone key, which - * would return a negative answer, which would contain - * an SOA and an NSEC signed by the missing key, which - * would trigger another query for the DNSKEY (since - * the first one is still in progress), and go into an - * infinite loop. Avoid that. - */ - if (val->event->type == dns_rdatatype_dnskey && - rdataset->type == dns_rdatatype_nsec && - dns_name_equal(name, val->event->name)) - { - dns_rdata_t nsec = DNS_RDATA_INIT; - - result = dns_rdataset_first(rdataset); - if (result != ISC_R_SUCCESS) - return (result); - dns_rdataset_current(rdataset, &nsec); - if (dns_nsec_typepresent(&nsec, - dns_rdatatype_soa)) - continue; } - val->currentset = rdataset; - result = create_validator(val, name, rdataset->type, - rdataset, sigrdataset, - authvalidated, - "validate_ncache"); - if (result != ISC_R_SUCCESS) - return (result); - val->authcount++; - return (DNS_R_WAIT); + + result = validate_neg_rrset(val, name, rdataset, sigrdataset); + if (result == DNS_R_CONTINUE) { + continue; + } + + return (result); } - if (result == ISC_R_NOMORE) + if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; + } + return (result); } @@ -2490,105 +2642,126 @@ validate_ncache(dns_validator_t *val, bool resume) { * answer is from a wildcard. * * Loop through the authority section looking for NODATA, NOWILDCARD - * and NOQNAME proofs in the NSEC records by calling authvalidated(). + * and NOQNAME proofs in the NSEC records by calling + * validator_callback_nsec(). * * If the required proofs are found we are done. * - * If the proofs are not found attempt to prove this is a unsecure + * If the proofs are not found attempt to prove this is an unsecure * response. */ static isc_result_t -nsecvalidate(dns_validator_t *val, bool resume) { +validate_nx(dns_validator_t *val, bool resume) { isc_result_t result; - if (resume) - validator_log(val, ISC_LOG_DEBUG(3), "resuming nsecvalidate"); + if (resume) { + validator_log(val, ISC_LOG_DEBUG(3), "resuming validate_nx"); + } - if (val->event->message == NULL) + if (val->event->message == NULL) { result = validate_ncache(val, resume); - else + } else { result = validate_authority(val, resume); + } - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (result); + } /* * Do we only need to check for NOQNAME? To get here we must have * had a secure wildcard answer. */ if (!NEEDNODATA(val) && !NEEDNOWILDCARD(val) && NEEDNOQNAME(val)) { - if (!FOUNDNOQNAME(val)) + if (!FOUNDNOQNAME(val)) { findnsec3proofs(val); + } + if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) && - !FOUNDOPTOUT(val)) { + !FOUNDOPTOUT(val)) + { validator_log(val, ISC_LOG_DEBUG(3), "marking as secure, noqname proof found"); marksecure(val->event); return (ISC_R_SUCCESS); } else if (FOUNDOPTOUT(val) && - dns_name_countlabels(dns_fixedname_name(&val->wild)) - != 0) { + dns_name_countlabels(dns_fixedname_name(&val->wild)) != 0) + { validator_log(val, ISC_LOG_DEBUG(3), "optout proof found"); val->event->optout = true; - markanswer(val, "nsecvalidate (1)"); + markanswer(val, "validate_nx (1)", NULL); return (ISC_R_SUCCESS); } else if ((val->attributes & VALATTR_FOUNDUNKNOWN) != 0) { validator_log(val, ISC_LOG_DEBUG(3), "unknown NSEC3 hash algorithm found"); - markanswer(val, "nsecvalidate (2)"); + markanswer(val, "validate_nx (2)", NULL); return (ISC_R_SUCCESS); } + validator_log(val, ISC_LOG_DEBUG(3), "noqname proof not found"); return (DNS_R_NOVALIDNSEC); } - if (!FOUNDNOQNAME(val) && !FOUNDNODATA(val)) + if (!FOUNDNOQNAME(val) && !FOUNDNODATA(val)) { findnsec3proofs(val); + } /* * Do we need to check for the wildcard? */ if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) && - ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) { + ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) + { result = checkwildcard(val, dns_rdatatype_nsec, NULL); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { return (result); + } } if ((NEEDNODATA(val) && (FOUNDNODATA(val) || FOUNDOPTOUT(val))) || (NEEDNOQNAME(val) && FOUNDNOQNAME(val) && NEEDNOWILDCARD(val) && FOUNDNOWILDCARD(val) && - FOUNDCLOSEST(val))) { - if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0) + FOUNDCLOSEST(val))) + { + if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0) { val->event->optout = true; + } validator_log(val, ISC_LOG_DEBUG(3), "nonexistence proof(s) found"); - if (val->event->message == NULL) + if (val->event->message == NULL) { marksecure(val->event); - else + } else { val->event->secure = true; + } return (ISC_R_SUCCESS); } - if (val->authfail != 0 && val->authcount == val->authfail) + if (val->authfail != 0 && val->authcount == val->authfail) { return (DNS_R_BROKENCHAIN); + } + validator_log(val, ISC_LOG_DEBUG(3), "nonexistence proof(s) not found"); - val->attributes |= VALATTR_INSECURITY; return (proveunsecure(val, false, false)); } +/*% + * Check that DS rdataset has at least one record with + * a supported algorithm and digest. + */ static bool -check_ds(dns_validator_t *val, dns_name_t *name, dns_rdataset_t *rdataset) { +check_ds_algs(dns_validator_t *val, dns_name_t *name, dns_rdataset_t *rdataset) +{ dns_rdata_t dsrdata = DNS_RDATA_INIT; dns_rdata_ds_t ds; isc_result_t result; for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; - result = dns_rdataset_next(rdataset)) { + result = dns_rdataset_next(rdataset)) + { dns_rdataset_current(rdataset, &dsrdata); result = dns_rdata_tostruct(&dsrdata, &ds, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); @@ -2596,7 +2769,8 @@ check_ds(dns_validator_t *val, dns_name_t *name, dns_rdataset_t *rdataset) { if (dns_resolver_ds_digest_supported(val->view->resolver, name, ds.digest_type) && dns_resolver_algorithm_supported(val->view->resolver, - name, ds.algorithm)) { + name, ds.algorithm)) + { dns_rdata_reset(&dsrdata); return (true); } @@ -2606,13 +2780,251 @@ check_ds(dns_validator_t *val, dns_name_t *name, dns_rdataset_t *rdataset) { } /*% - * 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 - * not exist at a delegation point or the DS exists at a delegation - * but we don't support the algorithm/digest. + * seek_ds is called to look up DS rrsets at the label of val->event->name + * indicated by val->labels. This is done while building an insecurity + * proof, and so it will attempt validation of NXDOMAIN, NXRRSET or CNAME + * responses. * * Returns: - * \li ISC_R_SUCCESS val->event->name is in a unsecure zone + * \li ISC_R_COMPLETE a result has been determined and copied + * into `*resp`; ISC_R_SUCCESS indicates that + * the name has been proven insecure and any + * other result indicates failure. + * \li DNS_R_CONTINUE result is indeterminate; caller should + * continue walking down labels. + */ +static isc_result_t +seek_ds(dns_validator_t *val, isc_result_t *resp) { + isc_result_t result; + char namebuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fixedfound; + dns_name_t *found = dns_fixedname_initname(&fixedfound); + dns_name_t *tname = dns_fixedname_initname(&val->fname); + + if (val->labels == dns_name_countlabels(val->event->name)) { + dns_name_copynf(val->event->name, tname); + } else { + dns_name_split(val->event->name, val->labels, NULL, tname); + } + + dns_name_format(tname, namebuf, sizeof(namebuf)); + validator_log(val, ISC_LOG_DEBUG(3), + "checking existence of DS at '%s'", namebuf); + + result = view_find(val, tname, dns_rdatatype_ds); + switch (result) { + case ISC_R_SUCCESS: + /* + * There is a DS here. If it's already been + * validated, continue walking down labels. + */ + if (val->frdataset.trust >= dns_trust_secure) { + if (!check_ds_algs(val, tname, &val->frdataset)) { + validator_log(val, ISC_LOG_DEBUG(3), + "no supported algorithm/" + "digest (%s/DS)", + namebuf); + *resp = markanswer(val, + "proveunsecure (5)", + "no supported " + "algorithm/digest (DS)"); + return (ISC_R_COMPLETE); + } + + break; + } + + /* + * Otherwise, try to validate it now. + */ + if (dns_rdataset_isassociated(&val->fsigrdataset)) { + result = create_validator(val, tname, + dns_rdatatype_ds, + &val->frdataset, + &val->fsigrdataset, + validator_callback_ds, + "proveunsecure"); + *resp = DNS_R_WAIT; + if (result != ISC_R_SUCCESS) { + *resp = result; + } + } else { + /* + * There should never be an unsigned DS. + */ + validator_log(val, ISC_LOG_DEBUG(3), + "unsigned DS record"); + *resp = DNS_R_NOVALIDSIG; + } + + return (ISC_R_COMPLETE); + + case ISC_R_NOTFOUND: + /* + * We don't know anything about the DS. Find it. + */ + *resp = DNS_R_WAIT; + result = create_fetch(val, tname, dns_rdatatype_ds, + fetch_callback_ds, "proveunsecure"); + if (result != ISC_R_SUCCESS) { + *resp = result; + } + return (ISC_R_COMPLETE); + + case DNS_R_NXRRSET: + case DNS_R_NCACHENXRRSET: + /* + * There is no DS. If this is a delegation, + * we may be done. + * + * If we have "trust == answer" then this namespace + * has switched from insecure to should be secure. + */ + if (DNS_TRUST_PENDING(val->frdataset.trust) || + DNS_TRUST_ANSWER(val->frdataset.trust)) + { + result = create_validator(val, tname, + dns_rdatatype_ds, + &val->frdataset, + &val->fsigrdataset, + validator_callback_ds, + "proveunsecure"); + *resp = DNS_R_WAIT; + if (result != ISC_R_SUCCESS) { + *resp = result; + } + return (ISC_R_COMPLETE); + } + + /* + * Zones using NSEC3 don't return a NSEC RRset so + * we need to use dns_view_findzonecut2 to find + * the zone cut. + */ + if (result == DNS_R_NXRRSET && + !dns_rdataset_isassociated(&val->frdataset) && + dns_view_findzonecut(val->view, tname, found, NULL, + 0, 0, false, false, + NULL, NULL) == ISC_R_SUCCESS && + dns_name_equal(tname, found)) + { + *resp = markanswer(val, "proveunsecure (3)", + "no DS at zone cut"); + return (ISC_R_COMPLETE); + } + + if (val->frdataset.trust < dns_trust_secure) { + /* + * This shouldn't happen, since the negative + * response should have been validated. Since + * there's no way of validating existing + * negative response blobs, give up. + */ + validator_log(val, ISC_LOG_WARNING, + "can't validate existing " + "negative responses (no DS)"); + *resp = DNS_R_MUSTBESECURE; + return (ISC_R_COMPLETE); + } + + if (isdelegation(tname, &val->frdataset, result)) { + *resp = markanswer(val, "proveunsecure (4)", + "this is a delegation"); + return (ISC_R_COMPLETE); + } + + break; + + case DNS_R_NXDOMAIN: + case DNS_R_NCACHENXDOMAIN: + /* + * This is not a zone cut. Assuming things are + * as expected, continue. + */ + if (!dns_rdataset_isassociated(&val->frdataset)) { + /* + * There should be an NSEC here, since we + * are still in a secure zone. + */ + *resp = DNS_R_NOVALIDNSEC; + return (ISC_R_COMPLETE); + } else if (DNS_TRUST_PENDING(val->frdataset.trust) || + DNS_TRUST_ANSWER(val->frdataset.trust)) + { + /* + * If we have "trust == answer" then this + * namespace has switched from insecure to + * should be secure. + */ + *resp = DNS_R_WAIT; + result = create_validator(val, tname, + dns_rdatatype_ds, + &val->frdataset, + &val->fsigrdataset, + validator_callback_ds, + "proveunsecure"); + if (result != ISC_R_SUCCESS) { + *resp = result; + } + return (ISC_R_COMPLETE); + } else if (val->frdataset.trust < dns_trust_secure) { + /* + * This shouldn't happen, since the negative + * response should have been validated. Since + * there's no way of validating existing + * negative response blobs, give up. + */ + validator_log(val, ISC_LOG_WARNING, + "can't validate existing " + "negative responses " + "(not a zone cut)"); + *resp = DNS_R_NOVALIDSIG; + return (ISC_R_COMPLETE); + } + + break; + + case DNS_R_CNAME: + if (DNS_TRUST_PENDING(val->frdataset.trust) || + DNS_TRUST_ANSWER(val->frdataset.trust)) + { + result = create_validator(val, tname, + dns_rdatatype_cname, + &val->frdataset, + &val->fsigrdataset, + validator_callback_cname, + "proveunsecure " + "(cname)"); + *resp = DNS_R_WAIT; + if (result != ISC_R_SUCCESS) { + *resp = result; + } + return (ISC_R_COMPLETE); + } + + break; + + default: + *resp = result; + return (ISC_R_COMPLETE); + } + + /* + * No definite answer yet; continue walking down labels. + */ + return (DNS_R_CONTINUE); +} + +/*% + * proveunsecure walks down, label by label, from the closest enclosing + * trust anchor to the name that is being validated, looking for an + * endpoint in the chain of trust. That occurs when we can prove that + * a DS record does not exist at a delegation point, or that a DS exists + * at a delegation point but we don't support its algorithm/digest. If + * no such endpoint is found, then the response should have been secure. + * + * Returns: + * \li ISC_R_SUCCESS val->event->name is in an 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. @@ -2624,286 +3036,94 @@ check_ds(dns_validator_t *val, dns_name_t *name, dns_rdataset_t *rdataset) { static isc_result_t proveunsecure(dns_validator_t *val, bool have_ds, bool resume) { isc_result_t result; - dns_fixedname_t fixedsecroot; - dns_name_t *secroot; - dns_name_t *tname; char namebuf[DNS_NAME_FORMATSIZE]; - dns_name_t *found; - dns_fixedname_t fixedfound; + dns_fixedname_t fixedsecroot; + dns_name_t *secroot = dns_fixedname_initname(&fixedsecroot); unsigned int labels; - secroot = dns_fixedname_initname(&fixedsecroot); - found = dns_fixedname_initname(&fixedfound); + /* + * We're attempting to prove insecurity. + */ + val->attributes |= VALATTR_INSECURITY; + dns_name_copynf(val->event->name, secroot); + /* * If this is a response to a DS query, we need to look in * the parent zone for the trust anchor. */ - labels = dns_name_countlabels(secroot); - if (val->event->type == dns_rdatatype_ds && labels > 1U) - dns_name_getlabelsequence(secroot, 1, labels - 1, - secroot); - result = dns_keytable_finddeepestmatch(val->keytable, - secroot, secroot); + if (val->event->type == dns_rdatatype_ds && labels > 1U) { + dns_name_getlabelsequence(secroot, 1, labels - 1, secroot); + } + + result = dns_keytable_finddeepestmatch(val->keytable, secroot, secroot); if (result == ISC_R_NOTFOUND) { - if (val->mustbesecure) { - validator_log(val, ISC_LOG_WARNING, - "must be secure failure, " - "not beneath secure root"); - result = DNS_R_MUSTBESECURE; - goto out; - } validator_log(val, ISC_LOG_DEBUG(3), "not beneath secure root"); - markanswer(val, "proveunsecure (1)"); - return (ISC_R_SUCCESS); + return (markanswer(val, "proveunsecure (1)", + "not beneath secure root")); } else if (result != ISC_R_SUCCESS) { return (result); } if (!resume) { /* - * We are looking for breaks below the SEP so add a label. + * We are looking for interruptions in the chain of trust. + * That can only happen *below* the trust anchor, so we + * start looking at the next label down. */ val->labels = dns_name_countlabels(secroot) + 1; } else { validator_log(val, ISC_LOG_DEBUG(3), "resuming proveunsecure"); + /* - * If we have a DS rdataset and it is secure then check if - * the DS rdataset has a supported algorithm combination. - * If not this is an insecure delegation as far as this - * resolver is concerned. + * If we have a DS rdataset and it is secure, check whether + * it has a supported algorithm combination. If not, this is + * an insecure delegation as far as this resolver is concerned. */ if (have_ds && val->frdataset.trust >= dns_trust_secure && - !check_ds(val, dns_fixedname_name(&val->fname), - &val->frdataset)) + !check_ds_algs(val, dns_fixedname_name(&val->fname), + &val->frdataset)) { dns_name_format(dns_fixedname_name(&val->fname), namebuf, sizeof(namebuf)); - if (val->mustbesecure) { - validator_log(val, ISC_LOG_WARNING, - "must be secure failure at '%s'", - namebuf); - result = DNS_R_MUSTBESECURE; - goto out; - } validator_log(val, ISC_LOG_DEBUG(3), "no supported algorithm/digest (%s/DS)", namebuf); - markanswer(val, "proveunsecure (2)"); - result = ISC_R_SUCCESS; + result = markanswer(val, "proveunsecure (2)", namebuf); goto out; } val->labels++; } - for (; - val->labels <= dns_name_countlabels(val->event->name); - val->labels++) - { + /* + * Walk down through each of the remaining labels in the name, + * looking for DS records. + */ + while (val->labels <= dns_name_countlabels(val->event->name)) { + isc_result_t tresult; - tname = dns_fixedname_initname(&val->fname); - if (val->labels == dns_name_countlabels(val->event->name)) - dns_name_copynf(val->event->name, tname); - else - dns_name_split(val->event->name, val->labels, - NULL, tname); + result = seek_ds(val, &tresult); + if (result == ISC_R_COMPLETE) { + result = tresult; + goto out; + } - dns_name_format(tname, namebuf, sizeof(namebuf)); - validator_log(val, ISC_LOG_DEBUG(3), - "checking existence of DS at '%s'", - namebuf); - - result = view_find(val, tname, dns_rdatatype_ds); - if (result == DNS_R_NXRRSET || result == DNS_R_NCACHENXRRSET) { - /* - * There is no DS. If this is a delegation, - * we may be done. - */ - /* - * If we have "trust == answer" then this namespace - * has switched from insecure to should be secure. - */ - if (DNS_TRUST_PENDING(val->frdataset.trust) || - DNS_TRUST_ANSWER(val->frdataset.trust)) { - result = create_validator(val, tname, - dns_rdatatype_ds, - &val->frdataset, - NULL, dsvalidated, - "proveunsecure"); - if (result != ISC_R_SUCCESS) - goto out; - return (DNS_R_WAIT); - } - /* - * Zones using NSEC3 don't return a NSEC RRset so - * we need to use dns_view_findzonecut2 to find - * the zone cut. - */ - if (result == DNS_R_NXRRSET && - !dns_rdataset_isassociated(&val->frdataset) && - dns_view_findzonecut(val->view, tname, found, NULL, - 0, 0, false, false, - NULL, NULL) == ISC_R_SUCCESS && - dns_name_equal(tname, found)) { - if (val->mustbesecure) { - validator_log(val, ISC_LOG_WARNING, - "must be secure failure, " - "no DS at zone cut"); - return (DNS_R_MUSTBESECURE); - } - markanswer(val, "proveunsecure (3)"); - return (ISC_R_SUCCESS); - } - if (val->frdataset.trust < dns_trust_secure) { - /* - * This shouldn't happen, since the negative - * response should have been validated. Since - * there's no way of validating existing - * negative response blobs, give up. - */ - validator_log(val, ISC_LOG_WARNING, - "can't validate existing " - "negative responses (no DS)"); - result = DNS_R_NOVALIDSIG; - goto out; - } - if (isdelegation(tname, &val->frdataset, result)) { - if (val->mustbesecure) { - validator_log(val, ISC_LOG_WARNING, - "must be secure failure, " - "%s is a delegation", - namebuf); - return (DNS_R_MUSTBESECURE); - } - markanswer(val, "proveunsecure (4)"); - return (ISC_R_SUCCESS); - } - continue; - } else if (result == DNS_R_CNAME) { - if (DNS_TRUST_PENDING(val->frdataset.trust) || - DNS_TRUST_ANSWER(val->frdataset.trust)) { - result = create_validator(val, tname, - dns_rdatatype_cname, - &val->frdataset, - NULL, cnamevalidated, - "proveunsecure " - "(cname)"); - if (result != ISC_R_SUCCESS) - goto out; - return (DNS_R_WAIT); - } - continue; - } else if (result == ISC_R_SUCCESS) { - /* - * There is a DS here. Verify that it's secure and - * continue. - */ - if (val->frdataset.trust >= dns_trust_secure) { - if (!check_ds(val, tname, &val->frdataset)) { - validator_log(val, ISC_LOG_DEBUG(3), - "no supported algorithm/" - "digest (%s/DS)", namebuf); - if (val->mustbesecure) { - validator_log(val, - ISC_LOG_WARNING, - "must be secure failure, " - "no supported algorithm/" - "digest (%s/DS)", - namebuf); - result = DNS_R_MUSTBESECURE; - goto out; - } - markanswer(val, "proveunsecure (5)"); - result = ISC_R_SUCCESS; - goto out; - } - continue; - } - else if (!dns_rdataset_isassociated(&val->fsigrdataset)) - { - validator_log(val, ISC_LOG_DEBUG(3), - "DS is unsigned"); - result = DNS_R_NOVALIDSIG; - goto out; - } - /* - * Validate / re-validate answer. - */ - result = create_validator(val, tname, dns_rdatatype_ds, - &val->frdataset, - &val->fsigrdataset, - dsvalidated, - "proveunsecure"); - if (result != ISC_R_SUCCESS) - goto out; - return (DNS_R_WAIT); - } else if (result == DNS_R_NXDOMAIN || - result == DNS_R_NCACHENXDOMAIN) { - /* - * This is not a zone cut. Assuming things are - * as expected, continue. - */ - if (!dns_rdataset_isassociated(&val->frdataset)) { - /* - * There should be an NSEC here, since we - * are still in a secure zone. - */ - result = DNS_R_NOVALIDNSEC; - goto out; - } else if (DNS_TRUST_PENDING(val->frdataset.trust) || - DNS_TRUST_ANSWER(val->frdataset.trust)) { - /* - * If we have "trust == answer" then this - * namespace has switched from insecure to - * should be secure. - */ - result = create_validator(val, tname, - dns_rdatatype_ds, - &val->frdataset, - NULL, dsvalidated, - "proveunsecure"); - if (result != ISC_R_SUCCESS) - goto out; - return (DNS_R_WAIT); - } else if (val->frdataset.trust < dns_trust_secure) { - /* - * This shouldn't happen, since the negative - * response should have been validated. Since - * there's no way of validating existing - * negative response blobs, give up. - */ - validator_log(val, ISC_LOG_WARNING, - "can't validate existing " - "negative responses " - "(not a zone cut)"); - result = DNS_R_NOVALIDSIG; - goto out; - } - continue; - } else if (result == ISC_R_NOTFOUND) { - /* - * We don't know anything about the DS. Find it. - */ - result = create_fetch(val, tname, dns_rdatatype_ds, - dsfetched2, "proveunsecure"); - if (result != ISC_R_SUCCESS) - goto out; - return (DNS_R_WAIT); - } else if (result == DNS_R_BROKENCHAIN) - return (result); + INSIST(result == DNS_R_CONTINUE); + val->labels++; } - /* Couldn't complete insecurity proof */ - validator_log(val, ISC_LOG_DEBUG(3), "insecurity proof failed"); + /* Couldn't complete insecurity proof. */ + validator_log(val, ISC_LOG_DEBUG(3), + "insecurity proof failed: %s", + isc_result_totext(result)); return (DNS_R_NOTINSECURE); out: - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); + if (result != DNS_R_WAIT) { + disassociate_rdatasets(val); + } return (result); } @@ -2916,7 +3136,7 @@ proveunsecure(dns_validator_t *val, bool have_ds, bool resume) { * \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 + * Note an answer that appears to be a secure positive answer may actually * be an unsecure positive answer. */ static void @@ -2932,8 +3152,9 @@ validator_start(isc_task_t *task, isc_event_t *event) { val = vevent->validator; /* If the validator has been canceled, val->event == NULL */ - if (val->event == NULL) + if (val->event == NULL) { return; + } validator_log(val, ISC_LOG_DEBUG(3), "starting"); @@ -2951,36 +3172,44 @@ validator_start(isc_task_t *task, isc_event_t *event) { INSIST(dns_rdataset_isassociated(val->event->rdataset)); INSIST(dns_rdataset_isassociated(val->event->sigrdataset)); - result = start_positive_validation(val); + if (selfsigned_dnskey(val)) { + result = validate_dnskey(val); + } else { + result = validate_answer(val, false); + } if (result == DNS_R_NOVALIDSIG && (val->attributes & VALATTR_TRIEDVERIFY) == 0) { saved_result = result; validator_log(val, ISC_LOG_DEBUG(3), "falling back to insecurity proof"); - val->attributes |= VALATTR_INSECURITY; result = proveunsecure(val, false, false); - if (result == DNS_R_NOTINSECURE) + if (result == DNS_R_NOTINSECURE) { result = saved_result; + } } } else if (val->event->rdataset != NULL && - val->event->rdataset->type != 0) { + val->event->rdataset->type != 0) + { /* - * This is either an unsecure subdomain or a response from - * a broken server. + * This is either an unsecure subdomain or a response + * from a broken server. */ INSIST(dns_rdataset_isassociated(val->event->rdataset)); validator_log(val, ISC_LOG_DEBUG(3), "attempting insecurity proof"); - val->attributes |= VALATTR_INSECURITY; result = proveunsecure(val, false, false); - if (result == DNS_R_NOTINSECURE) + if (result == DNS_R_NOTINSECURE) { validator_log(val, ISC_LOG_INFO, "got insecure response; " "parent indicates it should be secure"); - } else if (val->event->rdataset == NULL && - val->event->sigrdataset == NULL) + } + } else if ((val->event->rdataset == NULL && + val->event->sigrdataset == NULL) || + (val->event->rdataset != NULL && + NEGATIVE(val->event->rdataset))) + { /* * This is a nonexistence validation. @@ -2991,24 +3220,10 @@ validator_start(isc_task_t *task, isc_event_t *event) { if (val->event->message->rcode == dns_rcode_nxdomain) { val->attributes |= VALATTR_NEEDNOQNAME; val->attributes |= VALATTR_NEEDNOWILDCARD; - } else + } else { val->attributes |= VALATTR_NEEDNODATA; - result = nsecvalidate(val, false); - } else if (val->event->rdataset != NULL && - NEGATIVE(val->event->rdataset)) - { - /* - * This is a nonexistence validation. - */ - validator_log(val, ISC_LOG_DEBUG(3), - "attempting negative response validation"); - - if (val->event->rdataset->covers == dns_rdatatype_any) { - val->attributes |= VALATTR_NEEDNOQNAME; - val->attributes |= VALATTR_NEEDNOWILDCARD; - } else - val->attributes |= VALATTR_NEEDNODATA; - result = nsecvalidate(val, false); + } + result = validate_nx(val, false); } else { INSIST(0); ISC_UNREACHABLE(); @@ -3020,8 +3235,9 @@ validator_start(isc_task_t *task, isc_event_t *event) { } UNLOCK(&val->lock); - if (want_destroy) + if (want_destroy) { destroy(val); + } } isc_result_t @@ -3050,6 +3266,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, DNS_EVENT_VALIDATORSTART, validator_start, NULL, sizeof(dns_validatorevent_t)); + isc_task_attach(task, &tclone); event->validator = val; event->result = ISC_R_FAILURE; @@ -3073,9 +3290,9 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, val->keytable = NULL; result = dns_view_getsecroots(val->view, &val->keytable); - if (result != ISC_R_SUCCESS) - goto cleanup_mutex; - val->keynode = NULL; + if (result != ISC_R_SUCCESS) { + goto cleanup; + } val->key = NULL; val->siginfo = NULL; val->task = task; @@ -3085,7 +3302,6 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, val->currentset = NULL; val->keyset = NULL; val->dsset = NULL; - val->seensig = false; val->depth = 0; val->authcount = 0; val->authfail = 0; @@ -3093,20 +3309,20 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, dns_rdataset_init(&val->frdataset); dns_rdataset_init(&val->fsigrdataset); dns_fixedname_init(&val->wild); - dns_fixedname_init(&val->nearest); dns_fixedname_init(&val->closest); isc_stdtime_get(&val->start); ISC_LINK_INIT(val, link); val->magic = VALIDATOR_MAGIC; - if ((options & DNS_VALIDATOR_DEFER) == 0) + if ((options & DNS_VALIDATOR_DEFER) == 0) { isc_task_send(task, ISC_EVENT_PTR(&event)); + } *validatorp = val; return (ISC_R_SUCCESS); - cleanup_mutex: + cleanup: isc_mutex_destroy(&val->lock); isc_task_detach(&tclone); @@ -3149,8 +3365,9 @@ dns_validator_cancel(dns_validator_t *validator) { fetch = validator->fetch; validator->fetch = NULL; - if (validator->subvalidator != NULL) + if (validator->subvalidator != NULL) { dns_validator_cancel(validator->subvalidator); + } if ((validator->options & DNS_VALIDATOR_DEFER) != 0) { validator->options &= ~DNS_VALIDATOR_DEFER; validator_done(validator, ISC_R_CANCELED); @@ -3174,21 +3391,20 @@ destroy(dns_validator_t *val) { REQUIRE(val->event == NULL); REQUIRE(val->fetch == NULL); - if (val->keynode != NULL) - dns_keytable_detachkeynode(val->keytable, &val->keynode); - else if (val->key != NULL) + if (val->key != NULL) { dst_key_free(&val->key); - if (val->keytable != NULL) + } + if (val->keytable != NULL) { dns_keytable_detach(&val->keytable); - if (val->subvalidator != NULL) + } + if (val->subvalidator != NULL) { dns_validator_destroy(&val->subvalidator); - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); + } + disassociate_rdatasets(val); mctx = val->view->mctx; - if (val->siginfo != NULL) + if (val->siginfo != NULL) { isc_mem_put(mctx, val->siginfo, sizeof(*val->siginfo)); + } isc_mutex_destroy(&val->lock); dns_view_weakdetach(&val->view); val->magic = 0; @@ -3210,11 +3426,10 @@ dns_validator_destroy(dns_validator_t **validatorp) { validator_log(val, ISC_LOG_DEBUG(4), "dns_validator_destroy"); want_destroy = exit_check(val); - UNLOCK(&val->lock); - - if (want_destroy) + if (want_destroy) { destroy(val); + } *validatorp = NULL; } @@ -3230,8 +3445,9 @@ validator_logv(dns_validator_t *val, isc_logcategory_t *category, vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); - if ((unsigned int) depth >= sizeof spaces) + if ((unsigned int) depth >= sizeof spaces) { depth = sizeof spaces - 1; + } /* * Log the view name unless it's: @@ -3274,8 +3490,9 @@ static void validator_log(void *val, int level, const char *fmt, ...) { va_list ap; - if (! isc_log_wouldlog(dns_lctx, level)) + if (!isc_log_wouldlog(dns_lctx, level)) { return; + } va_start(ap, fmt); @@ -3285,9 +3502,9 @@ validator_log(void *val, int level, const char *fmt, ...) { } static void -validator_logcreate(dns_validator_t *val, - dns_name_t *name, dns_rdatatype_t type, - const char *caller, const char *operation) +validator_logcreate(dns_validator_t *val, dns_name_t *name, + dns_rdatatype_t type, const char *caller, + const char *operation) { char namestr[DNS_NAME_FORMATSIZE]; char typestr[DNS_RDATATYPE_FORMATSIZE];