]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
trust anchor storage and config.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 3 Aug 2007 11:51:20 +0000 (11:51 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 3 Aug 2007 11:51:20 +0000 (11:51 +0000)
git-svn-id: file:///svn/unbound/trunk@486 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
doc/example.conf
doc/unbound.conf.5
util/config_file.c
util/config_file.h
util/configlexer.lex
util/configparser.y
validator/val_anchor.c
validator/val_anchor.h

index b9554f4b75b34da6ed6b753dce7af623aef94a31..d6f2787588ec28944d685438302eefde785bccda 100644 (file)
@@ -1,3 +1,9 @@
+3 August 2007: Wouter
+       - replanning.
+       - scrubber check section of lame NS set.
+       - trust anchors can be in config file or read from zone file,
+         DS and DNSKEY entries.
+
 2 August 2007: Wouter
        - configure change for latest libevent trunk version (needs -lrt).
        - query_done and walk_supers are moved out of module interface.
index 4d1b04afe8aef3d2d49366f918c60fbd18105cd1..30db1943706520939447171a1ff1e3fe5b805d33 100644 (file)
@@ -152,9 +152,16 @@ server:
        # separated by spaces. "iterator" or "validator iterator"
        # module-config: "validator iterator"
        
-       # File with trusted keys for validation.
+       # File with trusted keys for validation. Specify more than one file
+       # with several entries, one file per entry.
        # Zone file format, with DS and DNSKEY entries.
        # trust-anchor-file: ""
+       
+       # Trusted key for validation. DS or DNSKEY. specify the RR on a
+       # single line, surrounded by "". TTL is ignored. class is IN default.
+       # (These examples are from August 2007 and may not be valid anymore).
+       # trust-anchor: "nlnetlabs.nl. DNSKEY 257 3 5 AQPzzTWMz8qSWIQlfRnPckx2BiVmkVN6LPupO3mbz7FhLSnm26n6iG9N Lby97Ji453aWZY3M5/xJBSOS2vWtco2t8C0+xeO1bc/d6ZTy32DHchpW 6rDH1vp86Ll+ha0tmwyy9QP7y2bVw5zSbFCrefk8qCUBgfHm9bHzMG1U BYtEIQ=="
+       # trust-anchor: "jelte.nlnetlabs.nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A"
 
 # Stub zones.
 # Create entries like below, to make all queries for 'example.com' and 
index b2166ecce6c1ab88a09dbfa3dda7773e880d0e8d..2c3f5429ad1e32e70a4a991d1c58cb0533ce55e6 100644 (file)
@@ -192,6 +192,13 @@ Setting this to "validator iterator" will turn on validation.
 File with trusted keys for validation. Both DS and DNSKEY entries can appear
 in the file. The format of the file is the standard DNS Zone file format.
 Default is "", or no trust anchor file.
+.It \fBtrust-anchor:\fR <"Resource Record">
+A DS or DNSKEY RR for a key to use for validation. Multiple entries can be
+given to specify multiple trusted keys, in addition to the trust-anchor-files.
+The resource record is entered in the same format as 'dig' or 'drill' prints
+them, the same format as in the zone file. Has to be on a single line, with
+"" around it. A TTL can be specified for ease of cut and paste, but is ignored. 
+A class can be specified, but class IN is default.
 .El
 
 .Ss Stub Zone Options
index 94175f0740d1903291afef344caa986bb891d3e2..0647fcffbdd88bfa3ab15092752b26d7017f7eee 100644 (file)
@@ -111,7 +111,8 @@ config_create()
        cfg->hide_version = 0;
        cfg->identity = NULL;
        cfg->version = NULL;
-       cfg->trust_anchor_file = NULL;
+       cfg->trust_anchor_file_list = NULL;
+       cfg->trust_anchor_list = NULL;
        if(!(cfg->module_conf = strdup("iterator"))) goto error_exit;
        return cfg;
 error_exit:
@@ -206,7 +207,8 @@ config_delete(struct config_file* cfg)
        free(cfg->identity);
        free(cfg->version);
        free(cfg->module_conf);
-       free(cfg->trust_anchor_file);
+       config_delstrlist(cfg->trust_anchor_file_list);
+       config_delstrlist(cfg->trust_anchor_list);
        free(cfg);
 }
 
index 71d5ead0e8d0255ef1d1952fdc628ec7d2099d5b..d91082e52bc8e7d144ea7402b11ce37714e4cbc2 100644 (file)
@@ -139,8 +139,10 @@ struct config_file {
        /** the module configuration string */
        char* module_conf;
        
-       /** file with trusted DS and DNSKEYs in zonefile format */
-       char* trust_anchor_file;
+       /** files with trusted DS and DNSKEYs in zonefile format, list */
+       struct config_strlist* trust_anchor_file_list;
+       /** list of trustanchor keys, linked list */
+       struct config_strlist* trust_anchor_list;
 
        /** daemonize, i.e. fork into the background. */
        int do_daemonize;
index d902f988a85cb8ff153d140588d481457a923df1..5f128ec4cb4ef0ac94fd16580e1e8a4854eaa831 100644 (file)
@@ -143,6 +143,7 @@ identity{COLON}             { YDOUT; return VAR_IDENTITY;}
 version{COLON}         { YDOUT; return VAR_VERSION;}
 module-conf{COLON}             { YDOUT; return VAR_MODULE_CONF;}
 trust-anchor-file{COLON}       { YDOUT; return VAR_TRUST_ANCHOR_FILE;}
+trust-anchor{COLON}    { YDOUT; return VAR_TRUST_ANCHOR;}
 {NEWLINE}              { LEXOUT(("NL\n")); cfg_parser->line++;}
 
        /* Quoted strings. Strip leading and ending quotes */
index b086f8cbe643a0073c0156ebac343992cdd1f471..fa2a57bfd4c33d7daeee01729fb977647f6aa59c 100644 (file)
@@ -80,7 +80,7 @@ extern struct config_parser_state* cfg_parser;
 %token VAR_FORWARD_ZONE VAR_FORWARD_HOST VAR_FORWARD_ADDR
 %token VAR_DO_NOT_QUERY_ADDRESS VAR_HIDE_IDENTITY VAR_HIDE_VERSION
 %token VAR_IDENTITY VAR_VERSION VAR_HARDEN_GLUE VAR_MODULE_CONF
-%token VAR_TRUST_ANCHOR_FILE
+%token VAR_TRUST_ANCHOR_FILE VAR_TRUST_ANCHOR
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -113,7 +113,8 @@ content_server: server_num_threads | server_verbosity | server_port |
        server_harden_short_bufsize | server_harden_large_queries |
        server_do_not_query_address | server_hide_identity |
        server_hide_version | server_identity | server_version |
-       server_harden_glue | server_module_conf | server_trust_anchor_file
+       server_harden_glue | server_module_conf | server_trust_anchor_file |
+       server_trust_anchor
        ;
 stubstart: VAR_STUB_ZONE
        {
@@ -288,8 +289,16 @@ server_pidfile: VAR_PIDFILE STRING
 server_trust_anchor_file: VAR_TRUST_ANCHOR_FILE STRING
        {
                OUTYY(("P(server_trust_anchor_file:%s)\n", $2));
-               free(cfg_parser->cfg->trust_anchor_file);
-               cfg_parser->cfg->trust_anchor_file = $2;
+               if(!cfg_strlist_insert(&cfg_parser->cfg->
+                       trust_anchor_file_list, $2))
+                       yyerror("out of memory");
+       }
+       ;
+server_trust_anchor: VAR_TRUST_ANCHOR STRING
+       {
+               OUTYY(("P(server_trust_anchor:%s)\n", $2));
+               if(!cfg_strlist_insert(&cfg_parser->cfg->trust_anchor_list, $2))
+                       yyerror("out of memory");
        }
        ;
 server_hide_identity: VAR_HIDE_IDENTITY STRING
index af0fef7638f9b8bd0b3540fa6baff532f68454fb..770765e5ea7a6e71e8abe94ad58467232d410a8e 100644 (file)
@@ -43,6 +43,7 @@
 #include "util/data/packed_rrset.h"
 #include "util/data/dname.h"
 #include "util/log.h"
+#include "util/net_help.h"
 #include "util/region-allocator.h"
 #include "util/config_file.h"
 
@@ -121,13 +122,274 @@ init_parents(struct val_anchors* anchors)
        }
 }
 
+/**
+ * Find a trust anchor. Exact matching.
+ * @param anchors: anchor storage.
+ * @param name: name of trust anchor (wireformat)
+ * @param namelabs: labels in name
+ * @param namelen: length of name
+ * @param dclass: class of trust anchor
+ * @return NULL if not found.
+ */
+static struct trust_anchor*
+anchor_find(struct val_anchors* anchors, uint8_t* name, int namelabs,
+       size_t namelen, uint16_t dclass)
+{
+       struct trust_anchor key;
+       rbnode_t* n;
+       key.node.key = &key;
+       key.name = name;
+       key.namelabs = namelabs;
+       key.namelen = namelen;
+       key.dclass = dclass;
+       n = rbtree_search(anchors->tree, &key);
+       if(!n)
+               return NULL;
+       return (struct trust_anchor*)n->key;
+}
+
+/** create new trust anchor object */
+static struct trust_anchor*
+anchor_new_ta(struct val_anchors* anchors, uint8_t* name, int namelabs,
+       size_t namelen, uint16_t dclass)
+{
+       struct trust_anchor* ta = (struct trust_anchor*)region_alloc(
+               anchors->region, sizeof(struct trust_anchor));
+       if(!ta)
+               return NULL;
+       memset(ta, 0, sizeof(*ta));
+       ta->node.key = ta;
+       ta->name = name;
+       ta->namelabs = namelabs;
+       ta->namelen = namelen;
+       ta->dclass = dclass;
+       return ta;
+}
+
+/** find trustanchor key by exact data match */
+static struct ta_key*
+anchor_find_key(struct trust_anchor* ta, uint8_t* rdata, size_t rdata_len,
+       uint16_t type)
+{
+       struct ta_key* k;
+       for(k = ta->keylist; k; k = k->next) {
+               if(k->type == type && k->len == rdata_len &&
+                       memcmp(k->data, rdata, rdata_len) == 0)
+                       return k;
+       }
+       return NULL;
+}
+       
+/** create new trustanchor key */
+static struct ta_key*
+anchor_new_ta_key(struct val_anchors* anchors, uint8_t* rdata, size_t rdata_len,
+       uint16_t type)
+{
+       struct ta_key* k = (struct ta_key*)region_alloc(anchors->region,
+               sizeof(*k));
+       if(!k)
+               return NULL;
+       memset(k, 0, sizeof(*k));
+       k->data = region_alloc_init(anchors->region, rdata, rdata_len);
+       if(!k->data)
+               return NULL;
+       k->len = rdata_len;
+       k->type = type;
+       return k;
+}
+
+/**
+ * This routine adds a new RR to a trust anchor. The trust anchor may not
+ * exist yet, and is created if not. The RR can be DS or DNSKEY.
+ * This routine will also remove duplicates; storing them only once.
+ * @param anchors: anchor storage.
+ * @param name: name of trust anchor (wireformat)
+ * @param type: type or RR
+ * @param dclass: class of RR
+ * @param rdata: rdata wireformat, starting with rdlength.
+ * @param rdata_len: length of rdata including rdlength.
+ * @return: 0 on error.
+ */
+static int
+anchor_store_new_key(struct val_anchors* anchors, uint8_t* name, uint16_t type,
+       uint16_t dclass, uint8_t* rdata, size_t rdata_len)
+{
+       struct ta_key* k;
+       struct trust_anchor* ta;
+       int namelabs;
+       size_t namelen;
+       namelabs = dname_count_size_labels(name, &namelen);
+       if(type != LDNS_RR_TYPE_DS && type != LDNS_RR_TYPE_DNSKEY) {
+               log_err("Bad type for trust anchor");
+               return 0;
+       }
+       /* lookup or create trustanchor */
+       ta = anchor_find(anchors, name, namelabs, namelen, dclass);
+       if(!ta) {
+               ta = anchor_new_ta(anchors, name, namelabs, namelen, dclass);
+               if(!ta)
+                       return 0;
+       }
+       /* look for duplicates */
+       if(anchor_find_key(ta, rdata, rdata_len, type)) {
+               return 1;
+       }
+       k = anchor_new_ta_key(anchors, rdata, rdata_len, type);
+       if(!k)
+               return 0;
+       /* add new key */
+       if(type == LDNS_RR_TYPE_DS)
+               ta->numDS++;
+       else    ta->numDNSKEY++;
+       k->next = ta->keylist;
+       ta->keylist = k;
+       return 1;
+}
+
+/**
+ * Add new RR. It converts ldns RR to wire format.
+ * @param anchors: anchor storage.
+ * @param buffer: parsing buffer.
+ * @param rr: the rr (allocated by caller).
+ * @return false on error.
+ */
+static int
+anchor_store_new_rr(struct val_anchors* anchors, ldns_buffer* buffer, 
+       ldns_rr* rr)
+{
+       ldns_rdf* owner = ldns_rr_owner(rr);
+       ldns_status status;
+       ldns_buffer_clear(buffer);
+       ldns_buffer_skip(buffer, 2); /* skip rdatalen */
+       status = ldns_rr_rdata2buffer_wire(buffer, rr);
+       if(status != LDNS_STATUS_OK) {
+               log_err("error converting trustanchor to wireformat: %s", 
+                       ldns_get_errorstr_by_id(status));
+               return 0;
+       }
+       ldns_buffer_flip(buffer);
+       ldns_buffer_write_u16_at(buffer, 0, ldns_buffer_limit(buffer) - 2);
+
+       if(!anchor_store_new_key(anchors, ldns_rdf_data(owner), 
+               ldns_rr_get_type(rr), ldns_rr_get_class(rr),
+               ldns_buffer_begin(buffer), ldns_buffer_limit(buffer))) {
+               return 0;
+       }
+       log_nametypeclass(VERB_DETAIL, "adding trusted key",
+               ldns_rdf_data(owner), 
+               ldns_rr_get_type(rr), ldns_rr_get_class(rr));
+       return 1;
+}
+
+/**
+ * Store one string as trust anchor RR.
+ * @param anchors: anchor storage.
+ * @param buffer: parsing buffer.
+ * @param str: string.
+ * @return false on error.
+ */
+static int
+anchor_store_str(struct val_anchors* anchors, ldns_buffer* buffer,
+       const char* str)
+{
+       ldns_rr* rr = NULL;
+       ldns_status status = ldns_rr_new_frm_str(&rr, str, 0, NULL, NULL);
+       if(status != LDNS_STATUS_OK) {
+               log_err("error parsing trust anchor: %s", 
+                       ldns_get_errorstr_by_id(status));
+               ldns_rr_free(rr);
+               return 0;
+       }
+       if(!anchor_store_new_rr(anchors, buffer, rr)) {
+               log_err("out of memory");
+               ldns_rr_free(rr);
+               return 0;
+       }
+       ldns_rr_free(rr);
+       return 1;
+}
+
+/**
+ * Read a file with trust anchors
+ * @param anchors: anchor storage.
+ * @param buffer: parsing buffer.
+ * @param fname: string.
+ * @return false on error.
+ */
+static int
+anchor_read_file(struct val_anchors* anchors, ldns_buffer* buffer,
+       const char* fname)
+{
+       uint32_t default_ttl = 3600;
+       ldns_rdf* origin = NULL, *prev = NULL;
+       int line_nr = 1;
+       ldns_status status;
+       ldns_rr* rr;
+       int ok = 1;
+       FILE* in = fopen(fname, "r");
+       if(!in) {
+               log_err("error opening file %s: %s", fname, strerror(errno));
+               return 0;
+       }
+       while(!feof(in)) {
+               rr = NULL;
+               status = ldns_rr_new_frm_fp_l(&rr, in, &default_ttl, &origin,
+                       &prev, &line_nr);
+               if(status == LDNS_STATUS_SYNTAX_EMPTY /* empty line */
+                       || status == LDNS_STATUS_SYNTAX_TTL /* $TTL */
+                       || status == LDNS_STATUS_SYNTAX_ORIGIN /* $ORIGIN */)
+                       continue;
+               if(status != LDNS_STATUS_OK) {
+                       log_err("parse error in %s:%d : %s", fname, line_nr,
+                               ldns_get_errorstr_by_id(status));
+                       ldns_rr_free(rr);
+                       ok = 0;
+                       break;
+               }
+               if(ldns_rr_get_type(rr) != LDNS_RR_TYPE_DS && 
+                       ldns_rr_get_type(rr) != LDNS_RR_TYPE_DNSKEY) {
+                       ldns_rr_free(rr);
+                       continue;
+               }
+               if(!anchor_store_new_rr(anchors, buffer, rr)) {
+                       log_err("error at %s line %d", fname, line_nr);
+                       ldns_rr_free(rr);
+                       ok = 0;
+                       break;
+               }
+               ldns_rr_free(rr);
+       }
+       ldns_rdf_deep_free(origin);
+       ldns_rdf_deep_free(prev);
+       fclose(in);
+       return ok;
+}
+
 int 
 anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg)
 {
-       if(cfg->trust_anchor_file && cfg->trust_anchor_file[0]) {
-               /* read trust anchor file */
+       struct config_strlist* f;
+       ldns_buffer* parsebuf = ldns_buffer_new(65535);
+       for(f = cfg->trust_anchor_file_list; f; f = f->next) {
+               if(!f->str || f->str[0] == 0) /* empty "" */
+                       continue;
+               if(!anchor_read_file(anchors, parsebuf, f->str)) {
+                       log_err("error reading trust-anchor-file: %s", f->str);
+                       ldns_buffer_free(parsebuf);
+                       return 0;
+               }
+       }
+       for(f = cfg->trust_anchor_list; f; f = f->next) {
+               if(!f->str || f->str[0] == 0) /* empty "" */
+                       continue;
+               if(!anchor_store_str(anchors, parsebuf, f->str)) {
+                       log_err("error in trust-anchor: \"%s\"", f->str);
+                       ldns_buffer_free(parsebuf);
+                       return 0;
+               }
        }
        init_parents(anchors);
+       ldns_buffer_free(parsebuf);
        return 1;
 }
 
@@ -135,5 +397,33 @@ struct trust_anchor*
 anchors_lookup(struct val_anchors* anchors,
         uint8_t* qname, size_t qname_len, uint16_t qclass)
 {
-       return NULL;
+       struct trust_anchor key;
+       struct trust_anchor* result;
+       rbnode_t* res = NULL;
+       key.node.key = &key;
+       key.name = qname;
+       key.namelabs = dname_count_labels(qname);
+       key.namelen = qname_len;
+       key.dclass = qclass;
+       if(rbtree_find_less_equal(anchors->tree, &key, &res)) {
+               /* exact */
+               result = (struct trust_anchor*)res->key;
+       } else {
+               /* smaller element (or no element) */
+               int m;
+               result = (struct trust_anchor*)res->key;
+               if(!result || result->dclass != qclass)
+                       return NULL;
+               /* count number of labels matched */
+               (void)dname_lab_cmp(result->name, result->namelabs, key.name,
+                       key.namelabs, &m);
+               while(result) { /* go up until qname is subdomain of stub */
+                       if(result->namelabs <= m)
+                               break;
+                       result = result->parent;
+               }
+               if(!result)
+                       return NULL;
+       }
+       return result;
 }
index f0de67eaa2129c1208410bc55e901493284052cf..c2cd40c4908a72f8cf1efd547ad0a37b1b3bf908 100644 (file)
@@ -67,9 +67,9 @@ struct val_anchors {
 struct ta_key {
        /** next in list */
        struct ta_key* next;
-       /** rdata, in wireformat of the key RR. */
+       /** rdata, in wireformat of the key RR. starts with rdlength. */
        uint8_t* data;
-       /** length of the rdata */
+       /** length of the rdata (including rdlength). */
        size_t len;
        /** DNS type (host format) of the key, DS or DNSKEY */
        uint16_t type;
@@ -84,6 +84,8 @@ struct trust_anchor {
        rbnode_t node;
        /** name of this trust anchor */
        uint8_t* name;
+       /** length of name */
+       size_t namelen;
        /** number of labels in name of rrset */
        int namelabs;
        /** the ancestor in the trustanchor tree */