From 701b4ccdd655636b4cadb4c77410d4ba3b860ff2 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Tue, 18 Aug 2009 15:36:46 +0000 Subject: [PATCH] autotrust event_update routine. git-svn-id: file:///svn/unbound/trunk@1766 be551aaa-1e26-0410-a405-d3ace91eadb9 --- doc/Changelog | 3 + util/data/packed_rrset.c | 147 ++++++++++++++++++ util/data/packed_rrset.h | 26 ++++ validator/autotrust.c | 320 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 488 insertions(+), 8 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 79c7168c3..4b6f9c86a 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +17 August 2009: Wouter + - autotrust: process events. + 17 August 2009: Wouter - Fix so that servers are only blacklisted if they fail to reply to 16 queries in a row and the timeout gets above 2 minutes. diff --git a/util/data/packed_rrset.c b/util/data/packed_rrset.c index 5925b8757..406d0e120 100644 --- a/util/data/packed_rrset.c +++ b/util/data/packed_rrset.c @@ -46,6 +46,7 @@ #include "util/log.h" #include "util/alloc.h" #include "util/regional.h" +#include "util/net_help.h" void ub_packed_rrset_parsedelete(struct ub_packed_rrset_key* pkey, @@ -303,3 +304,149 @@ packed_rrset_copy_region(struct ub_packed_rrset_key* key, return ck; } +struct ub_packed_rrset_key* +ub_packed_rrset_heap_key(ldns_rr_list* rrset) +{ + struct ub_packed_rrset_key* k; + ldns_rr* rr; + if(!rrset) + return NULL; + rr = ldns_rr_list_rr(rrset, 0); + if(!rr) + return NULL; + k = (struct ub_packed_rrset_key*)calloc(1, sizeof(*k)); + if(!k) + return NULL; + k->rk.type = htons(ldns_rr_get_type(rr)); + k->rk.rrset_class = htons(ldns_rr_get_class(rr)); + k->rk.dname_len = ldns_rdf_size(ldns_rr_owner(rr)); + k->rk.dname = memdup(ldns_rdf_data(ldns_rr_owner(rr)), + ldns_rdf_size(ldns_rr_owner(rr))); + if(!k->rk.dname) { + free(k); + return NULL; + } + return k; +} + +struct packed_rrset_data* +packed_rrset_heap_data(ldns_rr_list* rrset) +{ + struct packed_rrset_data* data; + size_t count=0, rrsig_count=0, len=0, i, j, total; + uint8_t* nextrdata; + if(!rrset || ldns_rr_list_rr_count(rrset)==0) + return NULL; + /* count sizes */ + for(i=0; ittl = ldns_rr_ttl(ldns_rr_list_rr(rrset, 0)); + data->count = count; + data->rrsig_count = rrsig_count; + data->rr_len = (size_t*)((uint8_t*)data + + sizeof(struct packed_rrset_data)); + data->rr_data = (uint8_t**)&(data->rr_len[total]); + data->rr_ttl = (uint32_t*)&(data->rr_data[total]); + nextrdata = (uint8_t*)&(data->rr_ttl[total]); + + /* fill out len, ttl, fields */ + for(i=0; irr_ttl[i] = ldns_rr_ttl(rr); + if(data->rr_ttl[i] < data->ttl) + data->ttl = data->rr_ttl[i]; + data->rr_len[i] = 0; + for(j=0; jrr_len[i] += ldns_rdf_size(ldns_rr_rdf(rr, j)); + } + + /* fixup rest of ptrs */ + for(i=0; irr_data[i] = nextrdata; + nextrdata += data->rr_len[i]; + } + + /* copy data in there */ + for(i=0; irr_len[i]); + size_t p = sizeof(rdlen); + memmove(data->rr_data[i], &rdlen, p); + for(j=0; jrr_data[i]+p, ldns_rdf_data(rd), + ldns_rdf_size(rd)); + p += ldns_rdf_size(rd); + } + } + + if(data->rrsig_count && data->count == 0) { + data->count = data->rrsig_count; /* rrset type is RRSIG */ + data->rrsig_count = 0; + } + return data; +} + +/** convert i'th rr to ldns_rr */ +static ldns_rr* +torr(struct ub_packed_rrset_key* k, ldns_buffer* buf, size_t i) +{ + struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; + ldns_rr* rr = NULL; + size_t pos = 0; + ldns_status s; + ldns_buffer_clear(buf); + ldns_buffer_write(buf, k->rk.dname, k->rk.dname_len); + if(i < d->count) + ldns_buffer_write(buf, &k->rk.type, sizeof(uint16_t)); + else ldns_buffer_write_u16(buf, LDNS_RR_TYPE_RRSIG); + ldns_buffer_write(buf, &k->rk.rrset_class, sizeof(uint16_t)); + ldns_buffer_write_u32(buf, d->rr_ttl[i]); + ldns_buffer_write(buf, d->rr_data[i], d->rr_len[i]); + ldns_buffer_flip(buf); + s = ldns_wire2rr(&rr, ldns_buffer_begin(buf), ldns_buffer_limit(buf), + &pos, LDNS_SECTION_ANSWER); + if(s == LDNS_STATUS_OK) + return rr; + return NULL; +} + +ldns_rr_list* +packed_rrset_to_rr_list(struct ub_packed_rrset_key* k, ldns_buffer* buf) +{ + struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; + ldns_rr_list* r = ldns_rr_list_new(); + size_t i; + if(!r) + return NULL; + for(i=0; icount+d->rrsig_count; i++) { + ldns_rr* rr = torr(k, buf, i); + if(!rr) { + ldns_rr_list_deep_free(r); + return NULL; + } + if(!ldns_rr_list_push_rr(r, rr)) { + ldns_rr_free(rr); + ldns_rr_list_deep_free(r); + return NULL; + } + } + return r; +} diff --git a/util/data/packed_rrset.h b/util/data/packed_rrset.h index b61f87534..a80ce24cf 100644 --- a/util/data/packed_rrset.h +++ b/util/data/packed_rrset.h @@ -376,4 +376,30 @@ struct ub_packed_rrset_key* packed_rrset_copy_region( struct ub_packed_rrset_key* key, struct regional* region, uint32_t now); +/** + * Create a ub_packed_rrset_key allocated on the heap. + * It therefore does not have the correct ID value, and cannot be used + * inside the cache. It can be used in storage outside of the cache. + * Keys for the cache have to be obtained from alloc.h . + * @param rrset: the ldns rr set. + * @return key allocated or NULL on failure. + */ +struct ub_packed_rrset_key* ub_packed_rrset_heap_key(ldns_rr_list* rrset); + +/** + * Create packed_rrset data on the heap. + * @param rrset: the ldns rr set with the data to copy. + * @return data allocated or NULL on failure. + */ +struct packed_rrset_data* packed_rrset_heap_data(ldns_rr_list* rrset); + +/** + * Convert packed rrset to ldns rr list. + * @param rrset: packed rrset. + * @param buf: scratch buffer. + * @return rr list or NULL on failure. + */ +ldns_rr_list* packed_rrset_to_rr_list(struct ub_packed_rrset_key* rrset, + ldns_buffer* buf); + #endif /* UTIL_DATA_PACKED_RRSET_H */ diff --git a/validator/autotrust.c b/validator/autotrust.c index 07463432b..be22ce72c 100644 --- a/validator/autotrust.c +++ b/validator/autotrust.c @@ -255,6 +255,13 @@ rr_is_dnskey_sep(ldns_rr* rr) return (dnskey_flags(rr)&DNSKEY_BIT_SEP); } +/** Check if REVOKED DNSKEY */ +static int +rr_is_dnskey_revoked(ldns_rr* rr) +{ + return (dnskey_flags(rr)&LDNS_KEY_REVOKE_KEY); +} + /** create ta */ static struct autr_ta* autr_ta_create(ldns_rr* rr) @@ -446,6 +453,7 @@ autr_assemble(struct trust_anchor* tp) { ldns_rr_list* ds, *dnskey; struct autr_ta* ta; + struct ub_packed_rrset_key* ubds=NULL, *ubdnskey=NULL; ds = ldns_rr_list_new(); dnskey = ldns_rr_list_new(); @@ -472,15 +480,44 @@ autr_assemble(struct trust_anchor* tp) /* make packed rrset keys - malloced with no ID number, they * are not in the cache */ - - /* make packed rrset data */ - - /* assign the data to replace the old */ + /* make packed rrset data (if there is a key) */ + if(ds) { + ubds = ub_packed_rrset_heap_key(ds); + if(!ubds) + goto error_cleanup; + ubds->entry.data = packed_rrset_heap_data(ds); + if(!ubds->entry.data) + goto error_cleanup; + } + if(dnskey) { + ubdnskey = ub_packed_rrset_heap_key(dnskey); + if(!ubdnskey) + goto error_cleanup; + ubdnskey->entry.data = packed_rrset_heap_data(dnskey); + if(!ubdnskey->entry.data) { + error_cleanup: + if(ubds) { + free(ubds->rk.dname); + free(ubds->entry.data); + free(ubds); + } + if(ubdnskey) { + free(ubdnskey->rk.dname); + free(ubdnskey->entry.data); + free(ubdnskey); + } + ldns_rr_list_free(ds); + ldns_rr_list_free(dnskey); + return 0; + } + } /* free the old data */ autr_rrset_delete(tp); - tp->ds_rrset = NULL; - tp->dnskey_rrset = NULL; + + /* assign the data to replace the old */ + tp->ds_rrset = ubds; + tp->dnskey_rrset = ubdnskey; ldns_rr_list_free(ds); ldns_rr_list_free(dnskey); @@ -579,13 +616,279 @@ verify_dnskey(struct module_env* env, struct val_env* ve, return 0; } +/** Find minimum expiration interval from signatures */ +static uint32_t +rrsig_min_exp_time(struct module_env* env, ldns_rr_list* rrset) +{ + size_t i; + uint32_t t, r = 15 * 24 * 3600; /* 15 days max */ + for(i=0; i *env->now) { + t = t - *env->now; + if(t < r) + r = t; + } + } + return r; +} + +/** Is rr self-signed revoked key */ +static int +rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve, + struct ub_packed_rrset_key* dnskey_rrset, size_t i) +{ + enum sec_status sec; + sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i); + return (sec == sec_status_secure); +} + +/** Set fetched value */ +static void +seen_trustanchor(struct autr_ta* ta, uint8_t seen) +{ + ta->fetched = seen; + ta->pending_count++; +} + +/** set revoked value */ +static void +seen_revoked_trustanchor(struct autr_ta* ta, uint8_t revoked) +{ + ta->revoked = revoked; +} + +/* Compare two RR buffers, skipping the REVOKED bit */ +static int +ldns_rr_compare_wire_skip_revbit(ldns_buffer* rr1_buf, ldns_buffer* rr2_buf) +{ + size_t rr1_len, rr2_len, min_len, i, offset; + rr1_len = ldns_buffer_capacity(rr1_buf); + rr2_len = ldns_buffer_capacity(rr2_buf); + /* jump past dname (checked in earlier part) and especially past TTL */ + offset = 0; + while (offset < rr1_len && *ldns_buffer_at(rr1_buf, offset) != 0) + offset += *ldns_buffer_at(rr1_buf, offset) + 1; + /* jump to rdata section (PAST the rdata length field */ + offset += 11; + min_len = (rr1_len < rr2_len) ? rr1_len : rr2_len; + /* compare RRs RDATA byte for byte. */ + for(i = offset; i < min_len; i++) + { + uint8_t *rdf1, *rdf2; + rdf1 = ldns_buffer_at(rr1_buf, i); + rdf2 = ldns_buffer_at(rr2_buf, i); + if (i==(offset+1)) + { + /* this is the second part of the flags field */ + *rdf1 = *rdf1 | LDNS_KEY_REVOKE_KEY; + *rdf2 = *rdf2 | LDNS_KEY_REVOKE_KEY; + } + if (*rdf1 < *rdf2) return -1; + else if (*rdf1 > *rdf2) return 1; + } + return 0; +} + +/** Compare two RRs skipping the REVOKED bit */ +static int +ldns_rr_compare_skip_revbit(const ldns_rr* rr1, const ldns_rr* rr2) +{ + int result; + size_t rr1_len, rr2_len; + ldns_buffer* rr1_buf; + ldns_buffer* rr2_buf; + + result = ldns_rr_compare_no_rdata(rr1, rr2); + if (result == 0) + { + rr1_len = ldns_rr_uncompressed_size(rr1); + rr2_len = ldns_rr_uncompressed_size(rr2); + rr1_buf = ldns_buffer_new(rr1_len); + rr2_buf = ldns_buffer_new(rr2_len); + if(!rr1_buf || !rr2_buf) { + ldns_buffer_free(rr1_buf); + ldns_buffer_free(rr2_buf); + return 0; + } + if (ldns_rr2buffer_wire_canonical(rr1_buf, rr1, + LDNS_SECTION_ANY) != LDNS_STATUS_OK) + { + ldns_buffer_free(rr1_buf); + ldns_buffer_free(rr2_buf); + return 0; + } + if (ldns_rr2buffer_wire_canonical(rr2_buf, rr2, + LDNS_SECTION_ANY) != LDNS_STATUS_OK) { + ldns_buffer_free(rr1_buf); + ldns_buffer_free(rr2_buf); + return 0; + } + result = ldns_rr_compare_wire_skip_revbit(rr1_buf, rr2_buf); + ldns_buffer_free(rr1_buf); + ldns_buffer_free(rr2_buf); + } + return result; +} + + +/** compare two trust anchors */ +static int +ta_compare(ldns_rr* a, ldns_rr* b) +{ + int result; + if (!a && !b) result = 0; + else if (!a) result = -1; + else if (!b) result = 1; + else if (ldns_rr_get_type(a) != ldns_rr_get_type(b)) + result = (int)ldns_rr_get_type(a) - (int)ldns_rr_get_type(b); + else if (ldns_rr_get_type(a) == LDNS_RR_TYPE_DNSKEY) + result = ldns_rr_compare_skip_revbit(a, b); + else if (ldns_rr_get_type(a) == LDNS_RR_TYPE_DS) + result = ldns_rr_compare(a, b); + else result = -1; + return result; +} + +/** find key */ +static struct autr_ta* +find_key(struct trust_anchor* tp, ldns_rr* rr) +{ + struct autr_ta* ta; + if(!tp || !rr) + return NULL; + for(ta=tp->autr->keys; ta; ta=ta->next) { + if(ta_compare(ta->rr, rr) == 0) + return ta; + } + return NULL; +} + +/** add key and clone RR and tp already locked */ +static struct autr_ta* +add_key(struct trust_anchor* tp, ldns_rr* rr) +{ + ldns_rr* c; + struct autr_ta* ta; + c = ldns_rr_clone(rr); + if(!c) return NULL; + ta = autr_ta_create(c); + if(!ta) { + ldns_rr_free(c); + return NULL; + } + /* link in, tp already locked */ + ta->next = tp->autr->keys; + tp->autr->keys = ta; + return ta; +} + +/** update the time values for the trustpoint */ +static void +set_tp_times(struct trust_anchor* tp, uint32_t rrsig_exp_interval, + struct ub_packed_rrset_key* k) +{ + struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; + uint32_t origttl = d->ttl; + uint32_t x; + + verbose(VERB_ALGO, "orig_ttl is %d", (int)origttl); + verbose(VERB_ALGO, "rrsig_exp_interval is %d", (int)rrsig_exp_interval); + + /* x = MIN(15days, ttl/2, expire/2) */ + x = 15 * 24 * 3600; + if(origttl/2 < x) + x = origttl/2; + if(rrsig_exp_interval/2 < x) + x = rrsig_exp_interval/2; + /* MAX(1hr, x) */ + if(x < 3600) + tp->autr->query_interval = 3600; + else tp->autr->query_interval = x; + + /* x= MIN(1day, ttl/10, expire/10) */ + x = 24 * 3600; + if(origttl/10 < x) + x = origttl/10; + if(rrsig_exp_interval/10 < x) + x = rrsig_exp_interval/10; + /* MAX(1hr, x) */ + if(x < 3600) + tp->autr->retry_time = 3600; + else tp->autr->retry_time = x; + + verbose(VERB_ALGO, "query_interval: %d, retry_time: %d", + (int)tp->autr->query_interval, (int)tp->autr->retry_time); +} + +/** init events to zero */ +static void +init_events(struct trust_anchor* tp) +{ + struct autr_ta* ta; + for(ta=tp->autr->keys; ta; ta=ta->next) { + ta->fetched = 0; + } +} + +/** Set update events */ +static int +update_events(struct module_env* env, struct val_env* ve, + struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset) +{ + ldns_rr_list* r = packed_rrset_to_rr_list(dnskey_rrset, + env->scratch_buffer); + size_t i; + if(!r) + return 0; + init_events(tp); + for(i=0; i= VERB_ALGO) + verbose(VERB_ALGO, "DNSKEY id=%d is " + "self-signed revoked", (int) + dnskey_calc_keytag(dnskey_rrset, i)); + seen_revoked_trustanchor(ta, 1); + } else { + seen_trustanchor(ta, 1); + if(verbosity >= VERB_ALGO) + verbose(VERB_ALGO, "DNSKEY id=%d in DNS " + "response", (int)ldns_calc_keytag(rr)); + } + } + set_tp_times(tp, rrsig_min_exp_time(env, r), dnskey_rrset); + ldns_rr_list_deep_free(r); + return 1; +} + int autr_process_prime(struct module_env* env, struct val_env* ve, struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset) { struct val_anchors* anchors = env->anchors; log_assert(tp->autr); /* autotrust update trust anchors */ - /* note: tp is locked */ + /* the tp is locked, and stays locked unless it is deleted */ /* query_dnskeys(): */ tp->autr->last_queried = *env->now; @@ -613,7 +916,8 @@ int autr_process_prime(struct module_env* env, struct val_env* ve, * - note revoked (selfsigned) anchors. * Set trustpoint query_interval and retry_time. */ - /* update_events(env, ve, tp, dnskey_rrset); */ + if(!update_events(env, ve, tp, dnskey_rrset)) + return 1; /* trust point unchanged, so exists */ /* do_statetable(): * - for every SEP key do the 5011 statetable. -- 2.47.3