From: Wouter Wijngaards Date: Tue, 7 Aug 2007 14:30:01 +0000 (+0000) Subject: val_util work. X-Git-Tag: release-0.5~144 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b2639ba96115d487555d5b9f3d2f04ae04e71cd7;p=thirdparty%2Funbound.git val_util work. git-svn-id: file:///svn/unbound/trunk@497 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index 2a70cf592..c271cff04 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/util/data/msgreply.c b/util/data/msgreply.c index 46d943bc3..d890fa9f1 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -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; ian_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) { diff --git a/util/data/msgreply.h b/util/data/msgreply.h index f7a30cc3c..1f45e561a 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -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. diff --git a/validator/val_kentry.c b/validator/val_kentry.c index 2d8337758..350221e63 100644 --- a/validator/val_kentry.c +++ b/validator/val_kentry.c @@ -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; +} diff --git a/validator/val_kentry.h b/validator/val_kentry.h index 74bb06f7c..d18d20e42 100644 --- a/validator/val_kentry.h +++ b/validator/val_kentry.h @@ -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 */ diff --git a/validator/val_utils.c b/validator/val_utils.c index 093ec5331..27f4d5064 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -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; irk.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; irk.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)); +} diff --git a/validator/val_utils.h b/validator/val_utils.h index 31f9f721a..65f764687 100644 --- a/validator/val_utils.h +++ b/validator/val_utils.h @@ -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 */ diff --git a/validator/validator.c b/validator/validator.c index 4d077133d..28ed54bf3 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -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; + } + /* 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; + } + } } - 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; } /** diff --git a/validator/validator.h b/validator/validator.h index 881f1292b..bfa3a42c9 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -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. */