+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
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;
}
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);
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,
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.
/* 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)
{
/* 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;
}
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.
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));
}
}
}
/* 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;
}