From c68aebb3d7f4beb2f9f7d47cfb13125ceff758a7 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Wed, 9 Dec 2009 14:55:19 +0000 Subject: [PATCH] - Fix SOA excluded from negative DS responses. Reported by Hauke Lampe. The negative cache did not include proper SOA records for negative qtype DS responses which makes BIND barf on it, such responses are now only used internally. - Fix negative cache lookup of closestencloser check of DS type bit. git-svn-id: file:///svn/unbound/trunk@1932 be551aaa-1e26-0410-a405-d3ace91eadb9 --- doc/Changelog | 5 +++ iterator/iterator.c | 3 +- validator/val_neg.c | 76 ++++++++++++++++++++++++++++++++++++++++--- validator/val_neg.h | 5 ++- validator/val_utils.c | 38 ++++++++++++++++++++++ validator/val_utils.h | 17 ++++++++++ validator/validator.c | 22 +++++++++++++ 7 files changed, 159 insertions(+), 7 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index f8e6b862b..608e41a64 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,11 @@ 9 December 2009: Wouter - Fix Bug#287(reopened): update of ldns tarball with fix for parse errors generated for domain names like '.example.com'. + - Fix SOA excluded from negative DS responses. Reported by Hauke + Lampe. The negative cache did not include proper SOA records for + negative qtype DS responses which makes BIND barf on it, such + responses are now only used internally. + - Fix negative cache lookup of closestencloser check of DS type bit. 8 December 2009: Wouter - Fix for lookup of parent-child disagreement domains, where the diff --git a/iterator/iterator.c b/iterator/iterator.c index a2491ae29..576038ef0 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -872,7 +872,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, * NOERROR/NODATA or NXDOMAIN answers that need validation */ msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase, qstate->region, qstate->env->rrset_cache, - qstate->env->scratch_buffer, *qstate->env->now); + qstate->env->scratch_buffer, + *qstate->env->now, 1/*add SOA*/); } } if(msg) { diff --git a/validator/val_neg.c b/validator/val_neg.c index 03b48a3ea..808708398 100644 --- a/validator/val_neg.c +++ b/validator/val_neg.c @@ -1093,6 +1093,25 @@ void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep, lock_basic_unlock(&neg->lock); } +/** + * Check that an NSEC3 rrset does not have a type set. + * None of the nsec3s in a hash-collision are allowed to have the type. + * (since we do not know which one is the nsec3 looked at, flags, ..., we + * ignore the cached item and let it bypass negative caching). + * @param k: the nsec3 rrset to check. + * @param t: type to check + * @return true if no RRs have the type. + */ +static int nsec3_no_type(struct ub_packed_rrset_key* k, uint16_t t) +{ + int count = (int)((struct packed_rrset_data*)k->entry.data)->count; + int i; + for(i=0; ientry.lock); return NULL; } @@ -1300,8 +1321,9 @@ neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, * ce_rrset equals a closer encloser. * nc_rrset is optout. * No need to check wildcard for type DS */ + /* capacity=3: ce + nc + soa(if needed) */ if(!(msg = dns_msg_create(qname, qname_len, - LDNS_RR_TYPE_DS, zone->dclass, region, 2))) + LDNS_RR_TYPE_DS, zone->dclass, region, 3))) return NULL; /* now=0 because TTL was reduced in grab_nsec */ if(!dns_msg_authadd(msg, region, ce_rrset, 0)) @@ -1313,10 +1335,48 @@ neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, return NULL; } +/** + * Add SOA record for external responses. + * @param rrset_cache: to look into. + * @param now: current time. + * @param region: where to perform the allocation + * @param msg: current msg with NSEC. + * @param zone: val_neg_zone if we have one. + * @return false on lookup or alloc failure. + */ +static int add_soa(struct rrset_cache* rrset_cache, uint32_t now, + struct regional* region, struct dns_msg* msg, struct val_neg_zone* zone) +{ + struct ub_packed_rrset_key* soa; + uint8_t* nm; + size_t nmlen; + uint16_t dclass; + if(zone) { + nm = zone->name; + nmlen = zone->len; + dclass = zone->dclass; + } else { + /* Assumes the signer is the zone SOA to add */ + nm = reply_nsec_signer(msg->rep, &nmlen, &dclass); + if(!nm) + return 0; + } + soa = rrset_cache_lookup(rrset_cache, nm, nmlen, LDNS_RR_TYPE_SOA, + dclass, 0, now, 0); + if(!soa) + return 0; + if(!dns_msg_authadd(msg, region, soa, now)) { + lock_rw_unlock(&soa->entry.lock); + return 0; + } + lock_rw_unlock(&soa->entry.lock); + return 1; +} + struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, struct regional* region, struct rrset_cache* rrset_cache, - ldns_buffer* buf, uint32_t now) + ldns_buffer* buf, uint32_t now, int addsoa) { struct dns_msg* msg; struct ub_packed_rrset_key* rrset; @@ -1340,11 +1400,13 @@ val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, if(rrset) { /* return msg with that rrset */ if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, - qinfo->qtype, qinfo->qclass, region, 1))) + qinfo->qtype, qinfo->qclass, region, 2))) return NULL; /* TTL already subtracted in grab_nsec */ if(!dns_msg_authadd(msg, region, rrset, 0)) return NULL; + if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL)) + return NULL; return msg; } @@ -1368,6 +1430,10 @@ val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, msg = neg_nsec3_proof_ds(zone, qinfo->qname, qinfo->qname_len, zname_labs+1, buf, rrset_cache, region, now); + if(addsoa && !add_soa(rrset_cache, now, region, msg, zone)) { + lock_basic_unlock(&neg->lock); + return NULL; + } lock_basic_unlock(&neg->lock); return msg; } diff --git a/validator/val_neg.h b/validator/val_neg.h index 834fa5c45..8b17b32db 100644 --- a/validator/val_neg.h +++ b/validator/val_neg.h @@ -240,13 +240,16 @@ int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len, * @param rrset_cache: rrset cache. * @param buf: temporary buffer. * @param now: to check TTLs against. + * @param addsoa: if true, produce result for external consumption. + * if false, do not add SOA - for unbound-internal consumption. * @return a reply message if something was found. * This reply may still need validation. * NULL if nothing found (or out of memory). */ struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, struct regional* region, - struct rrset_cache* rrset_cache, ldns_buffer* buf, uint32_t now); + struct rrset_cache* rrset_cache, ldns_buffer* buf, uint32_t now, + int addsoa); /**** functions exposed for unit test ****/ diff --git a/validator/val_utils.c b/validator/val_utils.c index 6534da156..2eb88cde7 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -44,7 +44,10 @@ #include "validator/val_kentry.h" #include "validator/val_sigcrypt.h" #include "validator/val_anchor.h" +#include "validator/val_nsec.h" +#include "validator/val_neg.h" #include "services/cache/rrset.h" +#include "services/cache/dns.h" #include "util/data/msgreply.h" #include "util/data/packed_rrset.h" #include "util/data/dname.h" @@ -881,3 +884,38 @@ int val_has_signed_nsecs(struct reply_info* rep, char** reason) else *reason = "no signatures over NSEC3s"; return 0; } + +struct dns_msg* +val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c, + struct regional* region) +{ + struct dns_msg* msg; + struct query_info qinfo; + struct ub_packed_rrset_key *rrset = rrset_cache_lookup( + env->rrset_cache, nm, nmlen, LDNS_RR_TYPE_DS, c, 0, + *env->now, 0); + if(rrset) { + /* DS rrset exists. Return it to the validator immediately*/ + struct ub_packed_rrset_key* copy = packed_rrset_copy_region( + rrset, region, *env->now); + lock_rw_unlock(&rrset->entry.lock); + if(!copy) + return NULL; + msg = dns_msg_create(nm, nmlen, LDNS_RR_TYPE_DS, c, region, 1); + if(!msg) + return NULL; + msg->rep->rrsets[0] = copy; + msg->rep->rrset_count++; + msg->rep->an_numrrsets++; + return msg; + } + /* lookup in rrset and negative cache for NSEC/NSEC3 */ + qinfo.qname = nm; + qinfo.qname_len = nmlen; + qinfo.qtype = LDNS_RR_TYPE_DS; + qinfo.qclass = c; + /* 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); + return msg; +} diff --git a/validator/val_utils.h b/validator/val_utils.h index 0e7704d9d..7340c4bd1 100644 --- a/validator/val_utils.h +++ b/validator/val_utils.h @@ -321,4 +321,21 @@ int val_has_signed_nsecs(struct reply_info* rep, char** reason); */ int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset); +/** + * Find DS denial message in cache. Saves new qstate allocation and allows + * the validator to use partial content which is not enough to construct a + * message for network (or user) consumption. Without SOA for example, + * which is a common occurence in the unbound code since the referrals contain + * NSEC/NSEC3 rrs without the SOA element, thus do not allow synthesis of a + * full negative reply, but do allow synthesis of sufficient proof. + * @param env: query env with caches and time. + * @param nm: name of DS record sought. + * @param nmlen: length of name. + * @param c: class of DS RR. + * @param region: where to allocate result. + * @return a dns_msg on success. NULL on failure. + */ +struct dns_msg* val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, + uint16_t c, struct regional* region); + #endif /* VALIDATOR_VAL_UTILS_H */ diff --git a/validator/validator.c b/validator/validator.c index 123fa2959..0e0582a5d 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -58,6 +58,11 @@ #include "util/config_file.h" #include "util/fptr_wlist.h" +/* 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, + struct query_info* qinfo, struct sock_list* origin); + /** fill up nsec3 key iterations config entry */ static int fill_nsec3_iter(struct val_env* ve, char* s, int c) @@ -1457,6 +1462,23 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname, target_key_name) != 0) { + /* check if there is a cache entry : pick up an NSEC if + * there is no DS, check if that NSEC has DS-bit unset, and + * thus can disprove the secure delagation we seek. + * We can then use that NSEC even in the absence of a SOA + * record that would be required by the iterator to supply + * a completely protocol-correct response. + * Uses negative cache for NSEC3 lookup of DS responses. */ + /* only if cache not blacklisted, of course */ + struct dns_msg* msg; + if(!qstate->blacklist && !vq->chain_blacklist && + (msg=val_find_DS(qstate->env, target_key_name, + target_key_len, vq->qchase.qclass, qstate->region)) ) { + verbose(VERB_ALGO, "Process cached DS response"); + process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, + msg, &msg->qinfo, NULL); + return 1; /* continue processing ds-response results */ + } if(!generate_request(qstate, id, target_key_name, target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass, BIT_CD)) {