diff options
Diffstat (limited to 'contrib/unbound/validator/val_utils.c')
-rw-r--r-- | contrib/unbound/validator/val_utils.c | 99 |
1 files changed, 75 insertions, 24 deletions
diff --git a/contrib/unbound/validator/val_utils.c b/contrib/unbound/validator/val_utils.c index 18a7c9c2e95d..c316183a9d9e 100644 --- a/contrib/unbound/validator/val_utils.c +++ b/contrib/unbound/validator/val_utils.c @@ -58,6 +58,10 @@ #include "sldns/wire2str.h" #include "sldns/parseutil.h" +/** Maximum allowed digest match failures per DS, for DNSKEYs with the same + * properties */ +#define MAX_DS_MATCH_FAILURES 4 + enum val_classification val_classify_response(uint16_t query_flags, struct query_info* origqinf, struct query_info* qinf, struct reply_info* rep, size_t skip) @@ -336,7 +340,8 @@ static 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, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, + int *verified) { enum sec_status sec; struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> @@ -346,6 +351,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, log_nametypeclass(VERB_ALGO, "verify rrset cached", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); + *verified = 0; return d->security; } /* check in the cache if verification has already been done */ @@ -354,12 +360,13 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, log_nametypeclass(VERB_ALGO, "verify rrset from cache", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); + *verified = 0; return d->security; } log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason, - reason_bogus, section, qstate); + reason_bogus, section, qstate, verified); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); regional_free_all(env->scratch); @@ -393,7 +400,8 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, + int* verified) { /* temporary dnskey rrset-key */ struct ub_packed_rrset_key dnskey; @@ -407,7 +415,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, dnskey.entry.key = &dnskey; dnskey.entry.data = kd->rrset_data; sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason, - reason_bogus, section, qstate); + reason_bogus, section, qstate, verified); return sec; } @@ -416,11 +424,13 @@ static enum sec_status verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason, - sldns_ede_code *reason_bogus, struct module_qstate* qstate) + sldns_ede_code *reason_bogus, struct module_qstate* qstate, + int *nonechecked) { enum sec_status sec = sec_status_bogus; size_t i, num, numchecked = 0, numhashok = 0, numsizesupp = 0; num = rrset_get_count(dnskey_rrset); + *nonechecked = 0; for(i=0; i<num; i++) { /* Skip DNSKEYs that don't match the basic criteria. */ if(ds_get_key_algo(ds_rrset, ds_idx) @@ -439,6 +449,12 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset, ds_idx)) { verbose(VERB_ALGO, "DS match attempt failed"); + if(numchecked > numhashok + MAX_DS_MATCH_FAILURES) { + verbose(VERB_ALGO, "DS match attempt reached " + "MAX_DS_MATCH_FAILURES (%d); bogus", + MAX_DS_MATCH_FAILURES); + return sec_status_bogus; + } continue; } numhashok++; @@ -458,17 +474,19 @@ 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(numsizesupp != 0) { + if(numsizesupp != 0 || sec == sec_status_indeterminate) { /* there is a working DS, but that DNSKEY is not supported */ return sec_status_insecure; } - if(numchecked == 0) + if(numchecked == 0) { algo_needs_reason(env, ds_get_key_algo(ds_rrset, ds_idx), reason, "no keys have a DS"); - else if(numhashok == 0) + *nonechecked = 1; + } else if(numhashok == 0) { *reason = "DS hash mismatches key"; - else if(!*reason) + } else if(!*reason) { *reason = "keyset not secured by DNSKEY that matches DS"; + } return sec_status_bogus; } @@ -489,7 +507,6 @@ int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset) return digest_algo; } -// @TODO change the use of this function to _ede function in authzone.c:8111 enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, @@ -498,7 +515,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, alg; + int has_useful_ds = 0, digest_algo, alg, has_algo_refusal = 0, + nonechecked, has_checked_ds = 0; struct algo_needs needs; size_t i, num; enum sec_status sec; @@ -531,9 +549,16 @@ 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, reason_bogus, qstate); - if(sec == sec_status_insecure) + ds_rrset, i, reason, reason_bogus, qstate, + &nonechecked); + if(sec == sec_status_insecure) { + /* DNSKEY too large unsupported or algo refused by + * crypto lib. */ + has_algo_refusal = 1; continue; + } + if(!nonechecked) + has_checked_ds = 1; /* Once we see a single DS with a known digestID and * algorithm, we cannot return INSECURE (with a @@ -558,6 +583,15 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, /* None of the DS's worked out. */ + /* If none of the DSes have been checked, eg. that means no matches + * for keytags, and the other dses are all algo_refusal, it is an + * insecure delegation point, since the only matched DS records + * have an algo refusal, or are unsupported. */ + if(has_algo_refusal && !has_checked_ds) { + verbose(VERB_ALGO, "No supported DS records were found -- " + "treating as insecure."); + return sec_status_insecure; + } /* If no DSs were understandable, then this is OK. */ if(!has_useful_ds) { verbose(VERB_ALGO, "No usable DS records were found -- " @@ -588,16 +622,18 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, return key_entry_create_rrset(region, ds_rrset->rk.dname, ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class), dnskey_rrset, - downprot?sigalg:NULL, *env->now); + downprot?sigalg:NULL, LDNS_EDE_NONE, NULL, + *env->now); } else if(sec == sec_status_insecure) { return key_entry_create_null(region, ds_rrset->rk.dname, - ds_rrset->rk.dname_len, + ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class), - rrset_get_ttl(ds_rrset), *env->now); + rrset_get_ttl(ds_rrset), *reason_bogus, *reason, + *env->now); } return key_entry_create_bad(region, ds_rrset->rk.dname, ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class), - BOGUS_KEY_TTL, *env->now); + BOGUS_KEY_TTL, *reason_bogus, *reason, *env->now); } enum sec_status @@ -609,7 +645,8 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, { /* as long as this is false, we can consider this anchor to be * equivalent to no anchor. */ - int has_useful_ta = 0, digest_algo = 0, alg; + int has_useful_ta = 0, digest_algo = 0, alg, has_algo_refusal = 0, + nonechecked, has_checked_ds = 0; struct algo_needs needs; size_t i, num; enum sec_status sec; @@ -655,9 +692,13 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, continue; sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, - ta_ds, i, reason, reason_bogus, qstate); - if(sec == sec_status_insecure) + ta_ds, i, reason, reason_bogus, qstate, &nonechecked); + if(sec == sec_status_insecure) { + has_algo_refusal = 1; continue; + } + if(!nonechecked) + has_checked_ds = 1; /* Once we see a single DS with a known digestID and * algorithm, we cannot return INSECURE (with a @@ -695,7 +736,7 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, has_useful_ta = 1; sec = dnskey_verify_rrset(env, ve, dnskey_rrset, - ta_dnskey, i, reason, NULL, LDNS_SECTION_ANSWER, qstate); + ta_dnskey, i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate); if(sec == sec_status_secure) { if(!sigalg || algo_needs_set_secure(&needs, (uint8_t)dnskey_get_algo(ta_dnskey, i))) { @@ -713,6 +754,15 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, } } + /* If none of the DSes have been checked, eg. that means no matches + * for keytags, and the other dses are all algo_refusal, it is an + * insecure delegation point, since the only matched DS records + * have an algo refusal, or are unsupported. */ + if(has_algo_refusal && !has_checked_ds) { + verbose(VERB_ALGO, "No supported trust anchors were found -- " + "treating as insecure."); + return sec_status_insecure; + } /* If no DSs were understandable, then this is OK. */ if(!has_useful_ta) { verbose(VERB_ALGO, "No usable trust anchors were found -- " @@ -744,16 +794,17 @@ val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env, 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); + downprot?sigalg:NULL, LDNS_EDE_NONE, 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); + rrset_get_ttl(dnskey_rrset), *reason_bogus, *reason, + *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); + BOGUS_KEY_TTL, *reason_bogus, *reason, *env->now); } int |