Fix 4035 compliance for algorithms from the DS rrset that MUST sign the DNSKEY.

git-svn-id: file:///svn/unbound/trunk@2172 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2010-07-01 12:08:48 +00:00
parent 6351307ab4
commit 518504ff5c
4 changed files with 173 additions and 46 deletions

View 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

View 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;
}
/** see which algo needed */
static int any_needed_bogus(uint8_t needs[])
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 */
}
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,

View 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.

View 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;
}