aboutsummaryrefslogtreecommitdiff
path: root/validator
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2018-05-12 11:56:38 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2018-05-12 11:56:38 +0000
commit197f1a0fe3e81cde0cd25a3a1f37ebedf9a99488 (patch)
tree9a121ad4cef31a32608c065400c31246d549c0dc /validator
parentb5c63b395d5df7ff6ee4d41a7dfecd938d894037 (diff)
Vendor import of Unbound 1.7.0.vendor/unbound/1.7.0
Diffstat (limited to 'validator')
-rw-r--r--validator/val_neg.c272
-rw-r--r--validator/val_neg.h3
-rw-r--r--validator/val_nsec.c7
-rw-r--r--validator/val_sigcrypt.c64
-rw-r--r--validator/val_utils.c6
-rw-r--r--validator/val_utils.h4
-rw-r--r--validator/validator.c61
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);
+ }
}
}