mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-31 06:25:31 +00:00
checkpoint
This commit is contained in:
@@ -18,16 +18,25 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <isc/assertions.h>
|
||||
#include <isc/buffer.h>
|
||||
#include <isc/magic.h>
|
||||
#include <isc/region.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/stdtime.h>
|
||||
#include <isc/task.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include <dns/validator.h>
|
||||
#include <dns/db.h>
|
||||
#include <dns/events.h>
|
||||
#include <dns/keytable.h>
|
||||
#include <dns/name.h>
|
||||
#include <dns/rdata.h>
|
||||
#include <dns/rdataset.h>
|
||||
#include <dns/view.h>
|
||||
|
||||
#include <dst/dst.h>
|
||||
|
||||
struct dns_validator {
|
||||
/* Unlocked. */
|
||||
unsigned int magic;
|
||||
@@ -37,11 +46,337 @@ struct dns_validator {
|
||||
unsigned int options;
|
||||
unsigned int attributes;
|
||||
dns_validatorevent_t * event;
|
||||
dns_fetch_t * fetch;
|
||||
dns_validator_t * keyvalidator;
|
||||
dns_keytable_t * keytable;
|
||||
dns_keynode_t * keynode;
|
||||
dst_key_t * key;
|
||||
};
|
||||
|
||||
#define VALIDATOR_MAGIC 0x56616c3fU /* Val?. */
|
||||
#define VALID_VALIDATOR(v) ISC_MAGIC_VALID(v, VALIDATOR_MAGIC)
|
||||
|
||||
#define VALATTR_SHUTDOWN 0x01
|
||||
#define SHUTDOWN(v) (((v)->attributes & VALATTR_SHUTDOWN) != 0)
|
||||
|
||||
/*
|
||||
* We don't use the SIG RR's _tostruct routine because it copies things.
|
||||
*/
|
||||
typedef struct dns_siginfo {
|
||||
dns_rdatatype_t covers;
|
||||
dns_secalg_t algorithm;
|
||||
isc_uint8_t labels;
|
||||
dns_ttl_t original_ttl;
|
||||
isc_stdtime_t expiration;
|
||||
isc_stdtime_t inception;
|
||||
dns_keytag_t tag;
|
||||
dns_name_t signer;
|
||||
isc_region_t signature;
|
||||
} dns_siginfo_t;
|
||||
|
||||
static void
|
||||
rdata_to_siginfo(dns_rdata_t *rdata, dns_siginfo_t *siginfo) {
|
||||
isc_buffer_t b;
|
||||
isc_region_t r;
|
||||
|
||||
REQUIRE(rdata->type == 24);
|
||||
|
||||
isc_buffer_init(&b, rdata->data, rdata->length, ISC_BUFFERTYPE_BINARY);
|
||||
isc_buffer_add(&b, rdata->length);
|
||||
siginfo->covers = (dns_rdatatype_t)isc_buffer_getuint16(&b);
|
||||
siginfo->algorithm = (dns_secalg_t)isc_buffer_getuint8(&b);
|
||||
siginfo->labels = isc_buffer_getuint8(&b);
|
||||
siginfo->original_ttl = (dns_ttl_t)isc_buffer_getuint32(&b);
|
||||
siginfo->expiration = (isc_stdtime_t)isc_buffer_getuint32(&b);
|
||||
siginfo->inception = (isc_stdtime_t)isc_buffer_getuint32(&b);
|
||||
siginfo->tag = (dns_keytag_t)isc_buffer_getuint16(&b);
|
||||
dns_name_init(&siginfo->signer, NULL);
|
||||
isc_buffer_remaining(&b, &r);
|
||||
dns_name_fromregion(&siginfo->signer, &r);
|
||||
isc_buffer_forward(&b, siginfo->signer.length);
|
||||
isc_buffer_remaining(&b, &siginfo->signature);
|
||||
}
|
||||
|
||||
static void
|
||||
validator_done(dns_validator_t *val, isc_result_t result) {
|
||||
isc_task_t *task;
|
||||
|
||||
REQUIRE(val->event != NULL);
|
||||
|
||||
/*
|
||||
* Caller must be holding the lock.
|
||||
*/
|
||||
|
||||
if (result != ISC_R_SUCCESS)
|
||||
val->event->result = result;
|
||||
task = val->event->sender;
|
||||
val->event->sender = val;
|
||||
isc_task_sendanddetach(&task, (isc_event_t **)&val->event);
|
||||
|
||||
}
|
||||
|
||||
static inline isc_result_t
|
||||
get_dst_key(dns_validator_t *val, dns_siginfo_t *siginfo,
|
||||
dns_rdataset_t *rdataset)
|
||||
{
|
||||
isc_result_t result;
|
||||
isc_buffer_t b;
|
||||
dns_rdata_t rdata;
|
||||
char ntext[1024];
|
||||
|
||||
result = dns_rdataset_first(rdataset);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
return (result);
|
||||
do {
|
||||
dns_rdataset_current(rdataset, &rdata);
|
||||
/*
|
||||
* We keep one byte of ntext in reserve so
|
||||
* we're sure we can NUL terminate.
|
||||
*/
|
||||
isc_buffer_init(&b, ntext, sizeof(ntext) - 1,
|
||||
ISC_BUFFERTYPE_TEXT);
|
||||
result = dns_name_totext(&siginfo->signer, ISC_FALSE, &b);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
return (result);
|
||||
|
||||
/*
|
||||
* NUL-terminate the character string.
|
||||
*/
|
||||
isc_buffer_putuint8(&b, 0);
|
||||
|
||||
isc_buffer_init(&b, rdata.data, rdata.length,
|
||||
ISC_BUFFERTYPE_BINARY);
|
||||
isc_buffer_add(&b, rdata.length);
|
||||
INSIST(val->key == NULL);
|
||||
result = dst_key_fromdns(ntext, &b, val->view->mctx,
|
||||
&val->key);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
return (result);
|
||||
if (siginfo->algorithm ==
|
||||
(dns_secalg_t)dst_key_alg(val->key) &&
|
||||
siginfo->tag ==
|
||||
(dns_keytag_t)dst_key_id(val->key) &&
|
||||
dst_key_iszonekey(val->key) &&
|
||||
dst_key_proto(val->key) == DST_KEYPROTO_DNSSEC) {
|
||||
/*
|
||||
* This is the key we're looking for.
|
||||
*/
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
dst_key_free(val->key);
|
||||
val->key = NULL;
|
||||
result = dns_rdataset_next(rdataset);
|
||||
} while (result == ISC_R_SUCCESS);
|
||||
if (result == ISC_R_NOMORE)
|
||||
result = ISC_R_NOTFOUND;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static inline isc_result_t
|
||||
get_key(dns_validator_t *val, dns_siginfo_t *siginfo) {
|
||||
isc_result_t result;
|
||||
dns_validatorevent_t *event;
|
||||
unsigned int nbits, nlabels;
|
||||
int order;
|
||||
dns_namereln_t namereln;
|
||||
dns_rdataset_t rdataset, sigrdataset;
|
||||
|
||||
event = val->event;
|
||||
|
||||
/*
|
||||
* Is the key used for the signature a security root?
|
||||
*/
|
||||
INSIST(val->keynode == NULL);
|
||||
val->keytable = val->view->secroots;
|
||||
result = dns_keytable_findkeynode(val->view->secroots,
|
||||
&siginfo->signer,
|
||||
siginfo->algorithm, siginfo->tag,
|
||||
&val->keynode);
|
||||
if (result == ISC_R_NOTFOUND) {
|
||||
/*
|
||||
* Is it a trusted key that is not a security root?
|
||||
*/
|
||||
val->keytable = val->view->trustedkeys;
|
||||
result = dns_keytable_findkeynode(val->view->trustedkeys,
|
||||
&siginfo->signer,
|
||||
siginfo->algorithm,
|
||||
siginfo->tag,
|
||||
&val->keynode);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
/*
|
||||
* The key is trusted.
|
||||
*/
|
||||
val->key = dns_keynode_key(val->keynode);
|
||||
return (ISC_R_SUCCESS);
|
||||
} else if (result != ISC_R_NOTFOUND)
|
||||
return (result);
|
||||
} else if (result == ISC_R_SUCCESS) {
|
||||
/*
|
||||
* The key is a security root.
|
||||
*/
|
||||
val->key = dns_keynode_key(val->keynode);
|
||||
return (ISC_R_SUCCESS);
|
||||
} else
|
||||
return (result);
|
||||
|
||||
/*
|
||||
* The signature was not made with a security root or trusted key.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Is the key name appropriate for this signature?
|
||||
*/
|
||||
namereln = dns_name_fullcompare(event->name, &siginfo->signer,
|
||||
&order, &nlabels, &nbits);
|
||||
if (rdataset.type == dns_rdatatype_key &&
|
||||
namereln != dns_namereln_subdomain) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
return (DNS_R_CONTINUE);
|
||||
} else 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do we know about this key?
|
||||
*/
|
||||
dns_rdataset_init(&rdataset);
|
||||
dns_rdataset_init(&sigrdataset);
|
||||
result = dns_view_simplefind(val->view, &siginfo->signer,
|
||||
dns_rdatatype_key, 0,
|
||||
DNS_DBFIND_PENDINGOK, ISC_FALSE,
|
||||
&rdataset, &sigrdataset);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
/*
|
||||
* We have an rrset for the given keyname.
|
||||
*/
|
||||
if (rdataset.trust == dns_trust_pending) {
|
||||
/*
|
||||
* We know the key but haven't validated it yet.
|
||||
*/
|
||||
result = DNS_R_NOTIMPLEMENTED;
|
||||
} else {
|
||||
/*
|
||||
* XXXRTH What should we do if this is an untrusted
|
||||
* rdataset?
|
||||
*/
|
||||
/*
|
||||
* See if we've got the key used in the signature.
|
||||
*/
|
||||
result = get_dst_key(val, siginfo, &rdataset);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
/*
|
||||
* Either the key we're looking for is not
|
||||
* in the rrset, or something bad happened.
|
||||
* Give up.
|
||||
*/
|
||||
result = DNS_R_CONTINUE;
|
||||
}
|
||||
}
|
||||
} else if (result == ISC_R_NOTFOUND) {
|
||||
/*
|
||||
* We don't know anything about this key.
|
||||
*
|
||||
* XXX Start a fetch.
|
||||
*/
|
||||
result = DNS_R_NOTIMPLEMENTED;
|
||||
} else if (result == DNS_R_NCACHENXDOMAIN ||
|
||||
result == DNS_R_NCACHENXRRSET ||
|
||||
result == DNS_R_NXDOMAIN ||
|
||||
result == DNS_R_NXRRSET) {
|
||||
/*
|
||||
* This key doesn't exist.
|
||||
*/
|
||||
result = DNS_R_CONTINUE;
|
||||
}
|
||||
|
||||
if (dns_rdataset_isassociated(&rdataset))
|
||||
dns_rdataset_disassociate(&rdataset);
|
||||
if (dns_rdataset_isassociated(&sigrdataset))
|
||||
dns_rdataset_disassociate(&sigrdataset);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static inline isc_result_t
|
||||
validate(dns_validator_t *val, isc_boolean_t resume) {
|
||||
isc_result_t result;
|
||||
dns_validatorevent_t *event;
|
||||
dns_rdata_t rdata;
|
||||
dns_siginfo_t siginfo;
|
||||
|
||||
/*
|
||||
* Caller must be holding the validator lock.
|
||||
*/
|
||||
|
||||
event = val->event;
|
||||
|
||||
if (!resume) {
|
||||
result = dns_rdataset_first(event->sigrdataset);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
return (result);
|
||||
}
|
||||
do {
|
||||
dns_rdataset_current(event->sigrdataset, &rdata);
|
||||
rdata_to_siginfo(&rdata, &siginfo);
|
||||
|
||||
/*
|
||||
* At this point we could check that the signature algorithm
|
||||
* was known and "sufficiently good". For now, any algorithm
|
||||
* is acceptable.
|
||||
*/
|
||||
|
||||
if (!resume) {
|
||||
result = get_key(val, &siginfo);
|
||||
if (result != DNS_R_CONTINUE)
|
||||
return (result);
|
||||
}
|
||||
INSIST(val->key != NULL);
|
||||
|
||||
result = dns_rdataset_next(event->sigrdataset);
|
||||
} while (result == ISC_R_SUCCESS);
|
||||
if (result == ISC_R_NOMORE)
|
||||
result = ISC_R_SUCCESS;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static inline void
|
||||
validator_start(dns_validator_t *val) {
|
||||
isc_result_t result;
|
||||
|
||||
LOCK(&val->lock);
|
||||
|
||||
if (val->event->rdataset != NULL && val->event->sigrdataset != NULL) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
result = validate(val, ISC_FALSE);
|
||||
} else {
|
||||
/*
|
||||
* This is a nonexistence validation.
|
||||
*/
|
||||
result = DNS_R_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
if (result != ISC_R_SUCCESS)
|
||||
validator_done(val, result);
|
||||
|
||||
UNLOCK(&val->lock);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
dns_validator_create(dns_view_t *view, dns_name_t *name,
|
||||
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
|
||||
@@ -84,8 +419,15 @@ dns_validator_create(dns_view_t *view, dns_name_t *name,
|
||||
val->event = event;
|
||||
val->options = options;
|
||||
val->attributes = 0;
|
||||
val->fetch = NULL;
|
||||
val->keyvalidator = NULL;
|
||||
val->keynode = NULL;
|
||||
val->magic = VALIDATOR_MAGIC;
|
||||
|
||||
validator_start(val);
|
||||
|
||||
*validatorp = val;
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
cleanup_event:
|
||||
@@ -123,9 +465,14 @@ static void
|
||||
destroy(dns_validator_t *val) {
|
||||
dns_view_t *view;
|
||||
|
||||
REQUIRE(VALID_VALIDATOR(val));
|
||||
REQUIRE(SHUTDOWN(val));
|
||||
REQUIRE(val->event == NULL);
|
||||
REQUIRE(val->fetch == NULL);
|
||||
|
||||
if (val->key != NULL)
|
||||
dst_key_free(val->key);
|
||||
if (val->keynode != NULL)
|
||||
dns_keytable_detachkeynode(val->keytable, &val->keynode);
|
||||
view = val->view;
|
||||
isc_mutex_destroy(&val->lock);
|
||||
val->magic = 0;
|
||||
@@ -136,11 +483,25 @@ destroy(dns_validator_t *val) {
|
||||
|
||||
void
|
||||
dns_validator_destroy(dns_validator_t **validatorp) {
|
||||
/*
|
||||
* XXXRTH This is incomplete; we may still be waiting for a fetch
|
||||
* to complete and need to see that it is done before calling
|
||||
* destroy().
|
||||
*/
|
||||
destroy(*validatorp);
|
||||
dns_validator_t *val;
|
||||
isc_boolean_t want_destroy = ISC_FALSE;
|
||||
|
||||
REQUIRE(validatorp != NULL);
|
||||
val = *validatorp;
|
||||
REQUIRE(VALID_VALIDATOR(val));
|
||||
|
||||
LOCK(&val->lock);
|
||||
|
||||
REQUIRE(val->event == NULL);
|
||||
|
||||
val->attributes |= VALATTR_SHUTDOWN;
|
||||
if (val->fetch == NULL)
|
||||
want_destroy = ISC_TRUE;
|
||||
|
||||
UNLOCK(&val->lock);
|
||||
|
||||
if (want_destroy)
|
||||
destroy(val);
|
||||
|
||||
*validatorp = NULL;
|
||||
}
|
||||
|
Reference in New Issue
Block a user