mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-01-08 15:52:53 -05:00
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:
parent
6351307ab4
commit
518504ff5c
4 changed files with 173 additions and 46 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue