nsec3 work, prove name error.

git-svn-id: file:///svn/unbound/trunk@610 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-09-13 15:02:33 +00:00
parent d85debfae4
commit facde2ca10
7 changed files with 499 additions and 25 deletions

View file

@ -1,3 +1,6 @@
13 September 2007: Wouter
- nsec3 find matching and covering, ce proof, prove namerror msg.
12 September 2007: Wouter 12 September 2007: Wouter
- fixup of manual page warnings, like for NSD bugreport. - fixup of manual page warnings, like for NSD bugreport.
- nsec3 work, config, max iterations, filter, and hash cache. - nsec3 work, config, max iterations, filter, and hash cache.

View file

@ -313,6 +313,13 @@ dstest_file(const char* fname)
ldns_buffer_free(buf); ldns_buffer_free(buf);
} }
/** helper for unittest of NSEC routines */
static int
unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type)
{
return nsecbitmap_has_type_rdata((uint8_t*)bitmap, len, type);
}
/** Test NSEC type bitmap routine */ /** Test NSEC type bitmap routine */
static void static void
nsectest() nsectest()

View file

@ -46,10 +46,10 @@
#include "util/data/msgreply.h" #include "util/data/msgreply.h"
#include "util/data/dname.h" #include "util/data/dname.h"
/** Check type present in NSEC typemap with bitmap arg */ int
static int nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type)
nsec_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type)
{ {
/* Check type present in NSEC typemap with bitmap arg */
/* bitmasks for determining type-lowerbits presence */ /* bitmasks for determining type-lowerbits presence */
uint8_t masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; uint8_t masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
uint8_t type_window = type>>8; uint8_t type_window = type>>8;
@ -82,12 +82,6 @@ nsec_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type)
return 0; return 0;
} }
int
unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type)
{
return nsec_has_type_rdata((uint8_t*)bitmap, len, type);
}
/** /**
* Check if type is present in the NSEC typemap * Check if type is present in the NSEC typemap
* @param nsec: the nsec RRset. * @param nsec: the nsec RRset.
@ -107,7 +101,7 @@ nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type)
len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2); len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2);
if(!len) if(!len)
return 0; return 0;
return nsec_has_type_rdata(d->rr_data[0]+2+len, return nsecbitmap_has_type_rdata(d->rr_data[0]+2+len,
d->rr_len[0]-2-len, type); d->rr_len[0]-2-len, type);
} }
@ -426,6 +420,8 @@ val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname,
strip = qname; strip = qname;
striplen = qnamelen; striplen = qnamelen;
dname_remove_labels(&strip, &striplen, i); dname_remove_labels(&strip, &striplen, i);
if(striplen > LDNS_MAX_DOMAINLEN-2)
continue; /* too long to prepend wildcard */
buf[0] = 1; buf[0] = 1;
buf[1] = (uint8_t)'*'; buf[1] = (uint8_t)'*';
memmove(buf+2, strip, striplen); memmove(buf+2, strip, striplen);

View file

@ -74,8 +74,15 @@ enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env,
struct reply_info* rep, struct key_entry_key* kkey, struct reply_info* rep, struct key_entry_key* kkey,
uint32_t* proof_ttl); uint32_t* proof_ttl);
/** Unit test call to test function for nsec typemap check */ /**
int unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type); * nsec typemap check, takes an NSEC-type bitmap as argument, checks for type.
* @param bitmap: pointer to the bitmap part of wireformat rdata.
* @param len: length of the bitmap, in bytes.
* @param type: the type (in host order) to check for.
* @return true if the type bit was set in the bitmap. false if not, or
* if the bitmap was malformed in some way.
*/
int nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type);
/** /**
* Determine if a NSEC proves the NOERROR/NODATA conditions. This will also * Determine if a NSEC proves the NOERROR/NODATA conditions. This will also

View file

@ -46,9 +46,25 @@
#include "validator/val_kentry.h" #include "validator/val_kentry.h"
#include "util/region-allocator.h" #include "util/region-allocator.h"
#include "util/rbtree.h" #include "util/rbtree.h"
#include "util/module.h"
#include "util/data/packed_rrset.h" #include "util/data/packed_rrset.h"
#include "util/data/dname.h" #include "util/data/dname.h"
#include "util/data/msgreply.h" #include "util/data/msgreply.h"
/* we include nsec.h for the bitmap_has_type function */
#include "validator/val_nsec.h"
/**
* This function we get from ldns-compat or from base system
* it returns the number of data bytes stored at the target, or <0 on error.
*/
int b32_ntop_extended_hex(uint8_t const *src, size_t srclength,
char *target, size_t targsize);
/**
* This function we get from ldns-compat or from base system
* it returns the number of data bytes stored at the target, or <0 on error.
*/
int b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len,
uint8_t *target, size_t targsize);
/** /**
* The NSEC3 hash result storage. * The NSEC3 hash result storage.
@ -89,11 +105,11 @@ struct ce_response {
/** NSEC3 record that proved ce. rrset */ /** NSEC3 record that proved ce. rrset */
struct ub_packed_rrset_key* ce_rrset; struct ub_packed_rrset_key* ce_rrset;
/** NSEC3 record that proved ce. rr number */ /** NSEC3 record that proved ce. rr number */
size_t ce_rr; int ce_rr;
/** NSEC3 record that proved nc. rrset */ /** NSEC3 record that proved nc. rrset */
struct ub_packed_rrset_key* nc_rrset; struct ub_packed_rrset_key* nc_rrset;
/** NSEC3 record that proved nc. rr*/ /** NSEC3 record that proved nc. rr*/
size_t nc_rr; int nc_rr;
}; };
/** /**
@ -201,6 +217,61 @@ nsec3_get_salt(struct ub_packed_rrset_key* rrset, int r,
return 1; return 1;
} }
/** return nsec3 RR next hashed owner name */
static int
nsec3_get_nextowner(struct ub_packed_rrset_key* rrset, int r,
uint8_t** next, size_t* nextlen)
{
size_t saltlen;
struct packed_rrset_data* d = (struct packed_rrset_data*)
rrset->entry.data;
log_assert(d && r < (int)d->count);
if(d->rr_len[r] < 2+5) {
*next = 0;
*nextlen = 0;
return 0; /* malformed */
}
saltlen = (size_t)d->rr_data[r][2+4];
if(d->rr_len[r] < 2+5+saltlen+1) {
*next = 0;
*nextlen = 0;
return 0; /* malformed */
}
*nextlen = (size_t)d->rr_data[r][2+5+saltlen];
if(d->rr_len[r] < 2+5+saltlen+1+*nextlen) {
*next = 0;
*nextlen = 0;
return 0; /* malformed */
}
*next = d->rr_data[r]+2+5+saltlen+1;
return 1;
}
/** see if NSEC3 RR contains given type */
static int
nsec3_has_type(struct ub_packed_rrset_key* rrset, int r, uint16_t type)
{
uint8_t* bitmap;
size_t bitlen, skiplen;
struct packed_rrset_data* d = (struct packed_rrset_data*)
rrset->entry.data;
log_assert(d && r < (int)d->count);
skiplen = 2+4;
/* skip salt */
if(d->rr_len[r] < skiplen+1)
return 0; /* malformed, too short */
skiplen += 1+(size_t)d->rr_len[skiplen];
/* skip next hashed owner */
if(d->rr_len[r] < skiplen+1)
return 0; /* malformed, too short */
skiplen += 1+(size_t)d->rr_len[skiplen];
if(d->rr_len[r] < skiplen)
return 0; /* malformed, too short */
bitlen = d->rr_len[r] - skiplen;
bitmap = d->rr_data[r]+skiplen;
return nsecbitmap_has_type_rdata(bitmap, bitlen, type);
}
/** /**
* Iterate through NSEC3 list, per RR * Iterate through NSEC3 list, per RR
* This routine gives the next RR in the list (or sets rrset null). * This routine gives the next RR in the list (or sets rrset null).
@ -470,10 +541,6 @@ nsec3_calc_hash(struct region* region, ldns_buffer* buf,
return 1; return 1;
} }
/** This function we get from ldns-compat or from base system */
int b32_ntop_extended_hex(uint8_t const *src, size_t srclength,
char *target, size_t targsize);
/** perform b32 encoding of hash */ /** perform b32 encoding of hash */
static int static int
nsec3_calc_b32(struct region* region, ldns_buffer* buf, nsec3_calc_b32(struct region* region, ldns_buffer* buf,
@ -549,34 +616,391 @@ nsec3_hash_name(rbtree_t* table, struct region* region, ldns_buffer* buf,
return 1; return 1;
} }
/**
* compare a label lowercased
*/
static int
label_compare_lower(uint8_t* lab1, uint8_t* lab2, size_t lablen)
{
size_t i;
for(i=0; i<lablen; i++) {
if(tolower((int)*lab1) != tolower((int)*lab2)) {
if(tolower((int)*lab1) < tolower((int)*lab2))
return -1;
return 1;
}
lab1++;
lab2++;
}
return 0;
}
/**
* Compare a hashed name with the owner name of an NSEC3 RRset.
* @param flt: filter with zone name.
* @param hash: the hashed name.
* @param s: rrset with owner name.
* @return true if matches exactly, false if not.
*/
static int
nsec3_hash_matches_owner(struct nsec3_filter* flt,
struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s)
{
uint8_t* nm = s->rk.dname;
/* compare, does hash of name based on params in this NSEC3
* match the owner name of this NSEC3?
* name must be: <hashlength>base32 . zone name
* so; first label must not be root label (not zero length),
* and match the b32 encoded hash length,
* and the label content match the b32 encoded hash
* and the rest must be the zone name.
*/
if(hash->b32_len != 0 && (size_t)nm[0] == hash->b32_len &&
label_compare_lower(nm+1, hash->b32, hash->b32_len) == 0 &&
query_dname_compare(nm+(size_t)nm[0]+1, flt->zone) == 0) {
return 1;
}
return 0;
}
/** /**
* Find matching NSEC3 * Find matching NSEC3
* Find the NSEC3Record that matches a hash of a name. * Find the NSEC3Record that matches a hash of a name.
* @param env: module environment with temporary region and buffer.
* @param flt: the NSEC3 RR filter, contains zone name and RRs.
* @param ct: cached hashes table.
* @param nm: name to look for.
* @param nmlen: length of name.
* @param rrset: nsec3 that matches is returned here.
* @param rr: rr number in nsec3 rrset that matches.
* @return true if a matching NSEC3 is found, false if not.
*/ */
static int
find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt,
rbtree_t* ct, uint8_t* nm, size_t nmlen,
struct ub_packed_rrset_key** rrset, int* rr)
{
size_t i_rs;
int i_rr;
struct ub_packed_rrset_key* s;
struct nsec3_cached_hash* hash;
int r;
/* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */
for(s=filter_first(flt, &i_rs, &i_rr); s;
s=filter_next(flt, &i_rs, &i_rr)) {
/* get name hashed for this NSEC3 RR */
r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer,
s, i_rr, nm, nmlen, &hash);
if(r == 0) {
log_err("nsec3: malloc failure");
break; /* alloc failure */
} else if(r < 0)
continue; /* malformed NSEC3 */
else if(nsec3_hash_matches_owner(flt, hash, s)) {
*rrset = s; /* rrset with this name */
*rr = i_rr; /* matches hash with these parameters */
return 1;
}
}
*rrset = NULL;
*rr = 0;
return 0;
}
/** /**
* nsec3Covers * nsec3Covers
* Given a hash and a candidate NSEC3Record, determine if that NSEC3Record * Given a hash and a candidate NSEC3Record, determine if that NSEC3Record
* covers the hash. Covers specifically means that the hash is in between * covers the hash. Covers specifically means that the hash is in between
* the owner and next hashes and does not equal either. * the owner and next hashes and does not equal either.
*
* @param flt: the NSEC3 RR filter, contains zone name.
* @param hash: the hash of the name
* @param rrset: the rrset of the NSEC3.
* @param rr: which rr in the rrset.
* @param buf: temporary buffer.
* @return true if covers, false if not.
*/ */
static int
nsec3_covers(struct nsec3_filter* flt, struct nsec3_cached_hash* hash,
struct ub_packed_rrset_key* rrset, int rr, ldns_buffer* buf)
{
uint8_t* next, *owner;
size_t nextlen;
int len;
if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen))
return 0; /* malformed RR proves nothing */
/* check the owner name is a hashed value . apex
* base32 encoded values must have equal length.
* hash_value and next hash value must have equal length. */
if(nextlen != hash->hash_len || hash->hash_len==0||hash->b32_len==0||
(size_t)*rrset->rk.dname != hash->b32_len ||
query_dname_compare(rrset->rk.dname+1+
(size_t)*rrset->rk.dname, flt->zone) != 0)
return 0; /* bad lengths or owner name */
/* This is the "normal case: owner < next and owner < hash < next */
if(label_compare_lower(rrset->rk.dname+1, hash->b32,
hash->b32_len) < 0 &&
memcmp(hash->hash, next, nextlen) < 0)
return 1;
/* convert owner name from text to binary */
ldns_buffer_clear(buf);
owner = ldns_buffer_begin(buf);
len = b32_pton_extended_hex((char*)rrset->rk.dname+1, hash->b32_len,
owner, ldns_buffer_limit(buf));
if(len<1)
return 0; /* bad owner name in some way */
if((size_t)len != hash->hash_len || (size_t)len != nextlen)
return 0; /* wrong length */
/* this is the end of zone case: next <= owner &&
* (hash > owner || hash < next)
* this also covers the only-apex case of next==owner.
*/
if(memcmp(next, owner, nextlen) <= 0 &&
( memcmp(hash->hash, owner, nextlen) > 0 ||
memcmp(hash->hash, next, nextlen) < 0)) {
return 1;
}
return 0;
}
/** /**
* findCoveringNSEC3 * findCoveringNSEC3
* Given a pre-hashed name, find a covering NSEC3 from among a list of * Given a name, find a covering NSEC3 from among a list of NSEC3s.
* NSEC3s. *
* @param env: module environment with temporary region and buffer.
* @param flt: the NSEC3 RR filter, contains zone name and RRs.
* @param ct: cached hashes table.
* @param nm: name to check if covered.
* @param nmlen: length of name.
* @param rrset: covering NSEC3 rrset is returned here.
* @param rr: rr of cover is returned here.
* @return true if a covering NSEC3 is found, false if not.
*/ */
static int
find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt,
rbtree_t* ct, uint8_t* nm, size_t nmlen,
struct ub_packed_rrset_key** rrset, int* rr)
{
size_t i_rs;
int i_rr;
struct ub_packed_rrset_key* s;
struct nsec3_cached_hash* hash;
int r;
/* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */
for(s=filter_first(flt, &i_rs, &i_rr); s;
s=filter_next(flt, &i_rs, &i_rr)) {
/* get name hashed for this NSEC3 RR */
r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer,
s, i_rr, nm, nmlen, &hash);
if(r == 0) {
log_err("nsec3: malloc failure");
break; /* alloc failure */
} else if(r < 0)
continue; /* malformed NSEC3 */
else if(nsec3_covers(flt, hash, s, i_rr,
env->scratch_buffer)) {
*rrset = s; /* rrset with this name */
*rr = i_rr; /* covers hash with these parameters */
return 1;
}
}
*rrset = NULL;
*rr = 0;
return 0;
}
/** /**
* findClosestEncloser * findClosestEncloser
* Given a name and a list of NSEC3s, find the candidate closest encloser. * Given a name and a list of NSEC3s, find the candidate closest encloser.
* This will be the first ancestor of 'name' (including itself) to have a * This will be the first ancestor of 'name' (including itself) to have a
* matching NSEC3 RR. * matching NSEC3 RR.
* @param env: module environment with temporary region and buffer.
* @param flt: the NSEC3 RR filter, contains zone name and RRs.
* @param ct: cached hashes table.
* @param qinfo: query that is verified for.
* @param ce: closest encloser information is returned in here.
* @return true if a closest encloser candidate is found, false if not.
*/ */
static int
nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
rbtree_t* ct, struct query_info* qinfo, struct ce_response* ce)
{
uint8_t* nm = qinfo->qname;
size_t nmlen = qinfo->qname_len;
/* This scans from longest name to shortest, so the first match
* we find is the only viable candidate. */
/* (David:) FIXME: modify so that the NSEC3 matching the zone apex need
* not be present. (Mark Andrews idea).
* (Wouter:) But make sure you check for DNAME bit in zone apex,
* if the NSEC3 you find is the only NSEC3 in the zone, then this
* may be the case. */
while(dname_subdomain_c(nm, flt->zone)) {
if(find_matching_nsec3(env, flt, ct, nm, nmlen,
&ce->ce_rrset, &ce->ce_rr)) {
ce->ce = nm;
ce->ce_len = nmlen;
return 1;
}
dname_remove_label(&nm, &nmlen);
}
return 0;
}
/**
* Given a qname and its proven closest encloser, calculate the "next
* closest" name. Basically, this is the name that is one label longer than
* the closest encloser that is still a subdomain of qname.
*
* @param qname: query name.
* @param qnamelen: length of qname.
* @param ce: closest encloser
* @param nm: result name.
* @param nmlen: length of nm.
*/
static void
next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce,
uint8_t** nm, size_t* nmlen)
{
int strip = dname_count_labels(qname) - dname_count_labels(ce) -1;
*nm = qname;
*nmlen = qnamelen;
if(strip>0)
dname_remove_labels(nm, nmlen, strip);
}
/** /**
* proveClosestEncloser * proveClosestEncloser
* Given a List of nsec3 RRs, find and prove the closest encloser to qname. * Given a List of nsec3 RRs, find and prove the closest encloser to qname.
* @param env: module environment with temporary region and buffer.
* @param flt: the NSEC3 RR filter, contains zone name and RRs.
* @param ct: cached hashes table.
* @param qinfo: query that is verified for.
* @param prove_does_not_exist: If true, then if the closest encloser
* turns out to be qname, then null is returned.
* @param ce: closest encloser information is returned in here.
* @return false if no closest encloser could be proven.
* true if a closest encloser could be proven, ce is set.
*/ */
static int
nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
rbtree_t* ct, struct query_info* qinfo, int prove_does_not_exist,
struct ce_response* ce)
{
uint8_t* nc;
size_t nc_len;
/* robust: clean out ce, in case it gets abused later */
memset(ce, 0, sizeof(*ce));
if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce)) {
verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could "
"not find a candidate for the closest encloser.");
return 0;
}
if(query_dname_compare(ce->ce, qinfo->qname) == 0) {
if(prove_does_not_exist) {
verbose(VERB_ALGO, "nsec3 proveClosestEncloser: "
"proved that qname existed, bad");
return 0;
}
/* otherwise, we need to nothing else to prove that qname
* is its own closest encloser. */
return 1;
}
/* If the closest encloser is actually a delegation, then the
* response should have been a referral. If it is a DNAME, then
* it should have been a DNAME response. */
if(nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_NS) &&
!nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_SOA)) {
verbose(VERB_ALGO, "nsec3 proveClosestEncloser: closest "
"encloser was a delegation, bad");
return 0;
}
if(nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_DNAME)) {
verbose(VERB_ALGO, "nsec3 proveClosestEncloser: closest "
"encloser was a DNAME, bad");
return 0;
}
/* Otherwise, we need to show that the next closer name is covered. */
next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len);
if(!find_covering_nsec3(env, flt, ct, nc, nc_len,
&ce->nc_rrset, &ce->nc_rr)) {
verbose(VERB_ALGO, "nsec3: Could not find proof that the "
"candidate encloser was the closest encloser");
return 0;
}
return 1;
}
/** allocate a wildcard for the closest encloser */
static uint8_t*
nsec3_ce_wildcard(struct region* region, uint8_t* ce, size_t celen,
size_t* len)
{
uint8_t* nm;
if(celen > LDNS_MAX_DOMAINLEN - 2)
return 0; /* too long */
nm = (uint8_t*)region_alloc(region, celen+2);
if(!nm) {
log_err("nsec3 wildcard: out of memory");
return 0; /* alloc failure */
}
nm[0] = 1;
nm[1] = (uint8_t)'*'; /* wildcard label */
memmove(nm+2, ce, celen);
*len = celen+2;
return nm;
}
enum sec_status
nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey)
{
rbtree_t ct;
struct nsec3_filter flt;
struct ce_response ce;
uint8_t* wc;
size_t wclen;
struct ub_packed_rrset_key* wc_rrset;
int wc_rr;
if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
return sec_status_bogus; /* no valid NSEC3s, bogus */
rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
filter_init(&flt, list, num, qinfo); /* init RR iterator */
if(nsec3_iteration_count_high(ve, &flt, kkey))
return sec_status_insecure; /* iteration count too high */
/* First locate and prove the closest encloser to qname. We will
* use the variant that fails if the closest encloser turns out
* to be qname. */
if(!nsec3_prove_closest_encloser(env, &flt, &ct, qinfo, 1, &ce)) {
verbose(VERB_ALGO, "nsec3 nameerror proof: failed to prove "
"a closest encloser");
return sec_status_bogus;
}
/* At this point, we know that qname does not exist. Now we need
* to prove that the wildcard does not exist. */
log_assert(ce.ce);
wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen);
if(!wc || !find_covering_nsec3(env, &flt, &ct, wc, wclen,
&wc_rrset, &wc_rr)) {
verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove "
"that the applicable wildcard did not exist.");
return sec_status_bogus;
}
return sec_status_secure;
}

View file

@ -93,4 +93,25 @@ struct key_entry_key;
/** The SHA1 hash algorithm for NSEC3 */ /** The SHA1 hash algorithm for NSEC3 */
#define NSEC3_HASH_SHA1 0x01 #define NSEC3_HASH_SHA1 0x01
/**
* Determine if the set of NSEC3 records provided with a response prove NAME
* ERROR. This means that the NSEC3s prove a) the closest encloser exists,
* b) the direct child of the closest encloser towards qname doesn't exist,
* and c) *.closest encloser does not exist.
*
* @param env: module environment with temporary region and buffer.
* @param ve: validator environment, with iteration count settings.
* @param list: array of RRsets, some of which are NSEC3s.
* @param num: number of RRsets in the array to examine.
* @param qinfo: query that is verified for.
* @param kkey: key entry that signed the NSEC3s.
* @return:
* sec_status SECURE of the Name Error is proven by the NSEC3 RRs,
* BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored.
*/
enum sec_status
nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey);
#endif /* VALIDATOR_VAL_NSEC3_H */ #endif /* VALIDATOR_VAL_NSEC3_H */

View file

@ -46,6 +46,7 @@
#include "validator/val_kentry.h" #include "validator/val_kentry.h"
#include "validator/val_utils.h" #include "validator/val_utils.h"
#include "validator/val_nsec.h" #include "validator/val_nsec.h"
#include "validator/val_nsec3.h"
#include "services/cache/dns.h" #include "services/cache/dns.h"
#include "util/data/dname.h" #include "util/data/dname.h"
#include "util/module.h" #include "util/module.h"
@ -610,12 +611,17 @@ validate_nodata_response(struct query_info* qchase,
* *
* The answer and authority RRsets must have already been verified as secure. * The answer and authority RRsets must have already been verified as secure.
* *
* @param env: module env for verify.
* @param ve: validator env for verify.
* @param qchase: query that was made. * @param qchase: query that was made.
* @param chase_reply: answer to that query to validate. * @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood().
*/ */
static void static void
validate_nameerror_response(struct query_info* qchase, validate_nameerror_response(struct module_env* env, struct val_env* ve,
struct reply_info* chase_reply) struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{ {
int has_valid_nsec = 0; int has_valid_nsec = 0;
int has_valid_wnsec = 0; int has_valid_wnsec = 0;
@ -637,7 +643,17 @@ validate_nameerror_response(struct query_info* qchase,
} }
if(!has_valid_nsec || !has_valid_wnsec) { if(!has_valid_nsec || !has_valid_wnsec) {
/* TODO: use NSEC3 proof */ /* use NSEC3 proof, both answer and auth rrsets, in case
* NSEC3s end up in the answer (due to qtype=NSEC3 or so) */
chase_reply->security = nsec3_prove_nameerror(env, ve,
chase_reply->rrsets, chase_reply->an_numrrsets+
chase_reply->ns_numrrsets, qchase, kkey);
if(chase_reply->security != sec_status_secure) {
verbose(VERB_DETAIL, "NameError response failed nsec, "
"nsec3 proof was %s", sec_status_to_string(
chase_reply->security));
return;
}
} }
/* If the message fails to prove either condition, it is bogus. */ /* If the message fails to prove either condition, it is bogus. */
@ -1229,8 +1245,8 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
case VAL_CLASS_NAMEERROR: case VAL_CLASS_NAMEERROR:
verbose(VERB_ALGO, "Validating a nxdomain response"); verbose(VERB_ALGO, "Validating a nxdomain response");
validate_nameerror_response(&vq->qchase, validate_nameerror_response(qstate->env, ve,
vq->chase_reply); &vq->qchase, vq->chase_reply, vq->key_entry);
break; break;
case VAL_CLASS_CNAME: case VAL_CLASS_CNAME: