nsec3 work.

git-svn-id: file:///svn/unbound/trunk@609 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-09-12 14:16:46 +00:00
parent db452ffc16
commit d85debfae4
15 changed files with 639 additions and 53 deletions

View file

@ -1,5 +1,6 @@
12 September 2007: Wouter
- fixup of manual page warnings.
- fixup of manual page warnings, like for NSD bugreport.
- nsec3 work, config, max iterations, filter, and hash cache.
6 September 2007: Wouter
- fixup to find libevent on mac port install.

View file

@ -37,7 +37,8 @@ o store primed key data in a overlaid keyhints file (sort of like drafttimers).
o windows version, auto update feature, a query to check for the version.
o autoreport of problems
o logrotation, syslog
o command the server with TSIG inband.
o command the server with TSIG inband. get-config, clearcache,
get stats, get memstats, get ..., reload, clear one zone from cache
o watch for spoof nearmisses.
o improve compression of DNS packets by first puttig uncompressible rrs, then
compress to their rdata.

View file

@ -197,6 +197,12 @@ server:
# result in interesting log files and possibly the AD bit in
# replies if the message is found secure. The default is off.
# val-permissive-mode: no
# It is possible to configure NSEC3 maximum iteration counts per
# keysize. Keep this table very short, as linear search is done.
# A message with an NSEC3 with larger count is marked insecure.
# List in ascending order the keysize and count values.
# val-nsec3-keysize-iterations: "1024 150 2048 500 4096 2500"
# the amount of memory to use for the key cache.
# in bytes. default is 4 Mb

View file

@ -232,6 +232,14 @@ reply is not withheld from the client with SERVFAIL as usual. The client
receives the bogus data. For messages that are found to be secure the AD bit
is set in replies. Also logging is performed as for full validation.
The default value is "no".
.It \fBval-nsec3-keysize-iterations:\fR <"list of values">
List of keysize and iteration count values, separated by spaces, surrounded
by quotes. Default is "1024 150 2048 500 4096 2500". This determines the
maximum allowed NSEC3 iteration count before a message is simply marked
insecure instead of performing the many hashing iterations. The list must
be in ascending order and have at least one entry. If you set it to
"1024 65535" there is no restriction to NSEC3 iteration values.
This table must be kept short; a very long list could cause slower operation.
.It \fBkey-cache-size:\fR <number>
Number of bytes size of the key cache. Default is 4 megabytes.
.It \fBkey-cache-slabs:\fR <number>

View file

@ -57,32 +57,6 @@
#include "util/data/msgparse.h"
#include "util/random.h"
/** count number of integers in fetch policy string */
static int
fetch_count(const char* s)
{
/* format ::= (sp num)+ sp */
/* num ::= [-](0-9)+ */
/* sp ::= (space|tab)* */
int num = 0;
while(*s) {
while(*s && isspace(*s))
s++;
if(!*s) /* end of string */
break;
if(*s == '-')
s++;
if(!*s) /* only - not allowed */
return 0;
if(!isdigit(*s)) /* bad character */
return 0;
while(*s && isdigit(*s))
s++;
num++;
}
return num;
}
/** fillup fetch policy array */
static void
fetch_fill(struct iter_env* ie, const char* str)
@ -91,7 +65,8 @@ fetch_fill(struct iter_env* ie, const char* str)
int i;
for(i=0; i<ie->max_dependency_depth+1; i++) {
ie->target_fetch_policy[i] = strtol(s, &e, 10);
log_assert(s != e); /* parsed syntax already */
if(s == e)
fatal_exit("cannot parse fetch policy number %s", s);
s = e;
}
}
@ -100,7 +75,7 @@ fetch_fill(struct iter_env* ie, const char* str)
static int
read_fetch_policy(struct iter_env* ie, const char* str)
{
int count = fetch_count(str);
int count = cfg_count_numbers(str);
if(count < 1) {
log_err("Cannot parse target fetch policy: \"%s\"", str);
return 0;

View file

@ -123,6 +123,8 @@ config_create()
cfg->key_cache_size = 4 * 1024 * 1024;
cfg->key_cache_slabs = 4;
if(!(cfg->module_conf = strdup("validator iterator"))) goto error_exit;
if(!(cfg->val_nsec3_key_iterations =
strdup("1024 150 2048 500 4096 2500"))) goto error_exit;
return cfg;
error_exit:
config_delete(cfg);
@ -219,6 +221,7 @@ config_delete(struct config_file* cfg)
config_delstrlist(cfg->trust_anchor_file_list);
config_delstrlist(cfg->trusted_keys_file_list);
config_delstrlist(cfg->trust_anchor_list);
free(cfg->val_nsec3_key_iterations);
free(cfg);
}
@ -290,3 +293,28 @@ cfg_convert_timeval(const char* str)
t = mktime_from_utc(&tm);
return t;
}
int
cfg_count_numbers(const char* s)
{
/* format ::= (sp num)+ sp */
/* num ::= [-](0-9)+ */
/* sp ::= (space|tab)* */
int num = 0;
while(*s) {
while(*s && isspace(*s))
s++;
if(!*s) /* end of string */
break;
if(*s == '-')
s++;
if(!*s) /* only - not allowed */
return 0;
if(!isdigit(*s)) /* bad character */
return 0;
while(*s && isdigit(*s))
s++;
num++;
}
return num;
}

View file

@ -158,6 +158,8 @@ struct config_file {
int val_clean_additional;
/** should validator allow bogus messages to go through */
int val_permissive_mode;
/** nsec3 maximum iterations per key size, string */
char* val_nsec3_key_iterations;
/** size of the key cache */
size_t key_cache_size;
@ -227,6 +229,18 @@ int cfg_strlist_insert(struct config_strlist** head, char* item);
*/
uint32_t cfg_convert_timeval(const char* str);
/**
* Count number of values in the string.
* format ::= (sp num)+ sp
* num ::= [-](0-9)+
* sp ::= (space|tab)*
*
* @param str: string
* @return: 0 on parse error, or empty string, else
* number of integer values in the string.
*/
int cfg_count_numbers(const char* str);
/**
* Used during options parsing
*/

View file

@ -153,6 +153,7 @@ val-clean-additional{COLON} { YDOUT; return VAR_VAL_CLEAN_ADDITIONAL;}
val-permissive-mode{COLON} { YDOUT; return VAR_VAL_PERMISSIVE_MODE;}
key-cache-size{COLON} { YDOUT; return VAR_KEY_CACHE_SIZE;}
key-cache-slabs{COLON} { YDOUT; return VAR_KEY_CACHE_SLABS;}
val-nsec3-keysize-iterations{COLON} { YDOUT; return VAR_VAL_NSEC3_KEYSIZE_ITERATIONS;}
{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
/* Quoted strings. Strip leading and ending quotes */

View file

@ -83,7 +83,8 @@ extern struct config_parser_state* cfg_parser;
%token VAR_TRUST_ANCHOR_FILE VAR_TRUST_ANCHOR VAR_VAL_OVERRIDE_DATE
%token VAR_BOGUS_TTL VAR_VAL_CLEAN_ADDITIONAL VAR_VAL_PERMISSIVE_MODE
%token VAR_INCOMING_NUM_TCP VAR_MSG_BUFFER_SIZE VAR_KEY_CACHE_SIZE
%token VAR_KEY_CACHE_SLABS VAR_TRUSTED_KEYS_FILE
%token VAR_KEY_CACHE_SLABS VAR_TRUSTED_KEYS_FILE
%token VAR_VAL_NSEC3_KEYSIZE_ITERATIONS
%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@ -121,7 +122,7 @@ content_server: server_num_threads | server_verbosity | server_port |
server_val_clean_additional | server_val_permissive_mode |
server_incoming_num_tcp | server_msg_buffer_size |
server_key_cache_size | server_key_cache_slabs |
server_trusted_keys_file
server_trusted_keys_file | server_val_nsec3_keysize_iterations
;
stubstart: VAR_STUB_ZONE
{
@ -568,6 +569,13 @@ server_val_permissive_mode: VAR_VAL_PERMISSIVE_MODE STRING
free($2);
}
;
server_val_nsec3_keysize_iterations: VAR_VAL_NSEC3_KEYSIZE_ITERATIONS STRING
{
OUTYY(("P(server_val_nsec3_keysize_iterations:%s)\n", $2));
free(cfg_parser->cfg->val_nsec3_key_iterations);
cfg_parser->cfg->val_nsec3_key_iterations = $2;
}
;
server_key_cache_size: VAR_KEY_CACHE_SIZE STRING
{
OUTYY(("P(server_key_cache_size:%s)\n", $2));

View file

@ -302,3 +302,49 @@ key_entry_get_rrset(struct key_entry_key* kkey, struct region* region)
packed_rrset_ptr_fixup(rrd);
return rrk;
}
/** Get size of key in keyset */
static size_t
dnskey_get_keysize(struct packed_rrset_data* data, size_t idx)
{
unsigned char* pk;
unsigned int pklen = 0;
int algo;
if(data->rr_len[idx] < 2+5)
return 0;
algo = (int)data->rr_data[idx][2+3];
pk = (unsigned char*)data->rr_data[idx]+2+4;
pklen = (unsigned)data->rr_len[idx]-2-4;
return ldns_rr_dnskey_key_size_raw(pk, pklen, algo);
}
/** get dnskey flags from data */
static uint16_t
kd_get_flags(struct packed_rrset_data* data, size_t idx)
{
uint16_t f;
if(data->rr_len[idx] < 2+2)
return 0;
memmove(&f, data->rr_data[idx]+2, 2);
f = ntohs(f);
return f;
}
size_t
key_entry_keysize(struct key_entry_key* kkey)
{
struct packed_rrset_data* d;
/* compute size of smallest ZSK key in the rrset */
size_t i;
size_t bits = 0;
if(!key_entry_isgood(kkey))
return 0;
d = ((struct key_entry_data*)kkey->entry.data)->rrset_data;
for(i=0; i<d->count; i++) {
if(!(kd_get_flags(d, i) & DNSKEY_BIT_ZSK))
continue;
if(i==0 || dnskey_get_keysize(d, i) < bits)
bits = dnskey_get_keysize(d, i);
}
return bits;
}

View file

@ -183,4 +183,11 @@ struct key_entry_key* key_entry_create_bad(struct region* region,
struct ub_packed_rrset_key* key_entry_get_rrset(struct key_entry_key* kkey,
struct region* region);
/**
* Get keysize of the keyentry.
* @param kkey: key, must be a good key, with contents.
* @return size in bits of the key.
*/
size_t key_entry_keysize(struct key_entry_key* kkey);
#endif /* VALIDATOR_VAL_KENTRY_H */

View file

@ -42,8 +42,13 @@
*/
#include "config.h"
#include "validator/val_nsec3.h"
#include "validator/validator.h"
#include "validator/val_kentry.h"
#include "util/region-allocator.h"
#include "util/rbtree.h"
#include "util/data/packed_rrset.h"
#include "util/data/dname.h"
#include "util/data/msgreply.h"
/**
* The NSEC3 hash result storage.
@ -55,9 +60,9 @@ struct nsec3_cached_hash {
/** rbtree node, key is this structure */
rbnode_t node;
/** where are the parameters for conversion, in this rrset data */
struct packed_rrset_data* data;
struct ub_packed_rrset_key* nsec3;
/** where are the parameters for conversion, this RR number in data */
size_t rr;
int rr;
/** the name to convert */
uint8_t* dname;
/** length of the dname */
@ -65,7 +70,7 @@ struct nsec3_cached_hash {
/** the hash result (not base32 encoded) */
uint8_t* hash;
/** length of hash in bytes */
size_t hashlen;
size_t hash_len;
/** the hash result in base32 encoding */
uint8_t* b32;
/** length of base32 encoding (as a label) */
@ -93,6 +98,7 @@ struct ce_response {
/**
* Filter conditions for NSEC3 proof
* Used to iterate over the applicable NSEC3 RRs.
*/
struct nsec3_filter {
/** Zone name, only NSEC3 records for this zone are considered */
@ -103,25 +109,182 @@ struct nsec3_filter {
struct ub_packed_rrset_key** list;
/** number of rrsets in list */
size_t num;
/** class of records for the NSEC3, only this class applies */
uint16_t fclass;
};
/** return number of rrs in an rrset */
static size_t
rrset_get_count(struct ub_packed_rrset_key* rrset)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)
rrset->entry.data;
if(!d) return 0;
return d->count;
}
/** return if nsec3 RR has unknown flags */
static int
nsec3_unknown_flags(struct ub_packed_rrset_key* rrset, int r)
{
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+2)
return 0; /* malformed */
return (int)(d->rr_data[r][2+1] & NSEC3_UNKNOWN_FLAGS);
}
/** return nsec3 RR algorithm */
static int
nsec3_get_algo(struct ub_packed_rrset_key* rrset, int r)
{
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+1)
return 0; /* malformed */
return (int)(d->rr_data[r][2+0]);
}
/** return if nsec3 RR has known algorithm */
static int
nsec3_known_algo(struct ub_packed_rrset_key* rrset, int r)
{
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+1)
return 0; /* malformed */
switch(d->rr_data[r][2+0]) {
case NSEC3_HASH_SHA1:
return 1;
}
return 0;
}
/** return nsec3 RR iteration count */
static size_t
nsec3_get_iter(struct ub_packed_rrset_key* rrset, int r)
{
uint16_t i;
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+4)
return 0; /* malformed */
memmove(&i, d->rr_data[r]+2+2, sizeof(i));
i = ntohs(i);
return (size_t)i;
}
/** return nsec3 RR salt */
static int
nsec3_get_salt(struct ub_packed_rrset_key* rrset, int r,
uint8_t** salt, 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) {
*salt = 0;
*saltlen = 0;
return 0; /* malformed */
}
*saltlen = (size_t)d->rr_data[r][2+4];
if(d->rr_len[r] < 2+5+(size_t)*saltlen) {
*salt = 0;
*saltlen = 0;
return 0; /* malformed */
}
*salt = d->rr_data[r]+2+5;
return 1;
}
/**
* Iterate through NSEC3 list, per RR
* Start with rrset = list, rrnum = 0.
* End when rrset becomes NULL.
* 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).
* Usage:
*
* size_t rrsetnum;
* int rrnum;
* struct ub_packed_rrset_key* rrset;
* for(rrset=filter_first(filter, &rrsetnum, &rrnum); rrset;
* rrset=filter_next(filter, &rrsetnum, &rrnum))
* do_stuff;
*
* Also filters out
* o unknown flag NSEC3s
* o unknown algorithm NSEC3s.
* @param filter: nsec3 filter structure.
* @param rrset: in/out rrset to look at.
* @param rrsetnum: in/out rrset number to look at.
* @param rrnum: in/out rr number in rrset to look at.
* @returns ptr to the next rrset (or NULL at end).
*/
static void
filter_next(struct nsec3_filter* filter, struct ub_packed_rrset_key** rrset,
size_t rrnum)
static struct ub_packed_rrset_key*
filter_next(struct nsec3_filter* filter, size_t* rrsetnum, int* rrnum)
{
size_t i;
int r;
uint8_t* nm;
size_t nmlen;
if(!filter->zone) /* empty list */
return NULL;
for(i=*rrsetnum; i<filter->num; i++) {
/* see if RRset qualifies */
if(ntohs(filter->list[i]->rk.type) != LDNS_RR_TYPE_NSEC3 ||
ntohs(filter->list[i]->rk.rrset_class) !=
filter->fclass)
continue;
/* check RRset zone */
nm = filter->list[i]->rk.dname;
nmlen = filter->list[i]->rk.dname_len;
dname_remove_label(&nm, &nmlen);
if(query_dname_compare(nm, filter->zone) != 0)
continue;
if(i == *rrsetnum)
r = (*rrnum) + 1; /* continue at next RR */
else r = 0; /* new RRset start at first RR */
for(; r < (int)rrset_get_count(filter->list[i]); r++) {
/* skip unknown flags, algo */
if(nsec3_unknown_flags(filter->list[i], r) ||
!nsec3_known_algo(filter->list[i], r))
continue;
/* this one is a good target */
*rrsetnum = i;
*rrnum = r;
return filter->list[i];
}
}
return NULL;
}
/**
* Start iterating over NSEC3 records.
* @param filter: the filter structure, must have been filter_init-ed.
* @param rrsetnum: can be undefined on call, inited.
* @param rrnum: can be undefined on call, inited.
* @return first rrset of an NSEC3, together with rrnum this points to
* the first RR to examine. Is NULL on empty list.
*/
static struct ub_packed_rrset_key*
filter_first(struct nsec3_filter* filter, size_t* rrsetnum, int* rrnum)
{
*rrsetnum = 0;
*rrnum = -1;
return filter_next(filter, rrsetnum, rrnum);
}
/** see if at least one RR is known (flags, algo) */
static int
nsec3_rrset_has_known(struct ub_packed_rrset_key* s)
{
int r;
for(r=0; r < (int)rrset_get_count(s); r++) {
if(!nsec3_unknown_flags(s, r) && nsec3_known_algo(s, r))
return 1;
}
return 0;
}
/**
@ -130,14 +293,71 @@ filter_next(struct nsec3_filter* filter, struct ub_packed_rrset_key** rrset,
* (skips the unknown flag and unknown algo NSEC3s).
*
* @param filter: nsec3 filter structure.
* @param list: list of rrsets.
* @param list: list of rrsets, an array of them.
* @param num: number of rrsets in list.
* @param qtype: query type (if DS a higher zone must be chosen)
* @param qinfo:
* query name to match a zone for.
* query type (if DS a higher zone must be chosen)
* qclass, to filter NSEC3s with.
*/
static void
filter_init(struct nsec3_filter* filter, struct ub_packed_rrset_key* list,
size_t num, uint16_t qtype)
filter_init(struct nsec3_filter* filter, struct ub_packed_rrset_key** list,
size_t num, struct query_info* qinfo)
{
size_t i;
uint8_t* nm;
size_t nmlen;
filter->zone = NULL;
filter->zone_len = 0;
filter->list = list;
filter->num = num;
filter->fclass = qinfo->qclass;
for(i=0; i<num; i++) {
/* ignore other stuff in the list */
if(ntohs(list[i]->rk.type) != LDNS_RR_TYPE_NSEC3 ||
ntohs(list[i]->rk.rrset_class) != qinfo->qclass)
continue;
/* skip unknown flags, algo */
if(!nsec3_rrset_has_known(list[i]))
continue;
/* since NSECs are base32.zonename, we can find the zone
* name by stripping off the first label of the record */
nm = list[i]->rk.dname;
nmlen = list[i]->rk.dname_len;
dname_remove_label(&nm, &nmlen);
/* if we find a domain that can prove about the qname,
* and if this domain is closer to the qname */
if(dname_subdomain_c(qinfo->qname, nm) && (!filter->zone ||
dname_subdomain_c(nm, filter->zone))) {
/* for a type DS do not accept a zone equal to qname*/
if(qinfo->qtype == LDNS_RR_TYPE_DS &&
query_dname_compare(qinfo->qname, nm) == 0)
continue;
filter->zone = nm;
filter->zone_len = nmlen;
}
}
}
/**
* Find max iteration count using config settings and key size
* @param ve: validator environment with iteration count config settings.
* @param bits: key size
* @return max iteration count
*/
static size_t
get_max_iter(struct val_env* ve, size_t bits)
{
int i;
log_assert(ve->nsec3_keyiter_count > 0);
/* round up to nearest config keysize, linear search, keep it small */
for(i=0; i<ve->nsec3_keyiter_count; i++) {
if(bits <= ve->nsec3_keysize[i])
return ve->nsec3_maxiter[i];
}
/* else, use value for biggest key */
return ve->nsec3_maxiter[ve->nsec3_keyiter_count-1];
}
/**
@ -145,18 +365,141 @@ filter_init(struct nsec3_filter* filter, struct ub_packed_rrset_key* list,
* @param ve: validator environment with iteration count config settings.
* @param filter: what NSEC3s to loop over.
* @param kkey: key entry used for verification; used for iteration counts.
* @return 0 if some nsec3s are above the max iteration count.
* @return 1 if some nsec3s are above the max iteration count.
*/
static int
nsec3_iteration_count_high(struct val_env* ve, struct nsec3_filter* filter,
struct key_entry_key* kkey)
{
size_t rrsetnum;
int rrnum;
struct ub_packed_rrset_key* rrset;
/* first determine the max number of iterations */
size_t bits = key_entry_keysize(kkey);
size_t max_iter = get_max_iter(ve, bits);
verbose(VERB_ALGO, "nsec3: keysize %d bits, max iterations %d",
(int)bits, (int)max_iter);
/** perform hash of name */
for(rrset=filter_first(filter, &rrsetnum, &rrnum); rrset;
rrset=filter_next(filter, &rrsetnum, &rrnum)) {
if(nsec3_get_iter(rrset, rrnum) > max_iter)
return 1;
}
return 0;
}
/** nsec3_cache_compare for rbtree */
static int
nsec3_hash_cmp(const void* c1, const void* c2)
{
struct nsec3_cached_hash* h1 = (struct nsec3_cached_hash*)c1;
struct nsec3_cached_hash* h2 = (struct nsec3_cached_hash*)c2;
uint8_t* s1, *s2;
size_t s1len, s2len;
int c = query_dname_compare(h1->dname, h2->dname);
if(c != 0)
return c;
/* compare parameters */
/* if both malformed, its equal, robustness */
if(nsec3_get_algo(h1->nsec3, h1->rr) !=
nsec3_get_algo(h2->nsec3, h2->rr)) {
if(nsec3_get_algo(h1->nsec3, h1->rr) <
nsec3_get_algo(h2->nsec3, h2->rr))
return -1;
return 1;
}
if(nsec3_get_iter(h1->nsec3, h1->rr) !=
nsec3_get_iter(h2->nsec3, h2->rr)) {
if(nsec3_get_iter(h1->nsec3, h1->rr) <
nsec3_get_iter(h2->nsec3, h2->rr))
return -1;
return 1;
}
(void)nsec3_get_salt(h1->nsec3, h1->rr, &s1, &s1len);
(void)nsec3_get_salt(h2->nsec3, h2->rr, &s2, &s2len);
if(s1len != s2len) {
if(s1len < s2len)
return -1;
return 1;
}
return memcmp(s1, s2, s1len);
}
/** perform hash of name */
static int
nsec3_calc_hash(struct region* region, ldns_buffer* buf,
struct nsec3_cached_hash* c)
{
int algo = nsec3_get_algo(c->nsec3, c->rr);
size_t iter = nsec3_get_iter(c->nsec3, c->rr);
uint8_t* salt;
size_t saltlen, i;
if(!nsec3_get_salt(c->nsec3, c->rr, &salt, &saltlen))
return -1;
/* prepare buffer for first iteration */
ldns_buffer_clear(buf);
ldns_buffer_write(buf, c->dname, c->dname_len);
query_dname_tolower(ldns_buffer_begin(buf));
ldns_buffer_write(buf, salt, saltlen);
ldns_buffer_flip(buf);
switch(algo) {
#ifdef SHA_DIGEST_LENGTH
case NSEC3_HASH_SHA1:
c->hash_len = SHA_DIGEST_LENGTH;
c->hash = (uint8_t*)region_alloc(region, c->hash_len);
if(!c->hash)
return 0;
(void)SHA1((unsigned char*)ldns_buffer_begin(buf),
(unsigned long)ldns_buffer_limit(buf),
(unsigned char*)c->hash);
for(i=0; i<iter; i++) {
ldns_buffer_clear(buf);
ldns_buffer_write(buf, c->hash, c->hash_len);
ldns_buffer_write(buf, salt, saltlen);
ldns_buffer_flip(buf);
(void)SHA1(
(unsigned char*)ldns_buffer_begin(buf),
(unsigned long)ldns_buffer_limit(buf),
(unsigned char*)c->hash);
}
#endif /* SHA_DIGEST_LENGTH */
default:
log_err("nsec3 hash of unknown algo %d", algo);
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 */
static int
nsec3_calc_b32(struct region* region, ldns_buffer* buf,
struct nsec3_cached_hash* c)
{
int r;
ldns_buffer_clear(buf);
r = b32_ntop_extended_hex(c->hash, c->hash_len,
(char*)ldns_buffer_begin(buf), ldns_buffer_limit(buf));
if(r < 1) {
log_err("b32_ntop_extended_hex: error in encoding: %d", r);
return 0;
}
c->b32_len = (size_t)r;
c->b32 = region_alloc_init(region, ldns_buffer_begin(buf), c->b32_len);
if(!c->b32)
return 0;
return 1;
}
/**
* Obtain the hash of an owner name.
* @param table: the cache table.
* @param table: the cache table. Must be inited at start.
* @param region: scratch region to use for allocation.
* @param d: the rrset data
* @param buf: temporary buffer.
* @param nsec3: the rrset with parameters
* @param rr: rr number from d that has the NSEC3 parameters to hash to.
* @param dname: name to hash
* @param dname_len: the length of the name.
@ -167,10 +510,43 @@ filter_init(struct nsec3_filter* filter, struct ub_packed_rrset_key* list,
* -1 if the NSEC3 rr was badly formatted (i.e. formerr).
*/
static int
nsec3_hash_name(rbtree_t* table, struct region* region,
struct packed_rrset_data* data, size_t rr, uint8_t* dname,
nsec3_hash_name(rbtree_t* table, struct region* region, ldns_buffer* buf,
struct ub_packed_rrset_key* nsec3, int rr, uint8_t* dname,
size_t dname_len, struct nsec3_cached_hash** hash)
{
struct nsec3_cached_hash* c;
struct nsec3_cached_hash looki;
rbnode_t* n;
int r;
looki.node.key = &looki;
looki.nsec3 = nsec3;
looki.rr = rr;
looki.dname = dname;
looki.dname_len = dname_len;
/* lookup first in cache */
c = (struct nsec3_cached_hash*)rbtree_search(table, &looki);
if(c) {
*hash = c;
return 1;
}
/* create a new entry */
c = (struct nsec3_cached_hash*)region_alloc(region, sizeof(*c));
if(!c) return 0;
c->node.key = c;
c->nsec3 = nsec3;
c->rr = rr;
c->dname = dname;
c->dname_len = dname_len;
r = nsec3_calc_hash(region, buf, c);
if(r != 1)
return r;
r = nsec3_calc_b32(region, buf, c);
if(r != 1)
return r;
n = rbtree_insert(table, &c->node);
log_assert(n); /* cannot be duplicate, just did lookup */
*hash = c;
return 1;
}
/**

View file

@ -39,6 +39,29 @@
* This file contains helper functions for the validator module.
* The functions help with NSEC3 checking, the different NSEC3 proofs
* for denial of existance, and proofs for presence of types.
*
* NSEC3
* 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Hash Alg. | Flags | Iterations |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Salt Length | Salt /
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Hash Length | Next Hashed Owner Name /
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* / Type Bit Maps /
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* NSEC3PARAM
* 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Hash Alg. | Flags | Iterations |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Salt Length | Salt /
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
#ifndef VALIDATOR_VAL_NSEC3_H
@ -51,4 +74,23 @@ struct reply_info;
struct query_info;
struct key_entry_key;
/**
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+
* | |O|
* +-+-+-+-+-+-+-+-+
* The OPT-OUT bit in the NSEC3 flags field.
* If enabled, there can be zero or more unsigned delegations in the span.
* If disabled, there are zero unsigned delegations in the span.
*/
#define NSEC3_OPTOUT 0x01
/**
* The unknown flags in the NSEC3 flags field.
* They must be zero, or the NSEC3 is ignored.
*/
#define NSEC3_UNKNOWN_FLAGS 0xFE
/** The SHA1 hash algorithm for NSEC3 */
#define NSEC3_HASH_SHA1 0x01
#endif /* VALIDATOR_VAL_NSEC3_H */

View file

@ -54,10 +54,50 @@
#include "util/region-allocator.h"
#include "util/config_file.h"
/** fill up nsec3 key iterations config entry */
static int
fill_nsec3_iter(struct val_env* ve, char* s, int c)
{
char* e;
int i;
free(ve->nsec3_keysize);
free(ve->nsec3_maxiter);
ve->nsec3_keysize = (size_t*)calloc(sizeof(size_t), (size_t)c);
ve->nsec3_maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c);
if(!ve->nsec3_keysize || !ve->nsec3_maxiter) {
log_err("out of memory");
return 0;
}
for(i=0; i<c; i++) {
ve->nsec3_keysize[i] = (size_t)strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse: %s", s);
return 0;
}
s = e;
ve->nsec3_maxiter[i] = (size_t)strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse: %s", s);
return 0;
}
s = e;
if(i>0 && ve->nsec3_keysize[i-1] >= ve->nsec3_keysize[i]) {
log_err("nsec3 key iterations not ascending: %d %d",
(int)ve->nsec3_keysize[i-1],
(int)ve->nsec3_keysize[i]);
return 0;
}
verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d",
(int)ve->nsec3_keysize[i], (int)ve->nsec3_maxiter[i]);
}
return 1;
}
/** apply config settings to validator */
static int
val_apply_cfg(struct val_env* val_env, struct config_file* cfg)
{
int c;
val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl;
val_env->clean_additional = cfg->val_clean_additional;
val_env->permissive_mode = cfg->val_permissive_mode;
@ -78,6 +118,17 @@ val_apply_cfg(struct val_env* val_env, struct config_file* cfg)
return 0;
}
val_env->date_override = cfg->val_date_override;
c = cfg_count_numbers(cfg->val_nsec3_key_iterations);
if(c < 1 || (c&1)) {
log_err("validator: unparseable or odd nsec3 key "
"iterations: %s", cfg->val_nsec3_key_iterations);
return 0;
}
val_env->nsec3_keyiter_count = c/2;
if(!fill_nsec3_iter(val_env, cfg->val_nsec3_key_iterations, c/2)) {
log_err("validator: cannot apply nsec3 key iterations");
return 0;
}
return 1;
}
@ -111,6 +162,8 @@ val_deinit(struct module_env* env, int id)
val_env = (struct val_env*)env->modinfo[id];
anchors_delete(val_env->anchors);
key_cache_delete(val_env->kcache);
free(val_env->nsec3_keysize);
free(val_env->nsec3_maxiter);
free(val_env);
}
@ -1819,7 +1872,8 @@ val_get_mem(struct module_env* env, int id)
if(!ve)
return 0;
return sizeof(*ve) + key_cache_get_mem(ve->kcache) +
anchors_get_mem(ve->anchors);
anchors_get_mem(ve->anchors) +
sizeof(size_t)*2*ve->nsec3_keyiter_count;
}
/**

View file

@ -87,6 +87,25 @@ struct val_env {
* hurting responses to clients.
*/
int permissive_mode;
/**
* Number of entries in the NSEC3 maximum iteration count table.
* Keep this table short, and sorted by size
*/
int nsec3_keyiter_count;
/**
* NSEC3 maximum iteration count per signing key size.
* This array contains key size values (in increasing order)
*/
size_t* nsec3_keysize;
/**
* NSEC3 maximum iteration count per signing key size.
* This array contains the maximum iteration count for the keysize
* in the keysize array.
*/
size_t* nsec3_maxiter;
};
/**