From: W.C.A. Wijngaards Date: Tue, 13 Feb 2024 12:02:08 +0000 (+0100) Subject: - Fix CVE-2023-50387, DNSSEC verification complexity can be exploited to X-Git-Tag: release-1.19.1~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=882903f2fa800c4cb6f5e225b728e2887bb7b9ae;p=thirdparty%2Funbound.git - Fix CVE-2023-50387, DNSSEC verification complexity can be exploited to exhaust CPU resources and stall DNS resolvers. --- diff --git a/services/authzone.c b/services/authzone.c index a1b3d2278..9d02cfbff 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -7774,6 +7774,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, enum sec_status sec; struct val_env* ve; int m; + int verified = 0; m = modstack_find(mods, "validator"); if(m == -1) { auth_zone_log(z->name, VERB_ALGO, "zonemd dnssec verify: have " @@ -7797,7 +7798,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, "zonemd: verify %s RRset with DNSKEY", typestr); } sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL, - LDNS_SECTION_ANSWER, NULL); + LDNS_SECTION_ANSWER, NULL, &verified); if(sec == sec_status_secure) { return 1; } diff --git a/testcode/unitverify.c b/testcode/unitverify.c index ff069a1bb..fb7d84467 100644 --- a/testcode/unitverify.c +++ b/testcode/unitverify.c @@ -180,6 +180,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, enum sec_status sec; char* reason = NULL; uint8_t sigalg[ALGO_NEEDS_MAX+1]; + int verified = 0; if(vsig) { log_nametypeclass(VERB_QUERY, "verify of rrset", rrset->rk.dname, ntohs(rrset->rk.type), @@ -188,7 +189,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */ /* ok to give null as qstate here, won't be used for answer section. */ sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason, NULL, - LDNS_SECTION_ANSWER, NULL); + LDNS_SECTION_ANSWER, NULL, &verified); if(vsig) { printf("verify outcome is: %s %s\n", sec_status_to_string(sec), reason?reason:""); diff --git a/testdata/val_any.rpl b/testdata/val_any.rpl index ee249ffb6..301f28425 100644 --- a/testdata/val_any.rpl +++ b/testdata/val_any.rpl @@ -162,6 +162,9 @@ SECTION QUESTION example.com. IN ANY ENTRY_END +; Allow validation resuming for the RRSIGs +STEP 2 TIME_PASSES ELAPSE 0.05 + ; recursion happens here. STEP 10 CHECK_ANSWER ENTRY_BEGIN diff --git a/testdata/val_any_dname.rpl b/testdata/val_any_dname.rpl index 005d29606..a88d7c22c 100644 --- a/testdata/val_any_dname.rpl +++ b/testdata/val_any_dname.rpl @@ -164,6 +164,9 @@ SECTION QUESTION example.com. IN ANY ENTRY_END +; Allow validation resuming for the RRSIGs +STEP 2 TIME_PASSES ELAPSE 0.05 + ; recursion happens here. STEP 10 CHECK_ANSWER ENTRY_BEGIN diff --git a/testdata/val_any_negcache.rpl b/testdata/val_any_negcache.rpl index 77aacba8c..8800a2140 100644 --- a/testdata/val_any_negcache.rpl +++ b/testdata/val_any_negcache.rpl @@ -199,6 +199,9 @@ SECTION QUESTION example.com. IN ANY ENTRY_END +; Allow validation resuming for the RRSIGs +STEP 21 TIME_PASSES ELAPSE 0.05 + ; recursion happens here. STEP 30 CHECK_ANSWER ENTRY_BEGIN diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 43d38dc37..de128168a 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -131,6 +131,7 @@ fptr_whitelist_comm_timer(void (*fptr)(void*)) else if(fptr == &pending_udp_timer_delay_cb) return 1; else if(fptr == &worker_stat_timer_cb) return 1; else if(fptr == &worker_probe_timer_cb) return 1; + else if(fptr == &validate_msg_signatures_timer_cb) return 1; #ifdef UB_ON_WINDOWS else if(fptr == &wsvc_cron_cb) return 1; #endif diff --git a/validator/val_nsec.c b/validator/val_nsec.c index 17c90d83f..d0cc67ff5 100644 --- a/validator/val_nsec.c +++ b/validator/val_nsec.c @@ -181,6 +181,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, { struct packed_rrset_data* d = (struct packed_rrset_data*) nsec->entry.data; + int verified = 0; if(!d) return 0; if(d->security == sec_status_secure) return 1; @@ -188,7 +189,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, if(d->security == sec_status_secure) return 1; d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, - reason_bogus, LDNS_SECTION_AUTHORITY, qstate); + reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified); if(d->security == sec_status_secure) { rrset_update_sec_status(env->rrset_cache, nsec, *env->now); return 1; diff --git a/validator/val_nsec3.c b/validator/val_nsec3.c index a2b3794f6..f4b9b2bca 100644 --- a/validator/val_nsec3.c +++ b/validator/val_nsec3.c @@ -1294,6 +1294,7 @@ list_is_secure(struct module_env* env, struct val_env* ve, { struct packed_rrset_data* d; size_t i; + int verified = 0; for(i=0; ientry.data; if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3)) @@ -1304,7 +1305,8 @@ list_is_secure(struct module_env* env, struct val_env* ve, if(d->security == sec_status_secure) continue; d->security = val_verify_rrset_entry(env, ve, list[i], kkey, - reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate); + reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate, + &verified); if(d->security != sec_status_secure) { verbose(VERB_ALGO, "NSEC3 did not verify"); return 0; diff --git a/validator/val_sigcrypt.c b/validator/val_sigcrypt.c index 37730f179..f4b866366 100644 --- a/validator/val_sigcrypt.c +++ b/validator/val_sigcrypt.c @@ -79,6 +79,9 @@ #include #endif +/** Maximum number of RRSIG validations for an RRset. */ +#define MAX_VALIDATE_RRSIGS 8 + /** return number of rrs in an rrset */ static size_t rrset_get_count(struct ub_packed_rrset_key* rrset) @@ -542,6 +545,8 @@ int algo_needs_missing(struct algo_needs* n) * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. + * @param numverified: incremented when the number of RRSIG validations + * increases. * @return secure if any key signs *this* signature. bogus if no key signs it, * unchecked on error, or indeterminate if all keys are not supported by * the crypto library (openssl3+ only). @@ -552,7 +557,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey, size_t sig_idx, struct rbtree_type** sortree, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate) + sldns_pkt_section section, struct module_qstate* qstate, + int* numverified) { /* find matching keys and check them */ enum sec_status sec = sec_status_bogus; @@ -576,6 +582,7 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, tag != dnskey_calc_keytag(dnskey, i)) continue; numchecked ++; + (*numverified)++; /* see if key verifies */ sec = dnskey_verify_rrset_sig(env->scratch, @@ -586,6 +593,13 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, return sec; else if(sec == sec_status_indeterminate) numindeterminate ++; + if(*numverified > MAX_VALIDATE_RRSIGS) { + *reason = "too many RRSIG validations"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + verbose(VERB_ALGO, "verify sig: too many RRSIG validations"); + return sec_status_bogus; + } } if(numchecked == 0) { *reason = "signatures from unknown keys"; @@ -609,7 +623,7 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, 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; size_t i, num; @@ -617,6 +631,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, /* make sure that for all DNSKEY algorithms there are valid sigs */ struct algo_needs needs; int alg; + *verified = 0; num = rrset_get_sigcount(rrset); if(num == 0) { @@ -641,7 +656,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, for(i=0; inow, rrset, dnskey, i, &sortree, reason, reason_bogus, - section, qstate); + section, qstate, verified); /* see which algorithm has been fixed up */ if(sec == sec_status_secure) { if(!sigalg) @@ -653,6 +668,13 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, algo_needs_set_bogus(&needs, (uint8_t)rrset_get_sig_algo(rrset, i)); } + if(*verified > MAX_VALIDATE_RRSIGS) { + verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations"); + *reason = "too many RRSIG validations"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + return sec_status_bogus; + } } if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { verbose(VERB_ALGO, "rrset failed to verify: " @@ -691,6 +713,7 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, int buf_canon = 0; uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx); int algo = dnskey_get_algo(dnskey, dnskey_idx); + int numverified = 0; num = rrset_get_sigcount(rrset); if(num == 0) { @@ -714,8 +737,16 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, if(sec == sec_status_secure) return sec; numchecked ++; + numverified ++; if(sec == sec_status_indeterminate) numindeterminate ++; + if(numverified > MAX_VALIDATE_RRSIGS) { + verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations"); + *reason = "too many RRSIG validations"; + if(reason_bogus) + *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + return sec_status_bogus; + } } verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus"); if(!numchecked) { diff --git a/validator/val_sigcrypt.h b/validator/val_sigcrypt.h index 7f52b71e4..1a3d8fcb2 100644 --- a/validator/val_sigcrypt.h +++ b/validator/val_sigcrypt.h @@ -260,6 +260,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. + * @param verified: if not NULL the number of RRSIG validations is returned. * @return SECURE if one key in the set verifies one rrsig. * UNCHECKED on allocation errors, unsupported algorithms, malformed data, * and BOGUS on verification failures (no keys match any signatures). @@ -268,7 +269,7 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, 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); /** diff --git a/validator/val_utils.c b/validator/val_utils.c index 8b388882b..67a958ae2 100644 --- a/validator/val_utils.c +++ b/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; } @@ -439,6 +447,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++; diff --git a/validator/val_utils.h b/validator/val_utils.h index 83e3d0ad8..e8cdcefa6 100644 --- a/validator/val_utils.h +++ b/validator/val_utils.h @@ -124,12 +124,14 @@ void val_find_signer(enum val_classification subtype, * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. + * @param verified: if not NULL, the number of RRSIG validations is returned. * @return security status of verification. */ 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); /** * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but diff --git a/validator/validator.c b/validator/validator.c index 6cd15cfc1..b01f388ca 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -64,6 +64,11 @@ #include "sldns/wire2str.h" #include "sldns/str2wire.h" +/** Max number of RRSIGs to validate at once, suspend query for later. */ +#define MAX_VALIDATE_AT_ONCE 8 +/** Max number of validation suspends allowed, error out otherwise. */ +#define MAX_VALIDATION_SUSPENDS 16 + /* forward decl for cache response and normal super inform calls of a DS */ static void process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, @@ -292,6 +297,21 @@ val_new(struct module_qstate* qstate, int id) return val_new_getmsg(qstate, vq); } +/** reset validator query state for query restart */ +static void +val_restart(struct val_qstate* vq) +{ + struct comm_timer* temp_timer; + int restart_count; + if(!vq) return; + temp_timer = vq->msg_signatures_timer; + restart_count = vq->restart_count+1; + memset(vq, 0, sizeof(*vq)); + vq->msg_signatures_timer = temp_timer; + vq->restart_count = restart_count; + vq->state = VAL_INIT_STATE; +} + /** * Exit validation with an error status * @@ -598,30 +618,42 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, * completed. * * @param qstate: query state. + * @param vq: validator query state. * @param env: module env for verify. * @param ve: validator env for verify. * @param qchase: query that was made. * @param chase_reply: answer to validate. * @param key_entry: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). + * @param suspend: returned true if the task takes to long and needs to + * suspend to continue the effort later. * @return false if any of the rrsets in the an or ns sections of the message * fail to verify. The message is then set to bogus. */ static int -validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - struct val_env* ve, struct query_info* qchase, - struct reply_info* chase_reply, struct key_entry_key* key_entry) +validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, + struct module_env* env, struct val_env* ve, struct query_info* qchase, + struct reply_info* chase_reply, struct key_entry_key* key_entry, + int* suspend) { uint8_t* sname; size_t i, slen; struct ub_packed_rrset_key* s; enum sec_status sec; - int dname_seen = 0; + int dname_seen = 0, num_verifies = 0, verified, have_state = 0; char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + *suspend = 0; + if(vq->msg_signatures_state) { + /* Pick up the state, and reset it, may not be needed now. */ + vq->msg_signatures_state = 0; + have_state = 1; + } /* validate the ANSWER section */ for(i=0; ian_numrrsets; i++) { + if(have_state && i <= vq->msg_signatures_index) + continue; s = chase_reply->rrsets[i]; /* Skip the CNAME following a (validated) DNAME. * Because of the normalization routines in the iterator, @@ -640,7 +672,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, /* Verify the answer rrset */ sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, - &reason_bogus, LDNS_SECTION_ANSWER, qstate); + &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); /* If the (answer) rrset failed to validate, then this * message is BAD. */ if(sec != sec_status_secure) { @@ -665,14 +697,33 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) { dname_seen = 1; } + num_verifies += verified; + if(num_verifies > MAX_VALIDATE_AT_ONCE && + i+1 < (env->cfg->val_clean_additional? + chase_reply->an_numrrsets+chase_reply->ns_numrrsets: + chase_reply->rrset_count)) { + /* If the number of RRSIGs exceeds the maximum in + * one go, suspend. Only suspend if there is a next + * rrset to verify, i+1msg_signatures_state = 1; + vq->msg_signatures_index = i; + verbose(VERB_ALGO, "msg signature validation " + "suspended"); + return 0; + } } /* validate the AUTHORITY section */ for(i=chase_reply->an_numrrsets; ian_numrrsets+ chase_reply->ns_numrrsets; i++) { + if(have_state && i <= vq->msg_signatures_index) + continue; s = chase_reply->rrsets[i]; sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, - &reason_bogus, LDNS_SECTION_AUTHORITY, qstate); + &reason_bogus, LDNS_SECTION_AUTHORITY, qstate, + &verified); /* If anything in the authority section fails to be secure, * we have a bad message. */ if(sec != sec_status_secure) { @@ -686,6 +737,18 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, update_reason_bogus(chase_reply, reason_bogus); return 0; } + num_verifies += verified; + if(num_verifies > MAX_VALIDATE_AT_ONCE && + i+1 < (env->cfg->val_clean_additional? + chase_reply->an_numrrsets+chase_reply->ns_numrrsets: + chase_reply->rrset_count)) { + *suspend = 1; + vq->msg_signatures_state = 1; + vq->msg_signatures_index = i; + verbose(VERB_ALGO, "msg signature validation " + "suspended"); + return 0; + } } /* If set, the validator should clean the additional section of @@ -695,22 +758,102 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, /* attempt to validate the ADDITIONAL section rrsets */ for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; irrset_count; i++) { + if(have_state && i <= vq->msg_signatures_index) + continue; s = chase_reply->rrsets[i]; /* only validate rrs that have signatures with the key */ /* leave others unchecked, those get removed later on too */ val_find_rrset_signer(s, &sname, &slen); + verified = 0; if(sname && query_dname_compare(sname, key_entry->name)==0) (void)val_verify_rrset_entry(env, ve, s, key_entry, - &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate); + &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate, + &verified); /* the additional section can fail to be secure, * it is optional, check signature in case we need * to clean the additional section later. */ + num_verifies += verified; + if(num_verifies > MAX_VALIDATE_AT_ONCE && + i+1 < chase_reply->rrset_count) { + *suspend = 1; + vq->msg_signatures_state = 1; + vq->msg_signatures_index = i; + verbose(VERB_ALGO, "msg signature validation " + "suspended"); + return 0; + } } return 1; } +void +validate_msg_signatures_timer_cb(void* arg) +{ + struct module_qstate* qstate = (struct module_qstate*)arg; + verbose(VERB_ALGO, "validate_msg_signatures timer, continue"); + mesh_run(qstate->env->mesh, qstate->mesh_info, module_event_pass, + NULL); +} + +/** Setup timer to continue validation of msg signatures later */ +static int +validate_msg_signatures_setup_timer(struct module_qstate* qstate, + struct val_qstate* vq, int id) +{ + struct timeval tv; + int usec, slack, base; + if(vq->suspend_count >= MAX_VALIDATION_SUSPENDS) { + verbose(VERB_ALGO, "validate_msg_signatures_setup_timer: " + "reached MAX_VALIDATION_SUSPENDS (%d); error out", + MAX_VALIDATION_SUSPENDS); + errinf(qstate, "max validation suspends reached, " + "too many RRSIG validations"); + return 0; + } + vq->state = VAL_VALIDATE_STATE; + qstate->ext_state[id] = module_wait_reply; + if(!vq->msg_signatures_timer) { + vq->msg_signatures_timer = comm_timer_create( + qstate->env->worker_base, + validate_msg_signatures_timer_cb, qstate); + if(!vq->msg_signatures_timer) { + log_err("validate_msg_signatures_setup_timer: " + "out of memory for comm_timer_create"); + return 0; + } + } + /* The timer is activated later, after other events in the event + * loop have been processed. The query state can also be deleted, + * when the list is full and query states are dropped. */ + /* Extend wait time if there are a lot of queries or if this one + * is taking long, to keep around cpu time for ordinary queries. */ + usec = 50000; /* 50 msec */ + slack = 0; + if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states) + slack += 3; + else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/2) + slack += 2; + else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/4) + slack += 1; + if(vq->suspend_count > 3) + slack += 3; + else if(vq->suspend_count > 0) + slack += vq->suspend_count; + if(slack != 0 && slack <= 12 /* No numeric overflow. */) { + usec = usec << slack; + } + /* Spread such timeouts within 90%-100% of the original timer. */ + base = usec * 9/10; + usec = base + ub_random_max(qstate->env->rnd, usec-base); + tv.tv_usec = (usec % 1000000); + tv.tv_sec = (usec / 1000000); + vq->suspend_count ++; + comm_timer_set(vq->msg_signatures_timer, &tv); + return 1; +} + /** * Detect wrong truncated response (say from BIND 9.6.1 that is forwarding * and saw the NS record without signatures from a referral). @@ -1875,7 +2018,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, struct val_env* ve, int id) { enum val_classification subtype; - int rcode; + int rcode, suspend; if(!vq->key_entry) { verbose(VERB_ALGO, "validate: no key entry, failed"); @@ -1932,8 +2075,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, /* check signatures in the message; * answer and authority must be valid, additional is only checked. */ - if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase, - vq->chase_reply, vq->key_entry)) { + if(!validate_msg_signatures(qstate, vq, qstate->env, ve, &vq->qchase, + vq->chase_reply, vq->key_entry, &suspend)) { + if(suspend) { + if(!validate_msg_signatures_setup_timer(qstate, vq, + id)) + return val_error(qstate, id); + return 0; + } /* workaround bad recursor out there that truncates (even * with EDNS4k) to 512 by removing RRSIG from auth section * for positive replies*/ @@ -2129,16 +2278,13 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, if(vq->orig_msg->rep->security == sec_status_bogus) { /* see if we can try again to fetch data */ if(vq->restart_count < ve->max_restart) { - int restart_count = vq->restart_count+1; verbose(VERB_ALGO, "validation failed, " "blacklist and retry to fetch data"); val_blacklist(&qstate->blacklist, qstate->region, qstate->reply_origin, 0); qstate->reply_origin = NULL; qstate->errinf = NULL; - memset(vq, 0, sizeof(*vq)); - vq->restart_count = restart_count; - vq->state = VAL_INIT_STATE; + val_restart(vq); verbose(VERB_ALGO, "pass back to next module"); qstate->ext_state[id] = module_restart_next; return 0; @@ -2476,6 +2622,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; enum val_classification subtype; + int verified; if(rcode != LDNS_RCODE_NOERROR) { char rc[16]; rc[0]=0; @@ -2506,7 +2653,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, /* Verify only returns BOGUS or SECURE. If the rrset is * bogus, then we are done. */ sec = val_verify_rrset_entry(qstate->env, ve, ds, - vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate); + vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); if(sec != sec_status_secure) { verbose(VERB_DETAIL, "DS rrset in DS response did " "not verify"); @@ -2652,7 +2799,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, } sec = val_verify_rrset_entry(qstate->env, ve, cname, vq->key_entry, &reason, &reason_bogus, - LDNS_SECTION_ANSWER, qstate); + LDNS_SECTION_ANSWER, qstate, &verified); if(sec == sec_status_secure) { verbose(VERB_ALGO, "CNAME validated, " "proof that DS does not exist"); @@ -2981,8 +3128,15 @@ val_inform_super(struct module_qstate* qstate, int id, void val_clear(struct module_qstate* qstate, int id) { + struct val_qstate* vq; if(!qstate) return; + vq = (struct val_qstate*)qstate->minfo[id]; + if(vq) { + if(vq->msg_signatures_timer) { + comm_timer_delete(vq->msg_signatures_timer); + } + } /* everything is allocated in the region, so assign NULL */ qstate->minfo[id] = NULL; } diff --git a/validator/validator.h b/validator/validator.h index 694e4c895..a997ca88f 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -50,6 +50,7 @@ struct key_cache; struct key_entry_key; struct val_neg_cache; struct config_strlist; +struct comm_timer; /** * This is the TTL to use when a trust anchor fails to prime. A trust anchor @@ -215,6 +216,15 @@ struct val_qstate { /** true if this state is waiting to prime a trust anchor */ int wait_prime_ta; + + /** State to continue with RRSIG validation in a message later */ + int msg_signatures_state; + /** The rrset index for the msg signatures to continue from */ + size_t msg_signatures_index; + /** The timer to resume processing msg signatures */ + struct comm_timer* msg_signatures_timer; + /** number of suspends */ + int suspend_count; }; /** @@ -262,4 +272,7 @@ void val_clear(struct module_qstate* qstate, int id); */ size_t val_get_mem(struct module_env* env, int id); +/** Timer callback for msg signatures continue timer */ +void validate_msg_signatures_timer_cb(void* arg); + #endif /* VALIDATOR_VALIDATOR_H */