]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
nsec3 work.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 12 Sep 2007 14:16:46 +0000 (14:16 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 12 Sep 2007 14:16:46 +0000 (14:16 +0000)
git-svn-id: file:///svn/unbound/trunk@609 be551aaa-1e26-0410-a405-d3ace91eadb9

15 files changed:
doc/Changelog
doc/TODO
doc/example.conf
doc/unbound.conf.5
iterator/iter_utils.c
util/config_file.c
util/config_file.h
util/configlexer.lex
util/configparser.y
validator/val_kentry.c
validator/val_kentry.h
validator/val_nsec3.c
validator/val_nsec3.h
validator/validator.c
validator/validator.h

index 8f041ba2dccee1504a05056a1f60e81806a16ec5..9cc95b7c4587fc448ffa310298938d4f70d4cfea 100644 (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.
index 2bd365da73e8274ba429cd641946922ee7c636ce..99d129b76f1677814130bc9e556d54e1d948797a 100644 (file)
--- a/doc/TODO
+++ b/doc/TODO
@@ -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.
index c8026a190bbfdd53eb640853059ea71adb76ee22..b721c286af55901b6d80ce7747e060d71716bcc3 100644 (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
index c7d0793e3d40aee34a54668d1b9409564b5dd7a0..71c5300a8a8bc6a7859fd04ed2c1ad7c3438fd87 100644 (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>
index a77bed4b15afd94897ad233217d2c3e23f49938b..ac1d7c42eb336513ac55b92879c48bcc502ce4c4 100644 (file)
 #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;
index a3c82b2b868216d665886ec837be094cf9e269a5..104d1856ec3173fde51e88042448d325b1ae3ba9 100644 (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;
+}
index 171e4b0dc83e9542dd87edfc5e973c4151a17701..a997d6c4939976cd95c41bfc2b81af02f2943bbd 100644 (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
  */
index a086cabcfea7cd47f6254cb18f1eec31c4f07ebb..124079505063d7382e25af198033a7db9e9e25b2 100644 (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 */
index ece4e4dcbc9a6655ac996281605dd58074ac61c9..69beb445b06e64def46e47f56576d433990caf76 100644 (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));
index ca999b6f397b34a67effd6f77e9086dd4078945d..d8fd2757ec1b44cc04d37916673ad10cecd6a280 100644 (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;
+}
index 06e1bf224886f01ac3f26b5a8c890f9c96a448fd..95c6b61f82284e7c90425094c33f5f8c03e334b4 100644 (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 */
index 830e962150d342b0f1d9035e3e5652a1178b5464..4e21ef63b0186dbad05a2cefae9db0d14c3fb818 100644 (file)
  */
 #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;
 }
 
 /**
index 8f1b52ec96751ce26de395821d35f0d50b3dfbf7..1ae612f4fff9e34f98039f6946704a180a43a41c 100644 (file)
  * 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 */
index c612f5ddece7cad01fb84227239ead61d98b92bc..47598c2e8e120890f5e9a3cdaa1189d061038eaf 100644 (file)
 #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;
 }
 
 /**
index 1fd08d4aab8ece26f1eba3b71dd1e64a85b7f2b6..5dacf1c63520eb8f994f9293eccdf860bd36a27e 100644 (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;
 };
 
 /**