]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
autotrust
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 17 Aug 2009 15:58:27 +0000 (15:58 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 17 Aug 2009 15:58:27 +0000 (15:58 +0000)
git-svn-id: file:///svn/unbound/trunk@1765 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
validator/autotrust.c
validator/val_anchor.c
validator/val_utils.c
validator/val_utils.h

index 73eb08638d2d61721e54c958d9426899e0ed2a6d..79c7168c33ebda54f2715ed75ea57a7bc7b26fb8 100644 (file)
@@ -1,6 +1,7 @@
 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.
+       - autotrust work, split up DS verification of DNSKEYs.
 
 14 August 2009: Wouter
        - unbound-control lookup prints out infra cache information, like RTT.
index fc2dc3431b19046ee01985147f0413a4d65071bb..07463432b4e0147e505838e1a36db6fe29f80e4c 100644 (file)
 #include "config.h"
 #include "validator/autotrust.h"
 #include "validator/val_anchor.h"
+#include "validator/val_utils.h"
+#include "validator/val_sigcrypt.h"
 #include "util/data/dname.h"
+#include "util/data/packed_rrset.h"
 #include "util/log.h"
 #include "util/module.h"
 #include "util/net_help.h"
@@ -211,6 +214,7 @@ parse_comments(char* str, struct autr_ta* ta)
                        "considered NOW", str, ldns_calc_keytag(ta->rr));
                free(str);
                */
+               /* cannot use event base timeptr, because not inited yet */
                ta->last_change = (uint32_t)time(NULL);
         }
         else
@@ -297,6 +301,23 @@ autr_tp_create(struct val_anchors* anchors, ldns_rr* rr)
        return tp;
 }
 
+/** delete assembled rrsets */
+static void
+autr_rrset_delete(struct trust_anchor* tp)
+{
+       if(tp->ds_rrset) {
+               free(tp->ds_rrset->rk.dname);
+               free(tp->ds_rrset->entry.data);
+               free(tp->ds_rrset);
+       }
+       if(tp->dnskey_rrset) {
+               free(tp->dnskey_rrset->rk.dname);
+               free(tp->dnskey_rrset->entry.data);
+               free(tp->dnskey_rrset);
+       }
+}
+
+
 void autr_point_delete(struct trust_anchor* tp)
 {
        if(!tp)
@@ -304,6 +325,7 @@ void autr_point_delete(struct trust_anchor* tp)
        lock_unprotect(&tp->lock, tp);
        lock_unprotect(&tp->lock, tp->autr);
        lock_basic_destroy(&tp->lock);
+       autr_rrset_delete(tp);
        free(tp->autr);
        free(tp->name);
        free(tp);
@@ -379,9 +401,9 @@ add_trustanchor_frm_str(struct val_anchors* anchors, char* str,
  * @param anchors: all points.
  * @param str: comments line
  * @param fname: filename
- * @return false on failure.
+ * @return false on failure, otherwise the tp read.
  */
-static int
+static struct trust_anchor*
 load_trustanchor(struct val_anchors* anchors, char* str, const char* fname)
 {
         struct autr_ta* ta = NULL;
@@ -389,11 +411,11 @@ load_trustanchor(struct val_anchors* anchors, char* str, const char* fname)
 
         ta = add_trustanchor_frm_str(anchors, str, &tp);
        if(!ta)
-               return 0;
+               return NULL;
        lock_basic_lock(&tp->lock);
        if(!parse_comments(str, ta)) {
                lock_basic_unlock(&tp->lock);
-               return 0;
+               return NULL;
        }
        if (rr_is_dnskey_sep(ta->rr)) {
                if (ta->s == AUTR_STATE_VALID)
@@ -406,11 +428,63 @@ load_trustanchor(struct val_anchors* anchors, char* str, const char* fname)
                tp->autr->file = strdup(fname);
                if(!tp->autr->file) {
                        lock_basic_unlock(&tp->lock);
-                       return 0;
+                       return NULL;
                }
        }
        lock_basic_unlock(&tp->lock);
-        return 1;
+        return tp;
+}
+
+/**
+ * Assemble the trust anchors into DS and DNSKEY packed rrsets.
+ * Read the ldns_rrs and builds packed rrsets
+ * @param tp: the trust point. Must be locked.
+ * @return false on malloc failure.
+ */
+static int 
+autr_assemble(struct trust_anchor* tp)
+{
+       ldns_rr_list* ds, *dnskey;
+       struct autr_ta* ta;
+
+       ds = ldns_rr_list_new();
+       dnskey = ldns_rr_list_new();
+       if(!ds || !dnskey) {
+               ldns_rr_list_free(ds);
+               ldns_rr_list_free(dnskey);
+               return 0;
+       }
+       for(ta = tp->autr->keys; ta; ta = ta->next) {
+               if(ldns_rr_get_type(ta->rr) == LDNS_RR_TYPE_DS) {
+                       if(!ldns_rr_list_push_rr(ds, ta->rr)) {
+                               ldns_rr_list_free(ds);
+                               ldns_rr_list_free(dnskey);
+                               return 0;
+                       }
+               } else {
+                       if(!ldns_rr_list_push_rr(dnskey, ta->rr)) {
+                               ldns_rr_list_free(ds);
+                               ldns_rr_list_free(dnskey);
+                               return 0;
+                       }
+               }
+       }
+
+       /* 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 */
+
+       /* free the old data */
+       autr_rrset_delete(tp);
+       tp->ds_rrset = NULL;
+       tp->dnskey_rrset = NULL;
+
+       ldns_rr_list_free(ds);
+       ldns_rr_list_free(dnskey);
+       return 1;
 }
 
 int autr_read_file(struct val_anchors* anchors, const char* nm)
@@ -421,6 +495,8 @@ int autr_read_file(struct val_anchors* anchors, const char* nm)
         int line_nr = 0;
         /* single line */
         char line[10240];
+       /* trust point being read */
+       struct trust_anchor *tp = NULL, *tp2;
 
         if (!(fd = fopen(nm, "r"))) {
                 log_err("unable to open %s for reading: %s", 
@@ -434,14 +510,30 @@ int autr_read_file(struct val_anchors* anchors, const char* nm)
                 line_nr++;
                if (!str_contains_data(line, ';'))
                        continue; /* empty lines allowed */
-                if (!load_trustanchor(anchors, line, nm)) {
+                if (!(tp2=load_trustanchor(anchors, line, nm))) {
                         log_err("failed to load trust anchor from %s "
                                "at line %i, skipping", nm, line_nr);
                         /* try to do the rest */
+                       continue;
                 }
+               if(tp && tp != tp2) {
+                       log_err("file %s has mismatching data inside", nm);
+                       fclose(fd);
+                       return 0;
+               }
+               tp = tp2;
         }
-
         fclose(fd);
+       if(!tp) {
+               log_err("failed to read %s", nm);
+               return 0;
+       }
+
+       /* now assemble the data into DNSKEY and DS packed rrsets */
+       lock_basic_lock(&tp->lock);
+       autr_assemble(tp);
+       lock_basic_unlock(&tp->lock);
+
        return 1;
 }
 
@@ -453,11 +545,83 @@ void autr_write_file(struct trust_anchor* tp)
        /* write anchors */
 }
 
+/** verify if dnskey works for trust point 
+ * @param env: environment (with time) for verification
+ * @param ve: validator environment (with options) for verification.
+ * @param tp: trust point to verify with
+ * @param rrset: DNSKEY rrset to verify.
+ * @return false on failure, true if verification successful.
+ */
+static int
+verify_dnskey(struct module_env* env, struct val_env* ve,
+        struct trust_anchor* tp, struct ub_packed_rrset_key* rrset)
+{
+       if(tp->ds_rrset) {
+               /* verify with ds, any will do to prime autotrust */
+               enum sec_status sec = val_verify_DNSKEY_with_DS(
+                       env, ve, rrset, tp->ds_rrset);
+               verbose(VERB_ALGO, "autotrust: validate DNSKEY with DS: %s",
+                       sec_status_to_string(sec));
+               if(sec == sec_status_secure) {
+                       return 1;
+               }
+       }
+       if(tp->dnskey_rrset) {
+               /* verify with keys */
+               enum sec_status sec = val_verify_rrset(env, ve, rrset,
+                       tp->dnskey_rrset);
+               verbose(VERB_ALGO, "autotrust: DNSKEY is %s",
+                       sec_status_to_string(sec));
+               if(sec == sec_status_secure) {
+                       return 1;
+               }
+       }
+       return 0;
+}
+
 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 */
+
+       /* query_dnskeys(): */
+       tp->autr->last_queried = *env->now;
+
+       log_nametypeclass(VERB_ALGO, "autotrust process for",
+               tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass);
+       if(!dnskey_rrset) {
+               verbose(VERB_ALGO, "autotrust: no dnskey rrset");
+               tp->autr->query_failed += 1;
+               return 1; /* trust point exists */
+       }
+       /* verify the dnskey rrset and see if it is valid. */
+       if(!verify_dnskey(env, ve, tp, dnskey_rrset)) {
+               verbose(VERB_ALGO, "autotrust: dnskey did not verify.");
+               tp->autr->query_failed += 1;
+               return 1; /* trust point exists */
+       }
+
+       tp->autr->query_failed = 0;
+
+       /* update_events(): 
+        * - find minimum rrsig expiration interval
+        * - add new trust anchors to the data structure
+        * - note which trust anchors are seen this probe.
+        * - note revoked (selfsigned) anchors.
+        * Set trustpoint query_interval and retry_time.
+        */
+       /* update_events(env, ve, tp, dnskey_rrset); */
+
+       /* do_statetable(): 
+        * - for every SEP key do the 5011 statetable.
+        * - remove missing trustanchors (if too many).
+        */
+       /* do_statetable(env, tp); */
+
+       autr_assemble(tp);
 
        return 1;
 }
index ce8d1b86259e32fb9919f7c7cee9d455cb131ffe..67bde196454ed308d6f6c1aabea69003d5566a81 100644 (file)
@@ -126,6 +126,8 @@ init_parents(struct val_anchors* anchors)
 {
        struct trust_anchor* node, *prev = NULL, *p;
        int m; 
+       /* nobody else can grab locks because we hold the main lock.
+        * Thus the previous items, after unlocked, are not deleted */
        lock_basic_lock(&anchors->lock);
        RBTREE_FOR(node, struct trust_anchor*, anchors->tree) {
                lock_basic_lock(&node->lock);
@@ -1057,6 +1059,8 @@ anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg)
                anchors->dlv_anchor = dlva;
                lock_basic_unlock(&anchors->lock);
        }
+       /* do autr last, so that it sees what anchors are filled by other
+        * means can can print errors about double config for the name */
        for(f = cfg->auto_trust_anchor_file_list; f; f = f->next) {
                if(!f->str || f->str[0] == 0) /* empty "" */
                        continue;
index 705e828f4ef642a1657ad180c471f5025b8d7074..32962630ed1872abb73a56bd43c1ad3fabfbe94d 100644 (file)
@@ -416,9 +416,9 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
        return sec_status_bogus;
 }
 
-struct key_entry_key* 
-val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, 
-       struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, 
+enum sec_status 
+val_verify_DNSKEY_with_DS(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
@@ -433,13 +433,11 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env,
                != 0) {
                verbose(VERB_QUERY, "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));
+               return sec_status_bogus;
        }
 
        num = rrset_get_count(ds_rrset);
-       /* find favority algo, for now, highest number supported */
+       /* find favorite algo, for now, highest number supported */
        for(i=0; i<num; i++) {
                if(!ds_digest_algo_is_supported(ds_rrset, i) ||
                        !ds_key_algo_is_supported(ds_rrset, i)) {
@@ -467,10 +465,7 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env,
                        ds_rrset, i);
                if(sec == sec_status_secure) {
                        verbose(VERB_ALGO, "DS matched DNSKEY.");
-                       return key_entry_create_rrset(region, 
-                               ds_rrset->rk.dname, ds_rrset->rk.dname_len,
-                               ntohs(ds_rrset->rk.rrset_class), dnskey_rrset,
-                               *env->now);
+                       return sec_status_secure;
                }
        }
 
@@ -480,13 +475,32 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env,
        if(!has_useful_ds) {
                verbose(VERB_ALGO, "No usable DS records were found -- "
                        "treating as insecure.");
+               return sec_status_insecure;
+       }
+       /* If any were understandable, then it is bad. */
+       verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY.");
+       return sec_status_bogus;
+}
+
+struct key_entry_key* 
+val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, 
+       struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, 
+       struct ub_packed_rrset_key* ds_rrset)
+{
+       enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, 
+               dnskey_rrset, ds_rrset);
+
+       if(sec == sec_status_secure) {
+               return key_entry_create_rrset(region, 
+                       ds_rrset->rk.dname, ds_rrset->rk.dname_len,
+                       ntohs(ds_rrset->rk.rrset_class), dnskey_rrset,
+                       *env->now);
+       } else if(sec == sec_status_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), *env->now);
-       }
-       /* If any were understandable, then it is bad. */
-       verbose(VERB_QUERY, "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));
 }
index 027cb806a7dd81d4a077fa0338fd181ed4e1c076..a1a2e25a3e37317ec952026b37bce0353aefec74 100644 (file)
@@ -133,6 +133,21 @@ enum sec_status val_verify_rrset_entry(struct module_env* env,
        struct val_env* ve, struct ub_packed_rrset_key* rrset, 
        struct key_entry_key* kkey);
 
+/**
+ * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but
+ * returns a sec_status instead of a key_entry.
+ * @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: sec_status_secure if a DS matches.
+ *     sec_status_insecure if end of trust (i.e., unknown algorithms).
+ *     sec_status_bogus if it fails.
+ */
+enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, 
+       struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, 
+       struct ub_packed_rrset_key* ds_rrset);
+
 /**
  * Verify new DNSKEYs with DS rrset. The DS contains hash values that should
  * match the DNSKEY keys.