]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
autotrust event_update routine.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 18 Aug 2009 15:36:46 +0000 (15:36 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 18 Aug 2009 15:36:46 +0000 (15:36 +0000)
git-svn-id: file:///svn/unbound/trunk@1766 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
util/data/packed_rrset.c
util/data/packed_rrset.h
validator/autotrust.c

index 79c7168c33ebda54f2715ed75ea57a7bc7b26fb8..4b6f9c86a12c78dccdeeab9a5092e728779e99d9 100644 (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.
index 5925b87577fdae51c49a4692b13ddb0829e79e93..406d0e12027a02af48565951379b17d15fec5508 100644 (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;
+}
index b61f87534d13d107cf249486b365a689d8016722..a80ce24cf87a354cec23d1cb8f085f571c1c5d7f 100644 (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 */
index 07463432b4e0147e505838e1a36db6fe29f80e4c..be22ce72c7bddd391f8dc56b61a5cda01dcfd673 100644 (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.