validator.c revision f439363eeb4052fddc0e3ec648658548daa10506
/*
* Copyright (C) 2000 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: validator.c,v 1.85 2000/11/08 00:51:24 bwelling Exp $ */
#include <config.h>
#include <dns/keytable.h>
#include <dns/rdatastruct.h>
#include <dns/rdataset.h>
#include <dns/rdatatype.h>
#include <dns/resolver.h>
#include <dns/validator.h>
#define VALATTR_SHUTDOWN 0x01
#define VALATTR_FOUNDNONEXISTENCE 0x02
#define VALATTR_TRIEDVERIFY 0x04
static void
static inline isc_boolean_t
static inline isc_result_t
static inline isc_result_t
static inline isc_result_t
static inline isc_result_t
static void
static void
return;
/*
* Caller must be holding the lock.
*/
}
static void
result == ISC_R_SUCCESS;
{
{
}
}
}
static void
return;
}
"fetch_callback_validator: event == NULL");
return;
}
if (eresult == ISC_R_SUCCESS) {
/*
* Only extract the dst key if the keyset is secure.
*/
if (result == ISC_R_SUCCESS)
}
if (result != DNS_R_WAIT) {
goto out;
}
} else {
"fetch_callback_validator: got %s",
}
out:
/*
* Free stuff from the event.
*/
}
static void
return;
}
"fetch_callback_nullkey: event == NULL");
return;
}
if (eresult == ISC_R_SUCCESS) {
/*
* No null key.
*/
"found a keyset, no null key");
if (result != DNS_R_WAIT)
else {
/*
* Don't free rdataset & sigrdataset, since
* they'll be freed in nullkeyvalidated.
*/
return;
}
} else {
"found a keyset with a null key");
"insecurity proof succeeded");
} else if (!dns_rdataset_isassociated(sigrdataset)) {
"insecurity proof failed");
} else {
val,
&val->keyvalidator);
if (result != ISC_R_SUCCESS)
/*
* Don't free rdataset & sigrdataset, since
* they'll be freed in nullkeyvalidated.
*/
return;
}
}
} else if (eresult == DNS_R_NCACHENXDOMAIN ||
eresult == DNS_R_NCACHENXRRSET ||
eresult == DNS_R_NXDOMAIN ||
eresult == DNS_R_NXRRSET)
{
/*
* No keys.
*/
"no keys found");
if (result != DNS_R_WAIT)
} else {
"fetch_callback_nullkey: got %s",
}
/*
* Free stuff from the event.
*/
}
static void
return;
}
return;
if (eresult == ISC_R_SUCCESS) {
/*
* Only extract the dst key if the keyset is secure.
*/
if (result != DNS_R_WAIT) {
goto out;
}
} else {
"keyvalidated: got %s",
}
out:
/*
* Free stuff from the event.
*/
}
static isc_boolean_t
{
int order;
"looking for relevant nxt");
if (order == 0) {
/*
* The names are the same, so look for the type present bit.
*/
return (ISC_FALSE);
}
"type should not be present");
return (ISC_FALSE);
}
} else if (order > 0) {
/*
* The NXT owner name is less than the nonexistent name.
*/
if (order >= 0) {
/*
* The NXT next name is less than the nonexistent
* name. This is only ok if the next name is the zone
* name.
*/
"next name is not greater");
return (ISC_FALSE);
}
"nxt points to zone apex, ok");
}
"nxt range ok");
} else {
"nxt owner name is not less");
/*
* The NXT owner name is greater than the supposedly
* nonexistent name. This NXT is irrelevant.
*/
return (ISC_FALSE);
}
return (ISC_TRUE);
}
static void
return;
}
return;
if (eresult != ISC_R_SUCCESS) {
"authvalidated: got %s",
if (result != DNS_R_WAIT)
} else {
if (result != DNS_R_WAIT)
}
/*
* Free stuff from the event.
*/
}
static void
return;
}
return;
if (eresult == ISC_R_SUCCESS) {
"nonexistence proof found");
} else {
"negauthvalidated: got %s",
}
/*
* Free stuff from the event.
*/
}
static void
return;
}
return;
if (eresult == ISC_R_SUCCESS) {
"proved that name is in an unsecure domain");
} else {
if (result != DNS_R_WAIT)
}
/*
* Free stuff from the event.
*/
}
/*
* Try to find a null zone key among those in 'rdataset'. If found, build
* a dst_key_t for it and point val->key at it.
*/
static inline isc_boolean_t
isc_buffer_t b;
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
/*
* conversion.
*/
if (result != ISC_R_SUCCESS)
continue;
if (dst_key_isnullkey(key))
dst_key_free(&key);
}
return (found);
}
/*
* 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.
*
* If val->key is non-NULL, this returns the next matching key.
*/
static inline isc_result_t
{
isc_buffer_t b;
else {
}
if (result != ISC_R_SUCCESS)
goto failure;
do {
if (result != ISC_R_SUCCESS)
goto failure;
{
if (foundold)
/*
* This is the key we're looking for.
*/
return (ISC_R_SUCCESS);
{
}
}
} while (result == ISC_R_SUCCESS);
if (result == ISC_R_NOMORE)
return (result);
}
static inline isc_result_t
int order;
/*
* Is the key name appropriate for this signature?
* This previously checked for self-signed keys. Now, if the key
* is self signed with a preconfigured key, it's ok.
*/
if (namereln != dns_namereln_subdomain &&
namereln != dns_namereln_equal) {
/*
* The key name is not at the same level
* as 'rdataset', nor is it closer to the
* DNS root.
*/
return (DNS_R_CONTINUE);
}
/*
* Is the key used for the signature a security root?
*/
if (result == ISC_R_SUCCESS) {
/*
* The key is a security root.
*/
return (ISC_R_SUCCESS);
}
/*
* A key set may not be self-signed unless the signing key is a
* security root. We don't want a KEY RR to authenticate
* itself, so we ignore the signature if it was not made by
* an ancestor of the KEY or a preconfigured key.
*/
{
"keyset was self-signed but not preconfigured");
return (DNS_R_CONTINUE);
}
/*
* Do we know about this key?
*/
if (result == ISC_R_SUCCESS) {
/*
* We have an rrset for the given keyname.
*/
{
/*
* We know the key but haven't validated it yet.
*/
&val->fsigrdataset,
NULL,
0,
val,
&val->keyvalidator);
if (result != ISC_R_SUCCESS)
return (result);
return (DNS_R_WAIT);
/*
* Having a pending key with no signature means that
* something is broken.
*/
/*
* The key is legitimately insecure. There's no
* point in even attempting verification.
*/
} else {
/*
* See if we've got the key used in the signature.
*/
"keyset with trust %d",
if (result != ISC_R_SUCCESS) {
/*
* Either the key we're looking for is not
* in the rrset, or something bad happened.
* Give up.
*/
}
}
} else if (result == ISC_R_NOTFOUND) {
/*
* We don't know anything about this key.
*/
val,
&val->fsigrdataset,
if (result != ISC_R_SUCCESS)
return (result);
return (DNS_R_WAIT);
} else if (result == DNS_R_NCACHENXDOMAIN ||
result == DNS_R_NCACHENXRRSET ||
result == DNS_R_NXDOMAIN ||
result == DNS_R_NXRRSET)
{
/*
* This key doesn't exist.
*/
}
return (result);
}
/*
* If the rdataset being validated is a key set, is each key a security root?
*/
static isc_boolean_t
result == ISC_R_SUCCESS;
{
if (result != ISC_R_SUCCESS)
continue;
&keynode);
while (result == ISC_R_SUCCESS) {
break;
}
&nextnode);
}
dst_key_free(&key);
if (!match)
return (ISC_FALSE);
}
return (match);
}
/*
* Attempts positive response validation.
*
* Returns:
* ISC_R_SUCCESS Validation completed successfully
* DNS_R_WAIT Validation has started but is waiting
* for an event.
* Other return codes are possible and all indicate failure.
*/
static inline isc_result_t
/*
* Caller must be holding the validator lock.
*/
/*
* If this is a security root, it's ok.
*/
if (!resume) {
secroot);
if (result == ISC_R_SUCCESS &&
{
return (ISC_R_SUCCESS);
}
}
if (resume) {
/*
* We already have a sigrdataset.
*/
} else {
}
for (;
result == ISC_R_SUCCESS;
{
return (ISC_R_NOMEMORY);
/*
* At this point we could check that the signature algorithm
* was known and "sufficiently good". For now, any algorithm
* is acceptable.
*/
if (!resume) {
if (result == DNS_R_CONTINUE)
continue; /* Try the next SIG RR. */
if (result != ISC_R_SUCCESS)
return (result);
}
"marking as answer");
return (ISC_R_SUCCESS);
}
do {
"verify rdataset: %s",
if (result == ISC_R_SUCCESS)
break;
&nextnode);
if (result != ISC_R_SUCCESS) {
break;
}
} else {
!= ISC_R_SUCCESS)
break;
}
} while (1);
if (result != ISC_R_SUCCESS)
"failed to verify rdataset");
else {
}
else {
}
}
if (result == ISC_R_SUCCESS) {
"marking as secure");
return (result);
}
else
"verify failure: %s",
}
return (DNS_R_NOVALIDSIG);
}
static inline isc_result_t
if (!resume) {
if (result != ISC_R_SUCCESS)
} else {
}
for (;
result == ISC_R_SUCCESS;
{
if (resume) {
}
else
for (;
{
continue;
sigrdataset != NULL;
link))
{
break;
}
if (sigrdataset == NULL)
continue;
/*
* 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 NXT signed by the missing key, which
* would trigger another query for the KEY (since the
* first one is still in progress), and go into an
* infinite loop. Avoid that.
*/
{
continue;
if (dns_nxt_typepresent(&nxt,
continue;
}
NULL, 0,
val,
&val->authvalidator);
if (result != ISC_R_SUCCESS)
return (result);
return (DNS_R_WAIT);
}
}
if (result == ISC_R_NOMORE)
if (result != ISC_R_SUCCESS)
val,
&val->authvalidator);
if (result != ISC_R_SUCCESS)
return (result);
return (DNS_R_WAIT);
}
"nonexistence proof not found");
return (DNS_R_NOVALIDNXT);
} else {
"nonexistence proof found");
return (ISC_R_SUCCESS);
}
}
static inline isc_result_t
/*
* If the name is not under a security root, it must be insecure.
*/
if (result == ISC_R_NOTFOUND)
return (ISC_R_SUCCESS);
else if (result != ISC_R_SUCCESS)
return (result);
/*
* If this is a security root, it's ok.
*/
{
return (ISC_R_SUCCESS);
}
if (!resume)
else {
}
for (;
{
char namebuf[1024];
break;
} else {
if (result != ISC_R_SUCCESS)
return (result);
}
"looking for null keyset at '%s'",
namebuf);
&val->fsigrdataset);
if (result == ISC_R_SUCCESS) {
goto out;
}
"found keyset, looking for null key");
continue;
"insecurity proof succeeded");
goto out;
}
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS) {
sizeof *fname);
goto out;
}
&val->fsigrdataset,
NULL,
0,
val,
&val->keyvalidator);
if (result != ISC_R_SUCCESS)
goto out;
return (DNS_R_WAIT);
} else if (result == ISC_R_NOTFOUND) {
val,
&val->fsigrdataset,
if (result != ISC_R_SUCCESS)
goto out;
return (DNS_R_WAIT);
} else if (result == DNS_R_NCACHENXDOMAIN ||
result == DNS_R_NCACHENXRRSET ||
result == DNS_R_NXDOMAIN ||
result == DNS_R_NXRRSET)
{
continue;
} else
goto out;
}
return (DNS_R_NOTINSECURE); /* Didn't find a null key */
out:
return (result);
}
static void
/* If the validator has been cancelled, val->event == NULL */
return;
/*
* This looks like a simple validation. We say "looks like"
* because we don't know if wildcards are involved yet so it
* could still get complicated.
*/
"attempting positive response validation");
if (result == DNS_R_NOVALIDSIG &&
{
"falling back to insecurity proof");
if (result == DNS_R_NOTINSECURE)
}
/*
* This is either an unsecure subdomain or a response from
* a broken server.
*/
"attempting insecurity proof");
{
/*
* This is a nonexistence validation.
*/
"attempting negative response validation");
} else {
/*
* This shouldn't happen.
*/
INSIST(0);
}
if (result != DNS_R_WAIT)
}
{
return (ISC_R_NOMEMORY);
event = (dns_validatorevent_t *)
sizeof (dns_validatorevent_t));
goto cleanup_val;
}
if (result != ISC_R_SUCCESS)
goto cleanup_event;
val->attributes = 0;
*validatorp = val;
return (ISC_R_SUCCESS);
return (result);
}
void
}
}
static void
}
void
val = *validatorp;
if (want_destroy)
*validatorp = NULL;
}
static void
{
char msgbuf[2048];
char namebuf[1024];
char typebuf[256];
isc_buffer_t b;
isc_region_t r;
!= ISC_R_SUCCESS)
{
isc_buffer_clear(&b);
isc_buffer_putstr(&b, "<bad type>");
}
isc_buffer_usedregion(&b, &r);
"validating %s %.*s: %s", namebuf,
} else {
}
}
static void
{
return;
}