autotrust event_update routine.

git-svn-id: file:///svn/unbound/trunk@1766 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2009-08-18 15:36:46 +00:00
parent a8dccbdd40
commit 701b4ccdd6
4 changed files with 488 additions and 8 deletions

View file

@ -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.

View file

@ -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; i<ldns_rr_list_rr_count(rrset); i++) {
ldns_rr* rr = ldns_rr_list_rr(rrset, i);
if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_RRSIG)
rrsig_count++;
else count++;
for(j=0; j<ldns_rr_rd_count(rr); j++)
len += ldns_rdf_size(ldns_rr_rdf(rr, j));
len += 2; /* sizeof the rdlength */
}
/* allocate */
total = count + rrsig_count;
len += sizeof(*data) + total*(sizeof(size_t) + sizeof(uint32_t) +
sizeof(uint8_t*));
data = (struct packed_rrset_data*)calloc(1, len);
if(!data)
return NULL;
/* fill it */
data->ttl = 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; i<total; i++) {
ldns_rr* rr = ldns_rr_list_rr(rrset, i);
data->rr_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; j<ldns_rr_rd_count(rr); j++)
data->rr_len[i] += ldns_rdf_size(ldns_rr_rdf(rr, j));
}
/* fixup rest of ptrs */
for(i=0; i<total; i++) {
data->rr_data[i] = nextrdata;
nextrdata += data->rr_len[i];
}
/* copy data in there */
for(i=0; i<total; i++) {
ldns_rr* rr = ldns_rr_list_rr(rrset, i);
uint16_t rdlen = htons(data->rr_len[i]);
size_t p = sizeof(rdlen);
memmove(data->rr_data[i], &rdlen, p);
for(j=0; j<ldns_rr_rd_count(rr); j++) {
ldns_rdf* rd = ldns_rr_rdf(rr, j);
memmove(data->rr_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; i<d->count+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;
}

View file

@ -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 */

View file

@ -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<ldns_rr_list_rr_count(rrset); i++) {
ldns_rr* rr = ldns_rr_list_rr(rrset, i);
if(ldns_rr_get_type(rr) != LDNS_RR_TYPE_RRSIG)
continue;
t = ldns_rdf2native_int32(ldns_rr_rrsig_expiration(rr));
if(t > *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<ldns_rr_list_rr_count(r); i++) {
ldns_rr* rr = ldns_rr_list_rr(r, i);
struct autr_ta* ta = NULL;
if(ldns_rr_get_type(rr) != LDNS_RR_TYPE_DNSKEY)
continue;
if(!rr_is_dnskey_sep(rr))
continue;
/* is it new? if revocation bit set, find the unrevoked key */
ta = find_key(tp, rr);
if(!ta)
ta = add_key(tp, rr);
if(!ta) {
ldns_rr_list_deep_free(r);
return 0;
}
if(rr_is_dnskey_revoked(rr) &&
rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i)) {
/* checked if there is an rrsig signed by this key. */
log_assert(dnskey_calc_keytag(dnskey_rrset, i) ==
ldns_calc_keytag(rr));
if(verbosity >= 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.