diff options
| author | Dag-Erling Smørgrav <des@FreeBSD.org> | 2018-05-12 11:56:38 +0000 |
|---|---|---|
| committer | Dag-Erling Smørgrav <des@FreeBSD.org> | 2018-05-12 11:56:38 +0000 |
| commit | 197f1a0fe3e81cde0cd25a3a1f37ebedf9a99488 (patch) | |
| tree | 9a121ad4cef31a32608c065400c31246d549c0dc /validator | |
| parent | b5c63b395d5df7ff6ee4d41a7dfecd938d894037 (diff) | |
Vendor import of Unbound 1.7.0.vendor/unbound/1.7.0
Diffstat (limited to 'validator')
| -rw-r--r-- | validator/val_neg.c | 272 | ||||
| -rw-r--r-- | validator/val_neg.h | 3 | ||||
| -rw-r--r-- | validator/val_nsec.c | 7 | ||||
| -rw-r--r-- | validator/val_sigcrypt.c | 64 | ||||
| -rw-r--r-- | validator/val_utils.c | 6 | ||||
| -rw-r--r-- | validator/val_utils.h | 4 | ||||
| -rw-r--r-- | validator/validator.c | 61 |
7 files changed, 334 insertions, 83 deletions
diff --git a/validator/val_neg.c b/validator/val_neg.c index fe57ac2c442e..541238148307 100644 --- a/validator/val_neg.c +++ b/validator/val_neg.c @@ -847,34 +847,71 @@ void neg_insert_data(struct val_neg_cache* neg, wipeout(neg, zone, el, nsec); } +/** see if the reply has signed NSEC records and return the signer */ +static uint8_t* reply_nsec_signer(struct reply_info* rep, size_t* signer_len, + uint16_t* dclass) +{ + size_t i; + struct packed_rrset_data* d; + uint8_t* s; + for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ + if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC || + ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC3) { + d = (struct packed_rrset_data*)rep->rrsets[i]-> + entry.data; + /* return first signer name of first NSEC */ + if(d->rrsig_count != 0) { + val_find_rrset_signer(rep->rrsets[i], + &s, signer_len); + if(s && *signer_len) { + *dclass = ntohs(rep->rrsets[i]-> + rk.rrset_class); + return s; + } + } + } + } + return 0; +} + void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep) { size_t i, need; struct ub_packed_rrset_key* soa; + uint8_t* dname = NULL; + size_t dname_len; + uint16_t rrset_class; struct val_neg_zone* zone; /* see if secure nsecs inside */ if(!reply_has_nsec(rep)) return; /* find the zone name in message */ - soa = reply_find_soa(rep); - if(!soa) - return; + if((soa = reply_find_soa(rep))) { + dname = soa->rk.dname; + dname_len = soa->rk.dname_len; + rrset_class = ntohs(soa->rk.rrset_class); + } + else { + /* No SOA in positive (wildcard) answer. Use signer from the + * validated answer RRsets' signature. */ + if(!(dname = reply_nsec_signer(rep, &dname_len, &rrset_class))) + return; + } log_nametypeclass(VERB_ALGO, "negcache insert for zone", - soa->rk.dname, LDNS_RR_TYPE_SOA, ntohs(soa->rk.rrset_class)); + dname, LDNS_RR_TYPE_SOA, rrset_class); /* ask for enough space to store all of it */ need = calc_data_need(rep) + - calc_zone_need(soa->rk.dname, soa->rk.dname_len); + calc_zone_need(dname, dname_len); lock_basic_lock(&neg->lock); neg_make_space(neg, need); /* find or create the zone entry */ - zone = neg_find_zone(neg, soa->rk.dname, soa->rk.dname_len, - ntohs(soa->rk.rrset_class)); + zone = neg_find_zone(neg, dname, dname_len, rrset_class); if(!zone) { - if(!(zone = neg_create_zone(neg, soa->rk.dname, - soa->rk.dname_len, ntohs(soa->rk.rrset_class)))) { + if(!(zone = neg_create_zone(neg, dname, dname_len, + rrset_class))) { lock_basic_unlock(&neg->lock); log_err("out of memory adding negative zone"); return; @@ -1029,33 +1066,6 @@ int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len, return 1; } -/** see if the reply has signed NSEC records and return the signer */ -static uint8_t* reply_nsec_signer(struct reply_info* rep, size_t* signer_len, - uint16_t* dclass) -{ - size_t i; - struct packed_rrset_data* d; - uint8_t* s; - for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC || - ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC3) { - d = (struct packed_rrset_data*)rep->rrsets[i]-> - entry.data; - /* return first signer name of first NSEC */ - if(d->rrsig_count != 0) { - val_find_rrset_signer(rep->rrsets[i], - &s, signer_len); - if(s && *signer_len) { - *dclass = ntohs(rep->rrsets[i]-> - rk.rrset_class); - return s; - } - } - } - } - return 0; -} - void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep, uint8_t* zone_name) { @@ -1183,6 +1193,73 @@ grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len, return r; } +/** + * Get best NSEC record for qname. Might be matching, covering or totally + * useless. + * @param neg_cache: neg cache + * @param qname: to lookup rrset name + * @param qname_len: length of qname. + * @param qclass: class of rrset to lookup, host order + * @param rrset_cache: rrset cache + * @param now: to check ttl against + * @param region: where to alloc result + * @return rrset or NULL + */ +static struct ub_packed_rrset_key* +neg_find_nsec(struct val_neg_cache* neg_cache, uint8_t* qname, size_t qname_len, + uint16_t qclass, struct rrset_cache* rrset_cache, time_t now, + struct regional* region) +{ + int labs; + uint32_t flags; + struct val_neg_zone* zone; + struct val_neg_data* data; + struct ub_packed_rrset_key* nsec; + + labs = dname_count_labels(qname); + lock_basic_lock(&neg_cache->lock); + zone = neg_closest_zone_parent(neg_cache, qname, qname_len, labs, + qclass); + while(zone && !zone->in_use) + zone = zone->parent; + if(!zone) { + lock_basic_unlock(&neg_cache->lock); + return NULL; + } + + /* NSEC only for now */ + if(zone->nsec3_hash) { + lock_basic_unlock(&neg_cache->lock); + return NULL; + } + + /* ignore return value, don't care if it is an exact or smaller match */ + (void)neg_closest_data(zone, qname, qname_len, labs, &data); + if(!data) { + lock_basic_unlock(&neg_cache->lock); + return NULL; + } + + /* ENT nodes are not in use, try the previous node. If the previous node + * is not in use, we don't have an useful NSEC and give up. */ + if(!data->in_use) { + data = (struct val_neg_data*)rbtree_previous((rbnode_type*)data); + if((rbnode_type*)data == RBTREE_NULL || !data->in_use) { + lock_basic_unlock(&neg_cache->lock); + return NULL; + } + } + + flags = 0; + if(query_dname_compare(data->name, zone->name) == 0) + flags = PACKED_RRSET_NSEC_AT_APEX; + + nsec = grab_nsec(rrset_cache, data->name, data->len, LDNS_RR_TYPE_NSEC, + zone->dclass, flags, region, 0, 0, now); + lock_basic_unlock(&neg_cache->lock); + return nsec; +} + /** find nsec3 closest encloser in neg cache */ static struct val_neg_data* neg_find_nsec3_ce(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, @@ -1400,41 +1477,132 @@ static int add_soa(struct rrset_cache* rrset_cache, time_t now, struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, struct regional* region, struct rrset_cache* rrset_cache, - sldns_buffer* buf, time_t now, int addsoa, uint8_t* topname) + sldns_buffer* buf, time_t now, int addsoa, uint8_t* topname, + struct config_file* cfg) { struct dns_msg* msg; - struct ub_packed_rrset_key* rrset; + struct ub_packed_rrset_key* nsec; /* qname matching/covering nsec */ + struct ub_packed_rrset_key* wcrr; /* wildcard record or nsec */ + uint8_t* nodata_wc = NULL; + uint8_t* ce = NULL; + size_t ce_len; + uint8_t wc_ce[LDNS_MAX_DOMAINLEN+3]; + struct query_info wc_qinfo; + struct ub_packed_rrset_key* cache_wc; + struct packed_rrset_data* wcrr_data; + int rcode = LDNS_RCODE_NOERROR; uint8_t* zname; size_t zname_len; int zname_labs; struct val_neg_zone* zone; - /* only for DS queries */ - if(qinfo->qtype != LDNS_RR_TYPE_DS) + /* only for DS queries when aggressive use of NSEC is disabled */ + if(qinfo->qtype != LDNS_RR_TYPE_DS && !cfg->aggressive_nsec) return NULL; log_assert(!topname || dname_subdomain_c(qinfo->qname, topname)); - /* see if info from neg cache is available - * For NSECs, because there is no optout; a DS next to a delegation - * always has exactly an NSEC for it itself; check its DS bit. - * flags=0 (not the zone apex). - */ - rrset = grab_nsec(rrset_cache, qinfo->qname, qinfo->qname_len, - LDNS_RR_TYPE_NSEC, qinfo->qclass, 0, region, 1, - qinfo->qtype, now); - if(rrset) { - /* return msg with that rrset */ + /* Get best available NSEC for qname */ + nsec = neg_find_nsec(neg, qinfo->qname, qinfo->qname_len, qinfo->qclass, + rrset_cache, now, region); + + /* Matching NSEC, use to generate No Data answer. Not creating answers + * yet for No Data proven using wildcard. */ + if(nsec && nsec_proves_nodata(nsec, qinfo, &nodata_wc) && !nodata_wc) { if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, qinfo->qtype, qinfo->qclass, region, 2))) return NULL; - /* TTL already subtracted in grab_nsec */ - if(!dns_msg_authadd(msg, region, rrset, 0)) + if(!dns_msg_authadd(msg, region, nsec, 0)) return NULL; if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL)) return NULL; return msg; + } else if(nsec && val_nsec_proves_name_error(nsec, qinfo->qname)) { + if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, + qinfo->qtype, qinfo->qclass, region, 3))) + return NULL; + if(!(ce = nsec_closest_encloser(qinfo->qname, nsec))) + return NULL; + dname_count_size_labels(ce, &ce_len); + + /* No extra extra NSEC required if both nameerror qname and + * nodata *.ce. are proven already. */ + if(!nodata_wc || query_dname_compare(nodata_wc, ce) != 0) { + /* Qname proven non existing, get wildcard record for + * QTYPE or NSEC covering or matching wildcard. */ + + /* Num labels in ce is always smaller than in qname, + * therefore adding the wildcard label cannot overflow + * buffer. */ + wc_ce[0] = 1; + wc_ce[1] = (uint8_t)'*'; + memmove(wc_ce+2, ce, ce_len); + wc_qinfo.qname = wc_ce; + wc_qinfo.qname_len = ce_len + 2; + wc_qinfo.qtype = qinfo->qtype; + + + if((cache_wc = rrset_cache_lookup(rrset_cache, wc_qinfo.qname, + wc_qinfo.qname_len, wc_qinfo.qtype, + qinfo->qclass, 0/*flags*/, now, 0/*read only*/))) { + /* Synthesize wildcard answer */ + wcrr_data = (struct packed_rrset_data*)cache_wc->entry.data; + if(!(wcrr_data->security == sec_status_secure || + (wcrr_data->security == sec_status_unchecked && + wcrr_data->rrsig_count > 0))) { + lock_rw_unlock(&cache_wc->entry.lock); + return NULL; + } + if(!(wcrr = packed_rrset_copy_region(cache_wc, + region, now))) { + lock_rw_unlock(&cache_wc->entry.lock); + return NULL; + }; + lock_rw_unlock(&cache_wc->entry.lock); + wcrr->rk.dname = qinfo->qname; + wcrr->rk.dname_len = qinfo->qname_len; + if(!dns_msg_ansadd(msg, region, wcrr, 0)) + return NULL; + /* No SOA needed for wildcard synthesised + * answer. */ + addsoa = 0; + } else { + /* Get wildcard NSEC for possible non existence + * proof */ + if(!(wcrr = neg_find_nsec(neg, wc_qinfo.qname, + wc_qinfo.qname_len, qinfo->qclass, + rrset_cache, now, region))) + return NULL; + + nodata_wc = NULL; + if(val_nsec_proves_name_error(wcrr, wc_ce)) + rcode = LDNS_RCODE_NXDOMAIN; + else if(!nsec_proves_nodata(wcrr, &wc_qinfo, + &nodata_wc) || nodata_wc) + /* &nodata_wc shoudn't be set, wc_qinfo + * already contains wildcard domain. */ + /* NSEC doesn't prove anything for + * wildcard. */ + return NULL; + if(query_dname_compare(wcrr->rk.dname, + nsec->rk.dname) != 0) + if(!dns_msg_authadd(msg, region, wcrr, 0)) + return NULL; + } + } + + if(!dns_msg_authadd(msg, region, nsec, 0)) + return NULL; + if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL)) + return NULL; + + FLAGS_SET_RCODE(msg->rep->flags, rcode); + return msg; } + /* No aggressive use of NSEC3 for now, only proceed for DS types. */ + if(qinfo->qtype != LDNS_RR_TYPE_DS){ + return NULL; + } /* check NSEC3 neg cache for type DS */ /* need to look one zone higher for DS type */ zname = qinfo->qname; diff --git a/validator/val_neg.h b/validator/val_neg.h index 6ae71306c378..00dad6df1f5c 100644 --- a/validator/val_neg.h +++ b/validator/val_neg.h @@ -250,6 +250,7 @@ int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len, * more conservative, especially for opt-out zones, since the receiver * may have a trust-anchor below the optout and thus the optout cannot * be used to create a proof from the negative cache. + * @param cfg: config options. * @return a reply message if something was found. * This reply may still need validation. * NULL if nothing found (or out of memory). @@ -257,7 +258,7 @@ int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len, struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, struct regional* region, struct rrset_cache* rrset_cache, struct sldns_buffer* buf, time_t now, - int addsoa, uint8_t* topname); + int addsoa, uint8_t* topname, struct config_file* cfg); /**** functions exposed for unit test ****/ diff --git a/validator/val_nsec.c b/validator/val_nsec.c index 4604f3d6d423..a795e77339d3 100644 --- a/validator/val_nsec.c +++ b/validator/val_nsec.c @@ -513,7 +513,6 @@ val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, /* Determine if a NSEC record proves the non-existence of a * wildcard that could have produced qname. */ int labs; - int i; uint8_t* ce = nsec_closest_encloser(qname, nsec); uint8_t* strip; size_t striplen; @@ -526,13 +525,13 @@ val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, * and next names. */ labs = dname_count_labels(qname) - dname_count_labels(ce); - for(i=labs; i>0; i--) { + if(labs > 0) { /* i is number of labels to strip off qname, prepend * wild */ strip = qname; striplen = qnamelen; - dname_remove_labels(&strip, &striplen, i); + dname_remove_labels(&strip, &striplen, labs); if(striplen > LDNS_MAX_DOMAINLEN-2) - continue; /* too long to prepend wildcard */ + return 0; /* too long to prepend wildcard */ buf[0] = 1; buf[1] = (uint8_t)'*'; memmove(buf+2, strip, striplen); diff --git a/validator/val_sigcrypt.c b/validator/val_sigcrypt.c index 9987b9b3f627..cfa3eadcf6a0 100644 --- a/validator/val_sigcrypt.c +++ b/validator/val_sigcrypt.c @@ -1225,13 +1225,51 @@ sigdate_error(const char* str, int32_t expi, int32_t incep, int32_t now) (unsigned)incep, (unsigned)now); } +/** RFC 1918 comparison, uses unsigned integers, and tries to avoid + * compiler optimization (eg. by avoiding a-b<0 comparisons), + * this routine matches compare_serial(), for SOA serial number checks */ +static int +compare_1918(uint32_t a, uint32_t b) +{ + /* for 32 bit values */ + const uint32_t cutoff = ((uint32_t) 1 << (32 - 1)); + + if (a == b) { + return 0; + } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) { + return -1; + } else { + return 1; + } +} + +/** if we know that b is larger than a, return the difference between them, + * that is the distance between them. in RFC1918 arith */ +static uint32_t +subtract_1918(uint32_t a, uint32_t b) +{ + /* for 32 bit values */ + const uint32_t cutoff = ((uint32_t) 1 << (32 - 1)); + + if(a == b) + return 0; + if(a < b && b - a < cutoff) { + return b-a; + } + if(a > b && a - b > cutoff) { + return ((uint32_t)0xffffffff) - (a-b-1); + } + /* wrong case, b smaller than a */ + return 0; +} + /** check rrsig dates */ static int check_dates(struct val_env* ve, uint32_t unow, uint8_t* expi_p, uint8_t* incep_p, char** reason) { /* read out the dates */ - int32_t expi, incep, now; + uint32_t expi, incep, now; memmove(&expi, expi_p, sizeof(expi)); memmove(&incep, incep_p, sizeof(incep)); expi = ntohl(expi); @@ -1245,21 +1283,21 @@ check_dates(struct val_env* ve, uint32_t unow, } now = ve->date_override; verbose(VERB_ALGO, "date override option %d", (int)now); - } else now = (int32_t)unow; + } else now = unow; /* check them */ - if(incep - expi > 0) { + if(compare_1918(incep, expi) > 0) { sigdate_error("verify: inception after expiration, " "signature bad", expi, incep, now); *reason = "signature inception after expiration"; return 0; } - if(incep - now > 0) { + if(compare_1918(incep, now) > 0) { /* within skew ? (calc here to avoid calculation normally) */ - int32_t skew = (expi-incep)/10; - if(skew < ve->skew_min) skew = ve->skew_min; - if(skew > ve->skew_max) skew = ve->skew_max; - if(incep - now > skew) { + uint32_t skew = subtract_1918(incep, expi)/10; + if(skew < (uint32_t)ve->skew_min) skew = ve->skew_min; + if(skew > (uint32_t)ve->skew_max) skew = ve->skew_max; + if(subtract_1918(now, incep) > skew) { sigdate_error("verify: signature bad, current time is" " before inception date", expi, incep, now); *reason = "signature before inception date"; @@ -1268,11 +1306,11 @@ check_dates(struct val_env* ve, uint32_t unow, sigdate_error("verify warning suspicious signature inception " " or bad local clock", expi, incep, now); } - if(now - expi > 0) { - int32_t skew = (expi-incep)/10; - if(skew < ve->skew_min) skew = ve->skew_min; - if(skew > ve->skew_max) skew = ve->skew_max; - if(now - expi > skew) { + if(compare_1918(now, expi) > 0) { + uint32_t skew = subtract_1918(incep, expi)/10; + if(skew < (uint32_t)ve->skew_min) skew = ve->skew_min; + if(skew > (uint32_t)ve->skew_max) skew = ve->skew_max; + if(subtract_1918(expi, now) > skew) { sigdate_error("verify: signature expired", expi, incep, now); *reason = "signature expired"; diff --git a/validator/val_utils.c b/validator/val_utils.c index 0eabb7f33ba4..2f36fccfd4fe 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -767,7 +767,8 @@ rrsig_get_labcount(struct packed_rrset_data* d, size_t sig) } int -val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc) +val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc, + size_t* wc_len) { struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> entry.data; @@ -800,6 +801,7 @@ val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc) if(labdiff > 0) { *wc = wn; dname_remove_labels(wc, &wl, labdiff); + *wc_len = wl; return 1; } return 1; @@ -1161,6 +1163,6 @@ val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c, qinfo.local_alias = NULL; /* do not add SOA to reply message, it is going to be used internal */ msg = val_neg_getmsg(env->neg_cache, &qinfo, region, env->rrset_cache, - env->scratch_buffer, *env->now, 0, topname); + env->scratch_buffer, *env->now, 0, topname, env->cfg); return msg; } diff --git a/validator/val_utils.h b/validator/val_utils.h index b582472f8faf..6e9867f6e3c7 100644 --- a/validator/val_utils.h +++ b/validator/val_utils.h @@ -271,6 +271,7 @@ int val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset); * @param wc: the wildcard name, if the rrset was synthesized from a wildcard. * unchanged if not. The wildcard name, without "*." in front, is * returned. This is a pointer into the rrset owner name. + * @param wc_len: the length of the returned wildcard name. * @return false if the signatures are inconsistent in indicating the * wildcard status; possible spoofing of wildcard response for other * responses is being tried. We lost the status which rrsig was verified @@ -279,7 +280,8 @@ int val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset); * of service; but in that you could also have removed the real * signature anyway. */ -int val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc); +int val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc, + size_t* wc_len); /** * Chase the cname to the next query name. diff --git a/validator/validator.c b/validator/validator.c index 456bffd005f3..5ed45e9bdefd 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -51,6 +51,7 @@ #include "validator/val_sigcrypt.h" #include "validator/autotrust.h" #include "services/cache/dns.h" +#include "services/cache/rrset.h" #include "util/data/dname.h" #include "util/module.h" #include "util/log.h" @@ -461,7 +462,7 @@ generate_keytag_query(struct module_qstate* qstate, int id, return 0; } - log_nametypeclass(VERB_ALGO, "keytag query", keytagdname, + log_nametypeclass(VERB_OPS, "generate keytag query", keytagdname, LDNS_RR_TYPE_NULL, ta->dclass); if(!generate_request(qstate, id, keytagdname, dnamebuf_len, LDNS_RR_TYPE_NULL, ta->dclass, 0, &newq, 1)) { @@ -745,6 +746,8 @@ validate_positive_response(struct module_env* env, struct val_env* ve, struct key_entry_key* kkey) { uint8_t* wc = NULL; + size_t wl; + int wc_cached = 0; int wc_NSEC_ok = 0; int nsec3s_seen = 0; size_t i; @@ -757,13 +760,19 @@ validate_positive_response(struct module_env* env, struct val_env* ve, /* Check to see if the rrset is the result of a wildcard * expansion. If so, an additional check will need to be * made in the authority section. */ - if(!val_rrset_wildcard(s, &wc)) { + if(!val_rrset_wildcard(s, &wc, &wl)) { log_nametypeclass(VERB_QUERY, "Positive response has " "inconsistent wildcard sigs:", s->rk.dname, ntohs(s->rk.type), ntohs(s->rk.rrset_class)); chase_reply->security = sec_status_bogus; return; } + if(wc && !wc_cached && env->cfg->aggressive_nsec) { + rrset_cache_update_wildcard(env->rrset_cache, s, wc, wl, + env->alloc, *env->now); + wc_cached = 1; + } + } /* validate the AUTHORITY section as well - this will generally be @@ -944,6 +953,9 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, int nsec3s_seen = 0; struct ub_packed_rrset_key* s; size_t i; + uint8_t* ce; + int ce_labs = 0; + int prev_ce_labs = 0; for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ chase_reply->ns_numrrsets; i++) { @@ -951,9 +963,19 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { if(val_nsec_proves_name_error(s, qchase->qname)) has_valid_nsec = 1; - if(val_nsec_proves_no_wc(s, qchase->qname, - qchase->qname_len)) - has_valid_wnsec = 1; + ce = nsec_closest_encloser(qchase->qname, s); + ce_labs = dname_count_labels(ce); + /* Use longest closest encloser to prove wildcard. */ + if(ce_labs > prev_ce_labs || + (ce_labs == prev_ce_labs && + has_valid_wnsec == 0)) { + if(val_nsec_proves_no_wc(s, qchase->qname, + qchase->qname_len)) + has_valid_wnsec = 1; + else + has_valid_wnsec = 0; + } + prev_ce_labs = ce_labs; if(val_nsec_proves_insecuredelegation(s, qchase)) { verbose(VERB_ALGO, "delegation is insecure"); chase_reply->security = sec_status_insecure; @@ -1068,6 +1090,7 @@ validate_any_response(struct module_env* env, struct val_env* ve, /* but check if a wildcard response is given, then check NSEC/NSEC3 * for qname denial to see if wildcard is applicable */ uint8_t* wc = NULL; + size_t wl; int wc_NSEC_ok = 0; int nsec3s_seen = 0; size_t i; @@ -1086,7 +1109,7 @@ validate_any_response(struct module_env* env, struct val_env* ve, /* Check to see if the rrset is the result of a wildcard * expansion. If so, an additional check will need to be * made in the authority section. */ - if(!val_rrset_wildcard(s, &wc)) { + if(!val_rrset_wildcard(s, &wc, &wl)) { log_nametypeclass(VERB_QUERY, "Positive ANY response" " has inconsistent wildcard sigs:", s->rk.dname, ntohs(s->rk.type), @@ -1175,6 +1198,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve, struct key_entry_key* kkey) { uint8_t* wc = NULL; + size_t wl; int wc_NSEC_ok = 0; int nsec3s_seen = 0; size_t i; @@ -1187,7 +1211,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve, /* Check to see if the rrset is the result of a wildcard * expansion. If so, an additional check will need to be * made in the authority section. */ - if(!val_rrset_wildcard(s, &wc)) { + if(!val_rrset_wildcard(s, &wc, &wl)) { log_nametypeclass(VERB_QUERY, "Cname response has " "inconsistent wildcard sigs:", s->rk.dname, ntohs(s->rk.type), ntohs(s->rk.rrset_class)); @@ -1296,6 +1320,9 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, int nsec3s_seen = 0; /* nsec3s seen */ struct ub_packed_rrset_key* s; size_t i; + uint8_t* nsec_ce; /* Used to find the NSEC with the longest ce */ + int ce_labs = 0; + int prev_ce_labs = 0; /* the AUTHORITY section */ for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ @@ -1314,9 +1341,19 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, ce = nsec_closest_encloser(qchase->qname, s); nxdomain_valid_nsec = 1; } - if(val_nsec_proves_no_wc(s, qchase->qname, - qchase->qname_len)) - nxdomain_valid_wnsec = 1; + nsec_ce = nsec_closest_encloser(qchase->qname, s); + ce_labs = dname_count_labels(nsec_ce); + /* Use longest closest encloser to prove wildcard. */ + if(ce_labs > prev_ce_labs || + (ce_labs == prev_ce_labs && + nxdomain_valid_wnsec == 0)) { + if(val_nsec_proves_no_wc(s, qchase->qname, + qchase->qname_len)) + nxdomain_valid_wnsec = 1; + else + nxdomain_valid_wnsec = 0; + } + prev_ce_labs = ce_labs; if(val_nsec_proves_insecuredelegation(s, qchase)) { verbose(VERB_ALGO, "delegation is insecure"); chase_reply->security = sec_status_insecure; @@ -2134,6 +2171,10 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, if(vq->orig_msg->rep->security == sec_status_secure) { log_query_info(VERB_DETAIL, "validation success", &qstate->qinfo); + if(!qstate->no_cache_store) { + val_neg_addreply(qstate->env->neg_cache, + vq->orig_msg->rep); + } } } |
