mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-01-27 08:59:19 -05:00
val_util work.
git-svn-id: file:///svn/unbound/trunk@497 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
c3a5bcb8d4
commit
b2639ba961
9 changed files with 412 additions and 7 deletions
|
|
@ -3,6 +3,7 @@
|
|||
- security status is copied when rdata is equal for rrsets.
|
||||
- rrset id is updated to invalidate all the message cache entries
|
||||
that refer to NSEC, NSEC3, DNAME rrsets that have changed.
|
||||
- val_util work
|
||||
|
||||
6 August 2007: Wouter
|
||||
- key cache for validator.
|
||||
|
|
|
|||
|
|
@ -670,6 +670,22 @@ reply_find_answer_rrset(struct query_info* qinfo, struct reply_info* rep)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct ub_packed_rrset_key* reply_find_rrset_section_an(struct reply_info* rep,
|
||||
uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<rep->an_numrrsets; i++) {
|
||||
struct ub_packed_rrset_key* s = rep->rrsets[i];
|
||||
if(ntohs(s->rk.type) == type &&
|
||||
ntohs(s->rk.rrset_class) == dclass &&
|
||||
namelen == s->rk.dname_len &&
|
||||
query_dname_compare(name, s->rk.dname) == 0) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* rep)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -321,6 +321,18 @@ int parse_copy_decompress_rrset(ldns_buffer* pkt, struct msg_parse* msg,
|
|||
struct ub_packed_rrset_key* reply_find_answer_rrset(struct query_info* qinfo,
|
||||
struct reply_info* rep);
|
||||
|
||||
/**
|
||||
* Find rrset in reply, inside the answer section. Does not follow CNAMEs.
|
||||
* @param rep: looks in answer section of this message.
|
||||
* @param name: what to look for.
|
||||
* @param namelen: length of name.
|
||||
* @param type: looks for (host order).
|
||||
* @param dclass: looks for (host order).
|
||||
* @return: pointer to rrset, or NULL if not found.
|
||||
*/
|
||||
struct ub_packed_rrset_key* reply_find_rrset_section_an(struct reply_info* rep,
|
||||
uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass);
|
||||
|
||||
/**
|
||||
* Debug send the query info and reply info to the log in readable form.
|
||||
* @param str: descriptive string printed with packet content.
|
||||
|
|
|
|||
|
|
@ -184,3 +184,93 @@ key_entry_isnull(struct key_entry_key* kkey)
|
|||
struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
|
||||
return (!d->isbad && d->rrset_data == NULL);
|
||||
}
|
||||
|
||||
int
|
||||
key_entry_isgood(struct key_entry_key* kkey)
|
||||
{
|
||||
struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
|
||||
return (!d->isbad && d->rrset_data != NULL);
|
||||
}
|
||||
|
||||
int
|
||||
key_entry_isbad(struct key_entry_key* kkey)
|
||||
{
|
||||
struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
|
||||
return (int)(d->isbad);
|
||||
}
|
||||
|
||||
/** setup key entry in region */
|
||||
static int
|
||||
key_entry_setup(struct region* region,
|
||||
uint8_t* name, size_t namelen, uint16_t dclass,
|
||||
struct key_entry_key** k, struct key_entry_data** d)
|
||||
{
|
||||
*k = region_alloc(region, sizeof(**k));
|
||||
if(!*k)
|
||||
return 0;
|
||||
memset(*k, 0, sizeof(**k));
|
||||
(*k)->entry.key = *k;
|
||||
(*k)->name = region_alloc_init(region, name, namelen);
|
||||
if(!(*k)->name)
|
||||
return 0;
|
||||
(*k)->namelen = namelen;
|
||||
(*k)->key_class = dclass;
|
||||
*d = region_alloc(region, sizeof(**d));
|
||||
if(!*d)
|
||||
return 0;
|
||||
(*k)->entry.data = d;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct key_entry_key*
|
||||
key_entry_create_null(struct region* region,
|
||||
uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl)
|
||||
{
|
||||
struct key_entry_key* k;
|
||||
struct key_entry_data* d;
|
||||
if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
|
||||
return NULL;
|
||||
d->ttl = ttl;
|
||||
d->isbad = 0;
|
||||
d->rrset_type = LDNS_RR_TYPE_DNSKEY;
|
||||
d->rrset_data = NULL;
|
||||
return k;
|
||||
}
|
||||
|
||||
struct key_entry_key*
|
||||
key_entry_create_rrset(struct region* region,
|
||||
uint8_t* name, size_t namelen, uint16_t dclass,
|
||||
struct ub_packed_rrset_key* rrset)
|
||||
{
|
||||
struct key_entry_key* k;
|
||||
struct key_entry_data* d;
|
||||
struct packed_rrset_data* rd = (struct packed_rrset_data*)
|
||||
rrset->entry.data;
|
||||
if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
|
||||
return NULL;
|
||||
d->ttl = rd->ttl;
|
||||
log_info("New key entry TTL is %d", (int)d->ttl);
|
||||
d->isbad = 0;
|
||||
d->rrset_type = ntohs(rrset->rk.type);
|
||||
d->rrset_data = (struct packed_rrset_data*)region_alloc_init(region,
|
||||
rd, packed_rrset_sizeof(rd));
|
||||
if(!d->rrset_data)
|
||||
return NULL;
|
||||
packed_rrset_ptr_fixup(d->rrset_data);
|
||||
return k;
|
||||
}
|
||||
|
||||
struct key_entry_key*
|
||||
key_entry_create_bad(struct region* region,
|
||||
uint8_t* name, size_t namelen, uint16_t dclass)
|
||||
{
|
||||
struct key_entry_key* k;
|
||||
struct key_entry_data* d;
|
||||
if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
|
||||
return NULL;
|
||||
d->ttl = 0;
|
||||
d->isbad = 1;
|
||||
d->rrset_type = LDNS_RR_TYPE_DNSKEY;
|
||||
d->rrset_data = NULL;
|
||||
return k;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#define VALIDATOR_VAL_KENTRY_H
|
||||
struct packed_rrset_data;
|
||||
struct region;
|
||||
struct ub_packed_rrset_key;
|
||||
#include "util/storage/lruhash.h"
|
||||
|
||||
/**
|
||||
|
|
@ -123,4 +124,54 @@ struct key_entry_key* key_entry_copy(struct key_entry_key* kkey);
|
|||
*/
|
||||
int key_entry_isnull(struct key_entry_key* kkey);
|
||||
|
||||
/**
|
||||
* See if this entry is good. Does not do locking.
|
||||
* @param kkey: must have data pointer set correctly
|
||||
* @return true if it is good.
|
||||
*/
|
||||
int key_entry_isgood(struct key_entry_key* kkey);
|
||||
|
||||
/**
|
||||
* See if this entry is bad. Does not do locking.
|
||||
* @param kkey: must have data pointer set correctly
|
||||
* @return true if it is bad.
|
||||
*/
|
||||
int key_entry_isbad(struct key_entry_key* kkey);
|
||||
|
||||
/**
|
||||
* Create a null entry, in the given region.
|
||||
* @param region: where to allocate
|
||||
* @param name: the key name
|
||||
* @param namelen: length of name
|
||||
* @param dclass: class of key entry.
|
||||
* @param ttl: what ttl should the key have.
|
||||
* @return new key entry or NULL on alloc failure
|
||||
*/
|
||||
struct key_entry_key* key_entry_create_null(struct region* region,
|
||||
uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl);
|
||||
|
||||
/**
|
||||
* Create a key entry from an rrset, in the given region.
|
||||
* @param region: where to allocate.
|
||||
* @param name: the key name
|
||||
* @param namelen: length of name
|
||||
* @param dclass: class of key entry.
|
||||
* @param rrset: data for key entry. This is copied to the region.
|
||||
* @return new key entry or NULL on alloc failure
|
||||
*/
|
||||
struct key_entry_key* key_entry_create_rrset(struct region* region,
|
||||
uint8_t* name, size_t namelen, uint16_t dclass,
|
||||
struct ub_packed_rrset_key* rrset);
|
||||
|
||||
/**
|
||||
* Create a bad entry, in the given region.
|
||||
* @param region: where to allocate
|
||||
* @param name: the key name
|
||||
* @param namelen: length of name
|
||||
* @param dclass: class of key entry.
|
||||
* @return new key entry or NULL on alloc failure
|
||||
*/
|
||||
struct key_entry_key* key_entry_create_bad(struct region* region,
|
||||
uint8_t* name, size_t namelen, uint16_t dclass);
|
||||
|
||||
#endif /* VALIDATOR_VAL_KENTRY_H */
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
*/
|
||||
#include "config.h"
|
||||
#include "validator/val_utils.h"
|
||||
#include "validator/val_kentry.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/data/dname.h"
|
||||
|
|
@ -172,3 +173,135 @@ val_find_signer(struct query_info* qinf, struct reply_info* rep,
|
|||
*signer_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** return number of rrs in an rrset */
|
||||
static size_t
|
||||
rrset_get_count(struct ub_packed_rrset_key* rrset)
|
||||
{
|
||||
struct packed_rrset_data* d = (struct packed_rrset_data*)
|
||||
rrset->entry.data;
|
||||
if(!d) return 0;
|
||||
return d->count;
|
||||
}
|
||||
|
||||
/** return TTL of rrset */
|
||||
static uint32_t
|
||||
rrset_get_ttl(struct ub_packed_rrset_key* rrset)
|
||||
{
|
||||
struct packed_rrset_data* d = (struct packed_rrset_data*)
|
||||
rrset->entry.data;
|
||||
if(!d) return 0;
|
||||
return d->ttl;
|
||||
}
|
||||
|
||||
enum sec_status
|
||||
val_verify_rrset(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys)
|
||||
{
|
||||
|
||||
return sec_status_bogus;
|
||||
}
|
||||
|
||||
/** verify that a DS RR hashes to a key and that key signs the set */
|
||||
static enum sec_status
|
||||
verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key* dnskey_rrset,
|
||||
struct ub_packed_rrset_key* ds_rrset, size_t ds_idx)
|
||||
{
|
||||
enum sec_status sec;
|
||||
size_t i, num;
|
||||
num = rrset_get_count(dnskey_rrset);
|
||||
for(i=0; i<num; i++) {
|
||||
/* Skip DNSKEYs that don't match the basic criteria. */
|
||||
/* if (ds.getFootprint() != dnskey.getFootprint()
|
||||
* || ds.getAlgorithm() != dnskey.getAlgorithm())
|
||||
* {
|
||||
* continue;
|
||||
* }
|
||||
*/
|
||||
|
||||
/* Convert the candidate DNSKEY into a hash using the
|
||||
* same DS hash algorithm. */
|
||||
/* byte[] key_hash = calculateDSHash(dnskey, ds.getDigestID());
|
||||
* byte[] ds_hash = ds.getDigest() */
|
||||
|
||||
/* if length or contents of the hash mismatch; continue */
|
||||
|
||||
/* Otherwise, we have a match! Make sure that the DNSKEY
|
||||
* verifies *with this key* */
|
||||
/*
|
||||
sec = verify_rrset_key(env, ve, dnskey_rrset, dnskey_rrset, i);
|
||||
*/
|
||||
if(sec == sec_status_secure) {
|
||||
return sec;
|
||||
}
|
||||
/* If it didn't validate with the DNSKEY, try the next one! */
|
||||
}
|
||||
return sec_status_bogus;
|
||||
}
|
||||
|
||||
struct key_entry_key*
|
||||
val_verify_new_DNSKEYs(struct region* region, struct module_env* env,
|
||||
struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset,
|
||||
struct ub_packed_rrset_key* ds_rrset)
|
||||
{
|
||||
/* as long as this is false, we can consider this DS rrset to be
|
||||
* equivalent to no DS rrset. */
|
||||
int has_useful_ds = 0;
|
||||
size_t i, num;
|
||||
enum sec_status sec;
|
||||
|
||||
if(dnskey_rrset->rk.dname_len != ds_rrset->rk.dname_len ||
|
||||
query_dname_compare(dnskey_rrset->rk.dname, ds_rrset->rk.dname)
|
||||
!= 0) {
|
||||
verbose(VERB_ALGO, "DNSKEY RRset did not match DS RRset "
|
||||
"by name");
|
||||
return key_entry_create_bad(region, ds_rrset->rk.dname,
|
||||
ds_rrset->rk.dname_len,
|
||||
ntohs(ds_rrset->rk.rrset_class));
|
||||
}
|
||||
|
||||
num = rrset_get_count(ds_rrset);
|
||||
for(i=0; i<num; i++) {
|
||||
|
||||
/* Check to see if we can understand this DS. */
|
||||
/* if (!supportsDigestID(ds.getDigestID())
|
||||
* || !mVerifier.supportsAlgorithm(ds.getAlgorithm()))
|
||||
* {
|
||||
* continue;
|
||||
* }
|
||||
*/
|
||||
|
||||
/* Once we see a single DS with a known digestID and
|
||||
* algorithm, we cannot return INSECURE (with a
|
||||
* "null" KeyEntry). */
|
||||
has_useful_ds = true;
|
||||
|
||||
sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset,
|
||||
ds_rrset, i);
|
||||
if(sec == sec_status_secure) {
|
||||
verbose(VERB_ALGO, "DS matched DNSKEY.");
|
||||
/* TODO -- cannot, wrong region for prime */
|
||||
/* update dnskey RRset status as secure */
|
||||
return key_entry_create_rrset(region,
|
||||
ds_rrset->rk.dname, ds_rrset->rk.dname_len,
|
||||
ntohs(ds_rrset->rk.rrset_class), dnskey_rrset);
|
||||
}
|
||||
}
|
||||
|
||||
/* None of the DS's worked out. */
|
||||
|
||||
/* If no DSs were understandable, then this is OK. */
|
||||
if(!has_useful_ds) {
|
||||
verbose(VERB_ALGO, "No usable DS records were found -- "
|
||||
"treating as insecure.");
|
||||
return key_entry_create_null(region, ds_rrset->rk.dname,
|
||||
ds_rrset->rk.dname_len,
|
||||
ntohs(ds_rrset->rk.rrset_class),
|
||||
rrset_get_ttl(ds_rrset));
|
||||
}
|
||||
/* If any were understandable, then it is bad. */
|
||||
verbose(VERB_ALGO, "Failed to match any usable DS to a DNSKEY.");
|
||||
return key_entry_create_bad(region, ds_rrset->rk.dname,
|
||||
ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,11 @@
|
|||
#define VALIDATOR_VAL_UTILS_H
|
||||
struct query_info;
|
||||
struct reply_info;
|
||||
struct val_env;
|
||||
struct module_env;
|
||||
struct ub_packed_rrset_key;
|
||||
struct region;
|
||||
enum sec_status;
|
||||
|
||||
/**
|
||||
* Response classifications for the validator. The different types of proofs.
|
||||
|
|
@ -87,4 +92,39 @@ enum val_classification val_classify_response(struct query_info* qinf,
|
|||
void val_find_signer(struct query_info* qinf, struct reply_info* rep,
|
||||
uint8_t** signer_name, size_t* signer_len);
|
||||
|
||||
/**
|
||||
* Verify RRset with keys
|
||||
* @param env: module environment (scratch buffer)
|
||||
* @param ve: validator environment (verification settings)
|
||||
* @param rrset: what to verify
|
||||
* @param keys: dnskey rrset to verify with.
|
||||
* @return security status of verification.
|
||||
*/
|
||||
enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys);
|
||||
|
||||
/**
|
||||
* Verify new DNSKEYs with DS rrset. The DS contains hash values that should
|
||||
* match the DNSKEY keys.
|
||||
* match the DS to a DNSKEY and verify the DNSKEY rrset with that key.
|
||||
*
|
||||
* @param region: where to allocate key entry result.
|
||||
* @param env: module environment (scratch buffer)
|
||||
* @param ve: validator environment (verification settings)
|
||||
* @param dnskey_rrset: DNSKEY rrset to verify
|
||||
* @param ds_rrset: DS rrset to verify with.
|
||||
* @return a KeyEntry. This will either contain the now trusted
|
||||
* dnskey_rrset, a "null" key entry indicating that this DS
|
||||
* rrset/DNSKEY pair indicate an secure end to the island of trust
|
||||
* (i.e., unknown algorithms), or a "bad" KeyEntry if the dnskey
|
||||
* rrset fails to verify. Note that the "null" response should
|
||||
* generally only occur in a private algorithm scenario: normally
|
||||
* this sort of thing is checked before fetching the matching DNSKEY
|
||||
* rrset.
|
||||
*/
|
||||
struct key_entry_key* val_verify_new_DNSKEYs(struct region* region,
|
||||
struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key* dnskey_rrset,
|
||||
struct ub_packed_rrset_key* ds_rrset);
|
||||
|
||||
#endif /* VALIDATOR_VAL_UTILS_H */
|
||||
|
|
|
|||
|
|
@ -298,9 +298,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
|
|||
/* response is under a null key, so we cannot validate
|
||||
* However, we do set the status to INSECURE, since it is
|
||||
* essentially proven insecure. */
|
||||
/* TODO
|
||||
vq->security_state = SEC_INSECURE;
|
||||
*/
|
||||
vq->chase_reply->security = sec_status_insecure;
|
||||
vq->state = vq->final_state;
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -414,17 +412,75 @@ static struct key_entry_key*
|
|||
primeResponseToKE(int rcode, struct dns_msg* msg, struct trust_anchor* ta,
|
||||
struct module_qstate* qstate, int id)
|
||||
{
|
||||
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
|
||||
struct ub_packed_rrset_key* dnskey_rrset = NULL;
|
||||
struct key_entry_key* kkey = NULL;
|
||||
enum sec_status sec = sec_status_unchecked;
|
||||
|
||||
if(rcode == LDNS_RCODE_NOERROR) {
|
||||
dnskey_rrset = 0/*find answer */;
|
||||
dnskey_rrset = reply_find_rrset_section_an(msg->rep,
|
||||
ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY,
|
||||
ta->dclass);
|
||||
}
|
||||
if(!dnskey_rrset) {
|
||||
log_query_info(VERB_ALGO, "failed to prime trust anchor -- "
|
||||
"could not fetch DNSKEY rrset", &msg->qinfo);
|
||||
/* create NULL key with NULL_KEY_TTL, store in cache. */
|
||||
return NULL;
|
||||
kkey = key_entry_create_null(qstate->region, ta->name,
|
||||
ta->namelen, ta->dclass, time(0)+NULL_KEY_TTL);
|
||||
if(!kkey) {
|
||||
log_err("out of memory: allocate null prime key");
|
||||
return NULL;
|
||||
}
|
||||
key_cache_insert(ve->kcache, kkey);
|
||||
return kkey;
|
||||
}
|
||||
return NULL;
|
||||
/* attempt to verify with trust anchor DS and DNSKEY */
|
||||
if(ta->ds_rrset) {
|
||||
kkey = val_verify_new_DNSKEYs(qstate->region, qstate->env, ve,
|
||||
dnskey_rrset, ta->ds_rrset);
|
||||
if(!kkey) {
|
||||
log_err("out of memory: verifying prime DS");
|
||||
return NULL;
|
||||
}
|
||||
if(key_entry_isgood(kkey))
|
||||
sec = sec_status_secure;
|
||||
else
|
||||
sec = sec_status_bogus;
|
||||
}
|
||||
if(sec != sec_status_secure && ta->dnskey_rrset) {
|
||||
sec = val_verify_rrset(qstate->env, ve, dnskey_rrset,
|
||||
ta->dnskey_rrset);
|
||||
if(sec == sec_status_secure) {
|
||||
kkey = key_entry_create_rrset(qstate->region,
|
||||
ta->name, ta->namelen, ta->dclass,
|
||||
dnskey_rrset);
|
||||
if(!kkey) {
|
||||
log_err("out of memory: allocate primed key");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sec != sec_status_secure) {
|
||||
log_query_info(VERB_ALGO, "failed to prime trust anchor -- "
|
||||
"could not fetch DNSKEY rrset", &msg->qinfo);
|
||||
/* NOTE: in this case, we should probably reject the trust
|
||||
* anchor for longer, perhaps forever. */
|
||||
kkey = key_entry_create_null(qstate->region, ta->name,
|
||||
ta->namelen, ta->dclass, time(0)+NULL_KEY_TTL);
|
||||
if(!kkey) {
|
||||
log_err("out of memory: allocate null prime key");
|
||||
return NULL;
|
||||
}
|
||||
key_cache_insert(ve->kcache, kkey);
|
||||
return kkey;
|
||||
}
|
||||
|
||||
log_query_info(VERB_ALGO, "Successfully primed trust anchor",
|
||||
&msg->qinfo);
|
||||
/* store the freshly primed entry in the cache */
|
||||
key_cache_insert(ve->kcache, kkey);
|
||||
return kkey;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@ struct val_anchors;
|
|||
struct key_cache;
|
||||
struct key_entry_key;
|
||||
|
||||
/**
|
||||
* This is the TTL to use when a trust anchor fails to prime. A trust anchor
|
||||
* will be primed no more often than this interval.
|
||||
*/
|
||||
#define NULL_KEY_TTL 900 /* seconds */
|
||||
|
||||
/**
|
||||
* Global state for the validator.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue