]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix SOA excluded from negative DS responses. Reported by Hauke
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 9 Dec 2009 14:55:19 +0000 (14:55 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 9 Dec 2009 14:55:19 +0000 (14:55 +0000)
  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
iterator/iterator.c
validator/val_neg.c
validator/val_neg.h
validator/val_utils.c
validator/val_utils.h
validator/validator.c

index f8e6b862b40510897cf62457c5a4687b000e8cd0..608e41a646b74f9a38694cb95b481eee9822e89e 100644 (file)
@@ -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
index a2491ae29d379aa5ecceec3c122a4986de6de75a..576038ef05e8979c21dc1263cfd322c6881fa595 100644 (file)
@@ -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) {
index 03b48a3eab09fc400bfa16f7ce22ea2773f29e24..808708398dc762306c60081ecfaacd188ec48a56 100644 (file)
@@ -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; i<count; i++)
+               if(nsec3_has_type(k, i, t))
+                       return 0;
+       return 1;
+}
+
 /**
  * See if rrset exists in rrset cache.
  * If it does, the bit is checked, and if not expired, it is returned
@@ -1132,8 +1151,10 @@ grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len,
                return NULL;
        }
        /* check if checktype is absent */
-       if(checkbit && qtype == LDNS_RR_TYPE_NSEC &&
-               nsec_has_type(k, checktype)) {
+       if(checkbit && (
+               (qtype == LDNS_RR_TYPE_NSEC && nsec_has_type(k, checktype)) ||
+               (qtype == LDNS_RR_TYPE_NSEC3 && !nsec3_no_type(k, checktype))
+               )) {
                lock_rw_unlock(&k->entry.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;
 }
index 834fa5c450c0904f81fdd1ffb55a73b99d57f4dd..8b17b32dbd31fb1b9a201151383b6dd07aefbfea 100644 (file)
@@ -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 ****/
index 6534da1568c3ae2050edd659ccdf5a752f39ad8a..2eb88cde7ec5f4aee22008fa977fb5558b33caf7 100644 (file)
 #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;
+}
index 0e7704d9dd75f5b90befcc3a2a680286271cf62e..7340c4bd1a08c65e910fd008306277c1696bdf68 100644 (file)
@@ -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 */
index 123fa295944ccd20a86cd63fe1d18b1d29d7550c..0e0582a5d49a8cf0a93a21ba05474635426b8e73 100644 (file)
 #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)) {