Work on validation of multiple algorithms.

git-svn-id: file:///svn/unbound/trunk@2356 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2010-12-20 15:58:12 +00:00
parent c4c8a65ff2
commit e9582487d9
8 changed files with 341 additions and 62 deletions

View file

@ -148,6 +148,33 @@ should_be_bogus(struct ub_packed_rrset_key* rrset, struct query_info* qinfo)
return 0; 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 */ /** verify and test one rrset against the key rrset */
static void static void
verifytest_rrset(struct module_env* env, struct val_env* ve, 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; enum sec_status sec;
char* reason = NULL; char* reason = NULL;
uint8_t sigalg[ALGO_NEEDS_MAX+1];
if(vsig) { if(vsig) {
log_nametypeclass(VERB_QUERY, "verify of rrset", log_nametypeclass(VERB_QUERY, "verify of rrset",
rrset->rk.dname, ntohs(rrset->rk.type), rrset->rk.dname, ntohs(rrset->rk.type),
ntohs(rrset->rk.rrset_class)); 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) { if(vsig) {
printf("verify outcome is: %s %s\n", sec_status_to_string(sec), printf("verify outcome is: %s %s\n", sec_status_to_string(sec),
reason?reason:""); reason?reason:"");

View file

@ -58,6 +58,8 @@ key_entry_sizefunc(void* key, void* data)
s += packed_rrset_sizeof(kd->rrset_data); s += packed_rrset_sizeof(kd->rrset_data);
if(kd->reason) if(kd->reason)
s += strlen(kd->reason)+1; s += strlen(kd->reason)+1;
if(kd->algo)
s += strlen((char*)kd->algo)+1;
return s; 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; struct key_entry_data* kd = (struct key_entry_data*)data;
free(kd->reason); free(kd->reason);
free(kd->rrset_data); free(kd->rrset_data);
free(kd->algo);
free(kd); free(kd);
} }
@ -136,6 +139,12 @@ key_entry_copy_toregion(struct key_entry_key* kkey, struct regional* region)
if(!newd->reason) if(!newd->reason)
return NULL; return NULL;
} }
if(d->algo) {
newd->algo = (uint8_t*)regional_strdup(region,
(char*)d->algo);
if(!newd->algo)
return NULL;
}
newk->entry.data = newd; newk->entry.data = newd;
} }
return newk; return newk;
@ -190,6 +199,17 @@ key_entry_copy(struct key_entry_key* kkey)
return NULL; 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; newk->entry.data = newd;
} }
return newk; return newk;
@ -267,13 +287,14 @@ key_entry_create_null(struct regional* region,
d->reason = NULL; d->reason = NULL;
d->rrset_type = LDNS_RR_TYPE_DNSKEY; d->rrset_type = LDNS_RR_TYPE_DNSKEY;
d->rrset_data = NULL; d->rrset_data = NULL;
d->algo = NULL;
return k; return k;
} }
struct key_entry_key* struct key_entry_key*
key_entry_create_rrset(struct regional* region, key_entry_create_rrset(struct regional* region,
uint8_t* name, size_t namelen, uint16_t dclass, 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_key* k;
struct key_entry_data* d; struct key_entry_data* d;
@ -289,6 +310,11 @@ key_entry_create_rrset(struct regional* region,
rd, packed_rrset_sizeof(rd)); rd, packed_rrset_sizeof(rd));
if(!d->rrset_data) if(!d->rrset_data)
return NULL; 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); packed_rrset_ptr_fixup(d->rrset_data);
return k; return k;
} }
@ -307,6 +333,7 @@ key_entry_create_bad(struct regional* region,
d->reason = NULL; d->reason = NULL;
d->rrset_type = LDNS_RR_TYPE_DNSKEY; d->rrset_type = LDNS_RR_TYPE_DNSKEY;
d->rrset_data = NULL; d->rrset_data = NULL;
d->algo = NULL;
return k; return k;
} }

View file

@ -80,6 +80,8 @@ struct key_entry_data {
struct packed_rrset_data* rrset_data; struct packed_rrset_data* rrset_data;
/** not NULL sometimes to give reason why bogus */ /** not NULL sometimes to give reason why bogus */
char* reason; char* reason;
/** list of algorithms signalled, ends with 0, or NULL */
uint8_t* algo;
/** DNS RR type of the rrset data (host order) */ /** DNS RR type of the rrset data (host order) */
uint16_t rrset_type; uint16_t rrset_type;
/** if the key is bad: Bogus or malformed */ /** 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 namelen: length of name
* @param dclass: class of key entry. (host order); * @param dclass: class of key entry. (host order);
* @param rrset: data for key entry. This is copied to the region. * @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) * @param now: current time (added to ttl of rrset)
* @return new key entry or NULL on alloc failure * @return new key entry or NULL on alloc failure
*/ */
struct key_entry_key* key_entry_create_rrset(struct regional* region, struct key_entry_key* key_entry_create_rrset(struct regional* region,
uint8_t* name, size_t namelen, uint16_t dclass, 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. * Create a bad entry, in the given region.

View file

@ -473,8 +473,45 @@ void algo_needs_init_dnskey(struct algo_needs* n,
n->num = total; 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, 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; uint8_t algo;
size_t i, total = 0; 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); algo = (uint8_t)ds_get_key_algo(ds, i);
if(!dnskey_algo_id_is_supported((int)algo)) if(!dnskey_algo_id_is_supported((int)algo))
continue; continue;
log_assert(algo != 0); /* we do not support 0 and is EOS */
if(n->needs[algo] == 0) { if(n->needs[algo] == 0) {
n->needs[algo] = 1; n->needs[algo] = 1;
sigalg[total] = algo;
total++; total++;
} }
} }
sigalg[total] = 0;
n->num = total; n->num = total;
} }
@ -533,7 +573,7 @@ int algo_needs_missing(struct algo_needs* n)
enum sec_status enum sec_status
dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, 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; enum sec_status sec;
size_t i, num; size_t i, num;
@ -550,30 +590,32 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
return sec_status_bogus; return sec_status_bogus;
} }
algo_needs_init_dnskey(&needs, dnskey); if(sigalg) {
if(algo_needs_num_missing(&needs) == 0) { algo_needs_init_list(&needs, sigalg);
verbose(VERB_QUERY, "DNSKEY has no known algorithms"); if(algo_needs_num_missing(&needs) == 0) {
*reason = "DNSKEY has no known algorithms"; verbose(VERB_QUERY, "zone has no known algorithms");
return sec_status_insecure; *reason = "zone has no known algorithms";
return sec_status_insecure;
}
} }
for(i=0; i<num; i++) { for(i=0; i<num; i++) {
sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset, sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset,
dnskey, i, &sortree, reason); dnskey, i, &sortree, reason);
/* see which algorithm has been fixed up */ /* see which algorithm has been fixed up */
if(sec == sec_status_secure) { if(sec == sec_status_secure) {
if(!downprot) if(!sigalg)
return sec; /* done! */ return sec; /* done! */
else if(algo_needs_set_secure(&needs, else if(algo_needs_set_secure(&needs,
(uint8_t)rrset_get_sig_algo(rrset, i))) (uint8_t)rrset_get_sig_algo(rrset, i)))
return sec; /* done! */ return sec; /* done! */
} else if(downprot && sec == sec_status_bogus) { } else if(sigalg && sec == sec_status_bogus) {
algo_needs_set_bogus(&needs, algo_needs_set_bogus(&needs,
(uint8_t)rrset_get_sig_algo(rrset, i)); (uint8_t)rrset_get_sig_algo(rrset, i));
} }
} }
verbose(VERB_ALGO, "rrset failed to verify: no valid signatures for " verbose(VERB_ALGO, "rrset failed to verify: no valid signatures for "
"%d algorithms", (int)algo_needs_num_missing(&needs)); "%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"); algo_needs_reason(env, alg, reason, "no signatures");
} }
return sec_status_bogus; return sec_status_bogus;

View file

@ -52,6 +52,7 @@ struct regional;
/** number of entries in algorithm needs array */ /** number of entries in algorithm needs array */
#define ALGO_NEEDS_MAX 256 #define ALGO_NEEDS_MAX 256
/** /**
* Storage for algorithm needs. DNSKEY algorithms. * Storage for algorithm needs. DNSKEY algorithms.
*/ */
@ -75,14 +76,33 @@ struct algo_needs {
void algo_needs_init_dnskey(struct algo_needs* n, void algo_needs_init_dnskey(struct algo_needs* n,
struct ub_packed_rrset_key* dnskey); 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. * Initialize algo needs structure, set algos from rrset as needed.
* @param n: struct with storage. * @param n: struct with storage.
* @param ds: algos from this struct set as necessary. DS set. * @param ds: algos from this struct set as necessary. DS set.
* @param fav_ds_algo: filter to use only this DS algo. * @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, 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. * 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 ve: validator environment, date settings.
* @param rrset: to be validated. * @param rrset: to be validated.
* @param dnskey: DNSKEY rrset, keyset to try. * @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. * algorithm is enough.
* @param reason: if bogus, a string returned, fixed or alloced in scratch. * @param reason: if bogus, a string returned, fixed or alloced in scratch.
* @return SECURE if one key in the set verifies one rrsig. * @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, enum sec_status dnskeyset_verify_rrset(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* rrset, 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) * verify rrset against one specific dnskey (from rrset)

View file

@ -310,7 +310,7 @@ rrset_get_ttl(struct ub_packed_rrset_key* rrset)
enum sec_status enum sec_status
val_verify_rrset(struct module_env* env, struct val_env* ve, val_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, 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; enum sec_status sec;
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> 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, log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname,
ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); 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)); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec));
regional_free_all(env->scratch); 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.rk.dname_len = kkey->namelen;
dnskey.entry.key = &dnskey; dnskey.entry.key = &dnskey;
dnskey.entry.data = kd->rrset_data; 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; return sec;
} }
@ -453,7 +453,7 @@ int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset)
enum sec_status enum sec_status
val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, 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* 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 /* as long as this is false, we can consider this DS rrset to be
* equivalent to no DS rrset. */ * 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); digest_algo = val_favorite_ds_algo(ds_rrset);
if(downprot) if(sigalg)
algo_needs_init_ds(&needs, ds_rrset, digest_algo); algo_needs_init_ds(&needs, ds_rrset, digest_algo, sigalg);
num = rrset_get_count(ds_rrset); num = rrset_get_count(ds_rrset);
for(i=0; i<num; i++) { for(i=0; i<num; i++) {
/* Check to see if we can understand this DS. /* 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, sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset,
ds_rrset, i, reason); ds_rrset, i, reason);
if(sec == sec_status_secure) { 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))) { (uint8_t)ds_get_key_algo(ds_rrset, i))) {
verbose(VERB_ALGO, "DS matched DNSKEY."); verbose(VERB_ALGO, "DS matched DNSKEY.");
return sec_status_secure; return sec_status_secure;
} }
} else if(downprot && sec == sec_status_bogus) { } else if(sigalg && sec == sec_status_bogus) {
algo_needs_set_bogus(&needs, algo_needs_set_bogus(&needs,
(uint8_t)ds_get_key_algo(ds_rrset, i)); (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. */ /* If any were understandable, then it is bad. */
verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY."); 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 " algo_needs_reason(env, alg, reason, "missing verification of "
"DNSKEY signature"); "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 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, int downprot, char** reason)
{ {
uint8_t sigalg[ALGO_NEEDS_MAX+1];
enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, 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) { if(sec == sec_status_secure) {
return key_entry_create_rrset(region, return key_entry_create_rrset(region,
ds_rrset->rk.dname, ds_rrset->rk.dname_len, ds_rrset->rk.dname, ds_rrset->rk.dname_len,
ntohs(ds_rrset->rk.rrset_class), dnskey_rrset, ntohs(ds_rrset->rk.rrset_class), dnskey_rrset,
*env->now); downprot?sigalg:NULL, *env->now);
} else if(sec == sec_status_insecure) { } else if(sec == sec_status_insecure) {
return key_entry_create_null(region, ds_rrset->rk.dname, return key_entry_create_null(region, ds_rrset->rk.dname,
ds_rrset->rk.dname_len, 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); 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 int
val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset) val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset)
{ {

View file

@ -117,14 +117,14 @@ void val_find_signer(enum val_classification subtype,
* @param ve: validator environment (verification settings) * @param ve: validator environment (verification settings)
* @param rrset: what to verify * @param rrset: what to verify
* @param keys: dnskey rrset to verify with. * @param keys: dnskey rrset to verify with.
* @param downprot: if true provide downgrade protection otherwise one * @param sigalg: if nonNULL provide downgrade protection otherwise one
* algorithm is enough. * algorithm is enough. Algo list is constructed in here.
* @param reason: reason of failure. Fixed string or alloced in scratch. * @param reason: reason of failure. Fixed string or alloced in scratch.
* @return security status of verification. * @return security status of verification.
*/ */
enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, 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, 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. * 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 ve: validator environment (verification settings)
* @param dnskey_rrset: DNSKEY rrset to verify * @param dnskey_rrset: DNSKEY rrset to verify
* @param ds_rrset: DS rrset to verify with. * @param ds_rrset: DS rrset to verify with.
* @param downprot: if true provide downgrade protection otherwise one * @param sigalg: if nonNULL provide downgrade protection otherwise one
* algorithm is enough. * 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. * @param reason: reason of failure. Fixed string or alloced in scratch.
* @return: sec_status_secure if a DS matches. * @return: sec_status_secure if a DS matches.
* sec_status_insecure if end of trust (i.e., unknown algorithms). * 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, enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, 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 * 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 * generally only occur in a private algorithm scenario: normally
* this sort of thing is checked before fetching the matching DNSKEY * this sort of thing is checked before fetching the matching DNSKEY
* rrset. * 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 key_entry_key* val_verify_new_DNSKEYs(struct regional* region,
struct module_env* env, struct val_env* ve, struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* dnskey_rrset, 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, 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. * Determine if DS rrset is usable for validator or not.
* Returns true if the algorithms for key and DShash are supported, * Returns true if the algorithms for key and DShash are supported,

View file

@ -49,6 +49,7 @@
#include "validator/val_nsec.h" #include "validator/val_nsec.h"
#include "validator/val_nsec3.h" #include "validator/val_nsec3.h"
#include "validator/val_neg.h" #include "validator/val_neg.h"
#include "validator/val_sigcrypt.h"
#include "validator/autotrust.h" #include "validator/autotrust.h"
#include "services/cache/dns.h" #include "services/cache/dns.h"
#include "util/data/dname.h" #include "util/data/dname.h"
@ -2260,35 +2261,18 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
return kkey; return kkey;
} }
/* attempt to verify with trust anchor DS and DNSKEY */ /* attempt to verify with trust anchor DS and DNSKEY */
if(ta->ds_rrset) { kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve,
kkey = val_verify_new_DNSKEYs(qstate->region, qstate->env, ve, dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, 0, &reason);
dnskey_rrset, ta->ds_rrset, 0, &reason); if(!kkey) {
if(!kkey) { log_err("out of memory: verifying prime TA");
log_err("out of memory: verifying prime DS"); return NULL;
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;
}
}
} }
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) { if(sec != sec_status_secure) {
log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " 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); log_query_info(VERB_DETAIL, "validated DS", qinfo);
*ke = key_entry_create_rrset(qstate->region, *ke = key_entry_create_rrset(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass, ds, qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
*qstate->env->now); NULL, *qstate->env->now);
return (*ke) != NULL; return (*ke) != NULL;
} else if(subtype == VAL_CLASS_NODATA || } else if(subtype == VAL_CLASS_NODATA ||
subtype == VAL_CLASS_NAMEERROR) { subtype == VAL_CLASS_NAMEERROR) {