trust anchor storage and config.

git-svn-id: file:///svn/unbound/trunk@486 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-08-03 11:51:20 +00:00
parent 669a20084c
commit d48e17e1dd
9 changed files with 340 additions and 14 deletions

View 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.

View 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

View 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

View 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);
}

View 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;

View 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 */

View 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

View 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;
}

View 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 */