diff --git a/doc/Changelog b/doc/Changelog index 4b3a01d9b..793e530c9 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +13 August 2009: Wouter + - autotrust read anchor files. locked trust anchors. + 12 August 2009: Wouter - autotrust import work. diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index 9ff474546..bb529759c 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -476,13 +476,16 @@ int iter_indicates_dnssec(struct module_env* env, struct delegpt* dp, struct dns_msg* msg, uint16_t dclass) { + struct trust_anchor* a; /* information not available, !env->anchors can be common */ if(!env || !env->anchors || !dp || !dp->name) return 0; /* a trust anchor exists with this name, RRSIGs expected */ - if(anchor_find(env->anchors, dp->name, dp->namelabs, dp->namelen, - dclass)) + if((a=anchor_find(env->anchors, dp->name, dp->namelabs, dp->namelen, + dclass))) { + lock_basic_unlock(&a->lock); return 1; + } /* see if DS rrset was given, in AUTH section */ if(msg && msg->rep && reply_find_rrset_section_ns(msg->rep, dp->name, dp->namelen, diff --git a/testcode/checklocks.c b/testcode/checklocks.c index 520e6ddd1..3ab9ebaf5 100644 --- a/testcode/checklocks.c +++ b/testcode/checklocks.c @@ -87,6 +87,7 @@ static void lock_error(struct checked_lock* lock, (lock->type==check_lock_rwlock)?"rwlock": "badtype")), err); log_err("complete status display:"); total_debug_info(); + abort(); fatal_exit("bailing out"); } diff --git a/testcode/unitanchor.c b/testcode/unitanchor.c index 597284463..ea090bc7e 100644 --- a/testcode/unitanchor.c +++ b/testcode/unitanchor.c @@ -64,6 +64,7 @@ test_anchor_empty(struct val_anchors* a) static void test_anchor_one(ldns_buffer* buff, struct val_anchors* a) { + struct trust_anchor* ta; uint16_t c = LDNS_RR_CLASS_IN; unit_assert(anchor_store_str(a, buff, "nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A")); @@ -71,11 +72,19 @@ test_anchor_one(ldns_buffer* buff, struct val_anchors* a) unit_assert(anchors_lookup(a, (uint8_t*)"\003com\000", 5, c) == NULL); unit_assert(anchors_lookup(a, (uint8_t*)"\007example\003com\000", 11, c) == NULL); - unit_assert(anchors_lookup(a, (uint8_t*)"\002nl\000", 4, c) != NULL); - unit_assert(anchors_lookup(a, - (uint8_t*)"\004labs\002nl\000", 9, c) != NULL); - unit_assert(anchors_lookup(a, - (uint8_t*)"\004fabs\002nl\000", 9, c) != NULL); + + unit_assert((ta=anchors_lookup(a, + (uint8_t*)"\002nl\000", 4, c)) != NULL); + lock_basic_unlock(&ta->lock); + + unit_assert((ta=anchors_lookup(a, + (uint8_t*)"\004labs\002nl\000", 9, c)) != NULL); + lock_basic_unlock(&ta->lock); + + unit_assert((ta=anchors_lookup(a, + (uint8_t*)"\004fabs\002nl\000", 9, c)) != NULL); + lock_basic_unlock(&ta->lock); + unit_assert(anchors_lookup(a, (uint8_t*)"\002oo\000", 4, c) == NULL); } @@ -91,16 +100,23 @@ test_anchors(ldns_buffer* buff, struct val_anchors* a) unit_assert(anchors_lookup(a, (uint8_t*)"\003com\000", 5, c) == NULL); unit_assert(anchors_lookup(a, (uint8_t*)"\007example\003com\000", 11, c) == NULL); + unit_assert(ta = anchors_lookup(a, (uint8_t*)"\002nl\000", 4, c)); unit_assert(query_dname_compare(ta->name, (uint8_t*)"\002nl\000")==0); + lock_basic_unlock(&ta->lock); + unit_assert(ta = anchors_lookup(a, (uint8_t*)"\004labs\002nl\000", 9, c)); unit_assert(query_dname_compare(ta->name, (uint8_t*)"\004labs\002nl\000") == 0); + lock_basic_unlock(&ta->lock); + unit_assert(ta = anchors_lookup(a, (uint8_t*)"\004fabs\002nl\000", 9, c)); unit_assert(query_dname_compare(ta->name, (uint8_t*)"\002nl\000") == 0); + lock_basic_unlock(&ta->lock); + unit_assert(anchors_lookup(a, (uint8_t*)"\002oo\000", 4, c) == NULL); } diff --git a/validator/autotrust.c b/validator/autotrust.c index d5593ccec..4cd463333 100644 --- a/validator/autotrust.c +++ b/validator/autotrust.c @@ -41,12 +41,11 @@ #include "config.h" #include "validator/autotrust.h" #include "validator/val_anchor.h" +#include "util/data/dname.h" #include "util/log.h" +#include "util/module.h" #include "util/net_help.h" -/** max length of parse lines */ -#define MAXLEN 10240 - struct autr_global_data* autr_global_create(void) { struct autr_global_data* global; @@ -104,31 +103,31 @@ position_in_string(char *str, const char* sub) } if (pos < 0) return pos; - return pos + strlen(sub); + return pos + (int)strlen(sub); } /** * Parse comments * @param str: to parse * @param ta: trust key autotrust metadata - * @param tp: trust anchor * @return false on failure. */ static int -parse_comments(char* str, struct autr_ta_data* ta, struct trust_anchor* tp) +parse_comments(char* str, struct autr_ta* ta) { - char* comment = (char*) malloc(sizeof(char)*MAXLEN); + int len = (int)strlen(str), pos = 0, timestamp = 0; + char* comment = (char*) malloc(sizeof(char)*len+1); char* comments = comment; - int len = strlen(str), pos = 0, timestamp = 0; - if (len >= MAXLEN) { - log_err("line too long"); + if(!comment) { + log_err("malloc failure in parse"); return 0; } + /* skip over whitespace and data at start of line */ while (*str != '\0' && *str != ';') str++; - /* comments */ if (*str == ';') str++; + /* copy comments */ while (*str != '\0') { *comments = *str; @@ -139,10 +138,6 @@ parse_comments(char* str, struct autr_ta_data* ta, struct trust_anchor* tp) comments = comment; - if (!ta || !comments) { - log_err("malloc failure in parse"); - return 0; - } /* read state */ pos = position_in_string(comments, "state="); if (pos >= (int) strlen(comments)) @@ -152,10 +147,11 @@ parse_comments(char* str, struct autr_ta_data* ta, struct trust_anchor* tp) return 0; } if (pos <= 0) - ta->s = AUTR_STATE_START; + ta->s = AUTR_STATE_VALID; else { int s = (int) comments[pos] - '0'; + char* str = ldns_rdf2str(ldns_rr_owner(ta->rr)); switch(s) { @@ -168,10 +164,9 @@ parse_comments(char* str, struct autr_ta_data* ta, struct trust_anchor* tp) ta->s = s; break; default: - log_nametypeclass(0, "warning: trust anchor " - "has undefined state, considered " - "NewKey", tp->name, - LDNS_RR_TYPE_DNSKEY, tp->dclass); + log_warn("trust anchor [%s, DNSKEY, id=%i] has " + "undefined state, considered NewKey", + str, ldns_calc_keytag(ta->rr)); ta->s = AUTR_STATE_START; break; } @@ -191,7 +186,7 @@ parse_comments(char* str, struct autr_ta_data* ta, struct trust_anchor* tp) else { comments += pos; - ta->pending_count = atoi(comments); + ta->pending_count = (uint8_t)atoi(comments); } /* read last change */ @@ -209,12 +204,17 @@ parse_comments(char* str, struct autr_ta_data* ta, struct trust_anchor* tp) } if (pos < 0 || !timestamp) { - log_warn("trust anchor has no timestamp, considered NOW"); - free(str); - ta->last_change = time(NULL); + /* Should we warn about this? It happens for key priming. + ldns_rdf* owner = ldns_rr_owner(ta->rr); + char* str = ldns_rdf2str(owner); + log_warn("trust anchor [%s, DNSKEY, id=%i] has no timestamp, " + "considered NOW", str, ldns_calc_keytag(ta->rr)); + free(str); + */ + ta->last_change = (uint32_t)time(NULL); } else - ta->last_change = timestamp; + ta->last_change = (uint32_t)timestamp; free(comment); return 1; @@ -236,80 +236,191 @@ str_contains_data(char* str, char comment) /** Get DNSKEY flags */ static int -ta_dnskey_flags(struct ta_key* ta) +dnskey_flags(ldns_rr* rr) { - uint16_t f; - if(ta->type != LDNS_RR_TYPE_DNSKEY) + if(ldns_rr_get_type(rr) != LDNS_RR_TYPE_DNSKEY) return 0; - if(ta->len < 2+2) - return 0; - memmove(&f, ta->data+2, 2); - f = ntohs(f); - return f; + return (int)ldns_read_uint16(ldns_rdf_data(ldns_rr_dnskey_flags(rr))); } /** Check if KSK DNSKEY */ static int -rr_is_dnskey_sep(struct ta_key* ta) +rr_is_dnskey_sep(ldns_rr* rr) { - return (ta_dnskey_flags(ta)&DNSKEY_BIT_SEP); + return (dnskey_flags(rr)&DNSKEY_BIT_SEP); +} + +/** create ta */ +static struct autr_ta* +autr_ta_create(ldns_rr* rr) +{ + struct autr_ta* ta = (struct autr_ta*)calloc(1, sizeof(*ta)); + if(!ta) { + ldns_rr_free(rr); + return NULL; + } + ta->rr = rr; + return ta; +} + +/** create tp */ +static struct trust_anchor* +autr_tp_create(struct val_anchors* anchors, ldns_rr* rr) +{ + ldns_rdf* own = ldns_rr_owner(rr); + struct trust_anchor* tp = (struct trust_anchor*)calloc(1, sizeof(*tp)); + if(!tp) return NULL; + tp->name = memdup(ldns_rdf_data(own), ldns_rdf_size(own)); + if(!tp->name) { + free(tp); + return NULL; + } + tp->namelen = ldns_rdf_size(own); + tp->namelabs = dname_count_labels(tp->name); + tp->node.key = tp; + tp->dclass = ldns_rr_get_class(rr); + tp->autr = (struct autr_point_data*)calloc(1, sizeof(*tp->autr)); + if(!tp->autr) { + free(tp->name); + free(tp); + return NULL; + } + tp->autr->pnode.key = tp; + + lock_basic_lock(&anchors->lock); + (void)rbtree_insert(anchors->tree, &tp->node); + lock_basic_unlock(&anchors->lock); + lock_basic_init(&tp->lock); + lock_protect(&tp->lock, tp, sizeof(*tp)); + lock_protect(&tp->lock, tp->autr, sizeof(*tp->autr)); + return tp; +} + +void autr_point_delete(struct trust_anchor* tp) +{ + if(!tp) + return; + lock_unprotect(&tp->lock, tp); + lock_unprotect(&tp->lock, tp->autr); + lock_basic_destroy(&tp->lock); + free(tp->autr); + free(tp->name); + free(tp); +} + +/** find or add a new trust point for autotrust */ +static struct trust_anchor* +find_add_tp(struct val_anchors* anchors, ldns_rr* rr) +{ + struct trust_anchor* tp; + ldns_rdf* own = ldns_rr_owner(rr); + tp = anchor_find(anchors, ldns_rdf_data(own), + dname_count_labels(ldns_rdf_data(own)), + ldns_rdf_size(own), ldns_rr_get_class(rr)); + if(tp) { + if(!tp->autr) { + log_err("anchor cannot be with and without autotrust"); + lock_basic_unlock(&tp->lock); + return NULL; + } + return tp; + } + tp = autr_tp_create(anchors, rr); + lock_basic_lock(&tp->lock); + return tp; +} + +/** Add trust anchor from RR. */ +static struct autr_ta* +add_trustanchor_frm_rr(struct val_anchors* anchors, ldns_rr* rr, + struct trust_anchor** tp) +{ + struct autr_ta* ta = autr_ta_create(rr); + if(!ta) + return NULL; + *tp = find_add_tp(anchors, rr); + /* add ta to tp */ + ta->next = (*tp)->autr->keys; + (*tp)->autr->keys = ta; + lock_basic_unlock(&(*tp)->lock); + return ta; } /** * Add new trust anchor from a string in file. * @param anchors: all anchors - * @param str: string with comments before the anchor, if any comments. + * @param str: string with anchor and comments, if any comments. * @param tp: trust point returned. * @return new key in trust point. */ -static struct ta_key* +static struct autr_ta* add_trustanchor_frm_str(struct val_anchors* anchors, char* str, struct trust_anchor** tp) { - if(!str_contains_data(str, ';')) - return NULL; /* empty line */ + ldns_rr* rr; + struct autr_ta* ta = NULL; + ldns_status lstatus; + if (!str_contains_data(str, ';')) + return NULL; /* empty line */ + if (LDNS_STATUS_OK != + (lstatus = ldns_rr_new_frm_str(&rr, str, 0, NULL, NULL))) + { + log_err("ldns error while converting string to RR: %s", + ldns_get_errorstr_by_id(lstatus)); + return NULL; + } + ta = add_trustanchor_frm_rr(anchors, rr, tp); + return ta; } /** * Load single anchor * @param anchors: all points. * @param str: comments line + * @param fname: filename * @return false on failure. */ static int -load_trustanchor(struct val_anchors* anchors, char* str) +load_trustanchor(struct val_anchors* anchors, char* str, const char* fname) { - int status_ok = 1; - struct ta_key* ta = NULL; + struct autr_ta* ta = NULL; struct trust_anchor* tp = NULL; - if (!str_contains_data(str, ';')) - return status_ok; /* empty lines allowed */ ta = add_trustanchor_frm_str(anchors, str, &tp); - if (ta) { - status_ok = parse_comments(str, ta->autr, tp); - if (rr_is_dnskey_sep(ta)) { - if (ta->autr->s == AUTR_STATE_VALID) - tp->autr->valid ++; - else if (ta->autr->s == AUTR_STATE_MISSING) - tp->autr->missing ++; + if(!ta) + return 0; + lock_basic_lock(&tp->lock); + if(!parse_comments(str, ta)) { + lock_basic_unlock(&tp->lock); + return 0; + } + if (rr_is_dnskey_sep(ta->rr)) { + if (ta->s == AUTR_STATE_VALID) + tp->autr->valid ++; + else if (ta->s == AUTR_STATE_MISSING) + tp->autr->missing ++; + } + if(!tp->autr->file) { + /* TODO insert tp into probe tree */ + tp->autr->file = strdup(fname); + if(!tp->autr->file) { + lock_basic_unlock(&tp->lock); + return 0; } - } - else - return 0; - return status_ok; + } + lock_basic_unlock(&tp->lock); + return 1; } -int autr_read_file(struct val_anchors* anchors, ldns_buffer* parsebuf, - const char* nm) +int autr_read_file(struct val_anchors* anchors, const char* nm) { /* the file descriptor */ FILE* fd; /* keep track of line numbers */ int line_nr = 0; /* single line */ - char line[MAXLEN]; + char line[10240]; if (!(fd = fopen(nm, "r"))) { log_err("unable to open %s for reading: %s", @@ -317,9 +428,13 @@ int autr_read_file(struct val_anchors* anchors, ldns_buffer* parsebuf, return 0; } verbose(VERB_ALGO, "reading trust anchor file %s", nm); - while (fgets(line, MAXLEN, fd) != NULL) { + /* TODO: read line to see if special marker for revoked tp */ + /* TODO: read next probe time (if in file, otherwise now+0-100s) */ + while (fgets(line, (int)sizeof(line), fd) != NULL) { line_nr++; - if (!load_trustanchor(anchors, line)) { + if (!str_contains_data(line, ';')) + continue; /* empty lines allowed */ + if (!load_trustanchor(anchors, line, nm)) { log_err("failed to load trust anchor from %s " "at line %i, skipping", nm, line_nr); /* try to do the rest */ @@ -330,3 +445,19 @@ int autr_read_file(struct val_anchors* anchors, ldns_buffer* parsebuf, return 1; } +void autr_write_file(struct trust_anchor* tp) +{ + /* write pretty header */ + /* write revoked tp special marker */ + /* write next probe time */ + /* write anchors */ +} + +int autr_process_prime(struct module_env* env, struct val_env* ve, + struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset) +{ + struct val_anchors* anchors = env->anchors; + /* autotrust update trust anchors */ + + return 1; +} diff --git a/validator/autotrust.h b/validator/autotrust.h index 1ce209d98..1b6b625de 100644 --- a/validator/autotrust.h +++ b/validator/autotrust.h @@ -43,6 +43,10 @@ #define VALIDATOR_AUTOTRUST_H #include "util/rbtree.h" struct val_anchors; +struct trust_anchor; +struct ub_packed_rrset_key; +struct module_env; +struct val_env; /** Autotrust anchor states */ typedef enum { @@ -57,7 +61,11 @@ typedef enum { /** * Autotrust metadata for one trust anchor key. */ -struct autr_ta_data { +struct autr_ta { + /** next key */ + struct autr_ta* next; + /** the RR */ + ldns_rr* rr; /** 5011 state */ autr_state_t s; /** last update of key */ @@ -78,7 +86,7 @@ struct autr_point_data { const char* file; /** next probe time */ uint32_t next_probe_time; - /** rbtree node for probe sort */ + /** rbtree node for probe sort, key is struct trust_anchor */ rbnode_t pnode; /** last queried DNSKEY set */ @@ -94,6 +102,8 @@ struct autr_point_data { uint8_t valid; /** number of missing DNSKEYs */ uint8_t missing; + /** the keys */ + struct autr_ta* keys; }; /** @@ -122,11 +132,35 @@ int probetree_cmp(const void* x, const void* y); /** * Read autotrust file. * @param anchors: the anchors structure. - * @param parsebuf: buffer temporary for parsing data. * @param nm: name of the file (copied). * @return false on failure. */ -int autr_read_file(struct val_anchors* anchors, ldns_buffer* parsebuf, - const char* nm); +int autr_read_file(struct val_anchors* anchors, const char* nm); + +/** + * Write autotrust file. + * @param tp: trust point to write. + */ +void autr_write_file(struct trust_anchor* tp); + +/** + * Delete autr anchor, deletes the autr data but does not do + * unlinking from trees, caller does that. + * @param tp: trust point to delete. + */ +void autr_point_delete(struct trust_anchor* tp); + +/** + * Perform autotrust processing. + * @param env: qstate environment with the anchors structure. + * @param ve: validator environment for verification of rrsigs. + * @param tp: trust anchor to process. + * @param dnskey_rrset: DNSKEY rrset probed (can be NULL if bad prime result). + * allocated in a region. Has not been validated yet. + * @return false if trust anchor was revoked completely. + * Otherwise logs errors to log, does not change return value. + */ +int autr_process_prime(struct module_env* env, struct val_env* ve, + struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset); #endif /* VALIDATOR_AUTOTRUST_H */ diff --git a/validator/val_anchor.c b/validator/val_anchor.c index e442044d4..ce8d1b862 100644 --- a/validator/val_anchor.c +++ b/validator/val_anchor.c @@ -89,14 +89,31 @@ anchors_create() anchors_delete(a); return NULL; } + lock_basic_init(&a->lock); + lock_protect(&a->lock, a, sizeof(*a)); return a; } +/** destroy locks in tree and delete autotrust anchors */ +static void +anchors_delfunc(rbnode_t* elem, void* ATTR_UNUSED(arg)) +{ + struct trust_anchor* ta = (struct trust_anchor*)elem; + if(ta->autr) { + autr_point_delete(ta); + } else { + lock_basic_destroy(&ta->lock); + } +} + void anchors_delete(struct val_anchors* anchors) { if(!anchors) return; + lock_unprotect(&anchors->lock, anchors); + lock_basic_destroy(&anchors->lock); + traverse_postorder(anchors->tree, anchors_delfunc, NULL); free(anchors->tree); regional_destroy(anchors->region); autr_global_delete(anchors->autr); @@ -109,10 +126,13 @@ init_parents(struct val_anchors* anchors) { struct trust_anchor* node, *prev = NULL, *p; int m; + lock_basic_lock(&anchors->lock); RBTREE_FOR(node, struct trust_anchor*, anchors->tree) { + lock_basic_lock(&node->lock); node->parent = NULL; if(!prev || prev->dclass != node->dclass) { prev = node; + lock_basic_unlock(&node->lock); continue; } (void)dname_lab_cmp(prev->name, prev->namelabs, node->name, @@ -128,8 +148,10 @@ init_parents(struct val_anchors* anchors) node->parent = p; break; } + lock_basic_unlock(&node->lock); prev = node; } + lock_basic_unlock(&anchors->lock); } struct trust_anchor* @@ -138,12 +160,18 @@ anchor_find(struct val_anchors* anchors, uint8_t* name, int namelabs, { struct trust_anchor key; rbnode_t* n; + if(!name) return NULL; key.node.key = &key; key.name = name; key.namelabs = namelabs; key.namelen = namelen; key.dclass = dclass; + lock_basic_lock(&anchors->lock); n = rbtree_search(anchors->tree, &key); + if(n) { + lock_basic_lock(&((struct trust_anchor*)n->key)->lock); + } + lock_basic_unlock(&anchors->lock); if(!n) return NULL; return (struct trust_anchor*)n->key; @@ -167,7 +195,10 @@ anchor_new_ta(struct val_anchors* anchors, uint8_t* name, int namelabs, ta->namelabs = namelabs; ta->namelen = namelen; ta->dclass = dclass; + lock_basic_init(&ta->lock); + lock_basic_lock(&anchors->lock); r = rbtree_insert(anchors->tree, &ta->node); + lock_basic_unlock(&anchors->lock); log_assert(r != NULL); return ta; } @@ -236,22 +267,29 @@ anchor_store_new_key(struct val_anchors* anchors, uint8_t* name, uint16_t type, ta = anchor_new_ta(anchors, name, namelabs, namelen, dclass); if(!ta) return NULL; + lock_basic_lock(&ta->lock); } - if(!rdata) + if(!rdata) { + lock_basic_unlock(&ta->lock); return ta; + } /* look for duplicates */ if(anchor_find_key(ta, rdata, rdata_len, type)) { + lock_basic_unlock(&ta->lock); return ta; } k = anchor_new_ta_key(anchors, rdata, rdata_len, type); - if(!k) + if(!k) { + lock_basic_unlock(&ta->lock); return NULL; + } /* add new key */ if(type == LDNS_RR_TYPE_DS) ta->numDS++; else ta->numDNSKEY++; k->next = ta->keylist; ta->keylist = k; + lock_basic_unlock(&ta->lock); return ta; } @@ -895,15 +933,20 @@ anchors_assemble_rrsets(struct val_anchors* anchors) struct trust_anchor* ta; struct trust_anchor* next; size_t nods, nokey; + lock_basic_lock(&anchors->lock); ta=(struct trust_anchor*)rbtree_first(anchors->tree); while((rbnode_t*)ta != RBTREE_NULL) { next = (struct trust_anchor*)rbtree_next(&ta->node); + lock_basic_lock(&ta->lock); if(ta->numDS == 0 && ta->numDNSKEY == 0) { + lock_basic_unlock(&ta->lock); ta = next; /* skip unsigned entries, nothing to do */ continue; } if(!anchors_assemble(anchors, ta)) { log_err("out of memory"); + lock_basic_unlock(&ta->lock); + lock_basic_unlock(&anchors->lock); return 0; } nods = anchors_ds_unsupported(ta); @@ -926,8 +969,10 @@ anchors_assemble_rrsets(struct val_anchors* anchors) " upgrade unbound and openssl)", b); (void)rbtree_delete(anchors->tree, &ta->node); } + lock_basic_unlock(&ta->lock); ta = next; } + lock_basic_unlock(&anchors->lock); return 1; } @@ -982,27 +1027,35 @@ anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg) } } if(cfg->dlv_anchor_file && cfg->dlv_anchor_file[0] != 0) { + struct trust_anchor* dlva; nm = cfg->dlv_anchor_file; if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm, cfg->chrootdir, strlen(cfg->chrootdir)) == 0) nm += strlen(cfg->chrootdir); - if(!(anchors->dlv_anchor = anchor_read_file(anchors, parsebuf, + if(!(dlva = anchor_read_file(anchors, parsebuf, nm, 1))) { log_err("error reading dlv-anchor-file: %s", cfg->dlv_anchor_file); ldns_buffer_free(parsebuf); return 0; } + lock_basic_lock(&anchors->lock); + anchors->dlv_anchor = dlva; + lock_basic_unlock(&anchors->lock); } for(f = cfg->dlv_anchor_list; f; f = f->next) { + struct trust_anchor* dlva; if(!f->str || f->str[0] == 0) /* empty "" */ continue; - if(!(anchors->dlv_anchor = anchor_store_str( + if(!(dlva = anchor_store_str( anchors, parsebuf, f->str))) { log_err("error in dlv-anchor: \"%s\"", f->str); ldns_buffer_free(parsebuf); return 0; } + lock_basic_lock(&anchors->lock); + anchors->dlv_anchor = dlva; + lock_basic_unlock(&anchors->lock); } for(f = cfg->auto_trust_anchor_file_list; f; f = f->next) { if(!f->str || f->str[0] == 0) /* empty "" */ @@ -1011,7 +1064,7 @@ anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg) if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm, cfg->chrootdir, strlen(cfg->chrootdir)) == 0) nm += strlen(cfg->chrootdir); - if(!autr_read_file(anchors, parsebuf, nm)) { + if(!autr_read_file(anchors, nm)) { log_err("error reading auto-trust-anchor-file: %s", f->str); ldns_buffer_free(parsebuf); @@ -1037,6 +1090,7 @@ anchors_lookup(struct val_anchors* anchors, key.namelabs = dname_count_labels(qname); key.namelen = qname_len; key.dclass = qclass; + lock_basic_lock(&anchors->lock); if(rbtree_find_less_equal(anchors->tree, &key, &res)) { /* exact */ result = (struct trust_anchor*)res; @@ -1044,8 +1098,10 @@ anchors_lookup(struct val_anchors* anchors, /* smaller element (or no element) */ int m; result = (struct trust_anchor*)res; - if(!result || result->dclass != qclass) + if(!result || result->dclass != qclass) { + lock_basic_unlock(&anchors->lock); return NULL; + } /* count number of labels matched */ (void)dname_lab_cmp(result->name, result->namelabs, key.name, key.namelabs, &m); @@ -1055,6 +1111,10 @@ anchors_lookup(struct val_anchors* anchors, result = result->parent; } } + if(result) { + lock_basic_lock(&result->lock); + } + lock_basic_unlock(&anchors->lock); return result; } diff --git a/validator/val_anchor.h b/validator/val_anchor.h index a5274d269..af530ba2e 100644 --- a/validator/val_anchor.h +++ b/validator/val_anchor.h @@ -42,11 +42,11 @@ #ifndef VALIDATOR_VAL_ANCHOR_H #define VALIDATOR_VAL_ANCHOR_H #include "util/rbtree.h" +#include "util/locks.h" struct regional; struct trust_anchor; struct config_file; struct ub_packed_rrset_key; -struct autr_ta_data; struct autr_point_data; struct autr_global_data; @@ -54,6 +54,8 @@ struct autr_global_data; * Trust anchor store. */ struct val_anchors { + /** lock on trees */ + lock_basic_t lock; /** region where trust anchors are allocated */ struct regional* region; /** @@ -81,8 +83,6 @@ struct ta_key { size_t len; /** DNS type (host format) of the key, DS or DNSKEY */ uint16_t type; - /** Autotrust ta key state, or NULL */ - struct autr_ta_data* autr; }; /** @@ -92,6 +92,8 @@ struct ta_key { struct trust_anchor { /** rbtree node, key is this structure */ rbnode_t node; + /** lock on the entire anchor and its keys; for autotrust changes */ + lock_basic_t lock; /** name of this trust anchor */ uint8_t* name; /** length of name */ @@ -147,7 +149,7 @@ int anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg); * @param qname: query name, uncompressed wireformat. * @param qname_len: length of qname. * @param qclass: class to query for. - * @return the trust anchor or NULL if none is found. + * @return the trust anchor or NULL if none is found. The anchor is locked. */ struct trust_anchor* anchors_lookup(struct val_anchors* anchors, uint8_t* qname, size_t qname_len, uint16_t qclass); @@ -159,7 +161,7 @@ struct trust_anchor* anchors_lookup(struct val_anchors* anchors, * @param namelabs: labels in name * @param namelen: length of name * @param dclass: class of trust anchor - * @return NULL if not found. + * @return NULL if not found. The anchor is locked. */ struct trust_anchor* anchor_find(struct val_anchors* anchors, uint8_t* name, int namelabs, size_t namelen, uint16_t dclass); diff --git a/validator/val_utils.c b/validator/val_utils.c index 829f93f68..705e828f4 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -712,6 +712,17 @@ val_check_nonsecure(struct val_env* ve, struct reply_info* rep) } } +/** check no anchor and unlock */ +static int +check_no_anchor(struct val_anchors* anchors, uint8_t* nm, size_t l, uint16_t c) +{ + struct trust_anchor* ta; + if((ta=anchors_lookup(anchors, nm, l, c))) { + lock_basic_unlock(&ta->lock); + } + return !ta; +} + void val_mark_indeterminate(struct reply_info* rep, struct val_anchors* anchors, struct rrset_cache* r, struct module_env* env) @@ -721,7 +732,7 @@ val_mark_indeterminate(struct reply_info* rep, struct val_anchors* anchors, for(i=0; irrset_count; i++) { d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; if(d->security == sec_status_unchecked && - !anchors_lookup(anchors, rep->rrsets[i]->rk.dname, + check_no_anchor(anchors, rep->rrsets[i]->rk.dname, rep->rrsets[i]->rk.dname_len, ntohs(rep->rrsets[i]->rk.rrset_class))) { diff --git a/validator/validator.c b/validator/validator.c index 526ff8dc9..b81dc215e 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -48,6 +48,7 @@ #include "validator/val_nsec.h" #include "validator/val_nsec3.h" #include "validator/val_neg.h" +#include "validator/autotrust.h" #include "services/cache/dns.h" #include "util/data/dname.h" #include "util/module.h" @@ -372,6 +373,15 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, * query, and its a 'normal' for iterator as well */ vq->wait_prime_ta = 1; /* to elicit PRIME_RESP_STATE processing from the validator inform_super() routine */ + /* store trust anchor name for later lookup when prime returns */ + vq->trust_anchor_name = regional_alloc_init(qstate->region, + toprime->name, toprime->namelen); + vq->trust_anchor_len = toprime->namelen; + vq->trust_anchor_labs = toprime->namelabs; + if(!vq->trust_anchor_name) { + log_err("Could not prime trust anchor: out of memory"); + return 0; + } return 1; } @@ -1160,6 +1170,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, { uint8_t* lookup_name; size_t lookup_len; + struct trust_anchor* anchor; enum val_classification subtype = val_classify_response( qstate->query_flags, &qstate->qinfo, &vq->qchase, vq->orig_msg->rep, vq->rrset_skip); @@ -1197,7 +1208,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, vq->key_entry = NULL; vq->empty_DS_name = NULL; vq->ds_rrset = 0; - vq->trust_anchor = anchors_lookup(qstate->env->anchors, + anchor = anchors_lookup(qstate->env->anchors, lookup_name, lookup_len, vq->qchase.qclass); /* Determine the signer/lookup name */ @@ -1216,13 +1227,11 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, /* for NXDOMAIN it could be signed by a parent of the trust anchor */ if(subtype == VAL_CLASS_NAMEERROR && vq->signer_name && - vq->trust_anchor && - dname_strict_subdomain_c(vq->trust_anchor->name, lookup_name)){ - while(vq->trust_anchor && dname_strict_subdomain_c( - vq->trust_anchor->name, lookup_name)) { - vq->trust_anchor = vq->trust_anchor->parent; - } - if(!vq->trust_anchor) { /* unsigned parent denies anchor*/ + anchor && dname_strict_subdomain_c(anchor->name, lookup_name)){ + lock_basic_unlock(&anchor->lock); + anchor = anchors_lookup(qstate->env->anchors, + lookup_name, lookup_len, vq->qchase.qclass); + if(!anchor) { /* unsigned parent denies anchor*/ verbose(VERB_QUERY, "unsigned parent zone denies" " trust anchor, indeterminate"); vq->chase_reply->security = sec_status_indeterminate; @@ -1248,7 +1257,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, vq->qchase.qclass, qstate->region, *qstate->env->now); /* there is no key(from DLV) and no trust anchor */ - if(vq->key_entry == NULL && vq->trust_anchor == NULL) { + if(vq->key_entry == NULL && anchor == NULL) { /*response isn't under a trust anchor, so we cannot validate.*/ vq->chase_reply->security = sec_status_indeterminate; /* go to finished state to cache this result */ @@ -1257,16 +1266,14 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, } /* if not key, or if keyentry is *above* the trustanchor, i.e. * the keyentry is based on another (higher) trustanchor */ - else if(vq->key_entry == NULL || (vq->trust_anchor && - dname_strict_subdomain_c(vq->trust_anchor->name, - vq->key_entry->name))) { + else if(vq->key_entry == NULL || (anchor && + dname_strict_subdomain_c(anchor->name, vq->key_entry->name))) { /* trust anchor is an 'unsigned' trust anchor */ - if(vq->trust_anchor && vq->trust_anchor->numDS == 0 && - vq->trust_anchor->numDNSKEY == 0) { + if(anchor && anchor->numDS == 0 && anchor->numDNSKEY == 0) { vq->chase_reply->security = sec_status_insecure; - val_mark_insecure(vq->chase_reply, - vq->trust_anchor->name, + val_mark_insecure(vq->chase_reply, anchor->name, qstate->env->rrset_cache, qstate->env); + lock_basic_unlock(&anchor->lock); vq->dlv_checked=1; /* skip DLV check */ /* go to finished state to cache this result */ vq->state = VAL_FINISHED_STATE; @@ -1274,13 +1281,21 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, } /* fire off a trust anchor priming query. */ verbose(VERB_DETAIL, "prime trust anchor"); - if(!prime_trust_anchor(qstate, vq, id, vq->trust_anchor)) + if(!prime_trust_anchor(qstate, vq, id, anchor)) { + lock_basic_unlock(&anchor->lock); return val_error(qstate, id); + } + lock_basic_unlock(&anchor->lock); /* and otherwise, don't continue processing this event. * (it will be reactivated when the priming query returns). */ vq->state = VAL_FINDKEY_STATE; return 0; - } else if(key_entry_isnull(vq->key_entry)) { + } + if(anchor) { + lock_basic_unlock(&anchor->lock); + } + + if(key_entry_isnull(vq->key_entry)) { /* response is under a null key, so we cannot validate * However, we do set the status to INSECURE, since it is * essentially proven insecure. */ @@ -2031,8 +2046,8 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id, /** * Evaluate the response to a priming request. * - * @param rcode: rcode return value. - * @param msg: message return value (allocated in a the wrong region). + * @param dnskey_rrset: DNSKEY rrset (can be NULL if none) in prime reply. + * (this rrset is allocated in the wrong region, not the qstate). * @param ta: trust anchor. * @param qstate: qstate that needs key. * @param id: module id. @@ -2042,19 +2057,13 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id, * Bad key (validation failed). */ static struct key_entry_key* -primeResponseToKE(int rcode, struct dns_msg* msg, struct trust_anchor* ta, - struct module_qstate* qstate, int id) +primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, + struct trust_anchor* ta, struct module_qstate* qstate, int id) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; - struct ub_packed_rrset_key* dnskey_rrset = NULL; struct key_entry_key* kkey = NULL; enum sec_status sec = sec_status_unchecked; - if(rcode == LDNS_RCODE_NOERROR) { - dnskey_rrset = reply_find_rrset_section_an(msg->rep, - ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY, - ta->dclass); - } if(!dnskey_rrset) { log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " "could not fetch DNSKEY rrset", @@ -2069,7 +2078,6 @@ primeResponseToKE(int rcode, struct dns_msg* msg, struct trust_anchor* ta, log_err("out of memory: allocate fail prime key"); return NULL; } - key_cache_insert(ve->kcache, kkey); return kkey; } /* attempt to verify with trust anchor DS and DNSKEY */ @@ -2119,14 +2127,11 @@ primeResponseToKE(int rcode, struct dns_msg* msg, struct trust_anchor* ta, log_err("out of memory: allocate null prime key"); return NULL; } - key_cache_insert(ve->kcache, kkey); return kkey; } log_nametypeclass(VERB_DETAIL, "Successfully primed trust anchor", ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); - /* store the freshly primed entry in the cache */ - key_cache_insert(ve->kcache, kkey); return kkey; } @@ -2422,10 +2427,39 @@ static void process_prime_response(struct module_qstate* qstate, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg) { + struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; + struct ub_packed_rrset_key* dnskey_rrset = NULL; + struct trust_anchor* ta = anchor_find(qstate->env->anchors, + vq->trust_anchor_name, vq->trust_anchor_labs, + vq->trust_anchor_len, vq->qchase.qclass); + if(!ta) { + /* trust anchor revoked, restart with less anchors */ + vq->state = VAL_INIT_STATE; + if(!vq->trust_anchor_name) + vq->state = VAL_VALIDATE_STATE; /* break a loop */ + vq->trust_anchor_name = NULL; + return; + } /* Fetch and validate the keyEntry that corresponds to the * current trust anchor. */ - vq->key_entry = primeResponseToKE(rcode, msg, vq->trust_anchor, - qstate, id); + if(rcode == LDNS_RCODE_NOERROR) { + dnskey_rrset = reply_find_rrset_section_an(msg->rep, + ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY, + ta->dclass); + } + if(ta->autr) { + if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset)) { + /* trust anchor revoked, restart with less anchors */ + vq->state = VAL_INIT_STATE; + vq->trust_anchor_name = NULL; + return; + } + } + vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id); + lock_basic_unlock(&ta->lock); + if(vq->key_entry) + /* store the freshly primed entry in the cache */ + key_cache_insert(ve->kcache, vq->key_entry); /* If the result of the prime is a null key, skip the FINDKEY state.*/ if(!vq->key_entry || key_entry_isnull(vq->key_entry) || diff --git a/validator/validator.h b/validator/validator.h index cdc479619..9587fedae 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -177,8 +177,12 @@ struct val_qstate { */ size_t rrset_skip; - /** the trust anchor rrset */ - struct trust_anchor* trust_anchor; + /** trust anchor name */ + uint8_t* trust_anchor_name; + /** trust anchor labels */ + int trust_anchor_labs; + /** trust anchor length */ + size_t trust_anchor_len; /** the DS rrset */ struct ub_packed_rrset_key* ds_rrset;