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
* 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) {
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
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;
}
* 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))
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;
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;
}
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;
}
* @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 ****/
#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"
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;
+}
*/
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 */
#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)
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)) {