]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Work on validation of multiple algorithms.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 20 Dec 2010 15:58:12 +0000 (15:58 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 20 Dec 2010 15:58:12 +0000 (15:58 +0000)
git-svn-id: file:///svn/unbound/trunk@2356 be551aaa-1e26-0410-a405-d3ace91eadb9

testcode/unitverify.c
validator/val_kentry.c
validator/val_kentry.h
validator/val_sigcrypt.c
validator/val_sigcrypt.h
validator/val_utils.c
validator/val_utils.h
validator/validator.c

index ba5180e55ae2f995f449713e86a5ef052e112372..2062d67428c90d24dbbcfc5e5722f1ae2365277f 100644 (file)
@@ -148,6 +148,33 @@ should_be_bogus(struct ub_packed_rrset_key* rrset, struct query_info* qinfo)
        return 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;
+}
+
+/** setup sig alg list from dnskey */
+static void
+setup_sigalg(struct ub_packed_rrset_key* dnskey, uint8_t* sigalg)
+{
+       uint8_t a[ALGO_NEEDS_MAX];
+       size_t i, n = 0;
+       memset(a, 0, sizeof(a));
+       for(i=0; i<rrset_get_count(dnskey); i++) {
+               uint8_t algo = dnskey_get_algo(dnskey, i);
+               if(a[algo] == 0) {
+                       a[algo] = 1;
+                       sigalg[n++] = algo;
+               }
+       }
+       sigalg[n] = 0;
+}
+
 /** verify and test one rrset against the key rrset */
 static void
 verifytest_rrset(struct module_env* env, struct val_env* ve, 
@@ -156,12 +183,14 @@ verifytest_rrset(struct module_env* env, struct val_env* ve,
 {
        enum sec_status sec;
        char* reason = NULL;
+       uint8_t sigalg[ALGO_NEEDS_MAX+1];
        if(vsig) {
                log_nametypeclass(VERB_QUERY, "verify of rrset",
                        rrset->rk.dname, ntohs(rrset->rk.type),
                        ntohs(rrset->rk.rrset_class));
        }
-       sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, 1, &reason);
+       setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */
+       sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason);
        if(vsig) {
                printf("verify outcome is: %s %s\n", sec_status_to_string(sec),
                        reason?reason:"");
index d58630fc5ad6f0429d65413b80e86e44a961b808..8d7edc26facf1c869d5a10834465d6bdd940342a 100644 (file)
@@ -58,6 +58,8 @@ key_entry_sizefunc(void* key, void* data)
                s += packed_rrset_sizeof(kd->rrset_data);
        if(kd->reason)
                s += strlen(kd->reason)+1;
+       if(kd->algo)
+               s += strlen((char*)kd->algo)+1;
        return s;
 }
 
@@ -91,6 +93,7 @@ key_entry_deldatafunc(void* data, void* ATTR_UNUSED(userarg))
        struct key_entry_data* kd = (struct key_entry_data*)data;
        free(kd->reason);
        free(kd->rrset_data);
+       free(kd->algo);
        free(kd);
 }
 
@@ -136,6 +139,12 @@ key_entry_copy_toregion(struct key_entry_key* kkey, struct regional* region)
                        if(!newd->reason)
                                return NULL;
                }
+               if(d->algo) {
+                       newd->algo = (uint8_t*)regional_strdup(region,
+                               (char*)d->algo);
+                       if(!newd->algo)
+                               return NULL;
+               }
                newk->entry.data = newd;
        }
        return newk;
@@ -190,6 +199,17 @@ key_entry_copy(struct key_entry_key* kkey)
                                return NULL;
                        }
                }
+               if(d->algo) {
+                       newd->algo = (uint8_t*)strdup((char*)d->algo);
+                       if(!newd->algo) {
+                               free(newd->rrset_data);
+                               free(newd->reason);
+                               free(newd);
+                               free(newk->name);
+                               free(newk);
+                               return NULL;
+                       }
+               }
                newk->entry.data = newd;
        }
        return newk;
@@ -267,13 +287,14 @@ key_entry_create_null(struct regional* region,
        d->reason = NULL;
        d->rrset_type = LDNS_RR_TYPE_DNSKEY;
        d->rrset_data = NULL;
+       d->algo = NULL;
        return k;
 }
 
 struct key_entry_key* 
 key_entry_create_rrset(struct regional* region,
        uint8_t* name, size_t namelen, uint16_t dclass,
-       struct ub_packed_rrset_key* rrset, uint32_t now)
+       struct ub_packed_rrset_key* rrset, uint8_t* sigalg, uint32_t now)
 {
        struct key_entry_key* k;
        struct key_entry_data* d;
@@ -289,6 +310,11 @@ key_entry_create_rrset(struct regional* region,
                rd, packed_rrset_sizeof(rd));
        if(!d->rrset_data)
                return NULL;
+       if(sigalg) {
+               d->algo = (uint8_t*)regional_strdup(region, (char*)sigalg);
+               if(!d->algo)
+                       return NULL;
+       } else d->algo = NULL;
        packed_rrset_ptr_fixup(d->rrset_data);
        return k;
 }
@@ -307,6 +333,7 @@ key_entry_create_bad(struct regional* region,
        d->reason = NULL;
        d->rrset_type = LDNS_RR_TYPE_DNSKEY;
        d->rrset_data = NULL;
+       d->algo = NULL;
        return k;
 }
 
index 07106e0a6173b7ead16afa69c79b4fd6d6588cc1..d14ffe588016ec16e486a78d2e565f8e7fd652bf 100644 (file)
@@ -80,6 +80,8 @@ struct key_entry_data {
        struct packed_rrset_data* rrset_data;
        /** not NULL sometimes to give reason why bogus */
        char* reason;
+       /** list of algorithms signalled, ends with 0, or NULL */
+       uint8_t* algo;
        /** DNS RR type of the rrset data (host order) */
        uint16_t rrset_type;
        /** if the key is bad: Bogus or malformed */
@@ -177,12 +179,13 @@ struct key_entry_key* key_entry_create_null(struct regional* region,
  * @param namelen: length of name
  * @param dclass: class of key entry. (host order);
  * @param rrset: data for key entry. This is copied to the region.
+ * @param sigalg: signalled algorithm list (or NULL).
  * @param now: current time (added to ttl of rrset)
  * @return new key entry or NULL on alloc failure
  */
 struct key_entry_key* key_entry_create_rrset(struct regional* region,
         uint8_t* name, size_t namelen, uint16_t dclass, 
-       struct ub_packed_rrset_key* rrset, uint32_t now);
+       struct ub_packed_rrset_key* rrset, uint8_t* sigalg, uint32_t now);
 
 /**
  * Create a bad entry, in the given region.
index dd3a694b6a995221655054f757d009daee2ae1d6..9a603c708f2f89ab7d3b10cabcb6fa83d9515668 100644 (file)
@@ -473,8 +473,45 @@ void algo_needs_init_dnskey(struct algo_needs* n,
        n->num = total;
 }
 
+void algo_needs_init_dnskey_add(struct algo_needs* n,
+        struct ub_packed_rrset_key* dnskey, uint8_t* sigalg)
+{
+       uint8_t algo;
+       size_t i, total = n->num;
+       size_t num = rrset_get_count(dnskey);
+
+       for(i=0; i<num; i++) {
+               algo = (uint8_t)dnskey_get_algo(dnskey, i);
+               if(!dnskey_algo_id_is_supported((int)algo))
+                       continue;
+               if(n->needs[algo] == 0) {
+                       n->needs[algo] = 1;
+                       sigalg[total] = algo;
+                       total++;
+               }
+       }
+       sigalg[total] = 0;
+       n->num = total;
+}
+
+void algo_needs_init_list(struct algo_needs* n, uint8_t* sigalg)
+{
+       uint8_t algo;
+       size_t total = 0;
+
+       memset(n->needs, 0, sizeof(uint8_t)*ALGO_NEEDS_MAX);
+       while( (algo=*sigalg) != 0) {
+               log_assert(dnskey_algo_id_is_supported((int)algo));
+               log_assert(n->needs[algo] == 0);
+               n->needs[algo] = 1;
+               total++;
+               sigalg++;
+       }
+       n->num = total;
+}
+
 void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds,
-       int fav_ds_algo)
+       int fav_ds_algo, uint8_t* sigalg)
 {
        uint8_t algo;
        size_t i, total = 0;
@@ -487,11 +524,14 @@ void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds,
                algo = (uint8_t)ds_get_key_algo(ds, i);
                if(!dnskey_algo_id_is_supported((int)algo))
                        continue;
+               log_assert(algo != 0); /* we do not support 0 and is EOS */
                if(n->needs[algo] == 0) {
                        n->needs[algo] = 1;
+                       sigalg[total] = algo;           
                        total++;
                }
        }
+       sigalg[total] = 0;
        n->num = total;
 }
 
@@ -533,7 +573,7 @@ int algo_needs_missing(struct algo_needs* n)
 enum sec_status 
 dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
        struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
-       int downprot, char** reason)
+       uint8_t* sigalg, char** reason)
 {
        enum sec_status sec;
        size_t i, num;
@@ -550,30 +590,32 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
                return sec_status_bogus;
        }
 
-       algo_needs_init_dnskey(&needs, dnskey);
-       if(algo_needs_num_missing(&needs) == 0) {
-               verbose(VERB_QUERY, "DNSKEY has no known algorithms");
-               *reason = "DNSKEY has no known algorithms";
-               return sec_status_insecure;
+       if(sigalg) {
+               algo_needs_init_list(&needs, sigalg);
+               if(algo_needs_num_missing(&needs) == 0) {
+                       verbose(VERB_QUERY, "zone has no known algorithms");
+                       *reason = "zone has no known algorithms";
+                       return sec_status_insecure;
+               }
        }
        for(i=0; i<num; i++) {
                sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset, 
                        dnskey, i, &sortree, reason);
                /* see which algorithm has been fixed up */
                if(sec == sec_status_secure) {
-                       if(!downprot)
+                       if(!sigalg)
                                return sec; /* done! */
                        else if(algo_needs_set_secure(&needs,
                                (uint8_t)rrset_get_sig_algo(rrset, i)))
                                return sec; /* done! */
-               } else if(downprot && sec == sec_status_bogus) {
+               } else if(sigalg && sec == sec_status_bogus) {
                        algo_needs_set_bogus(&needs,
                                (uint8_t)rrset_get_sig_algo(rrset, i));
                }
        }
        verbose(VERB_ALGO, "rrset failed to verify: no valid signatures for "
                "%d algorithms", (int)algo_needs_num_missing(&needs));
-       if(downprot && (alg=algo_needs_missing(&needs)) != 0) {
+       if(sigalg && (alg=algo_needs_missing(&needs)) != 0) {
                algo_needs_reason(env, alg, reason, "no signatures");
        }
        return sec_status_bogus;
index d021e8b63115ec252c7ca7d6528e84b2e2d2a9d2..6cef3d81759c2f5eccb836eec9306b18aa6b6292 100644 (file)
@@ -52,6 +52,7 @@ struct regional;
 
 /** number of entries in algorithm needs array */
 #define ALGO_NEEDS_MAX 256
+
 /**
  * Storage for algorithm needs.  DNSKEY algorithms.
  */
@@ -75,14 +76,33 @@ struct algo_needs {
 void algo_needs_init_dnskey(struct algo_needs* n,
        struct ub_packed_rrset_key* dnskey);
 
+/**
+ * Initialize algo needs structure, set algos from rrset as needed.
+ * Results are added to an existing need structure.
+ * @param n: struct with storage.
+ * @param dnskey: algos from this struct set as necessary. DNSKEY set.
+ * @param sigalg: adds to signalled algorithm list too.
+ */
+void algo_needs_init_dnskey_add(struct algo_needs* n,
+       struct ub_packed_rrset_key* dnskey, uint8_t* sigalg);
+
+/**
+ * Initialize algo needs structure from a signalled algo list.
+ * @param n: struct with storage.
+ * @param sigalg: signalled algorithm list, numbers ends with 0.
+ */
+void algo_needs_init_list(struct algo_needs* n, uint8_t* sigalg);
+
 /**
  * Initialize algo needs structure, set algos from rrset as needed.
  * @param n: struct with storage.
  * @param ds: algos from this struct set as necessary. DS set.
  * @param fav_ds_algo: filter to use only this DS algo.
+ * @param sigalg: list of signalled algos, constructed as output,
+ *     provide size ALGO_NEEDS_MAX+1. list of algonumbers, ends with a zero.
  */
 void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds,
-       int fav_ds_algo);
+       int fav_ds_algo, uint8_t* sigalg);
 
 /**
  * Mark this algorithm as a success, sec_secure, and see if we are done.
@@ -221,7 +241,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx);
  * @param ve: validator environment, date settings.
  * @param rrset: to be validated.
  * @param dnskey: DNSKEY rrset, keyset to try.
- * @param downprot: if true provide downgrade protection otherwise one
+ * @param sigalg: if nonNULL provide downgrade protection otherwise one
  *   algorithm is enough.
  * @param reason: if bogus, a string returned, fixed or alloced in scratch.
  * @return SECURE if one key in the set verifies one rrsig.
@@ -230,7 +250,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx);
  */
 enum sec_status dnskeyset_verify_rrset(struct module_env* env, 
        struct val_env* ve, struct ub_packed_rrset_key* rrset, 
-       struct ub_packed_rrset_key* dnskey, int downprot, char** reason);
+       struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason);
 
 /** 
  * verify rrset against one specific dnskey (from rrset) 
index 98ff8a8c6c7ec450b6f5cca9182f1da236d26fc1..928af2b2a4240481d1b1b7278851a297280437ae 100644 (file)
@@ -310,7 +310,7 @@ rrset_get_ttl(struct ub_packed_rrset_key* rrset)
 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,
-       int downprot, char** reason)
+       uint8_t* sigalg, char** reason)
 {
        enum sec_status sec;
        struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
@@ -332,7 +332,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
        }
        log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname,
                ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
-       sec = dnskeyset_verify_rrset(env, ve, rrset, keys, downprot, reason);
+       sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason);
        verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec));
        regional_free_all(env->scratch);
 
@@ -378,7 +378,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve,
        dnskey.rk.dname_len = kkey->namelen;
        dnskey.entry.key = &dnskey;
        dnskey.entry.data = kd->rrset_data;
-       sec = val_verify_rrset(env, ve, rrset, &dnskey, 1, reason);
+       sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason);
        return sec;
 }
 
@@ -453,7 +453,7 @@ int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_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, int downprot, char** reason)
+       struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason)
 {
        /* as long as this is false, we can consider this DS rrset to be
         * equivalent to no DS rrset. */
@@ -472,8 +472,8 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
        }
 
        digest_algo = val_favorite_ds_algo(ds_rrset);
-       if(downprot)
-               algo_needs_init_ds(&needs, ds_rrset, digest_algo);
+       if(sigalg)
+               algo_needs_init_ds(&needs, ds_rrset, digest_algo, sigalg);
        num = rrset_get_count(ds_rrset);
        for(i=0; i<num; i++) {
                /* Check to see if we can understand this DS. 
@@ -492,12 +492,12 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
                sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, 
                        ds_rrset, i, reason);
                if(sec == sec_status_secure) {
-                       if(!downprot || algo_needs_set_secure(&needs,
+                       if(!sigalg || algo_needs_set_secure(&needs,
                                (uint8_t)ds_get_key_algo(ds_rrset, i))) {
                                verbose(VERB_ALGO, "DS matched DNSKEY.");
                                return sec_status_secure;
                        }
-               } else if(downprot && sec == sec_status_bogus) {
+               } else if(sigalg && sec == sec_status_bogus) {
                        algo_needs_set_bogus(&needs,
                                (uint8_t)ds_get_key_algo(ds_rrset, i));
                }
@@ -513,7 +513,7 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
        }
        /* If any were understandable, then it is bad. */
        verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY.");
-       if(downprot && (alg=algo_needs_missing(&needs)) != 0) {
+       if(sigalg && (alg=algo_needs_missing(&needs)) != 0) {
                algo_needs_reason(env, alg, reason, "missing verification of "
                        "DNSKEY signature");
        }
@@ -525,14 +525,15 @@ 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, int downprot, char** reason)
 {
+       uint8_t sigalg[ALGO_NEEDS_MAX+1];
        enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, 
-               dnskey_rrset, ds_rrset, downprot, reason);
+               dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason);
 
        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);
+                       downprot?sigalg:NULL, *env->now);
        } else if(sec == sec_status_insecure) {
                return key_entry_create_null(region, ds_rrset->rk.dname,
                        ds_rrset->rk.dname_len, 
@@ -544,6 +545,147 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env,
                BOGUS_KEY_TTL, *env->now);
 }
 
+enum sec_status 
+val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
+       struct ub_packed_rrset_key* dnskey_rrset,
+       struct ub_packed_rrset_key* ta_ds,
+       struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason)
+{
+       /* as long as this is false, we can consider this DS rrset to be
+        * equivalent to no DS rrset. */
+       int has_useful_ta = 0, digest_algo = 0, alg;
+       struct algo_needs needs;
+       size_t i, num;
+       enum sec_status sec;
+
+       if(ta_ds && (dnskey_rrset->rk.dname_len != ta_ds->rk.dname_len ||
+               query_dname_compare(dnskey_rrset->rk.dname, ta_ds->rk.dname)
+               != 0)) {
+               verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset "
+                       "by name");
+               *reason = "DNSKEY RRset did not match DS RRset by name";
+               return sec_status_bogus;
+       }
+       if(ta_dnskey && (dnskey_rrset->rk.dname_len != ta_dnskey->rk.dname_len
+            || query_dname_compare(dnskey_rrset->rk.dname, ta_dnskey->rk.dname)
+               != 0)) {
+               verbose(VERB_QUERY, "DNSKEY RRset did not match anchor RRset "
+                       "by name");
+               *reason = "DNSKEY RRset did not match anchor RRset by name";
+               return sec_status_bogus;
+       }
+
+       if(ta_ds)
+               digest_algo = val_favorite_ds_algo(ta_ds);
+       if(sigalg) {
+               if(ta_ds)
+                       algo_needs_init_ds(&needs, ta_ds, digest_algo, sigalg);
+               else    memset(&needs, 0, sizeof(needs));
+               if(ta_dnskey)
+                       algo_needs_init_dnskey_add(&needs, ta_dnskey, sigalg);
+       }
+       if(ta_ds) {
+           num = rrset_get_count(ta_ds);
+           for(i=0; i<num; i++) {
+               /* Check to see if we can understand this DS. 
+                * And check it is the strongest digest */
+               if(!ds_digest_algo_is_supported(ta_ds, i) ||
+                       !ds_key_algo_is_supported(ta_ds, i) ||
+                       ds_get_digest_algo(ta_ds, i) != digest_algo) {
+                       continue;
+               }
+
+               /* Once we see a single DS with a known digestID and 
+                * algorithm, we cannot return INSECURE (with a 
+                * "null" KeyEntry). */
+               has_useful_ta = true;
+
+               sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, 
+                       ta_ds, i, reason);
+               if(sec == sec_status_secure) {
+                       if(!sigalg || algo_needs_set_secure(&needs,
+                               (uint8_t)ds_get_key_algo(ta_ds, i))) {
+                               verbose(VERB_ALGO, "DS matched DNSKEY.");
+                               return sec_status_secure;
+                       }
+               } else if(sigalg && sec == sec_status_bogus) {
+                       algo_needs_set_bogus(&needs,
+                               (uint8_t)ds_get_key_algo(ta_ds, i));
+               }
+           }
+       }
+
+       /* None of the DS's worked out: check the DNSKEYs. */
+       if(ta_dnskey) {
+           num = rrset_get_count(ta_dnskey);
+           for(i=0; i<num; i++) {
+               /* Check to see if we can understand this DNSKEY */
+               if(!dnskey_algo_is_supported(ta_dnskey, i)) {
+                       continue;
+               }
+
+               /* we saw a useful TA */
+               has_useful_ta = true;
+
+               sec = dnskey_verify_rrset(env, ve, dnskey_rrset,
+                       ta_dnskey, i, reason);
+               if(sec == sec_status_secure) {
+                       if(!sigalg || algo_needs_set_secure(&needs,
+                               (uint8_t)dnskey_get_algo(ta_dnskey, i))) {
+                               verbose(VERB_ALGO, "DS matched DNSKEY.");
+                               return sec_status_secure;
+                       }
+               } else if(sigalg && sec == sec_status_bogus) {
+                       algo_needs_set_bogus(&needs,
+                               (uint8_t)dnskey_get_algo(ta_dnskey, i));
+               }
+           }
+       }
+
+
+       /* If no DSs were understandable, then this is OK. */
+       if(!has_useful_ta) {
+               verbose(VERB_ALGO, "No usable trust anchors 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 anchor to a DNSKEY.");
+       if(sigalg && (alg=algo_needs_missing(&needs)) != 0) {
+               algo_needs_reason(env, alg, reason, "missing verification of "
+                       "DNSKEY signature");
+       }
+       return sec_status_bogus;
+}
+
+struct key_entry_key* 
+val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env, 
+       struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, 
+       struct ub_packed_rrset_key* ta_ds_rrset,
+       struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot,
+       char** reason)
+{
+       uint8_t sigalg[ALGO_NEEDS_MAX+1];
+       enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, 
+               dnskey_rrset, ta_ds_rrset, ta_dnskey_rrset,
+               downprot?sigalg:NULL, reason);
+
+       if(sec == sec_status_secure) {
+               return key_entry_create_rrset(region, 
+                       dnskey_rrset->rk.dname, dnskey_rrset->rk.dname_len,
+                       ntohs(dnskey_rrset->rk.rrset_class), dnskey_rrset,
+                       downprot?sigalg:NULL, *env->now);
+       } else if(sec == sec_status_insecure) {
+               return key_entry_create_null(region, dnskey_rrset->rk.dname,
+                       dnskey_rrset->rk.dname_len, 
+                       ntohs(dnskey_rrset->rk.rrset_class),
+                       rrset_get_ttl(dnskey_rrset), *env->now);
+       }
+       return key_entry_create_bad(region, dnskey_rrset->rk.dname,
+               dnskey_rrset->rk.dname_len, ntohs(dnskey_rrset->rk.rrset_class),
+               BOGUS_KEY_TTL, *env->now);
+}
+
 int 
 val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset)
 {
index c78ec92b13397d06ceff9163a6a40dd63f833468..c38b82ce9518470acc102a33cf3afd03a83daf9f 100644 (file)
@@ -117,14 +117,14 @@ void val_find_signer(enum val_classification subtype,
  * @param ve: validator environment (verification settings)
  * @param rrset: what to verify
  * @param keys: dnskey rrset to verify with.
- * @param downprot: if true provide downgrade protection otherwise one
- *   algorithm is enough.
+ * @param sigalg: if nonNULL provide downgrade protection otherwise one
+ *   algorithm is enough.  Algo list is constructed in here.
  * @param reason: reason of failure. Fixed string or alloced in scratch.
  * @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,
-       int downprot, char** reason);
+       uint8_t* sigalg, char** reason);
 
 /**
  * Verify RRset with keys from a keyset.
@@ -146,8 +146,9 @@ enum sec_status val_verify_rrset_entry(struct module_env* env,
  * @param ve: validator environment (verification settings)
  * @param dnskey_rrset: DNSKEY rrset to verify
  * @param ds_rrset: DS rrset to verify with.
- * @param downprot: if true provide downgrade protection otherwise one
- *   algorithm is enough.
+ * @param sigalg: if nonNULL provide downgrade protection otherwise one
+ *   algorithm is enough.  The list of signalled algorithms is returned,
+ *   must have enough space for ALGO_NEEDS_MAX+1.
  * @param reason: reason of failure. Fixed string or alloced in scratch.
  * @return: sec_status_secure if a DS matches.
  *     sec_status_insecure if end of trust (i.e., unknown algorithms).
@@ -155,7 +156,7 @@ enum sec_status val_verify_rrset_entry(struct module_env* env,
  */
 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, int downprot, char** reason);
+       struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason);
 
 /**
  * Verify new DNSKEYs with DS rrset. The DS contains hash values that should
@@ -178,12 +179,43 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env,
  *         generally only occur in a private algorithm scenario: normally
  *         this sort of thing is checked before fetching the matching DNSKEY
  *         rrset.
+ *         if downprot is set, a key entry with an algo list is made.
  */
 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, int downprot, char** reason);
 
+
+/**
+ * Verify rrset with trust anchor: DS and DNSKEY rrset.
+ *
+ * @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 ta_ds_rrset: DS rrset to verify with.
+ * @param ta_dnskey_rrset: the DNSKEY rrset to verify with.
+ * @param downprot: if true provide downgrade protection otherwise one
+ *   algorithm is enough.
+ * @param reason: reason of failure. Fixed string or alloced in scratch.
+ * @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.
+ *         if downprot is set, a key entry with an algo list is made.
+ */
+struct key_entry_key* val_verify_new_DNSKEYs_with_ta(struct regional* region, 
+       struct module_env* env, struct val_env* ve, 
+       struct ub_packed_rrset_key* dnskey_rrset, 
+       struct ub_packed_rrset_key* ta_ds_rrset, 
+       struct ub_packed_rrset_key* ta_dnskey_rrset,
+       int downprot, char** reason);
+
 /**
  * Determine if DS rrset is usable for validator or not.
  * Returns true if the algorithms for key and DShash are supported,
index cfce63c8ec005972610853a10266f120f9c06885..c907574d94c079f0c9c6e818c0ed224b56ee06ea 100644 (file)
@@ -49,6 +49,7 @@
 #include "validator/val_nsec.h"
 #include "validator/val_nsec3.h"
 #include "validator/val_neg.h"
+#include "validator/val_sigcrypt.h"
 #include "validator/autotrust.h"
 #include "services/cache/dns.h"
 #include "util/data/dname.h"
@@ -2260,35 +2261,18 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
                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, 0, &reason);
-               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;
-               verbose(VERB_DETAIL, "validate keys with anchor(DS): %s", 
-                       sec_status_to_string(sec));
-       }
-       if(sec != sec_status_secure && ta->dnskey_rrset) {
-               sec = val_verify_rrset(qstate->env, ve, dnskey_rrset,
-                       ta->dnskey_rrset, 0, &reason);
-               verbose(VERB_DETAIL, "validate keys with anchor(DNSKEY): %s", 
-                       sec_status_to_string(sec));
-               if(sec == sec_status_secure) {
-                       kkey = key_entry_create_rrset(qstate->region, 
-                               ta->name, ta->namelen, ta->dclass, 
-                               dnskey_rrset, *qstate->env->now);
-                       if(!kkey) {
-                               log_err("out of memory: allocate primed key");
-                               return NULL;
-                       }
-               }
+       kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve, 
+               dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, 0, &reason);
+       if(!kkey) {
+               log_err("out of memory: verifying prime TA");
+               return NULL;
        }
+       if(key_entry_isgood(kkey))
+               sec = sec_status_secure;
+       else
+               sec = sec_status_bogus;
+       verbose(VERB_DETAIL, "validate keys with anchor(DS): %s", 
+               sec_status_to_string(sec));
 
        if(sec != sec_status_secure) {
                log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- "
@@ -2390,7 +2374,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
                log_query_info(VERB_DETAIL, "validated DS", qinfo);
                *ke = key_entry_create_rrset(qstate->region,
                        qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
-                       *qstate->env->now);
+                       NULL, *qstate->env->now);
                return (*ke) != NULL;
        } else if(subtype == VAL_CLASS_NODATA || 
                subtype == VAL_CLASS_NAMEERROR) {