From: Ralph Dolmans Date: Thu, 22 Feb 2018 15:12:31 +0000 (+0000) Subject: - Save wildcard RRset from answer with original owner for use in aggressive X-Git-Tag: release-1.7.0rc1~11 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=24fc3242fc5cddfc39bfa725cece87a22aa1ea32;p=thirdparty%2Funbound.git - Save wildcard RRset from answer with original owner for use in aggressive NSEC. git-svn-id: file:///svn/unbound/trunk@4550 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index c1d822fc9..35a1958f7 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +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. diff --git a/services/cache/dns.c b/services/cache/dns.c index 71ff5266b..8a0e49f5c 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -787,10 +787,11 @@ dns_cache_lookup(struct module_env* env, (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); diff --git a/services/cache/rrset.c b/services/cache/rrset.c index 7e5732b76..0b41fcd7d 100644 --- a/services/cache/rrset.c +++ b/services/cache/rrset.c @@ -47,6 +47,7 @@ #include "util/data/msgreply.h" #include "util/regional.h" #include "util/alloc.h" +#include "util/net_help.h" void rrset_markdel(void* key) @@ -237,6 +238,37 @@ rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref, 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, diff --git a/services/cache/rrset.h b/services/cache/rrset.h index d5439ef08..35a0d732b 100644 --- a/services/cache/rrset.h +++ b/services/cache/rrset.h @@ -133,6 +133,24 @@ void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key, 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. diff --git a/validator/val_neg.c b/validator/val_neg.c index 25aaaa61e..403405d3b 100644 --- a/validator/val_neg.c +++ b/validator/val_neg.c @@ -847,34 +847,56 @@ void neg_insert_data(struct val_neg_cache* neg, 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; ian_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; diff --git a/validator/val_neg.h b/validator/val_neg.h index 00dad6df1..1d8128c41 100644 --- a/validator/val_neg.h +++ b/validator/val_neg.h @@ -198,9 +198,12 @@ int val_neg_zone_compare(const void* a, const void* b); * 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. diff --git a/validator/val_utils.c b/validator/val_utils.c index 42795ec46..2f36fccfd 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; diff --git a/validator/val_utils.h b/validator/val_utils.h index b582472f8..6e9867f6e 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 81d67cd36..d717c30e7 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" @@ -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 @@ -1081,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; @@ -1099,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), @@ -1188,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; @@ -1200,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)); @@ -2178,7 +2189,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, &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); } } } @@ -3109,7 +3120,7 @@ process_dlv_response(struct module_qstate* qstate, struct val_qstate* vq, 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