+22 February 2018: Ralph
+ - Save wildcard RRset from answer with original owner for use in
+ aggressive NSEC.
+
21 February 2018: Wouter
- Fix #3512: unbound incorrectly reports SERVFAIL for CAA query
when there is a CNAME loop.
(rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen,
LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) {
uint8_t* wc = NULL;
+ size_t wl;
/* if the rrset is not a wildcard expansion, with wcname */
/* because, if we return that CNAME rrset on its own, it is
* missing the NSEC or NSEC3 proof */
- if(!(val_rrset_wildcard(rrset, &wc) && wc != NULL)) {
+ if(!(val_rrset_wildcard(rrset, &wc, &wl) && wc != NULL)) {
struct dns_msg* msg = rrset_msg(rrset, region, now, &k);
if(msg) {
lock_rw_unlock(&rrset->entry.lock);
#include "util/data/msgreply.h"
#include "util/regional.h"
#include "util/alloc.h"
+#include "util/net_help.h"
void
rrset_markdel(void* key)
return 0;
}
+void rrset_cache_update_wildcard(struct rrset_cache* rrset_cache,
+ struct ub_packed_rrset_key* rrset, uint8_t* ce, size_t ce_len,
+ struct alloc_cache* alloc, time_t timenow)
+{
+ struct rrset_ref ref;
+ uint8_t wc_dname[LDNS_MAX_DOMAINLEN+3];
+ rrset = packed_rrset_copy_alloc(rrset, alloc, timenow);
+ if(!rrset) {
+ log_err("malloc failure in rrset_cache_update_wildcard");
+ return;
+ }
+ /* ce has at least one label less then qname, we can therefore safely
+ * add the wildcard label. */
+ wc_dname[0] = 1;
+ wc_dname[1] = (uint8_t)'*';
+ memmove(wc_dname+2, ce, ce_len);
+
+ rrset->rk.dname_len = ce_len + 2;
+ rrset->rk.dname = (uint8_t*)memdup(wc_dname, rrset->rk.dname_len);
+ if(!rrset->rk.dname) {
+ log_err("memdup failure in rrset_cache_update_wildcard");
+ return;
+ }
+
+ rrset->entry.hash = rrset_key_hash(&rrset->rk);
+ ref.key = rrset;
+ ref.id = rrset->id;
+ /* ignore ret: if it was in the cache, ref updated */
+ (void)rrset_cache_update(rrset_cache, &ref, alloc, timenow);
+}
+
struct ub_packed_rrset_key*
rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen,
uint16_t qtype, uint16_t qclass, uint32_t flags, time_t timenow,
int rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
struct alloc_cache* alloc, time_t timenow);
+/**
+ * Update or add an rrset in the rrset cache using a wildcard dname.
+ * Generates wildcard dname by prepending the wildcard label to the closest
+ * encloser. Will lookup if the rrset is in the cache and perform an update if
+ * necessary.
+ *
+ * @param rrset_cache: the rrset cache.
+ * @param rrset: which rrset to cache as wildcard. This rrset is left
+ * untouched.
+ * @param ce: the closest encloser, will be uses to generate the wildcard dname.
+ * @param ce_len: the closest encloser lenght.
+ * @param alloc: how to allocate (and deallocate) the special rrset key.
+ * @param timenow: current time (to see if ttl in cache is expired).
+ */
+void rrset_cache_update_wildcard(struct rrset_cache* rrset_cache,
+ struct ub_packed_rrset_key* rrset, uint8_t* ce, size_t ce_len,
+ struct alloc_cache* alloc, time_t timenow);
+
/**
* Lookup rrset. You obtain read/write lock. You must unlock before lookup
* anything of else.
wipeout(neg, zone, el, nsec);
}
-void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep)
+void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep,
+ uint8_t* qname)
{
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 = soa->rk.rrset_class;
+ }
+ else {
+ /* No SOA in positive (wildcard) answer. Use signer from the
+ * validated answer RRsets' signature. */
+ size_t i;
+ for(i=0; i<rep->an_numrrsets; i++) {
+ if(qname && query_dname_compare(qname,
+ rep->rrsets[i]->rk.dname) == 0) {
+ val_find_rrset_signer(rep->rrsets[i],
+ &dname, &dname_len);
+ rrset_class = rep->rrsets[i]->rk.rrset_class;
+ break;
+ }
+ }
+ if(!dname)
+ 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, ntohs(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,
+ ntohs(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, ntohs(rrset_class)))) {
lock_basic_unlock(&neg->lock);
log_err("out of memory adding negative zone");
return;
* Insert NSECs from this message into the negative cache for reference.
* @param neg: negative cache
* @param rep: reply with NSECs.
+ * @param qname: used to find correct signer, needed when rep does not contain
+ * a SOA record.
* Errors are ignored, means that storage is omitted.
*/
-void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep);
+void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep,
+ uint8_t* qname);
/**
* Insert NSECs from this referral into the negative cache for reference.
}
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;
if(labdiff > 0) {
*wc = wn;
dname_remove_labels(wc, &wl, labdiff);
+ *wc_len = wl;
return 1;
}
return 1;
* @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
* 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.
#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"
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;
/* 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
/* 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;
/* 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),
struct key_entry_key* kkey)
{
uint8_t* wc = NULL;
+ size_t wl;
int wc_NSEC_ok = 0;
int nsec3s_seen = 0;
size_t i;
/* 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));
&qstate->qinfo);
if(!qstate->no_cache_store) {
val_neg_addreply(qstate->env->neg_cache,
- vq->orig_msg->rep);
+ vq->orig_msg->rep, qstate->qinfo.qname);
}
}
}
return;
}
/* store NSECs into negative cache */
- val_neg_addreply(ve->neg_cache, msg->rep);
+ val_neg_addreply(ve->neg_cache, msg->rep, NULL);
/* was the lookup a failure?
* if we have to go up into the DLV for a higher DLV anchor