mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 06:37:08 +00:00
- Fix CVE-2023-50387, DNSSEC verification complexity can be exploited to
exhaust CPU resources and stall DNS resolvers.
This commit is contained in:
parent
3352b1090e
commit
882903f2fa
@ -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;
|
||||
}
|
||||
|
@ -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:"");
|
||||
|
3
testdata/val_any.rpl
vendored
3
testdata/val_any.rpl
vendored
@ -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
|
||||
|
3
testdata/val_any_dname.rpl
vendored
3
testdata/val_any_dname.rpl
vendored
@ -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
|
||||
|
3
testdata/val_any_negcache.rpl
vendored
3
testdata/val_any_negcache.rpl
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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; i<num; i++) {
|
||||
d = (struct packed_rrset_data*)list[i]->entry.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;
|
||||
|
@ -79,6 +79,9 @@
|
||||
#include <openssl/engine.h>
|
||||
#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; i<num; i++) {
|
||||
sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, 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) {
|
||||
|
@ -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);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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++;
|
||||
|
@ -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
|
||||
|
@ -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; i<chase_reply->an_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+1<loopmax. Store where to
|
||||
* continue later. */
|
||||
*suspend = 1;
|
||||
vq->msg_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; i<chase_reply->an_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;
|
||||
i<chase_reply->rrset_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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user