]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Fix 4035 compliance for algorithms from the DS rrset that MUST sign the DNSKEY.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 1 Jul 2010 12:08:48 +0000 (12:08 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 1 Jul 2010 12:08:48 +0000 (12:08 +0000)
git-svn-id: file:///svn/unbound/trunk@2172 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
validator/val_sigcrypt.c
validator/val_sigcrypt.h
validator/val_utils.c

index 268fb31d6e02126fea2d58e77064e756879fc122..20ef8870173b67dd2d1121be96c4689d815f63ad 100644 (file)
@@ -1,3 +1,8 @@
+1 July 2010: Wouter
+       - Fix RFC4035 compliance with 2.2 statement that the DNSKEY at apex
+         must be signed with all algorithms from the DS rrset at the parent.
+         This is now checked and becomes bogus if not.
+
 28 June 2010: Wouter
        - Fix jostle list bug found by Vince (luoce@cnnic), it caused the qps
          in overload situations to be about 5 qps for the class of shortly
index 2a3e8a5ff4e2efb6891d3c0b5c143f1a4b23940f..0b05bbfc360c76dc0a792c35930a6782f988d3a8 100644 (file)
@@ -452,41 +452,75 @@ int dnskey_algo_is_supported(struct ub_packed_rrset_key* dnskey_rrset,
                dnskey_idx));
 }
 
-/**
- * Fillup needed algorithm array for DNSKEY set
- * @param dnskey: the key
- * @param needs: array per algorithm.
- * @return the number of algorithms that need valid signatures
- */
-static size_t
-dnskeyset_needs(struct ub_packed_rrset_key* dnskey, uint8_t needs[])
+void algo_needs_init_dnskey(struct algo_needs* n,
+        struct ub_packed_rrset_key* dnskey)
 {
        uint8_t algo;
        size_t i, total = 0;
        size_t num = rrset_get_count(dnskey);
 
-       memset(needs, 0, sizeof(uint8_t)*256);
+       memset(n->needs, 0, sizeof(uint8_t)*ALGO_NEEDS_MAX);
        for(i=0; i<num; i++) {
                algo = (uint8_t)dnskey_get_algo(dnskey, i);
-               if(needs[algo] == 0) {
-                       needs[algo] = 1;
+               if(n->needs[algo] == 0) {
+                       n->needs[algo] = 1;
                        total++;
                }
        }
-       return total;
+       n->num = total;
+}
+
+void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds,
+       int fav_ds_algo)
+{
+       uint8_t algo;
+       size_t i, total = 0;
+       size_t num = rrset_get_count(ds);
+
+       memset(n->needs, 0, sizeof(uint8_t)*ALGO_NEEDS_MAX);
+       for(i=0; i<num; i++) {
+               if(ds_get_digest_algo(ds, i) != fav_ds_algo)
+                       continue;
+               algo = (uint8_t)ds_get_key_algo(ds, i);
+               if(n->needs[algo] == 0) {
+                       n->needs[algo] = 1;
+                       total++;
+               }
+       }
+       n->num = total;
+}
+
+int algo_needs_set_secure(struct algo_needs* n, uint8_t algo)
+{
+       if(n->needs[algo]) {
+               n->needs[algo] = 0;
+               n->num --;
+               if(n->num == 0) /* done! */
+                       return 1;
+       }
+       return 0;
+}
+
+void algo_needs_set_bogus(struct algo_needs* n, uint8_t algo)
+{
+       if(n->needs[algo]) n->needs[algo] = 2; /* need it, but bogus */
 }
 
-/** see which algo needed */
-static int any_needed_bogus(uint8_t needs[])
+size_t algo_needs_num_missing(struct algo_needs* n)
+{
+       return n->num;
+}
+
+int algo_needs_missing(struct algo_needs* n)
 {
        int i;
        /* first check if a needed algo was bogus - report that */
-       for(i=0; i<256; i++)
-               if(needs[i] == 2)
+       for(i=0; i<ALGO_NEEDS_MAX; i++)
+               if(n->needs[i] == 2)
                        return 0;
        /* now check which algo is missing */
-       for(i=0; i<256; i++)
-               if(needs[i] == 1)
+       for(i=0; i<ALGO_NEEDS_MAX; i++)
+               if(n->needs[i] == 1)
                        return i;
        return 0;
 }
@@ -497,10 +531,10 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
        char** reason)
 {
        enum sec_status sec;
-       size_t i, num, numneeds;
+       size_t i, num;
        rbtree_t* sortree = NULL;
        /* make sure that for all DNSKEY algorithms there are valid sigs */
-       uint8_t needs[256]; /* 1 if need sig for that algorithm */
+       struct algo_needs needs;
        int alg;
 
        num = rrset_get_sigcount(rrset);
@@ -511,41 +545,41 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
                return sec_status_bogus;
        }
 
-       numneeds = dnskeyset_needs(dnskey, needs);
+       algo_needs_init_dnskey(&needs, dnskey);
        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) {
-                       uint8_t a = (uint8_t)rrset_get_sig_algo(rrset, i);
-                       if(needs[a]) {
-                               needs[a] = 0;
-                               numneeds --;
-                               if(numneeds == 0) /* done! */
-                                       return sec;
-                       }
+                       if(algo_needs_set_secure(&needs,
+                               (uint8_t)rrset_get_sig_algo(rrset, i)))
+                               return sec; /* done! */
                } else if(sec == sec_status_bogus) {
-                       uint8_t a = (uint8_t)rrset_get_sig_algo(rrset, i);
-                       if(needs[a]) needs[a] = 2; /* need it, but 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)numneeds);
-       if((alg=any_needed_bogus(needs)) != 0) {
-               char buf[256];
-               ldns_lookup_table *t = ldns_lookup_by_id(ldns_algorithms, alg);
-               if(t&&t->name)
-                       snprintf(buf, sizeof(buf), "no signatures with "
-                               "algorithm %s", t->name);
-               else    snprintf(buf, sizeof(buf), "no signatures with "
-                               "algorithm ALG%u", (unsigned)alg);
-               *reason = regional_strdup(env->scratch, buf);
-               if(!*reason)
-                       *reason = "no signatures for all algorithms";
+               "%d algorithms", (int)algo_needs_num_missing(&needs));
+       if((alg=algo_needs_missing(&needs)) != 0) {
+               algo_needs_reason(env, alg, reason, "no signatures");
        }
        return sec_status_bogus;
 }
 
+void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s)
+{
+       char buf[256];
+       ldns_lookup_table *t = ldns_lookup_by_id(ldns_algorithms, alg);
+       if(t&&t->name)
+               snprintf(buf, sizeof(buf), "%s with algorithm %s", s, t->name);
+       else    snprintf(buf, sizeof(buf), "%s with algorithm ALG%u", s,
+                       (unsigned)alg);
+       *reason = regional_strdup(env->scratch, buf);
+       if(!*reason)
+               *reason = "%s with all algorithms";
+}
+
 enum sec_status 
 dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
         struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
index 95c702a096519ffdcf3cc5369c0f3bbd6ba2c499..3ced1e75a9e8c7f3a84d920c78971b0e4abec2c1 100644 (file)
@@ -50,6 +50,81 @@ struct ub_packed_rrset_key;
 struct rbtree_t;
 struct regional;
 
+/** number of entries in algorithm needs array */
+#define ALGO_NEEDS_MAX 256
+/**
+ * Storage for algorithm needs.  DNSKEY algorithms.
+ */
+struct algo_needs {
+       /** the algorithms (8-bit) with each a number.
+        * 0: not marked.
+        * 1: marked 'necessary but not yet fulfilled'
+        * 2: marked bogus.
+        * Indexed by algorithm number.
+        */
+       uint8_t needs[ALGO_NEEDS_MAX];
+       /** the number of entries in the array that are unfulfilled */
+       size_t num;
+};
+
+/**
+ * Initialize algo needs structure, set algos from rrset as needed.
+ * @param n: struct with storage.
+ * @param dnskey: algos from this struct set as necessary. DNSKEY set.
+ */
+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.
+ * @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.
+ */
+void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds,
+       int fav_ds_algo);
+
+/**
+ * Mark this algorithm as a success, sec_secure, and see if we are done.
+ * @param n: storage structure processed.
+ * @param algo: the algorithm processed to be secure.
+ * @return if true, processing has finished successfully, we are satisfied.
+ */
+int algo_needs_set_secure(struct algo_needs* n, uint8_t algo);
+
+/**
+ * Mark this algorithm a failure, sec_bogus.  It can later be overridden
+ * by a success for this algorithm (with a different signature).
+ * @param n: storage structure processed.
+ * @param algo: the algorithm processed to be bogus.
+ */
+void algo_needs_set_bogus(struct algo_needs* n, uint8_t algo);
+
+/**
+ * See how many algorithms are missing (not bogus or secure, but not processed)
+ * @param n: storage structure processed.
+ * @return number of algorithms missing after processing.
+ */
+size_t algo_needs_num_missing(struct algo_needs* n);
+
+/**
+ * See which algo is missing.
+ * @param n: struct after processing.
+ * @return if 0 an algorithm was bogus, if a number, this algorithm was
+ *   missing.  So on 0, report why that was bogus, on number report a missing
+ *   algorithm.  There could be multiple missing, this reports the first one.
+ */
+int algo_needs_missing(struct algo_needs* n);
+
+/**
+ * Format error reason for algorithm missing.
+ * @param env: module env with scratch for temp storage of string.
+ * @param alg: DNSKEY-algorithm missing.
+ * @param reason: destination.
+ * @param s: string, appended with 'with algorithm ..'.
+ */
+void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s);
+
 /** 
  * Check if dnskey matches a DS digest 
  * Does not check dnskey-keyid footprint, just the digest.
index 298d39c11647da14b70830a3ff0f39a94e28fa34..c915e3dfd81e11004d837d6ab984b120e07b84c8 100644 (file)
@@ -424,7 +424,8 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
                /* If it didn't validate with the DNSKEY, try the next one! */
        }
        if(numchecked == 0)
-               *reason = "no keys have a DS";
+               algo_needs_reason(env, ds_get_key_algo(ds_rrset, ds_idx),
+                       reason, "no keys have a DS");
        else if(numhashok == 0)
                *reason = "DS hash mismatches key";
        else if(!*reason)
@@ -456,7 +457,8 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
 {
        /* as long as this is false, we can consider this DS rrset to be
         * equivalent to no DS rrset. */
-       int has_useful_ds = 0, digest_algo;
+       int has_useful_ds = 0, digest_algo, alg;
+       struct algo_needs needs;
        size_t i, num;
        enum sec_status sec;
 
@@ -470,6 +472,7 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
        }
 
        digest_algo = val_favorite_ds_algo(ds_rrset);
+       algo_needs_init_ds(&needs, ds_rrset, digest_algo);
        num = rrset_get_count(ds_rrset);
        for(i=0; i<num; i++) {
                /* Check to see if we can understand this DS. 
@@ -488,8 +491,14 @@ 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) {
-                       verbose(VERB_ALGO, "DS matched DNSKEY.");
-                       return sec_status_secure;
+                       if(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(sec == sec_status_bogus) {
+                       algo_needs_set_bogus(&needs,
+                               (uint8_t)ds_get_key_algo(ds_rrset, i));
                }
        }
 
@@ -503,6 +512,10 @@ 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((alg=algo_needs_missing(&needs)) != 0) {
+               algo_needs_reason(env, alg, reason, "missing verification of "
+                       "DNSKEY signature");
+       }
        return sec_status_bogus;
 }