diff options
Diffstat (limited to 'external/unbound/validator')
22 files changed, 0 insertions, 18061 deletions
diff --git a/external/unbound/validator/autotrust.c b/external/unbound/validator/autotrust.c deleted file mode 100644 index a533733c7..000000000 --- a/external/unbound/validator/autotrust.c +++ /dev/null @@ -1,2416 +0,0 @@ -/* - * validator/autotrust.c - RFC5011 trust anchor management for unbound. - * - * Copyright (c) 2009, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * Contains autotrust implementation. The implementation was taken from - * the autotrust daemon (BSD licensed), written by Matthijs Mekking. - * It was modified to fit into unbound. The state table process is the same. - */ -#include "config.h" -#include "validator/autotrust.h" -#include "validator/val_anchor.h" -#include "validator/val_utils.h" -#include "validator/val_sigcrypt.h" -#include "util/data/dname.h" -#include "util/data/packed_rrset.h" -#include "util/log.h" -#include "util/module.h" -#include "util/net_help.h" -#include "util/config_file.h" -#include "util/regional.h" -#include "util/random.h" -#include "util/data/msgparse.h" -#include "services/mesh.h" -#include "services/cache/rrset.h" -#include "validator/val_kcache.h" -#include "sldns/sbuffer.h" -#include "sldns/wire2str.h" -#include "sldns/str2wire.h" -#include "sldns/keyraw.h" -#include "sldns/rrdef.h" -#include <stdarg.h> -#include <ctype.h> - -/** number of times a key must be seen before it can become valid */ -#define MIN_PENDINGCOUNT 2 - -/** Event: Revoked */ -static void do_revoked(struct module_env* env, struct autr_ta* anchor, int* c); - -struct autr_global_data* autr_global_create(void) -{ - struct autr_global_data* global; - global = (struct autr_global_data*)malloc(sizeof(*global)); - if(!global) - return NULL; - rbtree_init(&global->probe, &probetree_cmp); - return global; -} - -void autr_global_delete(struct autr_global_data* global) -{ - if(!global) - return; - /* elements deleted by parent */ - memset(global, 0, sizeof(*global)); - free(global); -} - -int probetree_cmp(const void* x, const void* y) -{ - struct trust_anchor* a = (struct trust_anchor*)x; - struct trust_anchor* b = (struct trust_anchor*)y; - log_assert(a->autr && b->autr); - if(a->autr->next_probe_time < b->autr->next_probe_time) - return -1; - if(a->autr->next_probe_time > b->autr->next_probe_time) - return 1; - /* time is equal, sort on trust point identity */ - return anchor_cmp(x, y); -} - -size_t -autr_get_num_anchors(struct val_anchors* anchors) -{ - size_t res = 0; - if(!anchors) - return 0; - lock_basic_lock(&anchors->lock); - if(anchors->autr) - res = anchors->autr->probe.count; - lock_basic_unlock(&anchors->lock); - return res; -} - -/** Position in string */ -static int -position_in_string(char *str, const char* sub) -{ - char* pos = strstr(str, sub); - if(pos) - return (int)(pos-str)+(int)strlen(sub); - return -1; -} - -/** Debug routine to print pretty key information */ -static void -verbose_key(struct autr_ta* ta, enum verbosity_value level, - const char* format, ...) ATTR_FORMAT(printf, 3, 4); - -/** - * Implementation of debug pretty key print - * @param ta: trust anchor key with DNSKEY data. - * @param level: verbosity level to print at. - * @param format: printf style format string. - */ -static void -verbose_key(struct autr_ta* ta, enum verbosity_value level, - const char* format, ...) -{ - va_list args; - va_start(args, format); - if(verbosity >= level) { - char* str = sldns_wire2str_dname(ta->rr, ta->dname_len); - int keytag = (int)sldns_calc_keytag_raw(sldns_wirerr_get_rdata( - ta->rr, ta->rr_len, ta->dname_len), - sldns_wirerr_get_rdatalen(ta->rr, ta->rr_len, - ta->dname_len)); - char msg[MAXSYSLOGMSGLEN]; - vsnprintf(msg, sizeof(msg), format, args); - verbose(level, "%s key %d %s", str?str:"??", keytag, msg); - free(str); - } - va_end(args); -} - -/** - * Parse comments - * @param str: to parse - * @param ta: trust key autotrust metadata - * @return false on failure. - */ -static int -parse_comments(char* str, struct autr_ta* ta) -{ - int len = (int)strlen(str), pos = 0, timestamp = 0; - char* comment = (char*) malloc(sizeof(char)*len+1); - char* comments = comment; - if(!comment) { - log_err("malloc failure in parse"); - return 0; - } - /* skip over whitespace and data at start of line */ - while (*str != '\0' && *str != ';') - str++; - if (*str == ';') - str++; - /* copy comments */ - while (*str != '\0') - { - *comments = *str; - comments++; - str++; - } - *comments = '\0'; - - comments = comment; - - /* read state */ - pos = position_in_string(comments, "state="); - if (pos >= (int) strlen(comments)) - { - log_err("parse error"); - free(comment); - return 0; - } - if (pos <= 0) - ta->s = AUTR_STATE_VALID; - else - { - int s = (int) comments[pos] - '0'; - switch(s) - { - case AUTR_STATE_START: - case AUTR_STATE_ADDPEND: - case AUTR_STATE_VALID: - case AUTR_STATE_MISSING: - case AUTR_STATE_REVOKED: - case AUTR_STATE_REMOVED: - ta->s = s; - break; - default: - verbose_key(ta, VERB_OPS, "has undefined " - "state, considered NewKey"); - ta->s = AUTR_STATE_START; - break; - } - } - /* read pending count */ - pos = position_in_string(comments, "count="); - if (pos >= (int) strlen(comments)) - { - log_err("parse error"); - free(comment); - return 0; - } - if (pos <= 0) - ta->pending_count = 0; - else - { - comments += pos; - ta->pending_count = (uint8_t)atoi(comments); - } - - /* read last change */ - pos = position_in_string(comments, "lastchange="); - if (pos >= (int) strlen(comments)) - { - log_err("parse error"); - free(comment); - return 0; - } - if (pos >= 0) - { - comments += pos; - timestamp = atoi(comments); - } - if (pos < 0 || !timestamp) - ta->last_change = 0; - else - ta->last_change = (time_t)timestamp; - - free(comment); - return 1; -} - -/** Check if a line contains data (besides comments) */ -static int -str_contains_data(char* str, char comment) -{ - while (*str != '\0') { - if (*str == comment || *str == '\n') - return 0; - if (*str != ' ' && *str != '\t') - return 1; - str++; - } - return 0; -} - -/** Get DNSKEY flags - * rdata without rdatalen in front of it. */ -static int -dnskey_flags(uint16_t t, uint8_t* rdata, size_t len) -{ - uint16_t f; - if(t != LDNS_RR_TYPE_DNSKEY) - return 0; - if(len < 2) - return 0; - memmove(&f, rdata, 2); - f = ntohs(f); - return (int)f; -} - -/** Check if KSK DNSKEY. - * pass rdata without rdatalen in front of it */ -static int -rr_is_dnskey_sep(uint16_t t, uint8_t* rdata, size_t len) -{ - return (dnskey_flags(t, rdata, len)&DNSKEY_BIT_SEP); -} - -/** Check if TA is KSK DNSKEY */ -static int -ta_is_dnskey_sep(struct autr_ta* ta) -{ - return (dnskey_flags( - sldns_wirerr_get_type(ta->rr, ta->rr_len, ta->dname_len), - sldns_wirerr_get_rdata(ta->rr, ta->rr_len, ta->dname_len), - sldns_wirerr_get_rdatalen(ta->rr, ta->rr_len, ta->dname_len) - ) & DNSKEY_BIT_SEP); -} - -/** Check if REVOKED DNSKEY - * pass rdata without rdatalen in front of it */ -static int -rr_is_dnskey_revoked(uint16_t t, uint8_t* rdata, size_t len) -{ - return (dnskey_flags(t, rdata, len)&LDNS_KEY_REVOKE_KEY); -} - -/** create ta */ -static struct autr_ta* -autr_ta_create(uint8_t* rr, size_t rr_len, size_t dname_len) -{ - struct autr_ta* ta = (struct autr_ta*)calloc(1, sizeof(*ta)); - if(!ta) { - free(rr); - return NULL; - } - ta->rr = rr; - ta->rr_len = rr_len; - ta->dname_len = dname_len; - return ta; -} - -/** create tp */ -static struct trust_anchor* -autr_tp_create(struct val_anchors* anchors, uint8_t* own, size_t own_len, - uint16_t dc) -{ - struct trust_anchor* tp = (struct trust_anchor*)calloc(1, sizeof(*tp)); - if(!tp) return NULL; - tp->name = memdup(own, own_len); - if(!tp->name) { - free(tp); - return NULL; - } - tp->namelen = own_len; - tp->namelabs = dname_count_labels(tp->name); - tp->node.key = tp; - tp->dclass = dc; - 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); - if(!rbtree_insert(anchors->tree, &tp->node)) { - lock_basic_unlock(&anchors->lock); - log_err("trust anchor presented twice"); - free(tp->name); - free(tp->autr); - free(tp); - return NULL; - } - if(!rbtree_insert(&anchors->autr->probe, &tp->autr->pnode)) { - (void)rbtree_delete(anchors->tree, tp); - lock_basic_unlock(&anchors->lock); - log_err("trust anchor in probetree twice"); - free(tp->name); - free(tp->autr); - free(tp); - return NULL; - } - 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; -} - -/** delete assembled rrsets */ -static void -autr_rrset_delete(struct ub_packed_rrset_key* r) -{ - if(r) { - free(r->rk.dname); - free(r->entry.data); - free(r); - } -} - -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); - autr_rrset_delete(tp->ds_rrset); - autr_rrset_delete(tp->dnskey_rrset); - if(tp->autr) { - struct autr_ta* p = tp->autr->keys, *np; - while(p) { - np = p->next; - free(p->rr); - free(p); - p = np; - } - free(tp->autr->file); - 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, uint8_t* rr, size_t rr_len, - size_t dname_len) -{ - struct trust_anchor* tp; - tp = anchor_find(anchors, rr, dname_count_labels(rr), dname_len, - sldns_wirerr_get_class(rr, rr_len, dname_len)); - 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, dname_len, sldns_wirerr_get_class(rr, - rr_len, dname_len)); - if(!tp) - return NULL; - lock_basic_lock(&tp->lock); - return tp; -} - -/** Add trust anchor from RR */ -static struct autr_ta* -add_trustanchor_frm_rr(struct val_anchors* anchors, uint8_t* rr, size_t rr_len, - size_t dname_len, struct trust_anchor** tp) -{ - struct autr_ta* ta = autr_ta_create(rr, rr_len, dname_len); - if(!ta) - return NULL; - *tp = find_add_tp(anchors, rr, rr_len, dname_len); - if(!*tp) { - free(ta->rr); - free(ta); - return NULL; - } - /* 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 anchor and comments, if any comments. - * @param tp: trust point returned. - * @param origin: what to use for @ - * @param origin_len: length of origin - * @param prev: previous rr name - * @param prev_len: length of prev - * @param skip: if true, the result is NULL, but not an error, skip it. - * @return new key in trust point. - */ -static struct autr_ta* -add_trustanchor_frm_str(struct val_anchors* anchors, char* str, - struct trust_anchor** tp, uint8_t* origin, size_t origin_len, - uint8_t** prev, size_t* prev_len, int* skip) -{ - uint8_t rr[LDNS_RR_BUF_SIZE]; - size_t rr_len = sizeof(rr), dname_len; - uint8_t* drr; - int lstatus; - if (!str_contains_data(str, ';')) { - *skip = 1; - return NULL; /* empty line */ - } - if(0 != (lstatus = sldns_str2wire_rr_buf(str, rr, &rr_len, &dname_len, - 0, origin, origin_len, *prev, *prev_len))) - { - log_err("ldns error while converting string to RR at%d: %s: %s", - LDNS_WIREPARSE_OFFSET(lstatus), - sldns_get_errorstr_parse(lstatus), str); - return NULL; - } - free(*prev); - *prev = memdup(rr, dname_len); - *prev_len = dname_len; - if(!*prev) { - log_err("malloc failure in add_trustanchor"); - return NULL; - } - if(sldns_wirerr_get_type(rr, rr_len, dname_len)!=LDNS_RR_TYPE_DNSKEY && - sldns_wirerr_get_type(rr, rr_len, dname_len)!=LDNS_RR_TYPE_DS) { - *skip = 1; - return NULL; /* only DS and DNSKEY allowed */ - } - drr = memdup(rr, rr_len); - if(!drr) { - log_err("malloc failure in add trustanchor"); - return NULL; - } - return add_trustanchor_frm_rr(anchors, drr, rr_len, dname_len, tp); -} - -/** - * Load single anchor - * @param anchors: all points. - * @param str: comments line - * @param fname: filename - * @param origin: the $ORIGIN. - * @param origin_len: length of origin - * @param prev: passed to ldns. - * @param prev_len: length of prev - * @param skip: if true, the result is NULL, but not an error, skip it. - * @return false on failure, otherwise the tp read. - */ -static struct trust_anchor* -load_trustanchor(struct val_anchors* anchors, char* str, const char* fname, - uint8_t* origin, size_t origin_len, uint8_t** prev, size_t* prev_len, - int* skip) -{ - struct autr_ta* ta = NULL; - struct trust_anchor* tp = NULL; - - ta = add_trustanchor_frm_str(anchors, str, &tp, origin, origin_len, - prev, prev_len, skip); - if(!ta) - return NULL; - lock_basic_lock(&tp->lock); - if(!parse_comments(str, ta)) { - lock_basic_unlock(&tp->lock); - return NULL; - } - if(!tp->autr->file) { - tp->autr->file = strdup(fname); - if(!tp->autr->file) { - lock_basic_unlock(&tp->lock); - log_err("malloc failure"); - return NULL; - } - } - lock_basic_unlock(&tp->lock); - return tp; -} - -/** iterator for DSes from keylist. return true if a next element exists */ -static int -assemble_iterate_ds(struct autr_ta** list, uint8_t** rr, size_t* rr_len, - size_t* dname_len) -{ - while(*list) { - if(sldns_wirerr_get_type((*list)->rr, (*list)->rr_len, - (*list)->dname_len) == LDNS_RR_TYPE_DS) { - *rr = (*list)->rr; - *rr_len = (*list)->rr_len; - *dname_len = (*list)->dname_len; - *list = (*list)->next; - return 1; - } - *list = (*list)->next; - } - return 0; -} - -/** iterator for DNSKEYs from keylist. return true if a next element exists */ -static int -assemble_iterate_dnskey(struct autr_ta** list, uint8_t** rr, size_t* rr_len, - size_t* dname_len) -{ - while(*list) { - if(sldns_wirerr_get_type((*list)->rr, (*list)->rr_len, - (*list)->dname_len) != LDNS_RR_TYPE_DS && - ((*list)->s == AUTR_STATE_VALID || - (*list)->s == AUTR_STATE_MISSING)) { - *rr = (*list)->rr; - *rr_len = (*list)->rr_len; - *dname_len = (*list)->dname_len; - *list = (*list)->next; - return 1; - } - *list = (*list)->next; - } - return 0; -} - -/** see if iterator-list has any elements in it, or it is empty */ -static int -assemble_iterate_hasfirst(int iter(struct autr_ta**, uint8_t**, size_t*, - size_t*), struct autr_ta* list) -{ - uint8_t* rr = NULL; - size_t rr_len = 0, dname_len = 0; - return iter(&list, &rr, &rr_len, &dname_len); -} - -/** number of elements in iterator list */ -static size_t -assemble_iterate_count(int iter(struct autr_ta**, uint8_t**, size_t*, - size_t*), struct autr_ta* list) -{ - uint8_t* rr = NULL; - size_t i = 0, rr_len = 0, dname_len = 0; - while(iter(&list, &rr, &rr_len, &dname_len)) { - i++; - } - return i; -} - -/** - * Create a ub_packed_rrset_key allocated on the heap. - * It therefore does not have the correct ID value, and cannot be used - * inside the cache. It can be used in storage outside of the cache. - * Keys for the cache have to be obtained from alloc.h . - * @param iter: iterator over the elements in the list. It filters elements. - * @param list: the list. - * @return key allocated or NULL on failure. - */ -static struct ub_packed_rrset_key* -ub_packed_rrset_heap_key(int iter(struct autr_ta**, uint8_t**, size_t*, - size_t*), struct autr_ta* list) -{ - uint8_t* rr = NULL; - size_t rr_len = 0, dname_len = 0; - struct ub_packed_rrset_key* k; - if(!iter(&list, &rr, &rr_len, &dname_len)) - return NULL; - k = (struct ub_packed_rrset_key*)calloc(1, sizeof(*k)); - if(!k) - return NULL; - k->rk.type = htons(sldns_wirerr_get_type(rr, rr_len, dname_len)); - k->rk.rrset_class = htons(sldns_wirerr_get_class(rr, rr_len, dname_len)); - k->rk.dname_len = dname_len; - k->rk.dname = memdup(rr, dname_len); - if(!k->rk.dname) { - free(k); - return NULL; - } - return k; -} - -/** - * Create packed_rrset data on the heap. - * @param iter: iterator over the elements in the list. It filters elements. - * @param list: the list. - * @return data allocated or NULL on failure. - */ -static struct packed_rrset_data* -packed_rrset_heap_data(int iter(struct autr_ta**, uint8_t**, size_t*, - size_t*), struct autr_ta* list) -{ - uint8_t* rr = NULL; - size_t rr_len = 0, dname_len = 0; - struct packed_rrset_data* data; - size_t count=0, rrsig_count=0, len=0, i, total; - uint8_t* nextrdata; - struct autr_ta* list_i; - time_t ttl = 0; - - list_i = list; - while(iter(&list_i, &rr, &rr_len, &dname_len)) { - if(sldns_wirerr_get_type(rr, rr_len, dname_len) == - LDNS_RR_TYPE_RRSIG) - rrsig_count++; - else count++; - /* sizeof the rdlength + rdatalen */ - len += 2 + sldns_wirerr_get_rdatalen(rr, rr_len, dname_len); - ttl = (time_t)sldns_wirerr_get_ttl(rr, rr_len, dname_len); - } - if(count == 0 && rrsig_count == 0) - return NULL; - - /* allocate */ - total = count + rrsig_count; - len += sizeof(*data) + total*(sizeof(size_t) + sizeof(time_t) + - sizeof(uint8_t*)); - data = (struct packed_rrset_data*)calloc(1, len); - if(!data) - return NULL; - - /* fill it */ - data->ttl = ttl; - data->count = count; - data->rrsig_count = rrsig_count; - data->rr_len = (size_t*)((uint8_t*)data + - sizeof(struct packed_rrset_data)); - data->rr_data = (uint8_t**)&(data->rr_len[total]); - data->rr_ttl = (time_t*)&(data->rr_data[total]); - nextrdata = (uint8_t*)&(data->rr_ttl[total]); - - /* fill out len, ttl, fields */ - list_i = list; - i = 0; - while(iter(&list_i, &rr, &rr_len, &dname_len)) { - data->rr_ttl[i] = (time_t)sldns_wirerr_get_ttl(rr, rr_len, - dname_len); - if(data->rr_ttl[i] < data->ttl) - data->ttl = data->rr_ttl[i]; - data->rr_len[i] = 2 /* the rdlength */ + - sldns_wirerr_get_rdatalen(rr, rr_len, dname_len); - i++; - } - - /* fixup rest of ptrs */ - for(i=0; i<total; i++) { - data->rr_data[i] = nextrdata; - nextrdata += data->rr_len[i]; - } - - /* copy data in there */ - list_i = list; - i = 0; - while(iter(&list_i, &rr, &rr_len, &dname_len)) { - memmove(data->rr_data[i], - sldns_wirerr_get_rdatawl(rr, rr_len, dname_len), - data->rr_len[i]); - i++; - } - - if(data->rrsig_count && data->count == 0) { - data->count = data->rrsig_count; /* rrset type is RRSIG */ - data->rrsig_count = 0; - } - return data; -} - -/** - * Assemble the trust anchors into DS and DNSKEY packed rrsets. - * Uses only VALID and MISSING DNSKEYs. - * Read the sldns_rrs and builds packed rrsets - * @param tp: the trust point. Must be locked. - * @return false on malloc failure. - */ -static int -autr_assemble(struct trust_anchor* tp) -{ - struct ub_packed_rrset_key* ubds=NULL, *ubdnskey=NULL; - - /* make packed rrset keys - malloced with no ID number, they - * are not in the cache */ - /* make packed rrset data (if there is a key) */ - if(assemble_iterate_hasfirst(assemble_iterate_ds, tp->autr->keys)) { - ubds = ub_packed_rrset_heap_key( - assemble_iterate_ds, tp->autr->keys); - if(!ubds) - goto error_cleanup; - ubds->entry.data = packed_rrset_heap_data( - assemble_iterate_ds, tp->autr->keys); - if(!ubds->entry.data) - goto error_cleanup; - } - - /* make packed DNSKEY data */ - if(assemble_iterate_hasfirst(assemble_iterate_dnskey, tp->autr->keys)) { - ubdnskey = ub_packed_rrset_heap_key( - assemble_iterate_dnskey, tp->autr->keys); - if(!ubdnskey) - goto error_cleanup; - ubdnskey->entry.data = packed_rrset_heap_data( - assemble_iterate_dnskey, tp->autr->keys); - if(!ubdnskey->entry.data) { - error_cleanup: - autr_rrset_delete(ubds); - autr_rrset_delete(ubdnskey); - return 0; - } - } - - /* we have prepared the new keys so nothing can go wrong any more. - * And we are sure we cannot be left without trustanchor after - * any errors. Put in the new keys and remove old ones. */ - - /* free the old data */ - autr_rrset_delete(tp->ds_rrset); - autr_rrset_delete(tp->dnskey_rrset); - - /* assign the data to replace the old */ - tp->ds_rrset = ubds; - tp->dnskey_rrset = ubdnskey; - tp->numDS = assemble_iterate_count(assemble_iterate_ds, - tp->autr->keys); - tp->numDNSKEY = assemble_iterate_count(assemble_iterate_dnskey, - tp->autr->keys); - return 1; -} - -/** parse integer */ -static unsigned int -parse_int(char* line, int* ret) -{ - char *e; - unsigned int x = (unsigned int)strtol(line, &e, 10); - if(line == e) { - *ret = -1; /* parse error */ - return 0; - } - *ret = 1; /* matched */ - return x; -} - -/** parse id sequence for anchor */ -static struct trust_anchor* -parse_id(struct val_anchors* anchors, char* line) -{ - struct trust_anchor *tp; - int r; - uint16_t dclass; - uint8_t* dname; - size_t dname_len; - /* read the owner name */ - char* next = strchr(line, ' '); - if(!next) - return NULL; - next[0] = 0; - dname = sldns_str2wire_dname(line, &dname_len); - if(!dname) - return NULL; - - /* read the class */ - dclass = parse_int(next+1, &r); - if(r == -1) { - free(dname); - return NULL; - } - - /* find the trust point */ - tp = autr_tp_create(anchors, dname, dname_len, dclass); - free(dname); - return tp; -} - -/** - * Parse variable from trustanchor header - * @param line: to parse - * @param anchors: the anchor is added to this, if "id:" is seen. - * @param anchor: the anchor as result value or previously returned anchor - * value to read the variable lines into. - * @return: 0 no match, -1 failed syntax error, +1 success line read. - * +2 revoked trust anchor file. - */ -static int -parse_var_line(char* line, struct val_anchors* anchors, - struct trust_anchor** anchor) -{ - struct trust_anchor* tp = *anchor; - int r = 0; - if(strncmp(line, ";;id: ", 6) == 0) { - *anchor = parse_id(anchors, line+6); - if(!*anchor) return -1; - else return 1; - } else if(strncmp(line, ";;REVOKED", 9) == 0) { - if(tp) { - log_err("REVOKED statement must be at start of file"); - return -1; - } - return 2; - } else if(strncmp(line, ";;last_queried: ", 16) == 0) { - if(!tp) return -1; - lock_basic_lock(&tp->lock); - tp->autr->last_queried = (time_t)parse_int(line+16, &r); - lock_basic_unlock(&tp->lock); - } else if(strncmp(line, ";;last_success: ", 16) == 0) { - if(!tp) return -1; - lock_basic_lock(&tp->lock); - tp->autr->last_success = (time_t)parse_int(line+16, &r); - lock_basic_unlock(&tp->lock); - } else if(strncmp(line, ";;next_probe_time: ", 19) == 0) { - if(!tp) return -1; - lock_basic_lock(&anchors->lock); - lock_basic_lock(&tp->lock); - (void)rbtree_delete(&anchors->autr->probe, tp); - tp->autr->next_probe_time = (time_t)parse_int(line+19, &r); - (void)rbtree_insert(&anchors->autr->probe, &tp->autr->pnode); - lock_basic_unlock(&tp->lock); - lock_basic_unlock(&anchors->lock); - } else if(strncmp(line, ";;query_failed: ", 16) == 0) { - if(!tp) return -1; - lock_basic_lock(&tp->lock); - tp->autr->query_failed = (uint8_t)parse_int(line+16, &r); - lock_basic_unlock(&tp->lock); - } else if(strncmp(line, ";;query_interval: ", 18) == 0) { - if(!tp) return -1; - lock_basic_lock(&tp->lock); - tp->autr->query_interval = (time_t)parse_int(line+18, &r); - lock_basic_unlock(&tp->lock); - } else if(strncmp(line, ";;retry_time: ", 14) == 0) { - if(!tp) return -1; - lock_basic_lock(&tp->lock); - tp->autr->retry_time = (time_t)parse_int(line+14, &r); - lock_basic_unlock(&tp->lock); - } - return r; -} - -/** handle origin lines */ -static int -handle_origin(char* line, uint8_t** origin, size_t* origin_len) -{ - size_t len = 0; - while(isspace((unsigned char)*line)) - line++; - if(strncmp(line, "$ORIGIN", 7) != 0) - return 0; - free(*origin); - line += 7; - while(isspace((unsigned char)*line)) - line++; - *origin = sldns_str2wire_dname(line, &len); - *origin_len = len; - if(!*origin) - log_warn("malloc failure or parse error in $ORIGIN"); - return 1; -} - -/** Read one line and put multiline RRs onto one line string */ -static int -read_multiline(char* buf, size_t len, FILE* in, int* linenr) -{ - char* pos = buf; - size_t left = len; - int depth = 0; - buf[len-1] = 0; - while(left > 0 && fgets(pos, (int)left, in) != NULL) { - size_t i, poslen = strlen(pos); - (*linenr)++; - - /* check what the new depth is after the line */ - /* this routine cannot handle braces inside quotes, - say for TXT records, but this routine only has to read keys */ - for(i=0; i<poslen; i++) { - if(pos[i] == '(') { - depth++; - } else if(pos[i] == ')') { - if(depth == 0) { - log_err("mismatch: too many ')'"); - return -1; - } - depth--; - } else if(pos[i] == ';') { - break; - } - } - - /* normal oneline or last line: keeps newline and comments */ - if(depth == 0) { - return 1; - } - - /* more lines expected, snip off comments and newline */ - if(poslen>0) - pos[poslen-1] = 0; /* strip newline */ - if(strchr(pos, ';')) - strchr(pos, ';')[0] = 0; /* strip comments */ - - /* move to paste other lines behind this one */ - poslen = strlen(pos); - pos += poslen; - left -= poslen; - /* the newline is changed into a space */ - if(left <= 2 /* space and eos */) { - log_err("line too long"); - return -1; - } - pos[0] = ' '; - pos[1] = 0; - pos += 1; - left -= 1; - } - if(depth != 0) { - log_err("mismatch: too many '('"); - return -1; - } - if(pos != buf) - return 1; - return 0; -} - -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[10240]; - /* trust point being read */ - struct trust_anchor *tp = NULL, *tp2; - int r; - /* for $ORIGIN parsing */ - uint8_t *origin=NULL, *prev=NULL; - size_t origin_len=0, prev_len=0; - - if (!(fd = fopen(nm, "r"))) { - log_err("unable to open %s for reading: %s", - nm, strerror(errno)); - return 0; - } - verbose(VERB_ALGO, "reading autotrust anchor file %s", nm); - while ( (r=read_multiline(line, sizeof(line), fd, &line_nr)) != 0) { - if(r == -1 || (r = parse_var_line(line, anchors, &tp)) == -1) { - log_err("could not parse auto-trust-anchor-file " - "%s line %d", nm, line_nr); - fclose(fd); - free(origin); - free(prev); - return 0; - } else if(r == 1) { - continue; - } else if(r == 2) { - log_warn("trust anchor %s has been revoked", nm); - fclose(fd); - free(origin); - free(prev); - return 1; - } - if (!str_contains_data(line, ';')) - continue; /* empty lines allowed */ - if(handle_origin(line, &origin, &origin_len)) - continue; - r = 0; - if(!(tp2=load_trustanchor(anchors, line, nm, origin, - origin_len, &prev, &prev_len, &r))) { - if(!r) log_err("failed to load trust anchor from %s " - "at line %i, skipping", nm, line_nr); - /* try to do the rest */ - continue; - } - if(tp && tp != tp2) { - log_err("file %s has mismatching data inside: " - "the file may only contain keys for one name, " - "remove keys for other domain names", nm); - fclose(fd); - free(origin); - free(prev); - return 0; - } - tp = tp2; - } - fclose(fd); - free(origin); - free(prev); - if(!tp) { - log_err("failed to read %s", nm); - return 0; - } - - /* now assemble the data into DNSKEY and DS packed rrsets */ - lock_basic_lock(&tp->lock); - if(!autr_assemble(tp)) { - lock_basic_unlock(&tp->lock); - log_err("malloc failure assembling %s", nm); - return 0; - } - lock_basic_unlock(&tp->lock); - return 1; -} - -/** string for a trustanchor state */ -static const char* -trustanchor_state2str(autr_state_type s) -{ - switch (s) { - case AUTR_STATE_START: return " START "; - case AUTR_STATE_ADDPEND: return " ADDPEND "; - case AUTR_STATE_VALID: return " VALID "; - case AUTR_STATE_MISSING: return " MISSING "; - case AUTR_STATE_REVOKED: return " REVOKED "; - case AUTR_STATE_REMOVED: return " REMOVED "; - } - return " UNKNOWN "; -} - -/** print ID to file */ -static int -print_id(FILE* out, char* fname, uint8_t* nm, size_t nmlen, uint16_t dclass) -{ - char* s = sldns_wire2str_dname(nm, nmlen); - if(!s) { - log_err("malloc failure in write to %s", fname); - return 0; - } - if(fprintf(out, ";;id: %s %d\n", s, (int)dclass) < 0) { - log_err("could not write to %s: %s", fname, strerror(errno)); - free(s); - return 0; - } - free(s); - return 1; -} - -static int -autr_write_contents(FILE* out, char* fn, struct trust_anchor* tp) -{ - char tmi[32]; - struct autr_ta* ta; - char* str; - - /* write pretty header */ - if(fprintf(out, "; autotrust trust anchor file\n") < 0) { - log_err("could not write to %s: %s", fn, strerror(errno)); - return 0; - } - if(tp->autr->revoked) { - if(fprintf(out, ";;REVOKED\n") < 0 || - fprintf(out, "; The zone has all keys revoked, and is\n" - "; considered as if it has no trust anchors.\n" - "; the remainder of the file is the last probe.\n" - "; to restart the trust anchor, overwrite this file.\n" - "; with one containing valid DNSKEYs or DSes.\n") < 0) { - log_err("could not write to %s: %s", fn, strerror(errno)); - return 0; - } - } - if(!print_id(out, fn, tp->name, tp->namelen, tp->dclass)) { - return 0; - } - if(fprintf(out, ";;last_queried: %u ;;%s", - (unsigned int)tp->autr->last_queried, - ctime_r(&(tp->autr->last_queried), tmi)) < 0 || - fprintf(out, ";;last_success: %u ;;%s", - (unsigned int)tp->autr->last_success, - ctime_r(&(tp->autr->last_success), tmi)) < 0 || - fprintf(out, ";;next_probe_time: %u ;;%s", - (unsigned int)tp->autr->next_probe_time, - ctime_r(&(tp->autr->next_probe_time), tmi)) < 0 || - fprintf(out, ";;query_failed: %d\n", (int)tp->autr->query_failed)<0 - || fprintf(out, ";;query_interval: %d\n", - (int)tp->autr->query_interval) < 0 || - fprintf(out, ";;retry_time: %d\n", (int)tp->autr->retry_time) < 0) { - log_err("could not write to %s: %s", fn, strerror(errno)); - return 0; - } - - /* write anchors */ - for(ta=tp->autr->keys; ta; ta=ta->next) { - /* by default do not store START and REMOVED keys */ - if(ta->s == AUTR_STATE_START) - continue; - if(ta->s == AUTR_STATE_REMOVED) - continue; - /* only store keys */ - if(sldns_wirerr_get_type(ta->rr, ta->rr_len, ta->dname_len) - != LDNS_RR_TYPE_DNSKEY) - continue; - str = sldns_wire2str_rr(ta->rr, ta->rr_len); - if(!str || !str[0]) { - free(str); - log_err("malloc failure writing %s", fn); - return 0; - } - str[strlen(str)-1] = 0; /* remove newline */ - if(fprintf(out, "%s ;;state=%d [%s] ;;count=%d " - ";;lastchange=%u ;;%s", str, (int)ta->s, - trustanchor_state2str(ta->s), (int)ta->pending_count, - (unsigned int)ta->last_change, - ctime_r(&(ta->last_change), tmi)) < 0) { - log_err("could not write to %s: %s", fn, strerror(errno)); - free(str); - return 0; - } - free(str); - } - return 1; -} - -void autr_write_file(struct module_env* env, struct trust_anchor* tp) -{ - FILE* out; - char* fname = tp->autr->file; - char tempf[2048]; - log_assert(tp->autr); - if(!env) { - log_err("autr_write_file: Module environment is NULL."); - return; - } - /* unique name with pid number and thread number */ - snprintf(tempf, sizeof(tempf), "%s.%d-%d", fname, (int)getpid(), - env->worker?*(int*)env->worker:0); - verbose(VERB_ALGO, "autotrust: write to disk: %s", tempf); - out = fopen(tempf, "w"); - if(!out) { - fatal_exit("could not open autotrust file for writing, %s: %s", - tempf, strerror(errno)); - return; - } - if(!autr_write_contents(out, tempf, tp)) { - /* failed to write contents (completely) */ - fclose(out); - unlink(tempf); - fatal_exit("could not completely write: %s", fname); - return; - } - if(fflush(out) != 0) - log_err("could not fflush(%s): %s", fname, strerror(errno)); -#ifdef HAVE_FSYNC - if(fsync(fileno(out)) != 0) - log_err("could not fsync(%s): %s", fname, strerror(errno)); -#else - FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(out))); -#endif - if(fclose(out) != 0) { - fatal_exit("could not complete write: %s: %s", - fname, strerror(errno)); - unlink(tempf); - return; - } - /* success; overwrite actual file */ - verbose(VERB_ALGO, "autotrust: replaced %s", fname); -#ifdef UB_ON_WINDOWS - (void)unlink(fname); /* windows does not replace file with rename() */ -#endif - if(rename(tempf, fname) < 0) { - fatal_exit("rename(%s to %s): %s", tempf, fname, strerror(errno)); - } -} - -/** - * Verify if dnskey works for trust point - * @param env: environment (with time) for verification - * @param ve: validator environment (with options) for verification. - * @param tp: trust point to verify with - * @param rrset: DNSKEY rrset to verify. - * @return false on failure, true if verification successful. - */ -static int -verify_dnskey(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* rrset) -{ - char* reason = NULL; - uint8_t sigalg[ALGO_NEEDS_MAX+1]; - int downprot = env->cfg->harden_algo_downgrade; - enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, rrset, - tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason); - /* sigalg is ignored, it returns algorithms signalled to exist, but - * in 5011 there are no other rrsets to check. if downprot is - * enabled, then it checks that the DNSKEY is signed with all - * algorithms available in the trust store. */ - verbose(VERB_ALGO, "autotrust: validate DNSKEY with anchor: %s", - sec_status_to_string(sec)); - return sec == sec_status_secure; -} - -static int32_t -rrsig_get_expiry(uint8_t* d, size_t len) -{ - /* rrsig: 2(rdlen), 2(type) 1(alg) 1(v) 4(origttl), then 4(expi), (4)incep) */ - if(len < 2+8+4) - return 0; - return sldns_read_uint32(d+2+8); -} - -/** Find minimum expiration interval from signatures */ -static time_t -min_expiry(struct module_env* env, struct packed_rrset_data* dd) -{ - size_t i; - int32_t t, r = 15 * 24 * 3600; /* 15 days max */ - for(i=dd->count; i<dd->count+dd->rrsig_count; i++) { - t = rrsig_get_expiry(dd->rr_data[i], dd->rr_len[i]); - if((int32_t)t - (int32_t)*env->now > 0) { - t -= (int32_t)*env->now; - if(t < r) - r = t; - } - } - return (time_t)r; -} - -/** Is rr self-signed revoked key */ -static int -rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, size_t i) -{ - enum sec_status sec; - char* reason = NULL; - verbose(VERB_ALGO, "seen REVOKE flag, check self-signed, rr %d", - (int)i); - /* no algorithm downgrade protection necessary, if it is selfsigned - * revoked it can be removed. */ - sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i, - &reason); - return (sec == sec_status_secure); -} - -/** Set fetched value */ -static void -seen_trustanchor(struct autr_ta* ta, uint8_t seen) -{ - ta->fetched = seen; - if(ta->pending_count < 250) /* no numerical overflow, please */ - ta->pending_count++; -} - -/** set revoked value */ -static void -seen_revoked_trustanchor(struct autr_ta* ta, uint8_t revoked) -{ - ta->revoked = revoked; -} - -/** revoke a trust anchor */ -static void -revoke_dnskey(struct autr_ta* ta, int off) -{ - uint16_t flags; - uint8_t* data; - if(sldns_wirerr_get_type(ta->rr, ta->rr_len, ta->dname_len) != - LDNS_RR_TYPE_DNSKEY) - return; - if(sldns_wirerr_get_rdatalen(ta->rr, ta->rr_len, ta->dname_len) < 2) - return; - data = sldns_wirerr_get_rdata(ta->rr, ta->rr_len, ta->dname_len); - flags = sldns_read_uint16(data); - if (off && (flags&LDNS_KEY_REVOKE_KEY)) - flags ^= LDNS_KEY_REVOKE_KEY; /* flip */ - else - flags |= LDNS_KEY_REVOKE_KEY; - sldns_write_uint16(data, flags); -} - -/** Compare two RRs skipping the REVOKED bit. Pass rdata(no len) */ -static int -dnskey_compare_skip_revbit(uint8_t* a, size_t a_len, uint8_t* b, size_t b_len) -{ - size_t i; - if(a_len != b_len) - return -1; - /* compare RRs RDATA byte for byte. */ - for(i = 0; i < a_len; i++) - { - uint8_t rdf1, rdf2; - rdf1 = a[i]; - rdf2 = b[i]; - if(i==1) { - /* this is the second part of the flags field */ - rdf1 |= LDNS_KEY_REVOKE_KEY; - rdf2 |= LDNS_KEY_REVOKE_KEY; - } - if (rdf1 < rdf2) return -1; - else if (rdf1 > rdf2) return 1; - } - return 0; -} - - -/** compare trust anchor with rdata, 0 if equal. Pass rdata(no len) */ -static int -ta_compare(struct autr_ta* a, uint16_t t, uint8_t* b, size_t b_len) -{ - if(!a) return -1; - else if(!b) return -1; - else if(sldns_wirerr_get_type(a->rr, a->rr_len, a->dname_len) != t) - return (int)sldns_wirerr_get_type(a->rr, a->rr_len, - a->dname_len) - (int)t; - else if(t == LDNS_RR_TYPE_DNSKEY) { - return dnskey_compare_skip_revbit( - sldns_wirerr_get_rdata(a->rr, a->rr_len, a->dname_len), - sldns_wirerr_get_rdatalen(a->rr, a->rr_len, - a->dname_len), b, b_len); - } - else if(t == LDNS_RR_TYPE_DS) { - if(sldns_wirerr_get_rdatalen(a->rr, a->rr_len, a->dname_len) != - b_len) - return -1; - return memcmp(sldns_wirerr_get_rdata(a->rr, - a->rr_len, a->dname_len), b, b_len); - } - return -1; -} - -/** - * Find key - * @param tp: to search in - * @param t: rr type of the rdata. - * @param rdata: to look for (no rdatalen in it) - * @param rdata_len: length of rdata - * @param result: returns NULL or the ta key looked for. - * @return false on malloc failure during search. if true examine result. - */ -static int -find_key(struct trust_anchor* tp, uint16_t t, uint8_t* rdata, size_t rdata_len, - struct autr_ta** result) -{ - struct autr_ta* ta; - if(!tp || !rdata) { - *result = NULL; - return 0; - } - for(ta=tp->autr->keys; ta; ta=ta->next) { - if(ta_compare(ta, t, rdata, rdata_len) == 0) { - *result = ta; - return 1; - } - } - *result = NULL; - return 1; -} - -/** add key and clone RR and tp already locked. rdata without rdlen. */ -static struct autr_ta* -add_key(struct trust_anchor* tp, uint32_t ttl, uint8_t* rdata, size_t rdata_len) -{ - struct autr_ta* ta; - uint8_t* rr; - size_t rr_len, dname_len; - uint16_t rrtype = htons(LDNS_RR_TYPE_DNSKEY); - uint16_t rrclass = htons(LDNS_RR_CLASS_IN); - uint16_t rdlen = htons(rdata_len); - dname_len = tp->namelen; - ttl = htonl(ttl); - rr_len = dname_len + 10 /* type,class,ttl,rdatalen */ + rdata_len; - rr = (uint8_t*)malloc(rr_len); - if(!rr) return NULL; - memmove(rr, tp->name, tp->namelen); - memmove(rr+dname_len, &rrtype, 2); - memmove(rr+dname_len+2, &rrclass, 2); - memmove(rr+dname_len+4, &ttl, 4); - memmove(rr+dname_len+8, &rdlen, 2); - memmove(rr+dname_len+10, rdata, rdata_len); - ta = autr_ta_create(rr, rr_len, dname_len); - if(!ta) { - /* rr freed in autr_ta_create */ - return NULL; - } - /* link in, tp already locked */ - ta->next = tp->autr->keys; - tp->autr->keys = ta; - return ta; -} - -/** get TTL from DNSKEY rrset */ -static time_t -key_ttl(struct ub_packed_rrset_key* k) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; - return d->ttl; -} - -/** update the time values for the trustpoint */ -static void -set_tp_times(struct trust_anchor* tp, time_t rrsig_exp_interval, - time_t origttl, int* changed) -{ - time_t x, qi = tp->autr->query_interval, rt = tp->autr->retry_time; - - /* x = MIN(15days, ttl/2, expire/2) */ - x = 15 * 24 * 3600; - if(origttl/2 < x) - x = origttl/2; - if(rrsig_exp_interval/2 < x) - x = rrsig_exp_interval/2; - /* MAX(1hr, x) */ - if(!autr_permit_small_holddown) { - if(x < 3600) - tp->autr->query_interval = 3600; - else tp->autr->query_interval = x; - } else tp->autr->query_interval = x; - - /* x= MIN(1day, ttl/10, expire/10) */ - x = 24 * 3600; - if(origttl/10 < x) - x = origttl/10; - if(rrsig_exp_interval/10 < x) - x = rrsig_exp_interval/10; - /* MAX(1hr, x) */ - if(!autr_permit_small_holddown) { - if(x < 3600) - tp->autr->retry_time = 3600; - else tp->autr->retry_time = x; - } else tp->autr->retry_time = x; - - if(qi != tp->autr->query_interval || rt != tp->autr->retry_time) { - *changed = 1; - verbose(VERB_ALGO, "orig_ttl is %d", (int)origttl); - verbose(VERB_ALGO, "rrsig_exp_interval is %d", - (int)rrsig_exp_interval); - verbose(VERB_ALGO, "query_interval: %d, retry_time: %d", - (int)tp->autr->query_interval, - (int)tp->autr->retry_time); - } -} - -/** init events to zero */ -static void -init_events(struct trust_anchor* tp) -{ - struct autr_ta* ta; - for(ta=tp->autr->keys; ta; ta=ta->next) { - ta->fetched = 0; - } -} - -/** check for revoked keys without trusting any other information */ -static void -check_contains_revoked(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, - int* changed) -{ - struct packed_rrset_data* dd = (struct packed_rrset_data*) - dnskey_rrset->entry.data; - size_t i; - log_assert(ntohs(dnskey_rrset->rk.type) == LDNS_RR_TYPE_DNSKEY); - for(i=0; i<dd->count; i++) { - struct autr_ta* ta = NULL; - if(!rr_is_dnskey_sep(ntohs(dnskey_rrset->rk.type), - dd->rr_data[i]+2, dd->rr_len[i]-2) || - !rr_is_dnskey_revoked(ntohs(dnskey_rrset->rk.type), - dd->rr_data[i]+2, dd->rr_len[i]-2)) - continue; /* not a revoked KSK */ - if(!find_key(tp, ntohs(dnskey_rrset->rk.type), - dd->rr_data[i]+2, dd->rr_len[i]-2, &ta)) { - log_err("malloc failure"); - continue; /* malloc fail in compare*/ - } - if(!ta) - continue; /* key not found */ - if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i)) { - /* checked if there is an rrsig signed by this key. */ - /* same keytag, but stored can be revoked already, so - * compare keytags, with +0 or +128(REVOKE flag) */ - log_assert(dnskey_calc_keytag(dnskey_rrset, i)-128 == - sldns_calc_keytag_raw(sldns_wirerr_get_rdata( - ta->rr, ta->rr_len, ta->dname_len), - sldns_wirerr_get_rdatalen(ta->rr, ta->rr_len, - ta->dname_len)) || - dnskey_calc_keytag(dnskey_rrset, i) == - sldns_calc_keytag_raw(sldns_wirerr_get_rdata( - ta->rr, ta->rr_len, ta->dname_len), - sldns_wirerr_get_rdatalen(ta->rr, ta->rr_len, - ta->dname_len))); /* checks conversion*/ - verbose_key(ta, VERB_ALGO, "is self-signed revoked"); - if(!ta->revoked) - *changed = 1; - seen_revoked_trustanchor(ta, 1); - do_revoked(env, ta, changed); - } - } -} - -/** See if a DNSKEY is verified by one of the DSes */ -static int -key_matches_a_ds(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, size_t key_idx, - struct ub_packed_rrset_key* ds_rrset) -{ - struct packed_rrset_data* dd = (struct packed_rrset_data*) - ds_rrset->entry.data; - size_t ds_idx, num = dd->count; - int d = val_favorite_ds_algo(ds_rrset); - char* reason = ""; - for(ds_idx=0; ds_idx<num; ds_idx++) { - if(!ds_digest_algo_is_supported(ds_rrset, ds_idx) || - !ds_key_algo_is_supported(ds_rrset, ds_idx) || - ds_get_digest_algo(ds_rrset, ds_idx) != d) - continue; - if(ds_get_key_algo(ds_rrset, ds_idx) - != dnskey_get_algo(dnskey_rrset, key_idx) - || dnskey_calc_keytag(dnskey_rrset, key_idx) - != ds_get_keytag(ds_rrset, ds_idx)) { - continue; - } - if(!ds_digest_match_dnskey(env, dnskey_rrset, key_idx, - ds_rrset, ds_idx)) { - verbose(VERB_ALGO, "DS match attempt failed"); - continue; - } - if(dnskey_verify_rrset(env, ve, dnskey_rrset, - dnskey_rrset, key_idx, &reason) == sec_status_secure) { - return 1; - } else { - verbose(VERB_ALGO, "DS match failed because the key " - "does not verify the keyset: %s", reason); - } - } - return 0; -} - -/** Set update events */ -static int -update_events(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, - int* changed) -{ - struct packed_rrset_data* dd = (struct packed_rrset_data*) - dnskey_rrset->entry.data; - size_t i; - log_assert(ntohs(dnskey_rrset->rk.type) == LDNS_RR_TYPE_DNSKEY); - init_events(tp); - for(i=0; i<dd->count; i++) { - struct autr_ta* ta = NULL; - if(!rr_is_dnskey_sep(ntohs(dnskey_rrset->rk.type), - dd->rr_data[i]+2, dd->rr_len[i]-2)) - continue; - if(rr_is_dnskey_revoked(ntohs(dnskey_rrset->rk.type), - dd->rr_data[i]+2, dd->rr_len[i]-2)) { - /* self-signed revoked keys already detected before, - * other revoked keys are not 'added' again */ - continue; - } - /* is a key of this type supported?. Note rr_list and - * packed_rrset are in the same order. */ - if(!dnskey_algo_is_supported(dnskey_rrset, i)) { - /* skip unknown algorithm key, it is useless to us */ - log_nametypeclass(VERB_DETAIL, "trust point has " - "unsupported algorithm at", - tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass); - continue; - } - - /* is it new? if revocation bit set, find the unrevoked key */ - if(!find_key(tp, ntohs(dnskey_rrset->rk.type), - dd->rr_data[i]+2, dd->rr_len[i]-2, &ta)) { - return 0; - } - if(!ta) { - ta = add_key(tp, (uint32_t)dd->rr_ttl[i], - dd->rr_data[i]+2, dd->rr_len[i]-2); - *changed = 1; - /* first time seen, do we have DSes? if match: VALID */ - if(ta && tp->ds_rrset && key_matches_a_ds(env, ve, - dnskey_rrset, i, tp->ds_rrset)) { - verbose_key(ta, VERB_ALGO, "verified by DS"); - ta->s = AUTR_STATE_VALID; - } - } - if(!ta) { - return 0; - } - seen_trustanchor(ta, 1); - verbose_key(ta, VERB_ALGO, "in DNS response"); - } - set_tp_times(tp, min_expiry(env, dd), key_ttl(dnskey_rrset), changed); - return 1; -} - -/** - * Check if the holddown time has already exceeded - * setting: add-holddown: add holddown timer - * setting: del-holddown: del holddown timer - * @param env: environment with current time - * @param ta: trust anchor to check for. - * @param holddown: the timer value - * @return number of seconds the holddown has passed. - */ -static time_t -check_holddown(struct module_env* env, struct autr_ta* ta, - unsigned int holddown) -{ - time_t elapsed; - if(*env->now < ta->last_change) { - log_warn("time goes backwards. delaying key holddown"); - return 0; - } - elapsed = *env->now - ta->last_change; - if (elapsed > (time_t)holddown) { - return elapsed-(time_t)holddown; - } - verbose_key(ta, VERB_ALGO, "holddown time " ARG_LL "d seconds to go", - (long long) ((time_t)holddown-elapsed)); - return 0; -} - - -/** Set last_change to now */ -static void -reset_holddown(struct module_env* env, struct autr_ta* ta, int* changed) -{ - ta->last_change = *env->now; - *changed = 1; -} - -/** Set the state for this trust anchor */ -static void -set_trustanchor_state(struct module_env* env, struct autr_ta* ta, int* changed, - autr_state_type s) -{ - verbose_key(ta, VERB_ALGO, "update: %s to %s", - trustanchor_state2str(ta->s), trustanchor_state2str(s)); - ta->s = s; - reset_holddown(env, ta, changed); -} - - -/** Event: NewKey */ -static void -do_newkey(struct module_env* env, struct autr_ta* anchor, int* c) -{ - if (anchor->s == AUTR_STATE_START) - set_trustanchor_state(env, anchor, c, AUTR_STATE_ADDPEND); -} - -/** Event: AddTime */ -static void -do_addtime(struct module_env* env, struct autr_ta* anchor, int* c) -{ - /* This not according to RFC, this is 30 days, but the RFC demands - * MAX(30days, TTL expire time of first DNSKEY set with this key), - * The value may be too small if a very large TTL was used. */ - time_t exceeded = check_holddown(env, anchor, env->cfg->add_holddown); - if (exceeded && anchor->s == AUTR_STATE_ADDPEND) { - verbose_key(anchor, VERB_ALGO, "add-holddown time exceeded " - ARG_LL "d seconds ago, and pending-count %d", - (long long)exceeded, anchor->pending_count); - if(anchor->pending_count >= MIN_PENDINGCOUNT) { - set_trustanchor_state(env, anchor, c, AUTR_STATE_VALID); - anchor->pending_count = 0; - return; - } - verbose_key(anchor, VERB_ALGO, "add-holddown time sanity check " - "failed (pending count: %d)", anchor->pending_count); - } -} - -/** Event: RemTime */ -static void -do_remtime(struct module_env* env, struct autr_ta* anchor, int* c) -{ - time_t exceeded = check_holddown(env, anchor, env->cfg->del_holddown); - if(exceeded && anchor->s == AUTR_STATE_REVOKED) { - verbose_key(anchor, VERB_ALGO, "del-holddown time exceeded " - ARG_LL "d seconds ago", (long long)exceeded); - set_trustanchor_state(env, anchor, c, AUTR_STATE_REMOVED); - } -} - -/** Event: KeyRem */ -static void -do_keyrem(struct module_env* env, struct autr_ta* anchor, int* c) -{ - if(anchor->s == AUTR_STATE_ADDPEND) { - set_trustanchor_state(env, anchor, c, AUTR_STATE_START); - anchor->pending_count = 0; - } else if(anchor->s == AUTR_STATE_VALID) - set_trustanchor_state(env, anchor, c, AUTR_STATE_MISSING); -} - -/** Event: KeyPres */ -static void -do_keypres(struct module_env* env, struct autr_ta* anchor, int* c) -{ - if(anchor->s == AUTR_STATE_MISSING) - set_trustanchor_state(env, anchor, c, AUTR_STATE_VALID); -} - -/* Event: Revoked */ -static void -do_revoked(struct module_env* env, struct autr_ta* anchor, int* c) -{ - if(anchor->s == AUTR_STATE_VALID || anchor->s == AUTR_STATE_MISSING) { - set_trustanchor_state(env, anchor, c, AUTR_STATE_REVOKED); - verbose_key(anchor, VERB_ALGO, "old id, prior to revocation"); - revoke_dnskey(anchor, 0); - verbose_key(anchor, VERB_ALGO, "new id, after revocation"); - } -} - -/** Do statestable transition matrix for anchor */ -static void -anchor_state_update(struct module_env* env, struct autr_ta* anchor, int* c) -{ - log_assert(anchor); - switch(anchor->s) { - /* START */ - case AUTR_STATE_START: - /* NewKey: ADDPEND */ - if (anchor->fetched) - do_newkey(env, anchor, c); - break; - /* ADDPEND */ - case AUTR_STATE_ADDPEND: - /* KeyRem: START */ - if (!anchor->fetched) - do_keyrem(env, anchor, c); - /* AddTime: VALID */ - else do_addtime(env, anchor, c); - break; - /* VALID */ - case AUTR_STATE_VALID: - /* RevBit: REVOKED */ - if (anchor->revoked) - do_revoked(env, anchor, c); - /* KeyRem: MISSING */ - else if (!anchor->fetched) - do_keyrem(env, anchor, c); - else if(!anchor->last_change) { - verbose_key(anchor, VERB_ALGO, "first seen"); - reset_holddown(env, anchor, c); - } - break; - /* MISSING */ - case AUTR_STATE_MISSING: - /* RevBit: REVOKED */ - if (anchor->revoked) - do_revoked(env, anchor, c); - /* KeyPres */ - else if (anchor->fetched) - do_keypres(env, anchor, c); - break; - /* REVOKED */ - case AUTR_STATE_REVOKED: - if (anchor->fetched) - reset_holddown(env, anchor, c); - /* RemTime: REMOVED */ - else do_remtime(env, anchor, c); - break; - /* REMOVED */ - case AUTR_STATE_REMOVED: - default: - break; - } -} - -/** if ZSK init then trust KSKs */ -static int -init_zsk_to_ksk(struct module_env* env, struct trust_anchor* tp, int* changed) -{ - /* search for VALID ZSKs */ - struct autr_ta* anchor; - int validzsk = 0; - int validksk = 0; - for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { - /* last_change test makes sure it was manually configured */ - if(sldns_wirerr_get_type(anchor->rr, anchor->rr_len, - anchor->dname_len) == LDNS_RR_TYPE_DNSKEY && - anchor->last_change == 0 && - !ta_is_dnskey_sep(anchor) && - anchor->s == AUTR_STATE_VALID) - validzsk++; - } - if(validzsk == 0) - return 0; - for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { - if (ta_is_dnskey_sep(anchor) && - anchor->s == AUTR_STATE_ADDPEND) { - verbose_key(anchor, VERB_ALGO, "trust KSK from " - "ZSK(config)"); - set_trustanchor_state(env, anchor, changed, - AUTR_STATE_VALID); - validksk++; - } - } - return validksk; -} - -/** Remove missing trustanchors so the list does not grow forever */ -static void -remove_missing_trustanchors(struct module_env* env, struct trust_anchor* tp, - int* changed) -{ - struct autr_ta* anchor; - time_t exceeded; - int valid = 0; - /* see if we have anchors that are valid */ - for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { - /* Only do KSKs */ - if (!ta_is_dnskey_sep(anchor)) - continue; - if (anchor->s == AUTR_STATE_VALID) - valid++; - } - /* if there are no SEP Valid anchors, see if we started out with - * a ZSK (last-change=0) anchor, which is VALID and there are KSKs - * now that can be made valid. Do this immediately because there - * is no guarantee that the ZSKs get announced long enough. Usually - * this is immediately after init with a ZSK trusted, unless the domain - * was not advertising any KSKs at all. In which case we perfectly - * track the zero number of KSKs. */ - if(valid == 0) { - valid = init_zsk_to_ksk(env, tp, changed); - if(valid == 0) - return; - } - - for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { - /* ignore ZSKs if newly added */ - if(anchor->s == AUTR_STATE_START) - continue; - /* remove ZSKs if a KSK is present */ - if (!ta_is_dnskey_sep(anchor)) { - if(valid > 0) { - verbose_key(anchor, VERB_ALGO, "remove ZSK " - "[%d key(s) VALID]", valid); - set_trustanchor_state(env, anchor, changed, - AUTR_STATE_REMOVED); - } - continue; - } - /* Only do MISSING keys */ - if (anchor->s != AUTR_STATE_MISSING) - continue; - if(env->cfg->keep_missing == 0) - continue; /* keep forever */ - - exceeded = check_holddown(env, anchor, env->cfg->keep_missing); - /* If keep_missing has exceeded and we still have more than - * one valid KSK: remove missing trust anchor */ - if (exceeded && valid > 0) { - verbose_key(anchor, VERB_ALGO, "keep-missing time " - "exceeded " ARG_LL "d seconds ago, [%d key(s) VALID]", - (long long)exceeded, valid); - set_trustanchor_state(env, anchor, changed, - AUTR_STATE_REMOVED); - } - } -} - -/** Do the statetable from RFC5011 transition matrix */ -static int -do_statetable(struct module_env* env, struct trust_anchor* tp, int* changed) -{ - struct autr_ta* anchor; - for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { - /* Only do KSKs */ - if(!ta_is_dnskey_sep(anchor)) - continue; - anchor_state_update(env, anchor, changed); - } - remove_missing_trustanchors(env, tp, changed); - return 1; -} - -/** See if time alone makes ADDPEND to VALID transition */ -static void -autr_holddown_exceed(struct module_env* env, struct trust_anchor* tp, int* c) -{ - struct autr_ta* anchor; - for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { - if(ta_is_dnskey_sep(anchor) && - anchor->s == AUTR_STATE_ADDPEND) - do_addtime(env, anchor, c); - } -} - -/** cleanup key list */ -static void -autr_cleanup_keys(struct trust_anchor* tp) -{ - struct autr_ta* p, **prevp; - prevp = &tp->autr->keys; - p = tp->autr->keys; - while(p) { - /* do we want to remove this key? */ - if(p->s == AUTR_STATE_START || p->s == AUTR_STATE_REMOVED || - sldns_wirerr_get_type(p->rr, p->rr_len, p->dname_len) - != LDNS_RR_TYPE_DNSKEY) { - struct autr_ta* np = p->next; - /* remove */ - free(p->rr); - free(p); - /* snip and go to next item */ - *prevp = np; - p = np; - continue; - } - /* remove pending counts if no longer pending */ - if(p->s != AUTR_STATE_ADDPEND) - p->pending_count = 0; - prevp = &p->next; - p = p->next; - } -} - -/** calculate next probe time */ -static time_t -calc_next_probe(struct module_env* env, time_t wait) -{ - /* make it random, 90-100% */ - time_t rnd, rest; - if(!autr_permit_small_holddown) { - if(wait < 3600) - wait = 3600; - } else { - if(wait == 0) wait = 1; - } - rnd = wait/10; - rest = wait-rnd; - rnd = (time_t)ub_random_max(env->rnd, (long int)rnd); - return (time_t)(*env->now + rest + rnd); -} - -/** what is first probe time (anchors must be locked) */ -static time_t -wait_probe_time(struct val_anchors* anchors) -{ - rbnode_type* t = rbtree_first(&anchors->autr->probe); - if(t != RBTREE_NULL) - return ((struct trust_anchor*)t->key)->autr->next_probe_time; - return 0; -} - -/** reset worker timer */ -static void -reset_worker_timer(struct module_env* env) -{ - struct timeval tv; -#ifndef S_SPLINT_S - time_t next = (time_t)wait_probe_time(env->anchors); - /* in case this is libunbound, no timer */ - if(!env->probe_timer) - return; - if(next > *env->now) - tv.tv_sec = (time_t)(next - *env->now); - else tv.tv_sec = 0; -#endif - tv.tv_usec = 0; - comm_timer_set(env->probe_timer, &tv); - verbose(VERB_ALGO, "scheduled next probe in " ARG_LL "d sec", (long long)tv.tv_sec); -} - -/** set next probe for trust anchor */ -static int -set_next_probe(struct module_env* env, struct trust_anchor* tp, - struct ub_packed_rrset_key* dnskey_rrset) -{ - struct trust_anchor key, *tp2; - time_t mold, mnew; - /* use memory allocated in rrset for temporary name storage */ - key.node.key = &key; - key.name = dnskey_rrset->rk.dname; - key.namelen = dnskey_rrset->rk.dname_len; - key.namelabs = dname_count_labels(key.name); - key.dclass = tp->dclass; - lock_basic_unlock(&tp->lock); - - /* fetch tp again and lock anchors, so that we can modify the trees */ - lock_basic_lock(&env->anchors->lock); - tp2 = (struct trust_anchor*)rbtree_search(env->anchors->tree, &key); - if(!tp2) { - verbose(VERB_ALGO, "trustpoint was deleted in set_next_probe"); - lock_basic_unlock(&env->anchors->lock); - return 0; - } - log_assert(tp == tp2); - lock_basic_lock(&tp->lock); - - /* schedule */ - mold = wait_probe_time(env->anchors); - (void)rbtree_delete(&env->anchors->autr->probe, tp); - tp->autr->next_probe_time = calc_next_probe(env, - tp->autr->query_interval); - (void)rbtree_insert(&env->anchors->autr->probe, &tp->autr->pnode); - mnew = wait_probe_time(env->anchors); - - lock_basic_unlock(&env->anchors->lock); - verbose(VERB_ALGO, "next probe set in %d seconds", - (int)tp->autr->next_probe_time - (int)*env->now); - if(mold != mnew) { - reset_worker_timer(env); - } - return 1; -} - -/** Revoke and Delete a trust point */ -static void -autr_tp_remove(struct module_env* env, struct trust_anchor* tp, - struct ub_packed_rrset_key* dnskey_rrset) -{ - struct trust_anchor* del_tp; - struct trust_anchor key; - struct autr_point_data pd; - time_t mold, mnew; - - log_nametypeclass(VERB_OPS, "trust point was revoked", - tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass); - tp->autr->revoked = 1; - - /* use space allocated for dnskey_rrset to save name of anchor */ - memset(&key, 0, sizeof(key)); - memset(&pd, 0, sizeof(pd)); - key.autr = &pd; - key.node.key = &key; - pd.pnode.key = &key; - pd.next_probe_time = tp->autr->next_probe_time; - key.name = dnskey_rrset->rk.dname; - key.namelen = tp->namelen; - key.namelabs = tp->namelabs; - key.dclass = tp->dclass; - - /* unlock */ - lock_basic_unlock(&tp->lock); - - /* take from tree. It could be deleted by someone else,hence (void). */ - lock_basic_lock(&env->anchors->lock); - del_tp = (struct trust_anchor*)rbtree_delete(env->anchors->tree, &key); - mold = wait_probe_time(env->anchors); - (void)rbtree_delete(&env->anchors->autr->probe, &key); - mnew = wait_probe_time(env->anchors); - anchors_init_parents_locked(env->anchors); - lock_basic_unlock(&env->anchors->lock); - - /* if !del_tp then the trust point is no longer present in the tree, - * it was deleted by someone else, who will write the zonefile and - * clean up the structure */ - if(del_tp) { - /* save on disk */ - del_tp->autr->next_probe_time = 0; /* no more probing for it */ - autr_write_file(env, del_tp); - - /* delete */ - autr_point_delete(del_tp); - } - if(mold != mnew) { - reset_worker_timer(env); - } -} - -int autr_process_prime(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset) -{ - int changed = 0; - log_assert(tp && tp->autr); - /* autotrust update trust anchors */ - /* the tp is locked, and stays locked unless it is deleted */ - - /* we could just catch the anchor here while another thread - * is busy deleting it. Just unlock and let the other do its job */ - if(tp->autr->revoked) { - log_nametypeclass(VERB_ALGO, "autotrust not processed, " - "trust point revoked", tp->name, - LDNS_RR_TYPE_DNSKEY, tp->dclass); - lock_basic_unlock(&tp->lock); - return 0; /* it is revoked */ - } - - /* query_dnskeys(): */ - tp->autr->last_queried = *env->now; - - log_nametypeclass(VERB_ALGO, "autotrust process for", - tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass); - /* see if time alone makes some keys valid */ - autr_holddown_exceed(env, tp, &changed); - if(changed) { - verbose(VERB_ALGO, "autotrust: morekeys, reassemble"); - if(!autr_assemble(tp)) { - log_err("malloc failure assembling autotrust keys"); - return 1; /* unchanged */ - } - } - /* did we get any data? */ - if(!dnskey_rrset) { - verbose(VERB_ALGO, "autotrust: no dnskey rrset"); - /* no update of query_failed, because then we would have - * to write to disk. But we cannot because we maybe are - * still 'initialising' with DS records, that we cannot write - * in the full format (which only contains KSKs). */ - return 1; /* trust point exists */ - } - /* check for revoked keys to remove immediately */ - check_contains_revoked(env, ve, tp, dnskey_rrset, &changed); - if(changed) { - verbose(VERB_ALGO, "autotrust: revokedkeys, reassemble"); - if(!autr_assemble(tp)) { - log_err("malloc failure assembling autotrust keys"); - return 1; /* unchanged */ - } - if(!tp->ds_rrset && !tp->dnskey_rrset) { - /* no more keys, all are revoked */ - /* this is a success for this probe attempt */ - tp->autr->last_success = *env->now; - autr_tp_remove(env, tp, dnskey_rrset); - return 0; /* trust point removed */ - } - } - /* verify the dnskey rrset and see if it is valid. */ - if(!verify_dnskey(env, ve, tp, dnskey_rrset)) { - verbose(VERB_ALGO, "autotrust: dnskey did not verify."); - /* only increase failure count if this is not the first prime, - * this means there was a previous successful probe */ - if(tp->autr->last_success) { - tp->autr->query_failed += 1; - autr_write_file(env, tp); - } - return 1; /* trust point exists */ - } - - tp->autr->last_success = *env->now; - tp->autr->query_failed = 0; - - /* Add new trust anchors to the data structure - * - note which trust anchors are seen this probe. - * Set trustpoint query_interval and retry_time. - * - find minimum rrsig expiration interval - */ - if(!update_events(env, ve, tp, dnskey_rrset, &changed)) { - log_err("malloc failure in autotrust update_events. " - "trust point unchanged."); - return 1; /* trust point unchanged, so exists */ - } - - /* - for every SEP key do the 5011 statetable. - * - remove missing trustanchors (if veryold and we have new anchors). - */ - if(!do_statetable(env, tp, &changed)) { - log_err("malloc failure in autotrust do_statetable. " - "trust point unchanged."); - return 1; /* trust point unchanged, so exists */ - } - - autr_cleanup_keys(tp); - if(!set_next_probe(env, tp, dnskey_rrset)) - return 0; /* trust point does not exist */ - autr_write_file(env, tp); - if(changed) { - verbose(VERB_ALGO, "autotrust: changed, reassemble"); - if(!autr_assemble(tp)) { - log_err("malloc failure assembling autotrust keys"); - return 1; /* unchanged */ - } - if(!tp->ds_rrset && !tp->dnskey_rrset) { - /* no more keys, all are revoked */ - autr_tp_remove(env, tp, dnskey_rrset); - return 0; /* trust point removed */ - } - } else verbose(VERB_ALGO, "autotrust: no changes"); - - return 1; /* trust point exists */ -} - -/** debug print a trust anchor key */ -static void -autr_debug_print_ta(struct autr_ta* ta) -{ - char buf[32]; - char* str = sldns_wire2str_rr(ta->rr, ta->rr_len); - if(!str) { - log_info("out of memory in debug_print_ta"); - return; - } - if(str && str[0]) str[strlen(str)-1]=0; /* remove newline */ - ctime_r(&ta->last_change, buf); - if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */ - log_info("[%s] %s ;;state:%d ;;pending_count:%d%s%s last:%s", - trustanchor_state2str(ta->s), str, ta->s, ta->pending_count, - ta->fetched?" fetched":"", ta->revoked?" revoked":"", buf); - free(str); -} - -/** debug print a trust point */ -static void -autr_debug_print_tp(struct trust_anchor* tp) -{ - struct autr_ta* ta; - char buf[257]; - if(!tp->autr) - return; - dname_str(tp->name, buf); - log_info("trust point %s : %d", buf, (int)tp->dclass); - log_info("assembled %d DS and %d DNSKEYs", - (int)tp->numDS, (int)tp->numDNSKEY); - if(tp->ds_rrset) { - log_packed_rrset(0, "DS:", tp->ds_rrset); - } - if(tp->dnskey_rrset) { - log_packed_rrset(0, "DNSKEY:", tp->dnskey_rrset); - } - log_info("file %s", tp->autr->file); - ctime_r(&tp->autr->last_queried, buf); - if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */ - log_info("last_queried: %u %s", (unsigned)tp->autr->last_queried, buf); - ctime_r(&tp->autr->last_success, buf); - if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */ - log_info("last_success: %u %s", (unsigned)tp->autr->last_success, buf); - ctime_r(&tp->autr->next_probe_time, buf); - if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */ - log_info("next_probe_time: %u %s", (unsigned)tp->autr->next_probe_time, - buf); - log_info("query_interval: %u", (unsigned)tp->autr->query_interval); - log_info("retry_time: %u", (unsigned)tp->autr->retry_time); - log_info("query_failed: %u", (unsigned)tp->autr->query_failed); - - for(ta=tp->autr->keys; ta; ta=ta->next) { - autr_debug_print_ta(ta); - } -} - -void -autr_debug_print(struct val_anchors* anchors) -{ - struct trust_anchor* tp; - lock_basic_lock(&anchors->lock); - RBTREE_FOR(tp, struct trust_anchor*, anchors->tree) { - lock_basic_lock(&tp->lock); - autr_debug_print_tp(tp); - lock_basic_unlock(&tp->lock); - } - lock_basic_unlock(&anchors->lock); -} - -void probe_answer_cb(void* arg, int ATTR_UNUSED(rcode), - sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(sec), - char* ATTR_UNUSED(why_bogus)) -{ - /* retry was set before the query was done, - * re-querytime is set when query succeeded, but that may not - * have reset this timer because the query could have been - * handled by another thread. In that case, this callback would - * get called after the original timeout is done. - * By not resetting the timer, it may probe more often, but not - * less often. - * Unless the new lookup resulted in smaller TTLs and thus smaller - * timeout values. In that case one old TTL could be mistakenly done. - */ - struct module_env* env = (struct module_env*)arg; - verbose(VERB_ALGO, "autotrust probe answer cb"); - reset_worker_timer(env); -} - -/** probe a trust anchor DNSKEY and unlocks tp */ -static void -probe_anchor(struct module_env* env, struct trust_anchor* tp) -{ - struct query_info qinfo; - uint16_t qflags = BIT_RD; - struct edns_data edns; - sldns_buffer* buf = env->scratch_buffer; - qinfo.qname = regional_alloc_init(env->scratch, tp->name, tp->namelen); - if(!qinfo.qname) { - log_err("out of memory making 5011 probe"); - return; - } - qinfo.qname_len = tp->namelen; - qinfo.qtype = LDNS_RR_TYPE_DNSKEY; - qinfo.qclass = tp->dclass; - qinfo.local_alias = NULL; - log_query_info(VERB_ALGO, "autotrust probe", &qinfo); - verbose(VERB_ALGO, "retry probe set in %d seconds", - (int)tp->autr->next_probe_time - (int)*env->now); - edns.edns_present = 1; - edns.ext_rcode = 0; - edns.edns_version = 0; - edns.bits = EDNS_DO; - edns.opt_list = NULL; - if(sldns_buffer_capacity(buf) < 65535) - edns.udp_size = (uint16_t)sldns_buffer_capacity(buf); - else edns.udp_size = 65535; - - /* can't hold the lock while mesh_run is processing */ - lock_basic_unlock(&tp->lock); - - /* delete the DNSKEY from rrset and key cache so an active probe - * is done. First the rrset so another thread does not use it - * to recreate the key entry in a race condition. */ - rrset_cache_remove(env->rrset_cache, qinfo.qname, qinfo.qname_len, - qinfo.qtype, qinfo.qclass, 0); - key_cache_remove(env->key_cache, qinfo.qname, qinfo.qname_len, - qinfo.qclass); - - if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, - &probe_answer_cb, env)) { - log_err("out of memory making 5011 probe"); - } -} - -/** fetch first to-probe trust-anchor and lock it and set retrytime */ -static struct trust_anchor* -todo_probe(struct module_env* env, time_t* next) -{ - struct trust_anchor* tp; - rbnode_type* el; - /* get first one */ - lock_basic_lock(&env->anchors->lock); - if( (el=rbtree_first(&env->anchors->autr->probe)) == RBTREE_NULL) { - /* in case of revoked anchors */ - lock_basic_unlock(&env->anchors->lock); - /* signal that there are no anchors to probe */ - *next = 0; - return NULL; - } - tp = (struct trust_anchor*)el->key; - lock_basic_lock(&tp->lock); - - /* is it eligible? */ - if((time_t)tp->autr->next_probe_time > *env->now) { - /* no more to probe */ - *next = (time_t)tp->autr->next_probe_time - *env->now; - lock_basic_unlock(&tp->lock); - lock_basic_unlock(&env->anchors->lock); - return NULL; - } - - /* reset its next probe time */ - (void)rbtree_delete(&env->anchors->autr->probe, tp); - tp->autr->next_probe_time = calc_next_probe(env, tp->autr->retry_time); - (void)rbtree_insert(&env->anchors->autr->probe, &tp->autr->pnode); - lock_basic_unlock(&env->anchors->lock); - - return tp; -} - -time_t -autr_probe_timer(struct module_env* env) -{ - struct trust_anchor* tp; - time_t next_probe = 3600; - int num = 0; - if(autr_permit_small_holddown) next_probe = 1; - verbose(VERB_ALGO, "autotrust probe timer callback"); - /* while there are still anchors to probe */ - while( (tp = todo_probe(env, &next_probe)) ) { - /* make a probe for this anchor */ - probe_anchor(env, tp); - num++; - } - regional_free_all(env->scratch); - if(next_probe == 0) - return 0; /* no trust points to probe */ - verbose(VERB_ALGO, "autotrust probe timer %d callbacks done", num); - return next_probe; -} diff --git a/external/unbound/validator/autotrust.h b/external/unbound/validator/autotrust.h deleted file mode 100644 index dbaf5126a..000000000 --- a/external/unbound/validator/autotrust.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * validator/autotrust.h - RFC5011 trust anchor management for unbound. - * - * Copyright (c) 2009, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * Contains autotrust definitions. - */ - -#ifndef VALIDATOR_AUTOTRUST_H -#define VALIDATOR_AUTOTRUST_H -#include "util/rbtree.h" -#include "util/data/packed_rrset.h" -struct val_anchors; -struct trust_anchor; -struct ub_packed_rrset_key; -struct module_env; -struct val_env; -struct sldns_buffer; - -/** Autotrust anchor states */ -typedef enum { - AUTR_STATE_START = 0, - AUTR_STATE_ADDPEND = 1, - AUTR_STATE_VALID = 2, - AUTR_STATE_MISSING = 3, - AUTR_STATE_REVOKED = 4, - AUTR_STATE_REMOVED = 5 -} autr_state_type; - -/** - * Autotrust metadata for one trust anchor key. - */ -struct autr_ta { - /** next key */ - struct autr_ta* next; - /** the RR */ - uint8_t* rr; - /** length of rr */ - size_t rr_len, dname_len; - /** last update of key state (new pending count keeps date the same) */ - time_t last_change; - /** 5011 state */ - autr_state_type s; - /** pending count */ - uint8_t pending_count; - /** fresh TA was seen */ - uint8_t fetched; - /** revoked TA was seen */ - uint8_t revoked; -}; - -/** - * Autotrust metadata for a trust point. - * This is part of the struct trust_anchor data. - */ -struct autr_point_data { - /** file to store the trust point in. chrootdir already applied. */ - char* file; - /** rbtree node for probe sort, key is struct trust_anchor */ - rbnode_type pnode; - - /** the keys */ - struct autr_ta* keys; - - /** last queried DNSKEY set - * Not all failures are captured in this entry. - * If the validator did not even start (e.g. timeout or localservfail), - * then the last_queried and query_failed values are not updated. - */ - time_t last_queried; - /** last successful DNSKEY set */ - time_t last_success; - /** next probe time */ - time_t next_probe_time; - - /** when to query if !failed */ - time_t query_interval; - /** when to retry if failed */ - time_t retry_time; - - /** - * How many times did it fail. diagnostic only (has no effect). - * Only updated if there was a dnskey rrset that failed to verify. - */ - uint8_t query_failed; - /** true if the trust point has been revoked */ - uint8_t revoked; -}; - -/** - * Autotrust global metadata. - */ -struct autr_global_data { - /** rbtree of autotrust anchors sorted by next probe time. - * When time is equal, sorted by anchor class, name. */ - rbtree_type probe; -}; - -/** - * Create new global 5011 data structure. - * @return new structure or NULL on malloc failure. - */ -struct autr_global_data* autr_global_create(void); - -/** - * Delete global 5011 data structure. - * @param global: global autotrust state to delete. - */ -void autr_global_delete(struct autr_global_data* global); - -/** - * See if autotrust anchors are configured and how many. - * @param anchors: the trust anchors structure. - * @return number of autotrust trust anchors - */ -size_t autr_get_num_anchors(struct val_anchors* anchors); - -/** - * Process probe timer. Add new probes if needed. - * @param env: module environment with time, with anchors and with the mesh. - * @return time of next probe (in seconds from now). - * If 0, then there is no next probe anymore (trust points deleted). - */ -time_t autr_probe_timer(struct module_env* env); - -/** probe tree compare function */ -int probetree_cmp(const void* x, const void* y); - -/** - * Read autotrust file. - * @param anchors: the anchors structure. - * @param nm: name of the file (copied). - * @return false on failure. - */ -int autr_read_file(struct val_anchors* anchors, const char* nm); - -/** - * Write autotrust file. - * @param env: environment with scratch space. - * @param tp: trust point to write. - */ -void autr_write_file(struct module_env* env, 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. - * On errors, likely the trust point has been unchanged. - */ -int autr_process_prime(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset); - -/** - * Debug printout of rfc5011 tracked anchors - * @param anchors: all the anchors. - */ -void autr_debug_print(struct val_anchors* anchors); - -/** callback for query answer to 5011 probe */ -void probe_answer_cb(void* arg, int rcode, struct sldns_buffer* buf, - enum sec_status sec, char* errinf); - -#endif /* VALIDATOR_AUTOTRUST_H */ diff --git a/external/unbound/validator/val_anchor.c b/external/unbound/validator/val_anchor.c deleted file mode 100644 index 6c6322447..000000000 --- a/external/unbound/validator/val_anchor.c +++ /dev/null @@ -1,1311 +0,0 @@ -/* - * validator/val_anchor.c - validator trust anchor storage. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains storage for the trust anchors for the validator. - */ -#include "config.h" -#include <ctype.h> -#include "validator/val_anchor.h" -#include "validator/val_sigcrypt.h" -#include "validator/autotrust.h" -#include "util/data/packed_rrset.h" -#include "util/data/dname.h" -#include "util/log.h" -#include "util/net_help.h" -#include "util/config_file.h" -#include "util/as112.h" -#include "sldns/sbuffer.h" -#include "sldns/rrdef.h" -#include "sldns/str2wire.h" -#ifdef HAVE_GLOB_H -#include <glob.h> -#endif - -int -anchor_cmp(const void* k1, const void* k2) -{ - int m; - struct trust_anchor* n1 = (struct trust_anchor*)k1; - struct trust_anchor* n2 = (struct trust_anchor*)k2; - /* no need to ntohs(class) because sort order is irrelevant */ - if(n1->dclass != n2->dclass) { - if(n1->dclass < n2->dclass) - return -1; - return 1; - } - return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs, - &m); -} - -struct val_anchors* -anchors_create(void) -{ - struct val_anchors* a = (struct val_anchors*)calloc(1, sizeof(*a)); - if(!a) - return NULL; - a->tree = rbtree_create(anchor_cmp); - if(!a->tree) { - anchors_delete(a); - return NULL; - } - a->autr = autr_global_create(); - if(!a->autr) { - anchors_delete(a); - return NULL; - } - lock_basic_init(&a->lock); - lock_protect(&a->lock, a, sizeof(*a)); - lock_protect(&a->lock, a->autr, sizeof(*a->autr)); - return a; -} - -/** delete assembled rrset */ -static void -assembled_rrset_delete(struct ub_packed_rrset_key* pkey) -{ - if(!pkey) return; - if(pkey->entry.data) { - struct packed_rrset_data* pd = (struct packed_rrset_data*) - pkey->entry.data; - free(pd->rr_data); - free(pd->rr_ttl); - free(pd->rr_len); - free(pd); - } - free(pkey->rk.dname); - free(pkey); -} - -/** destroy locks in tree and delete autotrust anchors */ -static void -anchors_delfunc(rbnode_type* elem, void* ATTR_UNUSED(arg)) -{ - struct trust_anchor* ta = (struct trust_anchor*)elem; - if(!ta) return; - if(ta->autr) { - autr_point_delete(ta); - } else { - struct ta_key* p, *np; - lock_basic_destroy(&ta->lock); - free(ta->name); - p = ta->keylist; - while(p) { - np = p->next; - free(p->data); - free(p); - p = np; - } - assembled_rrset_delete(ta->ds_rrset); - assembled_rrset_delete(ta->dnskey_rrset); - free(ta); - } -} - -void -anchors_delete(struct val_anchors* anchors) -{ - if(!anchors) - return; - lock_unprotect(&anchors->lock, anchors->autr); - lock_unprotect(&anchors->lock, anchors); - lock_basic_destroy(&anchors->lock); - if(anchors->tree) - traverse_postorder(anchors->tree, anchors_delfunc, NULL); - free(anchors->tree); - autr_global_delete(anchors->autr); - free(anchors); -} - -void -anchors_init_parents_locked(struct val_anchors* anchors) -{ - struct trust_anchor* node, *prev = NULL, *p; - int m; - /* nobody else can grab locks because we hold the main lock. - * Thus the previous items, after unlocked, are not deleted */ - 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, - node->namelabs, &m); /* we know prev is smaller */ - /* sort order like: . com. bla.com. zwb.com. net. */ - /* find the previous, or parent-parent-parent */ - for(p = prev; p; p = p->parent) - /* looking for name with few labels, a parent */ - if(p->namelabs <= m) { - /* ==: since prev matched m, this is closest*/ - /* <: prev matches more, but is not a parent, - * this one is a (grand)parent */ - node->parent = p; - break; - } - lock_basic_unlock(&node->lock); - prev = node; - } -} - -/** initialise parent pointers in the tree */ -static void -init_parents(struct val_anchors* anchors) -{ - lock_basic_lock(&anchors->lock); - anchors_init_parents_locked(anchors); - lock_basic_unlock(&anchors->lock); -} - -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_type* 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; -} - -/** 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, int lockit) -{ -#ifdef UNBOUND_DEBUG - rbnode_type* r; -#endif - struct trust_anchor* ta = (struct trust_anchor*)malloc( - sizeof(struct trust_anchor)); - if(!ta) - return NULL; - memset(ta, 0, sizeof(*ta)); - ta->node.key = ta; - ta->name = memdup(name, namelen); - if(!ta->name) { - free(ta); - return NULL; - } - ta->namelabs = namelabs; - ta->namelen = namelen; - ta->dclass = dclass; - lock_basic_init(&ta->lock); - if(lockit) { - lock_basic_lock(&anchors->lock); - } -#ifdef UNBOUND_DEBUG - r = -#else - (void) -#endif - rbtree_insert(anchors->tree, &ta->node); - if(lockit) { - lock_basic_unlock(&anchors->lock); - } - log_assert(r != NULL); - 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(uint8_t* rdata, size_t rdata_len, uint16_t type) -{ - struct ta_key* k = (struct ta_key*)malloc(sizeof(*k)); - if(!k) - return NULL; - memset(k, 0, sizeof(*k)); - k->data = memdup(rdata, rdata_len); - if(!k->data) { - free(k); - 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. - * If NULL, nothing is stored, but an entry is created. - * @param rdata_len: length of rdata including rdlength. - * @return: NULL on error, else the trust anchor. - */ -static struct trust_anchor* -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, 1); - if(!ta) - return NULL; - lock_basic_lock(&ta->lock); - } - 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(rdata, rdata_len, type); - 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; -} - -/** - * Add new RR. It converts ldns RR to wire format. - * @param anchors: anchor storage. - * @param rr: the wirerr. - * @param rl: length of rr. - * @param dl: length of dname. - * @return NULL on error, else the trust anchor. - */ -static struct trust_anchor* -anchor_store_new_rr(struct val_anchors* anchors, uint8_t* rr, size_t rl, - size_t dl) -{ - struct trust_anchor* ta; - if(!(ta=anchor_store_new_key(anchors, rr, - sldns_wirerr_get_type(rr, rl, dl), - sldns_wirerr_get_class(rr, rl, dl), - sldns_wirerr_get_rdatawl(rr, rl, dl), - sldns_wirerr_get_rdatalen(rr, rl, dl)+2))) { - return NULL; - } - log_nametypeclass(VERB_QUERY, "adding trusted key", - rr, sldns_wirerr_get_type(rr, rl, dl), - sldns_wirerr_get_class(rr, rl, dl)); - return ta; -} - -/** - * Insert insecure anchor - * @param anchors: anchor storage. - * @param str: the domain name. - * @return NULL on error, Else last trust anchor point - */ -static struct trust_anchor* -anchor_insert_insecure(struct val_anchors* anchors, const char* str) -{ - struct trust_anchor* ta; - size_t dname_len = 0; - uint8_t* nm = sldns_str2wire_dname(str, &dname_len); - if(!nm) { - log_err("parse error in domain name '%s'", str); - return NULL; - } - ta = anchor_store_new_key(anchors, nm, LDNS_RR_TYPE_DS, - LDNS_RR_CLASS_IN, NULL, 0); - free(nm); - return ta; -} - -struct trust_anchor* -anchor_store_str(struct val_anchors* anchors, sldns_buffer* buffer, - const char* str) -{ - struct trust_anchor* ta; - uint8_t* rr = sldns_buffer_begin(buffer); - size_t len = sldns_buffer_capacity(buffer), dname_len = 0; - int status = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, - 0, NULL, 0, NULL, 0); - if(status != 0) { - log_err("error parsing trust anchor %s: at %d: %s", - str, LDNS_WIREPARSE_OFFSET(status), - sldns_get_errorstr_parse(status)); - return NULL; - } - if(!(ta=anchor_store_new_rr(anchors, rr, len, dname_len))) { - log_err("out of memory"); - return NULL; - } - return ta; -} - -/** - * Read a file with trust anchors - * @param anchors: anchor storage. - * @param buffer: parsing buffer. - * @param fname: string. - * @param onlyone: only one trust anchor allowed in file. - * @return NULL on error. Else last trust-anchor point. - */ -static struct trust_anchor* -anchor_read_file(struct val_anchors* anchors, sldns_buffer* buffer, - const char* fname, int onlyone) -{ - struct trust_anchor* ta = NULL, *tanew; - struct sldns_file_parse_state pst; - int status; - size_t len, dname_len; - uint8_t* rr = sldns_buffer_begin(buffer); - int ok = 1; - FILE* in = fopen(fname, "r"); - if(!in) { - log_err("error opening file %s: %s", fname, strerror(errno)); - return 0; - } - memset(&pst, 0, sizeof(pst)); - pst.default_ttl = 3600; - pst.lineno = 1; - while(!feof(in)) { - len = sldns_buffer_capacity(buffer); - dname_len = 0; - status = sldns_fp2wire_rr_buf(in, rr, &len, &dname_len, &pst); - if(len == 0) /* empty, $TTL, $ORIGIN */ - continue; - if(status != 0) { - log_err("parse error in %s:%d:%d : %s", fname, - pst.lineno, LDNS_WIREPARSE_OFFSET(status), - sldns_get_errorstr_parse(status)); - ok = 0; - break; - } - if(sldns_wirerr_get_type(rr, len, dname_len) != - LDNS_RR_TYPE_DS && sldns_wirerr_get_type(rr, len, - dname_len) != LDNS_RR_TYPE_DNSKEY) { - continue; - } - if(!(tanew=anchor_store_new_rr(anchors, rr, len, dname_len))) { - log_err("mem error at %s line %d", fname, pst.lineno); - ok = 0; - break; - } - if(onlyone && ta && ta != tanew) { - log_err("error at %s line %d: no multiple anchor " - "domains allowed (you can have multiple " - "keys, but they must have the same name).", - fname, pst.lineno); - ok = 0; - break; - } - ta = tanew; - } - fclose(in); - if(!ok) return NULL; - /* empty file is OK when multiple anchors are allowed */ - if(!onlyone && !ta) return (struct trust_anchor*)1; - return ta; -} - -/** skip file to end of line */ -static void -skip_to_eol(FILE* in) -{ - int c; - while((c = getc(in)) != EOF ) { - if(c == '\n') - return; - } -} - -/** true for special characters in bind configs */ -static int -is_bind_special(int c) -{ - switch(c) { - case '{': - case '}': - case '"': - case ';': - return 1; - } - return 0; -} - -/** - * Read a keyword skipping bind comments; spaces, specials, restkeywords. - * The file is split into the following tokens: - * * special characters, on their own, rdlen=1, { } doublequote ; - * * whitespace becomes a single ' ' or tab. Newlines become spaces. - * * other words ('keywords') - * * comments are skipped if desired - * / / C++ style comment to end of line - * # to end of line - * / * C style comment * / - * @param in: file to read from. - * @param buf: buffer, what is read is stored after current buffer position. - * Space is left in the buffer to write a terminating 0. - * @param line: line number is increased per line, for error reports. - * @param comments: if 0, comments are not possible and become text. - * if 1, comments are skipped entirely. - * In BIND files, this is when reading quoted strings, for example - * " base 64 text with / / in there " - * @return the number of character written to the buffer. - * 0 on end of file. - */ -static int -readkeyword_bindfile(FILE* in, sldns_buffer* buf, int* line, int comments) -{ - int c; - int numdone = 0; - while((c = getc(in)) != EOF ) { - if(comments && c == '#') { /* # blabla */ - skip_to_eol(in); - (*line)++; - continue; - } else if(comments && c=='/' && numdone>0 && /* /_/ bla*/ - sldns_buffer_read_u8_at(buf, - sldns_buffer_position(buf)-1) == '/') { - sldns_buffer_skip(buf, -1); - numdone--; - skip_to_eol(in); - (*line)++; - continue; - } else if(comments && c=='*' && numdone>0 && /* /_* bla *_/ */ - sldns_buffer_read_u8_at(buf, - sldns_buffer_position(buf)-1) == '/') { - sldns_buffer_skip(buf, -1); - numdone--; - /* skip to end of comment */ - while(c != EOF && (c=getc(in)) != EOF ) { - if(c == '*') { - if((c=getc(in)) == '/') - break; - } - if(c == '\n') - (*line)++; - } - continue; - } - /* not a comment, complete the keyword */ - if(numdone > 0) { - /* check same type */ - if(isspace((unsigned char)c)) { - ungetc(c, in); - return numdone; - } - if(is_bind_special(c)) { - ungetc(c, in); - return numdone; - } - } - if(c == '\n') { - c = ' '; - (*line)++; - } - /* space for 1 char + 0 string terminator */ - if(sldns_buffer_remaining(buf) < 2) { - fatal_exit("trusted-keys, %d, string too long", *line); - } - sldns_buffer_write_u8(buf, (uint8_t)c); - numdone++; - if(isspace((unsigned char)c)) { - /* collate whitespace into ' ' */ - while((c = getc(in)) != EOF ) { - if(c == '\n') - (*line)++; - if(!isspace((unsigned char)c)) { - ungetc(c, in); - break; - } - } - return numdone; - } - if(is_bind_special(c)) - return numdone; - } - return numdone; -} - -/** skip through file to { or ; */ -static int -skip_to_special(FILE* in, sldns_buffer* buf, int* line, int spec) -{ - int rdlen; - sldns_buffer_clear(buf); - while((rdlen=readkeyword_bindfile(in, buf, line, 1))) { - if(rdlen == 1 && isspace((unsigned char)*sldns_buffer_begin(buf))) { - sldns_buffer_clear(buf); - continue; - } - if(rdlen != 1 || *sldns_buffer_begin(buf) != (uint8_t)spec) { - sldns_buffer_write_u8(buf, 0); - log_err("trusted-keys, line %d, expected %c", - *line, spec); - return 0; - } - return 1; - } - log_err("trusted-keys, line %d, expected %c got EOF", *line, spec); - return 0; -} - -/** - * read contents of trusted-keys{ ... ; clauses and insert keys into storage. - * @param anchors: where to store keys - * @param buf: buffer to use - * @param line: line number in file - * @param in: file to read from. - * @return 0 on error. - */ -static int -process_bind_contents(struct val_anchors* anchors, sldns_buffer* buf, - int* line, FILE* in) -{ - /* loop over contents, collate strings before ; */ - /* contents is (numbered): 0 1 2 3 4 5 6 7 8 */ - /* name. 257 3 5 base64 base64 */ - /* quoted value: 0 "111" 0 0 0 0 0 0 0 */ - /* comments value: 1 "000" 1 1 1 "0 0 0 0" 1 */ - int contnum = 0; - int quoted = 0; - int comments = 1; - int rdlen; - char* str = 0; - sldns_buffer_clear(buf); - while((rdlen=readkeyword_bindfile(in, buf, line, comments))) { - if(rdlen == 1 && sldns_buffer_position(buf) == 1 - && isspace((unsigned char)*sldns_buffer_begin(buf))) { - /* starting whitespace is removed */ - sldns_buffer_clear(buf); - continue; - } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == '"') { - /* remove " from the string */ - if(contnum == 0) { - quoted = 1; - comments = 0; - } - sldns_buffer_skip(buf, -1); - if(contnum > 0 && quoted) { - if(sldns_buffer_remaining(buf) < 8+1) { - log_err("line %d, too long", *line); - return 0; - } - sldns_buffer_write(buf, " DNSKEY ", 8); - quoted = 0; - comments = 1; - } else if(contnum > 0) - comments = !comments; - continue; - } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == ';') { - - if(contnum < 5) { - sldns_buffer_write_u8(buf, 0); - log_err("line %d, bad key", *line); - return 0; - } - sldns_buffer_skip(buf, -1); - sldns_buffer_write_u8(buf, 0); - str = strdup((char*)sldns_buffer_begin(buf)); - if(!str) { - log_err("line %d, allocation failure", *line); - return 0; - } - if(!anchor_store_str(anchors, buf, str)) { - log_err("line %d, bad key", *line); - free(str); - return 0; - } - free(str); - sldns_buffer_clear(buf); - contnum = 0; - quoted = 0; - comments = 1; - continue; - } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == '}') { - if(contnum > 0) { - sldns_buffer_write_u8(buf, 0); - log_err("line %d, bad key before }", *line); - return 0; - } - return 1; - } else if(rdlen == 1 && - isspace((unsigned char)sldns_buffer_current(buf)[-1])) { - /* leave whitespace here */ - } else { - /* not space or whatnot, so actual content */ - contnum ++; - if(contnum == 1 && !quoted) { - if(sldns_buffer_remaining(buf) < 8+1) { - log_err("line %d, too long", *line); - return 0; - } - sldns_buffer_write(buf, " DNSKEY ", 8); - } - } - } - - log_err("line %d, EOF before }", *line); - return 0; -} - -/** - * Read a BIND9 like file with trust anchors in named.conf format. - * @param anchors: anchor storage. - * @param buffer: parsing buffer. - * @param fname: string. - * @return false on error. - */ -static int -anchor_read_bind_file(struct val_anchors* anchors, sldns_buffer* buffer, - const char* fname) -{ - int line_nr = 1; - FILE* in = fopen(fname, "r"); - int rdlen = 0; - if(!in) { - log_err("error opening file %s: %s", fname, strerror(errno)); - return 0; - } - verbose(VERB_QUERY, "reading in bind-compat-mode: '%s'", fname); - /* scan for trusted-keys keyword, ignore everything else */ - sldns_buffer_clear(buffer); - while((rdlen=readkeyword_bindfile(in, buffer, &line_nr, 1)) != 0) { - if(rdlen != 12 || strncmp((char*)sldns_buffer_begin(buffer), - "trusted-keys", 12) != 0) { - sldns_buffer_clear(buffer); - /* ignore everything but trusted-keys */ - continue; - } - if(!skip_to_special(in, buffer, &line_nr, '{')) { - log_err("error in trusted key: \"%s\"", fname); - fclose(in); - return 0; - } - /* process contents */ - if(!process_bind_contents(anchors, buffer, &line_nr, in)) { - log_err("error in trusted key: \"%s\"", fname); - fclose(in); - return 0; - } - if(!skip_to_special(in, buffer, &line_nr, ';')) { - log_err("error in trusted key: \"%s\"", fname); - fclose(in); - return 0; - } - sldns_buffer_clear(buffer); - } - fclose(in); - return 1; -} - -/** - * Read a BIND9 like files with trust anchors in named.conf format. - * Performs wildcard processing of name. - * @param anchors: anchor storage. - * @param buffer: parsing buffer. - * @param pat: pattern string. (can be wildcarded) - * @return false on error. - */ -static int -anchor_read_bind_file_wild(struct val_anchors* anchors, sldns_buffer* buffer, - const char* pat) -{ -#ifdef HAVE_GLOB - glob_t g; - size_t i; - int r, flags; - if(!strchr(pat, '*') && !strchr(pat, '?') && !strchr(pat, '[') && - !strchr(pat, '{') && !strchr(pat, '~')) { - return anchor_read_bind_file(anchors, buffer, pat); - } - verbose(VERB_QUERY, "wildcard found, processing %s", pat); - flags = 0 -#ifdef GLOB_ERR - | GLOB_ERR -#endif -#ifdef GLOB_NOSORT - | GLOB_NOSORT -#endif -#ifdef GLOB_BRACE - | GLOB_BRACE -#endif -#ifdef GLOB_TILDE - | GLOB_TILDE -#endif - ; - memset(&g, 0, sizeof(g)); - r = glob(pat, flags, NULL, &g); - if(r) { - /* some error */ - if(r == GLOB_NOMATCH) { - verbose(VERB_QUERY, "trusted-keys-file: " - "no matches for %s", pat); - return 1; - } else if(r == GLOB_NOSPACE) { - log_err("wildcard trusted-keys-file %s: " - "pattern out of memory", pat); - } else if(r == GLOB_ABORTED) { - log_err("wildcard trusted-keys-file %s: expansion " - "aborted (%s)", pat, strerror(errno)); - } else { - log_err("wildcard trusted-keys-file %s: expansion " - "failed (%s)", pat, strerror(errno)); - } - /* ignore globs that yield no files */ - return 1; - } - /* process files found, if any */ - for(i=0; i<(size_t)g.gl_pathc; i++) { - if(!anchor_read_bind_file(anchors, buffer, g.gl_pathv[i])) { - log_err("error reading wildcard " - "trusted-keys-file: %s", g.gl_pathv[i]); - globfree(&g); - return 0; - } - } - globfree(&g); - return 1; -#else /* not HAVE_GLOB */ - return anchor_read_bind_file(anchors, buffer, pat); -#endif /* HAVE_GLOB */ -} - -/** - * Assemble an rrset structure for the type - * @param ta: trust anchor. - * @param num: number of items to fetch from list. - * @param type: fetch only items of this type. - * @return rrset or NULL on error. - */ -static struct ub_packed_rrset_key* -assemble_it(struct trust_anchor* ta, size_t num, uint16_t type) -{ - struct ub_packed_rrset_key* pkey = (struct ub_packed_rrset_key*) - malloc(sizeof(*pkey)); - struct packed_rrset_data* pd; - struct ta_key* tk; - size_t i; - if(!pkey) - return NULL; - memset(pkey, 0, sizeof(*pkey)); - pkey->rk.dname = memdup(ta->name, ta->namelen); - if(!pkey->rk.dname) { - free(pkey); - return NULL; - } - - pkey->rk.dname_len = ta->namelen; - pkey->rk.type = htons(type); - pkey->rk.rrset_class = htons(ta->dclass); - /* The rrset is build in an uncompressed way. This means it - * cannot be copied in the normal way. */ - pd = (struct packed_rrset_data*)malloc(sizeof(*pd)); - if(!pd) { - free(pkey->rk.dname); - free(pkey); - return NULL; - } - memset(pd, 0, sizeof(*pd)); - pd->count = num; - pd->trust = rrset_trust_ultimate; - pd->rr_len = (size_t*)reallocarray(NULL, num, sizeof(size_t)); - if(!pd->rr_len) { - free(pd); - free(pkey->rk.dname); - free(pkey); - return NULL; - } - pd->rr_ttl = (time_t*)reallocarray(NULL, num, sizeof(time_t)); - if(!pd->rr_ttl) { - free(pd->rr_len); - free(pd); - free(pkey->rk.dname); - free(pkey); - return NULL; - } - pd->rr_data = (uint8_t**)reallocarray(NULL, num, sizeof(uint8_t*)); - if(!pd->rr_data) { - free(pd->rr_ttl); - free(pd->rr_len); - free(pd); - free(pkey->rk.dname); - free(pkey); - return NULL; - } - /* fill in rrs */ - i=0; - for(tk = ta->keylist; tk; tk = tk->next) { - if(tk->type != type) - continue; - pd->rr_len[i] = tk->len; - /* reuse data ptr to allocation in talist */ - pd->rr_data[i] = tk->data; - pd->rr_ttl[i] = 0; - i++; - } - pkey->entry.data = (void*)pd; - return pkey; -} - -/** - * Assemble structures for the trust DS and DNSKEY rrsets. - * @param ta: trust anchor - * @return: false on error. - */ -static int -anchors_assemble(struct trust_anchor* ta) -{ - if(ta->numDS > 0) { - ta->ds_rrset = assemble_it(ta, ta->numDS, LDNS_RR_TYPE_DS); - if(!ta->ds_rrset) - return 0; - } - if(ta->numDNSKEY > 0) { - ta->dnskey_rrset = assemble_it(ta, ta->numDNSKEY, - LDNS_RR_TYPE_DNSKEY); - if(!ta->dnskey_rrset) - return 0; - } - return 1; -} - -/** - * Check DS algos for support, warn if not. - * @param ta: trust anchor - * @return number of DS anchors with unsupported algorithms. - */ -static size_t -anchors_ds_unsupported(struct trust_anchor* ta) -{ - size_t i, num = 0; - for(i=0; i<ta->numDS; i++) { - if(!ds_digest_algo_is_supported(ta->ds_rrset, i) || - !ds_key_algo_is_supported(ta->ds_rrset, i)) - num++; - } - return num; -} - -/** - * Check DNSKEY algos for support, warn if not. - * @param ta: trust anchor - * @return number of DNSKEY anchors with unsupported algorithms. - */ -static size_t -anchors_dnskey_unsupported(struct trust_anchor* ta) -{ - size_t i, num = 0; - for(i=0; i<ta->numDNSKEY; i++) { - if(!dnskey_algo_is_supported(ta->dnskey_rrset, i)) - num++; - } - return num; -} - -/** - * Assemble the rrsets in the anchors, ready for use by validator. - * @param anchors: trust anchor storage. - * @return: false on error. - */ -static int -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_type*)ta != RBTREE_NULL) { - next = (struct trust_anchor*)rbtree_next(&ta->node); - lock_basic_lock(&ta->lock); - if(ta->autr || (ta->numDS == 0 && ta->numDNSKEY == 0)) { - lock_basic_unlock(&ta->lock); - ta = next; /* skip */ - continue; - } - if(!anchors_assemble(ta)) { - log_err("out of memory"); - lock_basic_unlock(&ta->lock); - lock_basic_unlock(&anchors->lock); - return 0; - } - nods = anchors_ds_unsupported(ta); - nokey = anchors_dnskey_unsupported(ta); - if(nods) { - log_nametypeclass(0, "warning: unsupported " - "algorithm for trust anchor", - ta->name, LDNS_RR_TYPE_DS, ta->dclass); - } - if(nokey) { - log_nametypeclass(0, "warning: unsupported " - "algorithm for trust anchor", - ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); - } - if(nods == ta->numDS && nokey == ta->numDNSKEY) { - char b[257]; - dname_str(ta->name, b); - log_warn("trust anchor %s has no supported algorithms," - " the anchor is ignored (check if you need to" - " upgrade unbound and " -#ifdef HAVE_LIBRESSL - "libressl" -#else - "openssl" -#endif - ")", b); - (void)rbtree_delete(anchors->tree, &ta->node); - lock_basic_unlock(&ta->lock); - if(anchors->dlv_anchor == ta) - anchors->dlv_anchor = NULL; - anchors_delfunc(&ta->node, NULL); - ta = next; - continue; - } - lock_basic_unlock(&ta->lock); - ta = next; - } - lock_basic_unlock(&anchors->lock); - return 1; -} - -int -anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg) -{ - struct config_strlist* f; - const char** zstr; - char* nm; - sldns_buffer* parsebuf = sldns_buffer_new(65535); - if(cfg->insecure_lan_zones) { - for(zstr = as112_zones; *zstr; zstr++) { - if(!anchor_insert_insecure(anchors, *zstr)) { - log_err("error in insecure-lan-zones: %s", *zstr); - sldns_buffer_free(parsebuf); - return 0; - } - } - } - for(f = cfg->domain_insecure; f; f = f->next) { - if(!f->str || f->str[0] == 0) /* empty "" */ - continue; - if(!anchor_insert_insecure(anchors, f->str)) { - log_err("error in domain-insecure: %s", f->str); - sldns_buffer_free(parsebuf); - return 0; - } - } - for(f = cfg->trust_anchor_file_list; f; f = f->next) { - if(!f->str || f->str[0] == 0) /* empty "" */ - continue; - nm = f->str; - if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm, - cfg->chrootdir, strlen(cfg->chrootdir)) == 0) - nm += strlen(cfg->chrootdir); - if(!anchor_read_file(anchors, parsebuf, nm, 0)) { - log_err("error reading trust-anchor-file: %s", f->str); - sldns_buffer_free(parsebuf); - return 0; - } - } - for(f = cfg->trusted_keys_file_list; f; f = f->next) { - if(!f->str || f->str[0] == 0) /* empty "" */ - continue; - nm = f->str; - if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm, - cfg->chrootdir, strlen(cfg->chrootdir)) == 0) - nm += strlen(cfg->chrootdir); - if(!anchor_read_bind_file_wild(anchors, parsebuf, nm)) { - log_err("error reading trusted-keys-file: %s", f->str); - sldns_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); - sldns_buffer_free(parsebuf); - return 0; - } - } - 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(!(dlva = anchor_read_file(anchors, parsebuf, - nm, 1))) { - log_err("error reading dlv-anchor-file: %s", - cfg->dlv_anchor_file); - sldns_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(!(dlva = anchor_store_str( - anchors, parsebuf, f->str))) { - log_err("error in dlv-anchor: \"%s\"", f->str); - sldns_buffer_free(parsebuf); - return 0; - } - lock_basic_lock(&anchors->lock); - anchors->dlv_anchor = dlva; - lock_basic_unlock(&anchors->lock); - } - /* do autr last, so that it sees what anchors are filled by other - * means can can print errors about double config for the name */ - for(f = cfg->auto_trust_anchor_file_list; f; f = f->next) { - if(!f->str || f->str[0] == 0) /* empty "" */ - continue; - nm = f->str; - if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm, - cfg->chrootdir, strlen(cfg->chrootdir)) == 0) - nm += strlen(cfg->chrootdir); - if(!autr_read_file(anchors, nm)) { - log_err("error reading auto-trust-anchor-file: %s", - f->str); - sldns_buffer_free(parsebuf); - return 0; - } - } - /* first assemble, since it may delete useless anchors */ - anchors_assemble_rrsets(anchors); - init_parents(anchors); - sldns_buffer_free(parsebuf); - if(verbosity >= VERB_ALGO) autr_debug_print(anchors); - return 1; -} - -struct trust_anchor* -anchors_lookup(struct val_anchors* anchors, - uint8_t* qname, size_t qname_len, uint16_t qclass) -{ - struct trust_anchor key; - struct trust_anchor* result; - rbnode_type* res = NULL; - key.node.key = &key; - key.name = qname; - 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; - } else { - /* smaller element (or no element) */ - int m; - result = (struct trust_anchor*)res; - 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); - while(result) { /* go up until qname is subdomain of stub */ - if(result->namelabs <= m) - break; - result = result->parent; - } - } - if(result) { - lock_basic_lock(&result->lock); - } - lock_basic_unlock(&anchors->lock); - return result; -} - -size_t -anchors_get_mem(struct val_anchors* anchors) -{ - struct trust_anchor *ta; - size_t s = sizeof(*anchors); - if(!anchors) - return 0; - RBTREE_FOR(ta, struct trust_anchor*, anchors->tree) { - s += sizeof(*ta) + ta->namelen; - /* keys and so on */ - } - return s; -} - -int -anchors_add_insecure(struct val_anchors* anchors, uint16_t c, uint8_t* nm) -{ - struct trust_anchor key; - key.node.key = &key; - key.name = nm; - key.namelabs = dname_count_size_labels(nm, &key.namelen); - key.dclass = c; - lock_basic_lock(&anchors->lock); - if(rbtree_search(anchors->tree, &key)) { - lock_basic_unlock(&anchors->lock); - /* nothing to do, already an anchor or insecure point */ - return 1; - } - if(!anchor_new_ta(anchors, nm, key.namelabs, key.namelen, c, 0)) { - log_err("out of memory"); - lock_basic_unlock(&anchors->lock); - return 0; - } - /* no other contents in new ta, because it is insecure point */ - anchors_init_parents_locked(anchors); - lock_basic_unlock(&anchors->lock); - return 1; -} - -void -anchors_delete_insecure(struct val_anchors* anchors, uint16_t c, - uint8_t* nm) -{ - struct trust_anchor key; - struct trust_anchor* ta; - key.node.key = &key; - key.name = nm; - key.namelabs = dname_count_size_labels(nm, &key.namelen); - key.dclass = c; - lock_basic_lock(&anchors->lock); - if(!(ta=(struct trust_anchor*)rbtree_search(anchors->tree, &key))) { - lock_basic_unlock(&anchors->lock); - /* nothing there */ - return; - } - /* lock it to drive away other threads that use it */ - lock_basic_lock(&ta->lock); - /* see if its really an insecure point */ - if(ta->keylist || ta->autr || ta->numDS || ta->numDNSKEY) { - lock_basic_unlock(&anchors->lock); - lock_basic_unlock(&ta->lock); - /* its not an insecure point, do not remove it */ - return; - } - - /* remove from tree */ - (void)rbtree_delete(anchors->tree, &ta->node); - anchors_init_parents_locked(anchors); - lock_basic_unlock(&anchors->lock); - - /* actual free of data */ - lock_basic_unlock(&ta->lock); - anchors_delfunc(&ta->node, NULL); -} - -/** compare two keytags, return -1, 0 or 1 */ -static int -keytag_compare(const void* x, const void* y) -{ - if(*(uint16_t*)x == *(uint16_t*)y) - return 0; - if(*(uint16_t*)x > *(uint16_t*)y) - return 1; - return -1; -} - -size_t -anchor_list_keytags(struct trust_anchor* ta, uint16_t* list, size_t num) -{ - size_t i, ret = 0; - if(ta->numDS == 0 && ta->numDNSKEY == 0) - return 0; /* insecure point */ - if(ta->numDS != 0 && ta->ds_rrset) { - struct packed_rrset_data* d=(struct packed_rrset_data*) - ta->ds_rrset->entry.data; - for(i=0; i<d->count; i++) { - if(ret == num) continue; - list[ret++] = ds_get_keytag(ta->ds_rrset, i); - } - } - if(ta->numDNSKEY != 0 && ta->dnskey_rrset) { - struct packed_rrset_data* d=(struct packed_rrset_data*) - ta->dnskey_rrset->entry.data; - for(i=0; i<d->count; i++) { - if(ret == num) continue; - list[ret++] = dnskey_calc_keytag(ta->dnskey_rrset, i); - } - } - qsort(list, ret, sizeof(*list), keytag_compare); - return ret; -} diff --git a/external/unbound/validator/val_anchor.h b/external/unbound/validator/val_anchor.h deleted file mode 100644 index 318a2b227..000000000 --- a/external/unbound/validator/val_anchor.h +++ /dev/null @@ -1,230 +0,0 @@ -/* - * validator/val_anchor.h - validator trust anchor storage. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains storage for the trust anchors for the validator. - */ - -#ifndef VALIDATOR_VAL_ANCHOR_H -#define VALIDATOR_VAL_ANCHOR_H -#include "util/rbtree.h" -#include "util/locks.h" -struct trust_anchor; -struct config_file; -struct ub_packed_rrset_key; -struct autr_point_data; -struct autr_global_data; -struct sldns_buffer; - -/** - * Trust anchor store. - * The tree must be locked, while no other locks (from trustanchors) are held. - * And then an anchor searched for. Which can be locked or deleted. Then - * the tree can be unlocked again. This means you have to release the lock - * on a trust anchor and look it up again to delete it. - */ -struct val_anchors { - /** lock on trees */ - lock_basic_type lock; - /** - * Anchors are store in this tree. Sort order is chosen, so that - * dnames are in nsec-like order. A lookup on class, name will return - * an exact match of the closest match, with the ancestor needed. - * contents of type trust_anchor. - */ - rbtree_type* tree; - /** The DLV trust anchor (if one is configured, else NULL) */ - struct trust_anchor* dlv_anchor; - /** Autotrust global data, anchors sorted by next probe time */ - struct autr_global_data* autr; -}; - -/** - * Trust anchor key - */ -struct ta_key { - /** next in list */ - struct ta_key* next; - /** rdata, in wireformat of the key RR. starts with rdlength. */ - uint8_t* data; - /** length of the rdata (including rdlength). */ - size_t len; - /** DNS type (host format) of the key, DS or DNSKEY */ - uint16_t type; -}; - -/** - * A trust anchor in the trust anchor store. - * Unique by name, class. - */ -struct trust_anchor { - /** rbtree node, key is this structure */ - rbnode_type node; - /** lock on the entire anchor and its keys; for autotrust changes */ - lock_basic_type lock; - /** 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 */ - struct trust_anchor* parent; - /** - * List of DS or DNSKEY rrs that form the trust anchor. - */ - struct ta_key* keylist; - /** Autotrust anchor point data, or NULL */ - struct autr_point_data* autr; - /** number of DSs in the keylist */ - size_t numDS; - /** number of DNSKEYs in the keylist */ - size_t numDNSKEY; - /** the DS RRset */ - struct ub_packed_rrset_key* ds_rrset; - /** The DNSKEY RRset */ - struct ub_packed_rrset_key* dnskey_rrset; - /** class of the trust anchor */ - uint16_t dclass; -}; - -/** - * Create trust anchor storage - * @return new storage or NULL on error. - */ -struct val_anchors* anchors_create(void); - -/** - * Delete trust anchor storage. - * @param anchors: to delete. - */ -void anchors_delete(struct val_anchors* anchors); - -/** - * Process trust anchor config. - * @param anchors: struct anchor storage - * @param cfg: config options. - * @return 0 on error. - */ -int anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg); - -/** - * Recalculate parent pointers. The caller must hold the lock on the - * anchors structure (say after removing an item from the rbtree). - * Caller must not hold any locks on trust anchors. - * After the call is complete the parent pointers are updated and an item - * just removed is no longer referenced in parent pointers. - * @param anchors: the structure to update. - */ -void anchors_init_parents_locked(struct val_anchors* anchors); - -/** - * Given a qname/qclass combination, find the trust anchor closest above it. - * Or return NULL if none exists. - * - * @param anchors: struct anchor storage - * @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. The anchor is locked. - */ -struct trust_anchor* anchors_lookup(struct val_anchors* anchors, - uint8_t* qname, size_t qname_len, uint16_t qclass); - -/** - * 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. The anchor is locked. - */ -struct trust_anchor* anchor_find(struct val_anchors* anchors, - uint8_t* name, int namelabs, size_t namelen, uint16_t dclass); - -/** - * Store one string as trust anchor RR. - * @param anchors: anchor storage. - * @param buffer: parsing buffer, to generate the RR wireformat in. - * @param str: string. - * @return NULL on error. - */ -struct trust_anchor* anchor_store_str(struct val_anchors* anchors, - struct sldns_buffer* buffer, const char* str); - -/** - * Get memory in use by the trust anchor storage - * @param anchors: anchor storage. - * @return memory in use in bytes. - */ -size_t anchors_get_mem(struct val_anchors* anchors); - -/** compare two trust anchors */ -int anchor_cmp(const void* k1, const void* k2); - -/** - * Add insecure point trust anchor. For external use (locks and init_parents) - * @param anchors: anchor storage. - * @param c: class. - * @param nm: name of insecure trust point. - * @return false on alloc failure. - */ -int anchors_add_insecure(struct val_anchors* anchors, uint16_t c, uint8_t* nm); - -/** - * Delete insecure point trust anchor. Does not remove if no such point. - * For external use (locks and init_parents) - * @param anchors: anchor storage. - * @param c: class. - * @param nm: name of insecure trust point. - */ -void anchors_delete_insecure(struct val_anchors* anchors, uint16_t c, - uint8_t* nm); - -/** - * Get a list of keytags for the trust anchor. Zero tags for insecure points. - * @param ta: trust anchor (locked by caller). - * @param list: array of uint16_t. - * @param num: length of array. - * @return number of keytags filled into array. If total number of keytags is - * bigger than the array, it is truncated at num. On errors, less keytags - * are filled in. The array is sorted. - */ -size_t anchor_list_keytags(struct trust_anchor* ta, uint16_t* list, size_t num); - -#endif /* VALIDATOR_VAL_ANCHOR_H */ diff --git a/external/unbound/validator/val_kcache.c b/external/unbound/validator/val_kcache.c deleted file mode 100644 index 22070cc6a..000000000 --- a/external/unbound/validator/val_kcache.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * validator/val_kcache.c - validator key shared cache with validated keys - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains functions for dealing with the validator key cache. - */ -#include "config.h" -#include "validator/val_kcache.h" -#include "validator/val_kentry.h" -#include "util/log.h" -#include "util/config_file.h" -#include "util/data/dname.h" -#include "util/module.h" - -struct key_cache* -key_cache_create(struct config_file* cfg) -{ - struct key_cache* kcache = (struct key_cache*)calloc(1, - sizeof(*kcache)); - size_t numtables, start_size, maxmem; - if(!kcache) { - log_err("malloc failure"); - return NULL; - } - numtables = cfg->key_cache_slabs; - start_size = HASH_DEFAULT_STARTARRAY; - maxmem = cfg->key_cache_size; - kcache->slab = slabhash_create(numtables, start_size, maxmem, - &key_entry_sizefunc, &key_entry_compfunc, - &key_entry_delkeyfunc, &key_entry_deldatafunc, NULL); - if(!kcache->slab) { - log_err("malloc failure"); - free(kcache); - return NULL; - } - return kcache; -} - -void -key_cache_delete(struct key_cache* kcache) -{ - if(!kcache) - return; - slabhash_delete(kcache->slab); - free(kcache); -} - -void -key_cache_insert(struct key_cache* kcache, struct key_entry_key* kkey, - struct module_qstate* qstate) -{ - struct key_entry_key* k = key_entry_copy(kkey); - if(!k) - return; - if(key_entry_isbad(k) && qstate->errinf && - qstate->env->cfg->val_log_level >= 2) { - /* on malloc failure there is simply no reason string */ - key_entry_set_reason(k, errinf_to_str(qstate)); - } - key_entry_hash(k); - slabhash_insert(kcache->slab, k->entry.hash, &k->entry, - k->entry.data, NULL); -} - -/** - * Lookup exactly in the key cache. Returns pointer to locked entry. - * Caller must unlock it after use. - * @param kcache: the key cache. - * @param name: for what name to look; uncompressed wireformat - * @param namelen: length of the name. - * @param key_class: class of the key. - * @param wr: set true to get a writelock. - * @return key entry, locked, or NULL if not found. No TTL checking is - * performed. - */ -static struct key_entry_key* -key_cache_search(struct key_cache* kcache, uint8_t* name, size_t namelen, - uint16_t key_class, int wr) -{ - struct lruhash_entry* e; - struct key_entry_key lookfor; - lookfor.entry.key = &lookfor; - lookfor.name = name; - lookfor.namelen = namelen; - lookfor.key_class = key_class; - key_entry_hash(&lookfor); - e = slabhash_lookup(kcache->slab, lookfor.entry.hash, &lookfor, wr); - if(!e) - return NULL; - return (struct key_entry_key*)e->key; -} - -struct key_entry_key* -key_cache_obtain(struct key_cache* kcache, uint8_t* name, size_t namelen, - uint16_t key_class, struct regional* region, time_t now) -{ - /* keep looking until we find a nonexpired entry */ - while(1) { - struct key_entry_key* k = key_cache_search(kcache, name, - namelen, key_class, 0); - if(k) { - /* see if TTL is OK */ - struct key_entry_data* d = (struct key_entry_data*) - k->entry.data; - if(now <= d->ttl) { - /* copy and return it */ - struct key_entry_key* retkey = - key_entry_copy_toregion(k, region); - lock_rw_unlock(&k->entry.lock); - return retkey; - } - lock_rw_unlock(&k->entry.lock); - } - /* snip off first label to continue */ - if(dname_is_root(name)) - break; - dname_remove_label(&name, &namelen); - } - return NULL; -} - -size_t -key_cache_get_mem(struct key_cache* kcache) -{ - return sizeof(*kcache) + slabhash_get_mem(kcache->slab); -} - -void key_cache_remove(struct key_cache* kcache, - uint8_t* name, size_t namelen, uint16_t key_class) -{ - struct key_entry_key lookfor; - lookfor.entry.key = &lookfor; - lookfor.name = name; - lookfor.namelen = namelen; - lookfor.key_class = key_class; - key_entry_hash(&lookfor); - slabhash_remove(kcache->slab, lookfor.entry.hash, &lookfor); -} diff --git a/external/unbound/validator/val_kcache.h b/external/unbound/validator/val_kcache.h deleted file mode 100644 index 76c9dd094..000000000 --- a/external/unbound/validator/val_kcache.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * validator/val_kcache.h - validator key shared cache with validated keys - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains functions for caching validated key entries. - */ - -#ifndef VALIDATOR_VAL_KCACHE_H -#define VALIDATOR_VAL_KCACHE_H -#include "util/storage/slabhash.h" -struct key_entry_key; -struct key_entry_data; -struct config_file; -struct regional; -struct module_qstate; - -/** - * Key cache - */ -struct key_cache { - /** uses slabhash for storage, type key_entry_key, key_entry_data */ - struct slabhash* slab; -}; - -/** - * Create the key cache - * @param cfg: config settings for the key cache. - * @return new key cache or NULL on malloc failure. - */ -struct key_cache* key_cache_create(struct config_file* cfg); - -/** - * Delete the key cache - * @param kcache: to delete - */ -void key_cache_delete(struct key_cache* kcache); - -/** - * Insert or update a key cache entry. Note that the insert may silently - * fail if there is not enough memory. - * - * @param kcache: the key cache. - * @param kkey: key entry key, assumed malloced in a region, is copied - * to perform update or insertion. Its data pointer is also copied. - * @param qstate: store errinf reason in case its bad. - */ -void key_cache_insert(struct key_cache* kcache, struct key_entry_key* kkey, - struct module_qstate* qstate); - -/** - * Remove an entry from the key cache. - * @param kcache: the key cache. - * @param name: for what name to look; uncompressed wireformat - * @param namelen: length of the name. - * @param key_class: class of the key. - */ -void key_cache_remove(struct key_cache* kcache, - uint8_t* name, size_t namelen, uint16_t key_class); - -/** - * Lookup key entry in the cache. Looks up the closest key entry above the - * given name. - * @param kcache: the key cache. - * @param name: for what name to look; uncompressed wireformat - * @param namelen: length of the name. - * @param key_class: class of the key. - * @param region: a copy of the key_entry is allocated in this region. - * @param now: current time. - * @return pointer to a newly allocated key_entry copy in the region, if - * a key entry could be found, and allocation succeeded and TTL was OK. - * Otherwise, NULL is returned. - */ -struct key_entry_key* key_cache_obtain(struct key_cache* kcache, - uint8_t* name, size_t namelen, uint16_t key_class, - struct regional* region, time_t now); - -/** - * Get memory in use by the key cache. - * @param kcache: the key cache. - * @return memory in use in bytes. - */ -size_t key_cache_get_mem(struct key_cache* kcache); - -#endif /* VALIDATOR_VAL_KCACHE_H */ diff --git a/external/unbound/validator/val_kentry.c b/external/unbound/validator/val_kentry.c deleted file mode 100644 index 93fe2145e..000000000 --- a/external/unbound/validator/val_kentry.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * validator/val_kentry.c - validator key entry definition. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains functions for dealing with validator key entries. - */ -#include "config.h" -#include "validator/val_kentry.h" -#include "util/data/packed_rrset.h" -#include "util/data/dname.h" -#include "util/storage/lookup3.h" -#include "util/regional.h" -#include "util/net_help.h" -#include "sldns/rrdef.h" -#include "sldns/keyraw.h" - -size_t -key_entry_sizefunc(void* key, void* data) -{ - struct key_entry_key* kk = (struct key_entry_key*)key; - struct key_entry_data* kd = (struct key_entry_data*)data; - size_t s = sizeof(*kk) + kk->namelen; - s += sizeof(*kd) + lock_get_mem(&kk->entry.lock); - if(kd->rrset_data) - s += packed_rrset_sizeof(kd->rrset_data); - if(kd->reason) - s += strlen(kd->reason)+1; - if(kd->algo) - s += strlen((char*)kd->algo)+1; - return s; -} - -int -key_entry_compfunc(void* k1, void* k2) -{ - struct key_entry_key* n1 = (struct key_entry_key*)k1; - struct key_entry_key* n2 = (struct key_entry_key*)k2; - if(n1->key_class != n2->key_class) { - if(n1->key_class < n2->key_class) - return -1; - return 1; - } - return query_dname_compare(n1->name, n2->name); -} - -void -key_entry_delkeyfunc(void* key, void* ATTR_UNUSED(userarg)) -{ - struct key_entry_key* kk = (struct key_entry_key*)key; - if(!key) - return; - lock_rw_destroy(&kk->entry.lock); - free(kk->name); - free(kk); -} - -void -key_entry_deldatafunc(void* data, void* ATTR_UNUSED(userarg)) -{ - struct key_entry_data* kd = (struct key_entry_data*)data; - free(kd->reason); - free(kd->rrset_data); - free(kd->algo); - free(kd); -} - -void -key_entry_hash(struct key_entry_key* kk) -{ - kk->entry.hash = 0x654; - kk->entry.hash = hashlittle(&kk->key_class, sizeof(kk->key_class), - kk->entry.hash); - kk->entry.hash = dname_query_hash(kk->name, kk->entry.hash); -} - -struct key_entry_key* -key_entry_copy_toregion(struct key_entry_key* kkey, struct regional* region) -{ - struct key_entry_key* newk; - newk = regional_alloc_init(region, kkey, sizeof(*kkey)); - if(!newk) - return NULL; - newk->name = regional_alloc_init(region, kkey->name, kkey->namelen); - if(!newk->name) - return NULL; - newk->entry.key = newk; - if(newk->entry.data) { - /* copy data element */ - struct key_entry_data *d = (struct key_entry_data*) - kkey->entry.data; - struct key_entry_data *newd; - newd = regional_alloc_init(region, d, sizeof(*d)); - if(!newd) - return NULL; - /* copy rrset */ - if(d->rrset_data) { - newd->rrset_data = regional_alloc_init(region, - d->rrset_data, - packed_rrset_sizeof(d->rrset_data)); - if(!newd->rrset_data) - return NULL; - packed_rrset_ptr_fixup(newd->rrset_data); - } - if(d->reason) { - newd->reason = regional_strdup(region, d->reason); - if(!newd->reason) - return NULL; - } - if(d->algo) { - newd->algo = (uint8_t*)regional_strdup(region, - (char*)d->algo); - if(!newd->algo) - return NULL; - } - newk->entry.data = newd; - } - return newk; -} - -struct key_entry_key* -key_entry_copy(struct key_entry_key* kkey) -{ - struct key_entry_key* newk; - if(!kkey) - return NULL; - newk = memdup(kkey, sizeof(*kkey)); - if(!newk) - return NULL; - newk->name = memdup(kkey->name, kkey->namelen); - if(!newk->name) { - free(newk); - return NULL; - } - lock_rw_init(&newk->entry.lock); - newk->entry.key = newk; - if(newk->entry.data) { - /* copy data element */ - struct key_entry_data *d = (struct key_entry_data*) - kkey->entry.data; - struct key_entry_data *newd; - newd = memdup(d, sizeof(*d)); - if(!newd) { - free(newk->name); - free(newk); - return NULL; - } - /* copy rrset */ - if(d->rrset_data) { - newd->rrset_data = memdup(d->rrset_data, - packed_rrset_sizeof(d->rrset_data)); - if(!newd->rrset_data) { - free(newd); - free(newk->name); - free(newk); - return NULL; - } - packed_rrset_ptr_fixup(newd->rrset_data); - } - if(d->reason) { - newd->reason = strdup(d->reason); - if(!newd->reason) { - free(newd->rrset_data); - free(newd); - free(newk->name); - free(newk); - return NULL; - } - } - if(d->algo) { - newd->algo = (uint8_t*)strdup((char*)d->algo); - if(!newd->algo) { - free(newd->rrset_data); - free(newd->reason); - free(newd); - free(newk->name); - free(newk); - return NULL; - } - } - newk->entry.data = newd; - } - return newk; -} - -int -key_entry_isnull(struct key_entry_key* kkey) -{ - struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data; - return (!d->isbad && d->rrset_data == NULL); -} - -int -key_entry_isgood(struct key_entry_key* kkey) -{ - struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data; - return (!d->isbad && d->rrset_data != NULL); -} - -int -key_entry_isbad(struct key_entry_key* kkey) -{ - struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data; - return (int)(d->isbad); -} - -void -key_entry_set_reason(struct key_entry_key* kkey, char* reason) -{ - struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data; - d->reason = reason; -} - -char* -key_entry_get_reason(struct key_entry_key* kkey) -{ - struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data; - return d->reason; -} - -/** setup key entry in region */ -static int -key_entry_setup(struct regional* region, - uint8_t* name, size_t namelen, uint16_t dclass, - struct key_entry_key** k, struct key_entry_data** d) -{ - *k = regional_alloc(region, sizeof(**k)); - if(!*k) - return 0; - memset(*k, 0, sizeof(**k)); - (*k)->entry.key = *k; - (*k)->name = regional_alloc_init(region, name, namelen); - if(!(*k)->name) - return 0; - (*k)->namelen = namelen; - (*k)->key_class = dclass; - *d = regional_alloc(region, sizeof(**d)); - if(!*d) - return 0; - (*k)->entry.data = *d; - return 1; -} - -struct key_entry_key* -key_entry_create_null(struct regional* region, - uint8_t* name, size_t namelen, uint16_t dclass, time_t ttl, - time_t now) -{ - struct key_entry_key* k; - struct key_entry_data* d; - if(!key_entry_setup(region, name, namelen, dclass, &k, &d)) - return NULL; - d->ttl = now + ttl; - d->isbad = 0; - d->reason = NULL; - d->rrset_type = LDNS_RR_TYPE_DNSKEY; - d->rrset_data = NULL; - d->algo = NULL; - return k; -} - -struct key_entry_key* -key_entry_create_rrset(struct regional* region, - uint8_t* name, size_t namelen, uint16_t dclass, - struct ub_packed_rrset_key* rrset, uint8_t* sigalg, time_t now) -{ - struct key_entry_key* k; - struct key_entry_data* d; - struct packed_rrset_data* rd = (struct packed_rrset_data*) - rrset->entry.data; - if(!key_entry_setup(region, name, namelen, dclass, &k, &d)) - return NULL; - d->ttl = rd->ttl + now; - d->isbad = 0; - d->reason = NULL; - d->rrset_type = ntohs(rrset->rk.type); - d->rrset_data = (struct packed_rrset_data*)regional_alloc_init(region, - rd, packed_rrset_sizeof(rd)); - if(!d->rrset_data) - return NULL; - if(sigalg) { - d->algo = (uint8_t*)regional_strdup(region, (char*)sigalg); - if(!d->algo) - return NULL; - } else d->algo = NULL; - packed_rrset_ptr_fixup(d->rrset_data); - return k; -} - -struct key_entry_key* -key_entry_create_bad(struct regional* region, - uint8_t* name, size_t namelen, uint16_t dclass, time_t ttl, - time_t now) -{ - struct key_entry_key* k; - struct key_entry_data* d; - if(!key_entry_setup(region, name, namelen, dclass, &k, &d)) - return NULL; - d->ttl = now + ttl; - d->isbad = 1; - d->reason = NULL; - d->rrset_type = LDNS_RR_TYPE_DNSKEY; - d->rrset_data = NULL; - d->algo = NULL; - return k; -} - -struct ub_packed_rrset_key* -key_entry_get_rrset(struct key_entry_key* kkey, struct regional* region) -{ - struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data; - struct ub_packed_rrset_key* rrk; - struct packed_rrset_data* rrd; - if(!d || !d->rrset_data) - return NULL; - rrk = regional_alloc(region, sizeof(*rrk)); - if(!rrk) - return NULL; - memset(rrk, 0, sizeof(*rrk)); - rrk->rk.dname = regional_alloc_init(region, kkey->name, kkey->namelen); - if(!rrk->rk.dname) - return NULL; - rrk->rk.dname_len = kkey->namelen; - rrk->rk.type = htons(d->rrset_type); - rrk->rk.rrset_class = htons(kkey->key_class); - rrk->entry.key = rrk; - rrd = regional_alloc_init(region, d->rrset_data, - packed_rrset_sizeof(d->rrset_data)); - if(!rrd) - return NULL; - rrk->entry.data = rrd; - 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 sldns_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; -} diff --git a/external/unbound/validator/val_kentry.h b/external/unbound/validator/val_kentry.h deleted file mode 100644 index ade65571a..000000000 --- a/external/unbound/validator/val_kentry.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * validator/val_kentry.h - validator key entry definition. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains functions for dealing with validator key entries. - */ - -#ifndef VALIDATOR_VAL_KENTRY_H -#define VALIDATOR_VAL_KENTRY_H -struct packed_rrset_data; -struct regional; -struct ub_packed_rrset_key; -#include "util/storage/lruhash.h" - -/** - * A key entry for the validator. - * This may or may not be a trusted key. - * This is what is stored in the key cache. - * This is the key part for the cache; the key entry key. - */ -struct key_entry_key { - /** lru hash entry */ - struct lruhash_entry entry; - /** name of the key */ - uint8_t* name; - /** length of name */ - size_t namelen; - /** class of the key, host byteorder */ - uint16_t key_class; -}; - -/** - * Key entry for the validator. - * Contains key status. - * This is the data part for the cache, the key entry data. - * - * Can be in three basic states: - * isbad=0: good key - * isbad=1: bad key - * isbad=0 && rrset=0: insecure space. - */ -struct key_entry_data { - /** the TTL of this entry (absolute time) */ - time_t ttl; - /** the key rrdata. can be NULL to signal keyless name. */ - struct packed_rrset_data* rrset_data; - /** not NULL sometimes to give reason why bogus */ - char* reason; - /** list of algorithms signalled, ends with 0, or NULL */ - uint8_t* algo; - /** DNS RR type of the rrset data (host order) */ - uint16_t rrset_type; - /** if the key is bad: Bogus or malformed */ - uint8_t isbad; -}; - -/** function for lruhash operation */ -size_t key_entry_sizefunc(void* key, void* data); - -/** function for lruhash operation */ -int key_entry_compfunc(void* k1, void* k2); - -/** function for lruhash operation */ -void key_entry_delkeyfunc(void* key, void* userarg); - -/** function for lruhash operation */ -void key_entry_deldatafunc(void* data, void* userarg); - -/** calculate hash for key entry - * @param kk: key entry. The lruhash entry.hash value is filled in. - */ -void key_entry_hash(struct key_entry_key* kk); - -/** - * Copy a key entry, to be region-allocated. - * @param kkey: the key entry key (and data pointer) to copy. - * @param region: where to allocate it - * @return newly region-allocated entry or NULL on a failure to allocate. - */ -struct key_entry_key* key_entry_copy_toregion(struct key_entry_key* kkey, - struct regional* region); - -/** - * Copy a key entry, malloced. - * @param kkey: the key entry key (and data pointer) to copy. - * @return newly allocated entry or NULL on a failure to allocate memory. - */ -struct key_entry_key* key_entry_copy(struct key_entry_key* kkey); - -/** - * See if this is a null entry. Does not do locking. - * @param kkey: must have data pointer set correctly - * @return true if it is a NULL rrset entry. - */ -int key_entry_isnull(struct key_entry_key* kkey); - -/** - * See if this entry is good. Does not do locking. - * @param kkey: must have data pointer set correctly - * @return true if it is good. - */ -int key_entry_isgood(struct key_entry_key* kkey); - -/** - * See if this entry is bad. Does not do locking. - * @param kkey: must have data pointer set correctly - * @return true if it is bad. - */ -int key_entry_isbad(struct key_entry_key* kkey); - -/** - * Set reason why a key is bad. - * @param kkey: bad key. - * @param reason: string to attach, you must allocate it. - * Not safe to call twice unless you deallocate it yourself. - */ -void key_entry_set_reason(struct key_entry_key* kkey, char* reason); - -/** - * Get reason why a key is bad. - * @param kkey: bad key - * @return pointer to string. - * String is part of key entry and is deleted with it. - */ -char* key_entry_get_reason(struct key_entry_key* kkey); - -/** - * Create a null entry, in the given region. - * @param region: where to allocate - * @param name: the key name - * @param namelen: length of name - * @param dclass: class of key entry. (host order); - * @param ttl: what ttl should the key have. relative. - * @param now: current time (added to ttl). - * @return new key entry or NULL on alloc failure - */ -struct key_entry_key* key_entry_create_null(struct regional* region, - uint8_t* name, size_t namelen, uint16_t dclass, time_t ttl, - time_t now); - -/** - * Create a key entry from an rrset, in the given region. - * @param region: where to allocate. - * @param name: the key name - * @param namelen: length of name - * @param dclass: class of key entry. (host order); - * @param rrset: data for key entry. This is copied to the region. - * @param sigalg: signalled algorithm list (or NULL). - * @param now: current time (added to ttl of rrset) - * @return new key entry or NULL on alloc failure - */ -struct key_entry_key* key_entry_create_rrset(struct regional* region, - uint8_t* name, size_t namelen, uint16_t dclass, - struct ub_packed_rrset_key* rrset, uint8_t* sigalg, time_t now); - -/** - * Create a bad entry, in the given region. - * @param region: where to allocate - * @param name: the key name - * @param namelen: length of name - * @param dclass: class of key entry. (host order); - * @param ttl: what ttl should the key have. relative. - * @param now: current time (added to ttl). - * @return new key entry or NULL on alloc failure - */ -struct key_entry_key* key_entry_create_bad(struct regional* region, - uint8_t* name, size_t namelen, uint16_t dclass, time_t ttl, - time_t now); - -/** - * Obtain rrset from a key entry, allocated in region. - * @param kkey: key entry to convert to a rrset. - * @param region: where to allocate rrset - * @return rrset copy; if no rrset or alloc error returns NULL. - */ -struct ub_packed_rrset_key* key_entry_get_rrset(struct key_entry_key* kkey, - struct regional* 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 */ diff --git a/external/unbound/validator/val_neg.c b/external/unbound/validator/val_neg.c deleted file mode 100644 index fe57ac2c4..000000000 --- a/external/unbound/validator/val_neg.c +++ /dev/null @@ -1,1470 +0,0 @@ -/* - * validator/val_neg.c - validator aggressive negative caching functions. - * - * Copyright (c) 2008, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - * The functions help with aggressive negative caching. - * This creates new denials of existence, and proofs for absence of types - * from cached NSEC records. - */ -#include "config.h" -#ifdef HAVE_OPENSSL_SSL_H -#include "openssl/ssl.h" -#define NSEC3_SHA_LEN SHA_DIGEST_LENGTH -#else -#define NSEC3_SHA_LEN 20 -#endif -#include "validator/val_neg.h" -#include "validator/val_nsec.h" -#include "validator/val_nsec3.h" -#include "validator/val_utils.h" -#include "util/data/dname.h" -#include "util/data/msgreply.h" -#include "util/log.h" -#include "util/net_help.h" -#include "util/config_file.h" -#include "services/cache/rrset.h" -#include "services/cache/dns.h" -#include "sldns/rrdef.h" -#include "sldns/sbuffer.h" - -int val_neg_data_compare(const void* a, const void* b) -{ - struct val_neg_data* x = (struct val_neg_data*)a; - struct val_neg_data* y = (struct val_neg_data*)b; - int m; - return dname_canon_lab_cmp(x->name, x->labs, y->name, y->labs, &m); -} - -int val_neg_zone_compare(const void* a, const void* b) -{ - struct val_neg_zone* x = (struct val_neg_zone*)a; - struct val_neg_zone* y = (struct val_neg_zone*)b; - int m; - if(x->dclass != y->dclass) { - if(x->dclass < y->dclass) - return -1; - return 1; - } - return dname_canon_lab_cmp(x->name, x->labs, y->name, y->labs, &m); -} - -struct val_neg_cache* val_neg_create(struct config_file* cfg, size_t maxiter) -{ - struct val_neg_cache* neg = (struct val_neg_cache*)calloc(1, - sizeof(*neg)); - if(!neg) { - log_err("Could not create neg cache: out of memory"); - return NULL; - } - neg->nsec3_max_iter = maxiter; - neg->max = 1024*1024; /* 1 M is thousands of entries */ - if(cfg) neg->max = cfg->neg_cache_size; - rbtree_init(&neg->tree, &val_neg_zone_compare); - lock_basic_init(&neg->lock); - lock_protect(&neg->lock, neg, sizeof(*neg)); - return neg; -} - -size_t val_neg_get_mem(struct val_neg_cache* neg) -{ - size_t result; - lock_basic_lock(&neg->lock); - result = sizeof(*neg) + neg->use; - lock_basic_unlock(&neg->lock); - return result; -} - -/** clear datas on cache deletion */ -static void -neg_clear_datas(rbnode_type* n, void* ATTR_UNUSED(arg)) -{ - struct val_neg_data* d = (struct val_neg_data*)n; - free(d->name); - free(d); -} - -/** clear zones on cache deletion */ -static void -neg_clear_zones(rbnode_type* n, void* ATTR_UNUSED(arg)) -{ - struct val_neg_zone* z = (struct val_neg_zone*)n; - /* delete all the rrset entries in the tree */ - traverse_postorder(&z->tree, &neg_clear_datas, NULL); - free(z->nsec3_salt); - free(z->name); - free(z); -} - -void neg_cache_delete(struct val_neg_cache* neg) -{ - if(!neg) return; - lock_basic_destroy(&neg->lock); - /* delete all the zones in the tree */ - traverse_postorder(&neg->tree, &neg_clear_zones, NULL); - free(neg); -} - -/** - * Put data element at the front of the LRU list. - * @param neg: negative cache with LRU start and end. - * @param data: this data is fronted. - */ -static void neg_lru_front(struct val_neg_cache* neg, - struct val_neg_data* data) -{ - data->prev = NULL; - data->next = neg->first; - if(!neg->first) - neg->last = data; - else neg->first->prev = data; - neg->first = data; -} - -/** - * Remove data element from LRU list. - * @param neg: negative cache with LRU start and end. - * @param data: this data is removed from the list. - */ -static void neg_lru_remove(struct val_neg_cache* neg, - struct val_neg_data* data) -{ - if(data->prev) - data->prev->next = data->next; - else neg->first = data->next; - if(data->next) - data->next->prev = data->prev; - else neg->last = data->prev; -} - -/** - * Touch LRU for data element, put it at the start of the LRU list. - * @param neg: negative cache with LRU start and end. - * @param data: this data is used. - */ -static void neg_lru_touch(struct val_neg_cache* neg, - struct val_neg_data* data) -{ - if(data == neg->first) - return; /* nothing to do */ - /* remove from current lru position */ - neg_lru_remove(neg, data); - /* add at front */ - neg_lru_front(neg, data); -} - -/** - * Delete a zone element from the negative cache. - * May delete other zone elements to keep tree coherent, or - * only mark the element as 'not in use'. - * @param neg: negative cache. - * @param z: zone element to delete. - */ -static void neg_delete_zone(struct val_neg_cache* neg, struct val_neg_zone* z) -{ - struct val_neg_zone* p, *np; - if(!z) return; - log_assert(z->in_use); - log_assert(z->count > 0); - z->in_use = 0; - - /* go up the tree and reduce counts */ - p = z; - while(p) { - log_assert(p->count > 0); - p->count --; - p = p->parent; - } - - /* remove zones with zero count */ - p = z; - while(p && p->count == 0) { - np = p->parent; - (void)rbtree_delete(&neg->tree, &p->node); - neg->use -= p->len + sizeof(*p); - free(p->nsec3_salt); - free(p->name); - free(p); - p = np; - } -} - -void neg_delete_data(struct val_neg_cache* neg, struct val_neg_data* el) -{ - struct val_neg_zone* z; - struct val_neg_data* p, *np; - if(!el) return; - z = el->zone; - log_assert(el->in_use); - log_assert(el->count > 0); - el->in_use = 0; - - /* remove it from the lru list */ - neg_lru_remove(neg, el); - - /* go up the tree and reduce counts */ - p = el; - while(p) { - log_assert(p->count > 0); - p->count --; - p = p->parent; - } - - /* delete 0 count items from tree */ - p = el; - while(p && p->count == 0) { - np = p->parent; - (void)rbtree_delete(&z->tree, &p->node); - neg->use -= p->len + sizeof(*p); - free(p->name); - free(p); - p = np; - } - - /* check if the zone is now unused */ - if(z->tree.count == 0) { - neg_delete_zone(neg, z); - } -} - -/** - * Create more space in negative cache - * The oldest elements are deleted until enough space is present. - * Empty zones are deleted. - * @param neg: negative cache. - * @param need: how many bytes are needed. - */ -static void neg_make_space(struct val_neg_cache* neg, size_t need) -{ - /* delete elements until enough space or its empty */ - while(neg->last && neg->max < neg->use + need) { - neg_delete_data(neg, neg->last); - } -} - -struct val_neg_zone* neg_find_zone(struct val_neg_cache* neg, - uint8_t* nm, size_t len, uint16_t dclass) -{ - struct val_neg_zone lookfor; - struct val_neg_zone* result; - lookfor.node.key = &lookfor; - lookfor.name = nm; - lookfor.len = len; - lookfor.labs = dname_count_labels(lookfor.name); - lookfor.dclass = dclass; - - result = (struct val_neg_zone*) - rbtree_search(&neg->tree, lookfor.node.key); - return result; -} - -/** - * Find the given data - * @param zone: negative zone - * @param nm: what to look for. - * @param len: length of nm - * @param labs: labels in nm - * @return data or NULL if not found. - */ -static struct val_neg_data* neg_find_data(struct val_neg_zone* zone, - uint8_t* nm, size_t len, int labs) -{ - struct val_neg_data lookfor; - struct val_neg_data* result; - lookfor.node.key = &lookfor; - lookfor.name = nm; - lookfor.len = len; - lookfor.labs = labs; - - result = (struct val_neg_data*) - rbtree_search(&zone->tree, lookfor.node.key); - return result; -} - -/** - * Calculate space needed for the data and all its parents - * @param rep: NSEC entries. - * @return size. - */ -static size_t calc_data_need(struct reply_info* rep) -{ - uint8_t* d; - size_t i, len, res = 0; - - for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) { - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC) { - d = rep->rrsets[i]->rk.dname; - len = rep->rrsets[i]->rk.dname_len; - res = sizeof(struct val_neg_data) + len; - while(!dname_is_root(d)) { - log_assert(len > 1); /* not root label */ - dname_remove_label(&d, &len); - res += sizeof(struct val_neg_data) + len; - } - } - } - return res; -} - -/** - * Calculate space needed for zone and all its parents - * @param d: name of zone - * @param len: length of name - * @return size. - */ -static size_t calc_zone_need(uint8_t* d, size_t len) -{ - size_t res = sizeof(struct val_neg_zone) + len; - while(!dname_is_root(d)) { - log_assert(len > 1); /* not root label */ - dname_remove_label(&d, &len); - res += sizeof(struct val_neg_zone) + len; - } - return res; -} - -/** - * Find closest existing parent zone of the given name. - * @param neg: negative cache. - * @param nm: name to look for - * @param nm_len: length of nm - * @param labs: labelcount of nm. - * @param qclass: class. - * @return the zone or NULL if none found. - */ -static struct val_neg_zone* neg_closest_zone_parent(struct val_neg_cache* neg, - uint8_t* nm, size_t nm_len, int labs, uint16_t qclass) -{ - struct val_neg_zone key; - struct val_neg_zone* result; - rbnode_type* res = NULL; - key.node.key = &key; - key.name = nm; - key.len = nm_len; - key.labs = labs; - key.dclass = qclass; - if(rbtree_find_less_equal(&neg->tree, &key, &res)) { - /* exact match */ - result = (struct val_neg_zone*)res; - } else { - /* smaller element (or no element) */ - int m; - result = (struct val_neg_zone*)res; - if(!result || result->dclass != qclass) - return NULL; - /* count number of labels matched */ - (void)dname_lab_cmp(result->name, result->labs, key.name, - key.labs, &m); - while(result) { /* go up until qname is subdomain of stub */ - if(result->labs <= m) - break; - result = result->parent; - } - } - return result; -} - -/** - * Find closest existing parent data for the given name. - * @param zone: to look in. - * @param nm: name to look for - * @param nm_len: length of nm - * @param labs: labelcount of nm. - * @return the data or NULL if none found. - */ -static struct val_neg_data* neg_closest_data_parent( - struct val_neg_zone* zone, uint8_t* nm, size_t nm_len, int labs) -{ - struct val_neg_data key; - struct val_neg_data* result; - rbnode_type* res = NULL; - key.node.key = &key; - key.name = nm; - key.len = nm_len; - key.labs = labs; - if(rbtree_find_less_equal(&zone->tree, &key, &res)) { - /* exact match */ - result = (struct val_neg_data*)res; - } else { - /* smaller element (or no element) */ - int m; - result = (struct val_neg_data*)res; - if(!result) - return NULL; - /* count number of labels matched */ - (void)dname_lab_cmp(result->name, result->labs, key.name, - key.labs, &m); - while(result) { /* go up until qname is subdomain of stub */ - if(result->labs <= m) - break; - result = result->parent; - } - } - return result; -} - -/** - * Create a single zone node - * @param nm: name for zone (copied) - * @param nm_len: length of name - * @param labs: labels in name. - * @param dclass: class of zone, host order. - * @return new zone or NULL on failure - */ -static struct val_neg_zone* neg_setup_zone_node( - uint8_t* nm, size_t nm_len, int labs, uint16_t dclass) -{ - struct val_neg_zone* zone = - (struct val_neg_zone*)calloc(1, sizeof(*zone)); - if(!zone) { - return NULL; - } - zone->node.key = zone; - zone->name = memdup(nm, nm_len); - if(!zone->name) { - free(zone); - return NULL; - } - zone->len = nm_len; - zone->labs = labs; - zone->dclass = dclass; - - rbtree_init(&zone->tree, &val_neg_data_compare); - return zone; -} - -/** - * Create a linked list of parent zones, starting at longname ending on - * the parent (can be NULL, creates to the root). - * @param nm: name for lowest in chain - * @param nm_len: length of name - * @param labs: labels in name. - * @param dclass: class of zone. - * @param parent: NULL for to root, else so it fits under here. - * @return zone; a chain of zones and their parents up to the parent. - * or NULL on malloc failure - */ -static struct val_neg_zone* neg_zone_chain( - uint8_t* nm, size_t nm_len, int labs, uint16_t dclass, - struct val_neg_zone* parent) -{ - int i; - int tolabs = parent?parent->labs:0; - struct val_neg_zone* zone, *prev = NULL, *first = NULL; - - /* create the new subtree, i is labelcount of current creation */ - /* this creates a 'first' to z->parent=NULL list of zones */ - for(i=labs; i!=tolabs; i--) { - /* create new item */ - zone = neg_setup_zone_node(nm, nm_len, i, dclass); - if(!zone) { - /* need to delete other allocations in this routine!*/ - struct val_neg_zone* p=first, *np; - while(p) { - np = p->parent; - free(p->name); - free(p); - p = np; - } - return NULL; - } - if(i == labs) { - first = zone; - } else { - prev->parent = zone; - } - /* prepare for next name */ - prev = zone; - dname_remove_label(&nm, &nm_len); - } - return first; -} - -void val_neg_zone_take_inuse(struct val_neg_zone* zone) -{ - if(!zone->in_use) { - struct val_neg_zone* p; - zone->in_use = 1; - /* increase usage count of all parents */ - for(p=zone; p; p = p->parent) { - p->count++; - } - } -} - -struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg, - uint8_t* nm, size_t nm_len, uint16_t dclass) -{ - struct val_neg_zone* zone; - struct val_neg_zone* parent; - struct val_neg_zone* p, *np; - int labs = dname_count_labels(nm); - - /* find closest enclosing parent zone that (still) exists */ - parent = neg_closest_zone_parent(neg, nm, nm_len, labs, dclass); - if(parent && query_dname_compare(parent->name, nm) == 0) - return parent; /* already exists, weird */ - /* if parent exists, it is in use */ - log_assert(!parent || parent->count > 0); - zone = neg_zone_chain(nm, nm_len, labs, dclass, parent); - if(!zone) { - return NULL; - } - - /* insert the list of zones into the tree */ - p = zone; - while(p) { - np = p->parent; - /* mem use */ - neg->use += sizeof(struct val_neg_zone) + p->len; - /* insert in tree */ - (void)rbtree_insert(&neg->tree, &p->node); - /* last one needs proper parent pointer */ - if(np == NULL) - p->parent = parent; - p = np; - } - return zone; -} - -/** find zone name of message, returns the SOA record */ -static struct ub_packed_rrset_key* reply_find_soa(struct reply_info* rep) -{ - size_t i; - for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA) - return rep->rrsets[i]; - } - return NULL; -} - -/** see if the reply has NSEC records worthy of caching */ -static int reply_has_nsec(struct reply_info* rep) -{ - size_t i; - struct packed_rrset_data* d; - if(rep->security != sec_status_secure) - return 0; - for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC) { - d = (struct packed_rrset_data*)rep->rrsets[i]-> - entry.data; - if(d->security == sec_status_secure) - return 1; - } - } - return 0; -} - - -/** - * Create single node of data element. - * @param nm: name (copied) - * @param nm_len: length of name - * @param labs: labels in name. - * @return element with name nm, or NULL malloc failure. - */ -static struct val_neg_data* neg_setup_data_node( - uint8_t* nm, size_t nm_len, int labs) -{ - struct val_neg_data* el; - el = (struct val_neg_data*)calloc(1, sizeof(*el)); - if(!el) { - return NULL; - } - el->node.key = el; - el->name = memdup(nm, nm_len); - if(!el->name) { - free(el); - return NULL; - } - el->len = nm_len; - el->labs = labs; - return el; -} - -/** - * Create chain of data element and parents - * @param nm: name - * @param nm_len: length of name - * @param labs: labels in name. - * @param parent: up to where to make, if NULL up to root label. - * @return lowest element with name nm, or NULL malloc failure. - */ -static struct val_neg_data* neg_data_chain( - uint8_t* nm, size_t nm_len, int labs, struct val_neg_data* parent) -{ - int i; - int tolabs = parent?parent->labs:0; - struct val_neg_data* el, *first = NULL, *prev = NULL; - - /* create the new subtree, i is labelcount of current creation */ - /* this creates a 'first' to z->parent=NULL list of zones */ - for(i=labs; i!=tolabs; i--) { - /* create new item */ - el = neg_setup_data_node(nm, nm_len, i); - if(!el) { - /* need to delete other allocations in this routine!*/ - struct val_neg_data* p = first, *np; - while(p) { - np = p->parent; - free(p->name); - free(p); - p = np; - } - return NULL; - } - if(i == labs) { - first = el; - } else { - prev->parent = el; - } - - /* prepare for next name */ - prev = el; - dname_remove_label(&nm, &nm_len); - } - return first; -} - -/** - * Remove NSEC records between start and end points. - * By walking the tree, the tree is sorted canonically. - * @param neg: negative cache. - * @param zone: the zone - * @param el: element to start walking at. - * @param nsec: the nsec record with the end point - */ -static void wipeout(struct val_neg_cache* neg, struct val_neg_zone* zone, - struct val_neg_data* el, struct ub_packed_rrset_key* nsec) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> - entry.data; - uint8_t* end; - size_t end_len; - int end_labs, m; - rbnode_type* walk, *next; - struct val_neg_data* cur; - uint8_t buf[257]; - /* get endpoint */ - if(!d || d->count == 0 || d->rr_len[0] < 2+1) - return; - if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC) { - end = d->rr_data[0]+2; - end_len = dname_valid(end, d->rr_len[0]-2); - end_labs = dname_count_labels(end); - } else { - /* NSEC3 */ - if(!nsec3_get_nextowner_b32(nsec, 0, buf, sizeof(buf))) - return; - end = buf; - end_labs = dname_count_size_labels(end, &end_len); - } - - /* sanity check, both owner and end must be below the zone apex */ - if(!dname_subdomain_c(el->name, zone->name) || - !dname_subdomain_c(end, zone->name)) - return; - - /* detect end of zone NSEC ; wipe until the end of zone */ - if(query_dname_compare(end, zone->name) == 0) { - end = NULL; - } - - walk = rbtree_next(&el->node); - while(walk && walk != RBTREE_NULL) { - cur = (struct val_neg_data*)walk; - /* sanity check: must be larger than start */ - if(dname_canon_lab_cmp(cur->name, cur->labs, - el->name, el->labs, &m) <= 0) { - /* r == 0 skip original record. */ - /* r < 0 too small! */ - walk = rbtree_next(walk); - continue; - } - /* stop at endpoint, also data at empty nonterminals must be - * removed (no NSECs there) so everything between - * start and end */ - if(end && dname_canon_lab_cmp(cur->name, cur->labs, - end, end_labs, &m) >= 0) { - break; - } - /* this element has to be deleted, but we cannot do it - * now, because we are walking the tree still ... */ - /* get the next element: */ - next = rbtree_next(walk); - /* now delete the original element, this may trigger - * rbtree rebalances, but really, the next element is - * the one we need. - * But it may trigger delete of other data and the - * entire zone. However, if that happens, this is done - * by deleting the *parents* of the element for deletion, - * and maybe also the entire zone if it is empty. - * But parents are smaller in canonical compare, thus, - * if a larger element exists, then it is not a parent, - * it cannot get deleted, the zone cannot get empty. - * If the next==NULL, then zone can be empty. */ - if(cur->in_use) - neg_delete_data(neg, cur); - walk = next; - } -} - -void neg_insert_data(struct val_neg_cache* neg, - struct val_neg_zone* zone, struct ub_packed_rrset_key* nsec) -{ - struct packed_rrset_data* d; - struct val_neg_data* parent; - struct val_neg_data* el; - uint8_t* nm = nsec->rk.dname; - size_t nm_len = nsec->rk.dname_len; - int labs = dname_count_labels(nsec->rk.dname); - - d = (struct packed_rrset_data*)nsec->entry.data; - if( !(d->security == sec_status_secure || - (d->security == sec_status_unchecked && d->rrsig_count > 0))) - return; - log_nametypeclass(VERB_ALGO, "negcache rr", - nsec->rk.dname, ntohs(nsec->rk.type), - ntohs(nsec->rk.rrset_class)); - - /* find closest enclosing parent data that (still) exists */ - parent = neg_closest_data_parent(zone, nm, nm_len, labs); - if(parent && query_dname_compare(parent->name, nm) == 0) { - /* perfect match already exists */ - log_assert(parent->count > 0); - el = parent; - } else { - struct val_neg_data* p, *np; - - /* create subtree for perfect match */ - /* if parent exists, it is in use */ - log_assert(!parent || parent->count > 0); - - el = neg_data_chain(nm, nm_len, labs, parent); - if(!el) { - log_err("out of memory inserting NSEC negative cache"); - return; - } - el->in_use = 0; /* set on below */ - - /* insert the list of zones into the tree */ - p = el; - while(p) { - np = p->parent; - /* mem use */ - neg->use += sizeof(struct val_neg_data) + p->len; - /* insert in tree */ - p->zone = zone; - (void)rbtree_insert(&zone->tree, &p->node); - /* last one needs proper parent pointer */ - if(np == NULL) - p->parent = parent; - p = np; - } - } - - if(!el->in_use) { - struct val_neg_data* p; - - el->in_use = 1; - /* increase usage count of all parents */ - for(p=el; p; p = p->parent) { - p->count++; - } - - neg_lru_front(neg, el); - } else { - /* in use, bring to front, lru */ - neg_lru_touch(neg, el); - } - - /* if nsec3 store last used parameters */ - if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC3) { - int h; - uint8_t* s; - size_t slen, it; - if(nsec3_get_params(nsec, 0, &h, &it, &s, &slen) && - it <= neg->nsec3_max_iter && - (h != zone->nsec3_hash || it != zone->nsec3_iter || - slen != zone->nsec3_saltlen || - memcmp(zone->nsec3_salt, s, slen) != 0)) { - - if(slen > 0) { - uint8_t* sa = memdup(s, slen); - if(sa) { - free(zone->nsec3_salt); - zone->nsec3_salt = sa; - zone->nsec3_saltlen = slen; - zone->nsec3_iter = it; - zone->nsec3_hash = h; - } - } else { - free(zone->nsec3_salt); - zone->nsec3_salt = NULL; - zone->nsec3_saltlen = 0; - zone->nsec3_iter = it; - zone->nsec3_hash = h; - } - } - } - - /* wipe out the cache items between NSEC start and end */ - wipeout(neg, zone, el, nsec); -} - -void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep) -{ - size_t i, need; - struct ub_packed_rrset_key* soa; - struct val_neg_zone* zone; - /* see if secure nsecs inside */ - if(!reply_has_nsec(rep)) - return; - /* find the zone name in message */ - soa = reply_find_soa(rep); - if(!soa) - return; - - log_nametypeclass(VERB_ALGO, "negcache insert for zone", - soa->rk.dname, LDNS_RR_TYPE_SOA, ntohs(soa->rk.rrset_class)); - - /* ask for enough space to store all of it */ - need = calc_data_need(rep) + - calc_zone_need(soa->rk.dname, soa->rk.dname_len); - lock_basic_lock(&neg->lock); - neg_make_space(neg, need); - - /* find or create the zone entry */ - zone = neg_find_zone(neg, soa->rk.dname, soa->rk.dname_len, - ntohs(soa->rk.rrset_class)); - if(!zone) { - if(!(zone = neg_create_zone(neg, soa->rk.dname, - soa->rk.dname_len, ntohs(soa->rk.rrset_class)))) { - lock_basic_unlock(&neg->lock); - log_err("out of memory adding negative zone"); - return; - } - } - val_neg_zone_take_inuse(zone); - - /* insert the NSECs */ - for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ - if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC) - continue; - if(!dname_subdomain_c(rep->rrsets[i]->rk.dname, - zone->name)) continue; - /* insert NSEC into this zone's tree */ - neg_insert_data(neg, zone, rep->rrsets[i]); - } - if(zone->tree.count == 0) { - /* remove empty zone if inserts failed */ - neg_delete_zone(neg, zone); - } - lock_basic_unlock(&neg->lock); -} - -/** - * Lookup closest data record. For NSEC denial. - * @param zone: zone to look in - * @param qname: name to look for. - * @param len: length of name - * @param labs: labels in name - * @param data: data element, exact or smaller or NULL - * @return true if exact match. - */ -static int neg_closest_data(struct val_neg_zone* zone, - uint8_t* qname, size_t len, int labs, struct val_neg_data** data) -{ - struct val_neg_data key; - rbnode_type* r; - key.node.key = &key; - key.name = qname; - key.len = len; - key.labs = labs; - if(rbtree_find_less_equal(&zone->tree, &key, &r)) { - /* exact match */ - *data = (struct val_neg_data*)r; - return 1; - } else { - /* smaller match */ - *data = (struct val_neg_data*)r; - return 0; - } -} - -int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len, - uint16_t qclass, struct rrset_cache* rrset_cache, time_t now) -{ - /* lookup closest zone */ - struct val_neg_zone* zone; - struct val_neg_data* data; - int labs; - struct ub_packed_rrset_key* nsec; - struct packed_rrset_data* d; - uint32_t flags; - uint8_t* wc; - struct query_info qinfo; - if(!neg) return 0; - - log_nametypeclass(VERB_ALGO, "negcache dlvlookup", qname, - LDNS_RR_TYPE_DLV, qclass); - - labs = dname_count_labels(qname); - lock_basic_lock(&neg->lock); - zone = neg_closest_zone_parent(neg, qname, len, labs, qclass); - while(zone && !zone->in_use) - zone = zone->parent; - if(!zone) { - lock_basic_unlock(&neg->lock); - return 0; - } - log_nametypeclass(VERB_ALGO, "negcache zone", zone->name, 0, - zone->dclass); - - /* DLV is defined to use NSEC only */ - if(zone->nsec3_hash) { - lock_basic_unlock(&neg->lock); - return 0; - } - - /* lookup closest data record */ - (void)neg_closest_data(zone, qname, len, labs, &data); - while(data && !data->in_use) - data = data->parent; - if(!data) { - lock_basic_unlock(&neg->lock); - return 0; - } - log_nametypeclass(VERB_ALGO, "negcache rr", data->name, - LDNS_RR_TYPE_NSEC, zone->dclass); - - /* lookup rrset in rrset cache */ - flags = 0; - if(query_dname_compare(data->name, zone->name) == 0) - flags = PACKED_RRSET_NSEC_AT_APEX; - nsec = rrset_cache_lookup(rrset_cache, data->name, data->len, - LDNS_RR_TYPE_NSEC, zone->dclass, flags, now, 0); - - /* check if secure and TTL ok */ - if(!nsec) { - lock_basic_unlock(&neg->lock); - return 0; - } - d = (struct packed_rrset_data*)nsec->entry.data; - if(!d || now > d->ttl) { - lock_rw_unlock(&nsec->entry.lock); - /* delete data record if expired */ - neg_delete_data(neg, data); - lock_basic_unlock(&neg->lock); - return 0; - } - if(d->security != sec_status_secure) { - lock_rw_unlock(&nsec->entry.lock); - neg_delete_data(neg, data); - lock_basic_unlock(&neg->lock); - return 0; - } - verbose(VERB_ALGO, "negcache got secure rrset"); - - /* check NSEC security */ - /* check if NSEC proves no DLV type exists */ - /* check if NSEC proves NXDOMAIN for qname */ - qinfo.qname = qname; - qinfo.qtype = LDNS_RR_TYPE_DLV; - qinfo.qclass = qclass; - qinfo.local_alias = NULL; - if(!nsec_proves_nodata(nsec, &qinfo, &wc) && - !val_nsec_proves_name_error(nsec, qname)) { - /* the NSEC is not a denial for the DLV */ - lock_rw_unlock(&nsec->entry.lock); - lock_basic_unlock(&neg->lock); - verbose(VERB_ALGO, "negcache not proven"); - return 0; - } - /* so the NSEC was a NODATA proof, or NXDOMAIN proof. */ - - /* no need to check for wildcard NSEC; no wildcards in DLV repos */ - /* no need to lookup SOA record for client; no response message */ - - lock_rw_unlock(&nsec->entry.lock); - /* if OK touch the LRU for neg_data element */ - neg_lru_touch(neg, data); - lock_basic_unlock(&neg->lock); - verbose(VERB_ALGO, "negcache DLV denial proven"); - return 1; -} - -/** see if the reply has signed NSEC records and return the signer */ -static uint8_t* reply_nsec_signer(struct reply_info* rep, size_t* signer_len, - uint16_t* dclass) -{ - size_t i; - struct packed_rrset_data* d; - uint8_t* s; - for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC || - ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC3) { - d = (struct packed_rrset_data*)rep->rrsets[i]-> - entry.data; - /* return first signer name of first NSEC */ - if(d->rrsig_count != 0) { - val_find_rrset_signer(rep->rrsets[i], - &s, signer_len); - if(s && *signer_len) { - *dclass = ntohs(rep->rrsets[i]-> - rk.rrset_class); - return s; - } - } - } - } - return 0; -} - -void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep, - uint8_t* zone_name) -{ - size_t i, need; - uint8_t* signer; - size_t signer_len; - uint16_t dclass; - struct val_neg_zone* zone; - /* no SOA in this message, find RRSIG over NSEC's signer name. - * note the NSEC records are maybe not validated yet */ - signer = reply_nsec_signer(rep, &signer_len, &dclass); - if(!signer) - return; - if(!dname_subdomain_c(signer, zone_name)) { - /* the signer is not in the bailiwick, throw it out */ - return; - } - - log_nametypeclass(VERB_ALGO, "negcache insert referral ", - signer, LDNS_RR_TYPE_NS, dclass); - - /* ask for enough space to store all of it */ - need = calc_data_need(rep) + calc_zone_need(signer, signer_len); - lock_basic_lock(&neg->lock); - neg_make_space(neg, need); - - /* find or create the zone entry */ - zone = neg_find_zone(neg, signer, signer_len, dclass); - if(!zone) { - if(!(zone = neg_create_zone(neg, signer, signer_len, - dclass))) { - lock_basic_unlock(&neg->lock); - log_err("out of memory adding negative zone"); - return; - } - } - val_neg_zone_take_inuse(zone); - - /* insert the NSECs */ - for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ - if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC && - ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC3) - continue; - if(!dname_subdomain_c(rep->rrsets[i]->rk.dname, - zone->name)) continue; - /* insert NSEC into this zone's tree */ - neg_insert_data(neg, zone, rep->rrsets[i]); - } - if(zone->tree.count == 0) { - /* remove empty zone if inserts failed */ - neg_delete_zone(neg, zone); - } - lock_basic_unlock(&neg->lock); -} - -/** - * Check that an NSEC3 rrset does not have a type set. - * None of the nsec3s in a hash-collision are allowed to have the type. - * (since we do not know which one is the nsec3 looked at, flags, ..., we - * ignore the cached item and let it bypass negative caching). - * @param k: the nsec3 rrset to check. - * @param t: type to check - * @return true if no RRs have the type. - */ -static int nsec3_no_type(struct ub_packed_rrset_key* k, uint16_t t) -{ - int count = (int)((struct packed_rrset_data*)k->entry.data)->count; - int i; - for(i=0; i<count; i++) - if(nsec3_has_type(k, i, t)) - return 0; - return 1; -} - -/** - * See if rrset exists in rrset cache. - * If it does, the bit is checked, and if not expired, it is returned - * allocated in region. - * @param rrset_cache: rrset cache - * @param qname: to lookup rrset name - * @param qname_len: length of qname. - * @param qtype: type of rrset to lookup, host order - * @param qclass: class of rrset to lookup, host order - * @param flags: flags for rrset to lookup - * @param region: where to alloc result - * @param checkbit: if true, a bit in the nsec typemap is checked for absence. - * @param checktype: which bit to check - * @param now: to check ttl against - * @return rrset or NULL - */ -static struct ub_packed_rrset_key* -grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len, - uint16_t qtype, uint16_t qclass, uint32_t flags, - struct regional* region, int checkbit, uint16_t checktype, - time_t now) -{ - struct ub_packed_rrset_key* r, *k = rrset_cache_lookup(rrset_cache, - qname, qname_len, qtype, qclass, flags, now, 0); - struct packed_rrset_data* d; - if(!k) return NULL; - d = (struct packed_rrset_data*)k->entry.data; - if(d->ttl < now) { - lock_rw_unlock(&k->entry.lock); - return NULL; - } - /* only secure or unchecked records that have signatures. */ - if( ! ( d->security == sec_status_secure || - (d->security == sec_status_unchecked && - d->rrsig_count > 0) ) ) { - lock_rw_unlock(&k->entry.lock); - return NULL; - } - /* check if checktype is absent */ - if(checkbit && ( - (qtype == LDNS_RR_TYPE_NSEC && nsec_has_type(k, checktype)) || - (qtype == LDNS_RR_TYPE_NSEC3 && !nsec3_no_type(k, checktype)) - )) { - lock_rw_unlock(&k->entry.lock); - return NULL; - } - /* looks OK! copy to region and return it */ - r = packed_rrset_copy_region(k, region, now); - /* if it failed, we return the NULL */ - lock_rw_unlock(&k->entry.lock); - return r; -} - -/** find nsec3 closest encloser in neg cache */ -static struct val_neg_data* -neg_find_nsec3_ce(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, - int qlabs, sldns_buffer* buf, uint8_t* hashnc, size_t* nclen) -{ - struct val_neg_data* data; - uint8_t hashce[NSEC3_SHA_LEN]; - uint8_t b32[257]; - size_t celen, b32len; - - *nclen = 0; - while(qlabs > 0) { - /* hash */ - if(!(celen=nsec3_get_hashed(buf, qname, qname_len, - zone->nsec3_hash, zone->nsec3_iter, zone->nsec3_salt, - zone->nsec3_saltlen, hashce, sizeof(hashce)))) - return NULL; - if(!(b32len=nsec3_hash_to_b32(hashce, celen, zone->name, - zone->len, b32, sizeof(b32)))) - return NULL; - - /* lookup (exact match only) */ - data = neg_find_data(zone, b32, b32len, zone->labs+1); - if(data && data->in_use) { - /* found ce match! */ - return data; - } - - *nclen = celen; - memmove(hashnc, hashce, celen); - dname_remove_label(&qname, &qname_len); - qlabs --; - } - return NULL; -} - -/** check nsec3 parameters on nsec3 rrset with current zone values */ -static int -neg_params_ok(struct val_neg_zone* zone, struct ub_packed_rrset_key* rrset) -{ - int h; - uint8_t* s; - size_t slen, it; - if(!nsec3_get_params(rrset, 0, &h, &it, &s, &slen)) - return 0; - return (h == zone->nsec3_hash && it == zone->nsec3_iter && - slen == zone->nsec3_saltlen && - memcmp(zone->nsec3_salt, s, slen) == 0); -} - -/** get next closer for nsec3 proof */ -static struct ub_packed_rrset_key* -neg_nsec3_getnc(struct val_neg_zone* zone, uint8_t* hashnc, size_t nclen, - struct rrset_cache* rrset_cache, struct regional* region, - time_t now, uint8_t* b32, size_t maxb32) -{ - struct ub_packed_rrset_key* nc_rrset; - struct val_neg_data* data; - size_t b32len; - - if(!(b32len=nsec3_hash_to_b32(hashnc, nclen, zone->name, - zone->len, b32, maxb32))) - return NULL; - (void)neg_closest_data(zone, b32, b32len, zone->labs+1, &data); - if(!data && zone->tree.count != 0) { - /* could be before the first entry ; return the last - * entry (possibly the rollover nsec3 at end) */ - data = (struct val_neg_data*)rbtree_last(&zone->tree); - } - while(data && !data->in_use) - data = data->parent; - if(!data) - return NULL; - /* got a data element in tree, grab it */ - nc_rrset = grab_nsec(rrset_cache, data->name, data->len, - LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 0, 0, now); - if(!nc_rrset) - return NULL; - if(!neg_params_ok(zone, nc_rrset)) - return NULL; - return nc_rrset; -} - -/** neg cache nsec3 proof procedure*/ -static struct dns_msg* -neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, - int qlabs, sldns_buffer* buf, struct rrset_cache* rrset_cache, - struct regional* region, time_t now, uint8_t* topname) -{ - struct dns_msg* msg; - struct val_neg_data* data; - uint8_t hashnc[NSEC3_SHA_LEN]; - size_t nclen; - struct ub_packed_rrset_key* ce_rrset, *nc_rrset; - struct nsec3_cached_hash c; - uint8_t nc_b32[257]; - - /* for NSEC3 ; determine the closest encloser for which we - * can find an exact match. Remember the hashed lower name, - * since that is the one we need a closest match for. - * If we find a match straight away, then it becomes NODATA. - * Otherwise, NXDOMAIN or if OPTOUT, an insecure delegation. - * Also check that parameters are the same on closest encloser - * and on closest match. - */ - if(!zone->nsec3_hash) - return NULL; /* not nsec3 zone */ - - if(!(data=neg_find_nsec3_ce(zone, qname, qname_len, qlabs, buf, - hashnc, &nclen))) { - return NULL; - } - - /* grab the ce rrset */ - ce_rrset = grab_nsec(rrset_cache, data->name, data->len, - LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 1, - LDNS_RR_TYPE_DS, now); - if(!ce_rrset) - return NULL; - if(!neg_params_ok(zone, ce_rrset)) - return NULL; - - if(nclen == 0) { - /* exact match, just check the type bits */ - /* need: -SOA, -DS, +NS */ - if(nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_SOA) || - nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_DS) || - !nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_NS)) - return NULL; - if(!(msg = dns_msg_create(qname, qname_len, - LDNS_RR_TYPE_DS, zone->dclass, region, 1))) - return NULL; - /* TTL reduced in grab_nsec */ - if(!dns_msg_authadd(msg, region, ce_rrset, 0)) - return NULL; - return msg; - } - - /* optout is not allowed without knowing the trust-anchor in use, - * otherwise the optout could spoof away that anchor */ - if(!topname) - return NULL; - - /* if there is no exact match, it must be in an optout span - * (an existing DS implies an NSEC3 must exist) */ - nc_rrset = neg_nsec3_getnc(zone, hashnc, nclen, rrset_cache, - region, now, nc_b32, sizeof(nc_b32)); - if(!nc_rrset) - return NULL; - if(!neg_params_ok(zone, nc_rrset)) - return NULL; - if(!nsec3_has_optout(nc_rrset, 0)) - return NULL; - c.hash = hashnc; - c.hash_len = nclen; - c.b32 = nc_b32+1; - c.b32_len = (size_t)nc_b32[0]; - if(nsec3_covers(zone->name, &c, nc_rrset, 0, buf)) { - /* nc_rrset covers the next closer name. - * ce_rrset equals a closer encloser. - * nc_rrset is optout. - * No need to check wildcard for type DS */ - /* capacity=3: ce + nc + soa(if needed) */ - if(!(msg = dns_msg_create(qname, qname_len, - LDNS_RR_TYPE_DS, zone->dclass, region, 3))) - return NULL; - /* now=0 because TTL was reduced in grab_nsec */ - if(!dns_msg_authadd(msg, region, ce_rrset, 0)) - return NULL; - if(!dns_msg_authadd(msg, region, nc_rrset, 0)) - return NULL; - return msg; - } - return NULL; -} - -/** - * Add SOA record for external responses. - * @param rrset_cache: to look into. - * @param now: current time. - * @param region: where to perform the allocation - * @param msg: current msg with NSEC. - * @param zone: val_neg_zone if we have one. - * @return false on lookup or alloc failure. - */ -static int add_soa(struct rrset_cache* rrset_cache, time_t now, - struct regional* region, struct dns_msg* msg, struct val_neg_zone* zone) -{ - struct ub_packed_rrset_key* soa; - uint8_t* nm; - size_t nmlen; - uint16_t dclass; - if(zone) { - nm = zone->name; - nmlen = zone->len; - dclass = zone->dclass; - } else { - /* Assumes the signer is the zone SOA to add */ - nm = reply_nsec_signer(msg->rep, &nmlen, &dclass); - if(!nm) - return 0; - } - soa = rrset_cache_lookup(rrset_cache, nm, nmlen, LDNS_RR_TYPE_SOA, - dclass, PACKED_RRSET_SOA_NEG, now, 0); - if(!soa) - return 0; - if(!dns_msg_authadd(msg, region, soa, now)) { - lock_rw_unlock(&soa->entry.lock); - return 0; - } - lock_rw_unlock(&soa->entry.lock); - return 1; -} - -struct dns_msg* -val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, - struct regional* region, struct rrset_cache* rrset_cache, - sldns_buffer* buf, time_t now, int addsoa, uint8_t* topname) -{ - struct dns_msg* msg; - struct ub_packed_rrset_key* rrset; - uint8_t* zname; - size_t zname_len; - int zname_labs; - struct val_neg_zone* zone; - - /* only for DS queries */ - if(qinfo->qtype != LDNS_RR_TYPE_DS) - return NULL; - log_assert(!topname || dname_subdomain_c(qinfo->qname, topname)); - - /* see if info from neg cache is available - * For NSECs, because there is no optout; a DS next to a delegation - * always has exactly an NSEC for it itself; check its DS bit. - * flags=0 (not the zone apex). - */ - rrset = grab_nsec(rrset_cache, qinfo->qname, qinfo->qname_len, - LDNS_RR_TYPE_NSEC, qinfo->qclass, 0, region, 1, - qinfo->qtype, now); - if(rrset) { - /* return msg with that rrset */ - if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, - qinfo->qtype, qinfo->qclass, region, 2))) - return NULL; - /* TTL already subtracted in grab_nsec */ - if(!dns_msg_authadd(msg, region, rrset, 0)) - return NULL; - if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL)) - return NULL; - return msg; - } - - /* check NSEC3 neg cache for type DS */ - /* need to look one zone higher for DS type */ - zname = qinfo->qname; - zname_len = qinfo->qname_len; - dname_remove_label(&zname, &zname_len); - zname_labs = dname_count_labels(zname); - - /* lookup closest zone */ - lock_basic_lock(&neg->lock); - zone = neg_closest_zone_parent(neg, zname, zname_len, zname_labs, - qinfo->qclass); - while(zone && !zone->in_use) - zone = zone->parent; - /* check that the zone is not too high up so that we do not pick data - * out of a zone that is above the last-seen key (or trust-anchor). */ - if(zone && topname) { - if(!dname_subdomain_c(zone->name, topname)) - zone = NULL; - } - if(!zone) { - lock_basic_unlock(&neg->lock); - return NULL; - } - - msg = neg_nsec3_proof_ds(zone, qinfo->qname, qinfo->qname_len, - zname_labs+1, buf, rrset_cache, region, now, topname); - if(msg && addsoa && !add_soa(rrset_cache, now, region, msg, zone)) { - lock_basic_unlock(&neg->lock); - return NULL; - } - lock_basic_unlock(&neg->lock); - return msg; -} diff --git a/external/unbound/validator/val_neg.h b/external/unbound/validator/val_neg.h deleted file mode 100644 index 6ae71306c..000000000 --- a/external/unbound/validator/val_neg.h +++ /dev/null @@ -1,315 +0,0 @@ -/* - * validator/val_neg.h - validator aggressive negative caching functions. - * - * Copyright (c) 2008, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - * The functions help with aggressive negative caching. - * This creates new denials of existence, and proofs for absence of types - * from cached NSEC records. - */ - -#ifndef VALIDATOR_VAL_NEG_H -#define VALIDATOR_VAL_NEG_H -#include "util/locks.h" -#include "util/rbtree.h" -struct sldns_buffer; -struct val_neg_data; -struct config_file; -struct reply_info; -struct rrset_cache; -struct regional; -struct query_info; -struct dns_msg; -struct ub_packed_rrset_key; - -/** - * The negative cache. It is shared between the threads, so locked. - * Kept as validator-environ-state. It refers back to the rrset cache for - * data elements. It can be out of date and contain conflicting data - * from zone content changes. - * It contains a tree of zones, every zone has a tree of data elements. - * The data elements are part of one big LRU list, with one memory counter. - */ -struct val_neg_cache { - /** the big lock on the negative cache. Because we use a rbtree - * for the data (quick lookup), we need a big lock */ - lock_basic_type lock; - /** The zone rbtree. contents sorted canonical, type val_neg_zone */ - rbtree_type tree; - /** the first in linked list of LRU of val_neg_data */ - struct val_neg_data* first; - /** last in lru (least recently used element) */ - struct val_neg_data* last; - /** current memory in use (bytes) */ - size_t use; - /** max memory to use (bytes) */ - size_t max; - /** max nsec3 iterations allowed */ - size_t nsec3_max_iter; -}; - -/** - * Per Zone aggressive negative caching data. - */ -struct val_neg_zone { - /** rbtree node element, key is this struct: the name, class */ - rbnode_type node; - /** name; the key */ - uint8_t* name; - /** length of name */ - size_t len; - /** labels in name */ - int labs; - - /** pointer to parent zone in the negative cache */ - struct val_neg_zone* parent; - - /** the number of elements, including this one and the ones whose - * parents (-parents) include this one, that are in_use - * No elements have a count of zero, those are removed. */ - int count; - - /** if 0: NSEC zone, else NSEC3 hash algorithm in use */ - int nsec3_hash; - /** nsec3 iteration count in use */ - size_t nsec3_iter; - /** nsec3 salt in use */ - uint8_t* nsec3_salt; - /** length of salt in bytes */ - size_t nsec3_saltlen; - - /** tree of NSEC data for this zone, sorted canonical - * by NSEC owner name */ - rbtree_type tree; - - /** class of node; host order */ - uint16_t dclass; - /** if this element is in use, boolean */ - uint8_t in_use; -}; - -/** - * Data element for aggressive negative caching. - * The tree of these elements acts as an index onto the rrset cache. - * It shows the NSEC records that (may) exist and are (possibly) secure. - * The rbtree allows for logN search for a covering NSEC record. - * To make tree insertion and deletion logN too, all the parent (one label - * less than the name) data elements are also in the rbtree, with a usage - * count for every data element. - * There is no actual data stored in this data element, if it is in_use, - * then the data can (possibly) be found in the rrset cache. - */ -struct val_neg_data { - /** rbtree node element, key is this struct: the name */ - rbnode_type node; - /** name; the key */ - uint8_t* name; - /** length of name */ - size_t len; - /** labels in name */ - int labs; - - /** pointer to parent node in the negative cache */ - struct val_neg_data* parent; - - /** the number of elements, including this one and the ones whose - * parents (-parents) include this one, that are in use - * No elements have a count of zero, those are removed. */ - int count; - - /** the zone that this denial is part of */ - struct val_neg_zone* zone; - - /** previous in LRU */ - struct val_neg_data* prev; - /** next in LRU (next element was less recently used) */ - struct val_neg_data* next; - - /** if this element is in use, boolean */ - uint8_t in_use; -}; - -/** - * Create negative cache - * @param cfg: config options. - * @param maxiter: max nsec3 iterations allowed. - * @return neg cache, empty or NULL on failure. - */ -struct val_neg_cache* val_neg_create(struct config_file* cfg, size_t maxiter); - -/** - * see how much memory is in use by the negative cache. - * @param neg: negative cache - * @return number of bytes in use. - */ -size_t val_neg_get_mem(struct val_neg_cache* neg); - -/** - * Destroy negative cache. There must no longer be any other threads. - * @param neg: negative cache. - */ -void neg_cache_delete(struct val_neg_cache* neg); - -/** - * Comparison function for rbtree val neg data elements - */ -int val_neg_data_compare(const void* a, const void* b); - -/** - * Comparison function for rbtree val neg zone elements - */ -int val_neg_zone_compare(const void* a, const void* b); - -/** - * Insert NSECs from this message into the negative cache for reference. - * @param neg: negative cache - * @param rep: reply with NSECs. - * Errors are ignored, means that storage is omitted. - */ -void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep); - -/** - * Insert NSECs from this referral into the negative cache for reference. - * @param neg: negative cache - * @param rep: referral reply with NS, NSECs. - * @param zone: bailiwick for the referral. - * Errors are ignored, means that storage is omitted. - */ -void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep, - uint8_t* zone); - -/** - * Perform a DLV style lookup - * During the lookup, we could find out that data has expired. In that - * case the neg_cache entries are removed, and lookup fails. - * - * @param neg: negative cache. - * @param qname: name to look for - * @param len: length of qname. - * @param qclass: class to look in. - * @param rrset_cache: the rrset cache, for NSEC lookups. - * @param now: current time for ttl checks. - * @return - * 0 on error - * 0 if no proof of negative - * 1 if indeed negative was proven - * thus, qname DLV qclass does not exist. - */ -int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len, - uint16_t qclass, struct rrset_cache* rrset_cache, time_t now); - -/** - * For the given query, try to get a reply out of the negative cache. - * The reply still needs to be validated. - * @param neg: negative cache. - * @param qinfo: query - * @param region: where to allocate reply. - * @param rrset_cache: rrset cache. - * @param buf: temporary buffer. - * @param now: to check TTLs against. - * @param addsoa: if true, produce result for external consumption. - * if false, do not add SOA - for unbound-internal consumption. - * @param topname: do not look higher than this name, - * so that the result cannot be taken from a zone above the current - * trust anchor. Which could happen with multiple islands of trust. - * if NULL, then no trust anchor is used, but also the algorithm becomes - * more conservative, especially for opt-out zones, since the receiver - * may have a trust-anchor below the optout and thus the optout cannot - * be used to create a proof from the negative cache. - * @return a reply message if something was found. - * This reply may still need validation. - * NULL if nothing found (or out of memory). - */ -struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, - struct query_info* qinfo, struct regional* region, - struct rrset_cache* rrset_cache, struct sldns_buffer* buf, time_t now, - int addsoa, uint8_t* topname); - - -/**** functions exposed for unit test ****/ -/** - * Insert data into the data tree of a zone - * Does not do locking. - * @param neg: negative cache - * @param zone: zone to insert into - * @param nsec: record to insert. - */ -void neg_insert_data(struct val_neg_cache* neg, - struct val_neg_zone* zone, struct ub_packed_rrset_key* nsec); - -/** - * Delete a data element from the negative cache. - * May delete other data elements to keep tree coherent, or - * only mark the element as 'not in use'. - * Does not do locking. - * @param neg: negative cache. - * @param el: data element to delete. - */ -void neg_delete_data(struct val_neg_cache* neg, struct val_neg_data* el); - -/** - * Find the given zone, from the SOA owner name and class - * Does not do locking. - * @param neg: negative cache - * @param nm: what to look for. - * @param len: length of nm - * @param dclass: class to look for. - * @return zone or NULL if not found. - */ -struct val_neg_zone* neg_find_zone(struct val_neg_cache* neg, - uint8_t* nm, size_t len, uint16_t dclass); - -/** - * Create a new zone. - * Does not do locking. - * @param neg: negative cache - * @param nm: what to look for. - * @param nm_len: length of name. - * @param dclass: class of zone, host order. - * @return zone or NULL if out of memory. - */ -struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg, - uint8_t* nm, size_t nm_len, uint16_t dclass); - -/** - * take a zone into use. increases counts of parents. - * Does not do locking. - * @param zone: zone to take into use. - */ -void val_neg_zone_take_inuse(struct val_neg_zone* zone); - -#endif /* VALIDATOR_VAL_NEG_H */ diff --git a/external/unbound/validator/val_nsec.c b/external/unbound/validator/val_nsec.c deleted file mode 100644 index 1e4f440ff..000000000 --- a/external/unbound/validator/val_nsec.c +++ /dev/null @@ -1,624 +0,0 @@ -/* - * validator/val_nsec.c - validator NSEC denial of existence functions. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - * The functions help with NSEC checking, the different NSEC proofs - * for denial of existence, and proofs for presence of types. - */ -#include "config.h" -#include "validator/val_nsec.h" -#include "validator/val_utils.h" -#include "util/data/msgreply.h" -#include "util/data/dname.h" -#include "util/net_help.h" -#include "util/module.h" -#include "services/cache/rrset.h" - -/** get ttl of rrset */ -static uint32_t -rrset_get_ttl(struct ub_packed_rrset_key* k) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; - return d->ttl; -} - -int -nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type) -{ - /* Check type present in NSEC typemap with bitmap arg */ - /* bitmasks for determining type-lowerbits presence */ - uint8_t masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; - uint8_t type_window = type>>8; - uint8_t type_low = type&0xff; - uint8_t win, winlen; - /* read each of the type bitmap windows and see if the searched - * type is amongst it */ - while(len > 0) { - if(len < 3) /* bad window, at least window# winlen bitmap */ - return 0; - win = *bitmap++; - winlen = *bitmap++; - len -= 2; - if(len < winlen || winlen < 1 || winlen > 32) - return 0; /* bad window length */ - if(win == type_window) { - /* search window bitmap for the correct byte */ - /* mybyte is 0 if we need the first byte */ - size_t mybyte = type_low>>3; - if(winlen <= mybyte) - return 0; /* window too short */ - return (int)(bitmap[mybyte] & masks[type_low&0x7]); - } else { - /* not the window we are looking for */ - bitmap += winlen; - len -= winlen; - } - } - /* end of bitmap reached, no type found */ - return 0; -} - -int -nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> - entry.data; - size_t len; - if(!d || d->count == 0 || d->rr_len[0] < 2+1) - return 0; - len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2); - if(!len) - return 0; - return nsecbitmap_has_type_rdata(d->rr_data[0]+2+len, - d->rr_len[0]-2-len, type); -} - -/** - * Get next owner name from nsec record - * @param nsec: the nsec RRset. - * If there are multiple RRs, then this will only return one of them. - * @param nm: the next name is returned. - * @param ln: length of nm is returned. - * @return false on a bad NSEC RR (too short, malformed dname). - */ -static int -nsec_get_next(struct ub_packed_rrset_key* nsec, uint8_t** nm, size_t* ln) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> - entry.data; - if(!d || d->count == 0 || d->rr_len[0] < 2+1) { - *nm = 0; - *ln = 0; - return 0; - } - *nm = d->rr_data[0]+2; - *ln = dname_valid(*nm, d->rr_len[0]-2); - if(!*ln) { - *nm = 0; - *ln = 0; - return 0; - } - return 1; -} - -/** - * For an NSEC that matches the DS queried for, check absence of DS type. - * - * @param nsec: NSEC for proof, must be trusted. - * @param qinfo: what is queried for. - * @return if secure the nsec proves that no DS is present, or - * insecure if it proves it is not a delegation point. - * or bogus if something was wrong. - */ -static enum sec_status -val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec, - struct query_info* qinfo) -{ - log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); - log_assert(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC); - - if(nsec_has_type(nsec, LDNS_RR_TYPE_SOA) && qinfo->qname_len != 1) { - /* SOA present means that this is the NSEC from the child, - * not the parent (so it is the wrong one). */ - return sec_status_bogus; - } - if(nsec_has_type(nsec, LDNS_RR_TYPE_DS)) { - /* DS present means that there should have been a positive - * response to the DS query, so there is something wrong. */ - return sec_status_bogus; - } - - if(!nsec_has_type(nsec, LDNS_RR_TYPE_NS)) { - /* If there is no NS at this point at all, then this - * doesn't prove anything one way or the other. */ - return sec_status_insecure; - } - /* Otherwise, this proves no DS. */ - return sec_status_secure; -} - -/** check security status from cache or verify rrset, returns true if secure */ -static int -nsec_verify_rrset(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey, - char** reason) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*) - nsec->entry.data; - if(d->security == sec_status_secure) - return 1; - rrset_check_sec_status(env->rrset_cache, nsec, *env->now); - if(d->security == sec_status_secure) - return 1; - d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason); - if(d->security == sec_status_secure) { - rrset_update_sec_status(env->rrset_cache, nsec, *env->now); - return 1; - } - return 0; -} - -enum sec_status -val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, - struct query_info* qinfo, struct reply_info* rep, - struct key_entry_key* kkey, time_t* proof_ttl, char** reason) -{ - struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns( - rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC, - qinfo->qclass); - enum sec_status sec; - size_t i; - uint8_t* wc = NULL, *ce = NULL; - int valid_nsec = 0; - struct ub_packed_rrset_key* wc_nsec = NULL; - - /* If we have a NSEC at the same name, it must prove one - * of two things - * -- - * 1) this is a delegation point and there is no DS - * 2) this is not a delegation point */ - if(nsec) { - if(!nsec_verify_rrset(env, ve, nsec, kkey, reason)) { - verbose(VERB_ALGO, "NSEC RRset for the " - "referral did not verify."); - return sec_status_bogus; - } - sec = val_nsec_proves_no_ds(nsec, qinfo); - if(sec == sec_status_bogus) { - /* something was wrong. */ - *reason = "NSEC does not prove absence of DS"; - return sec; - } else if(sec == sec_status_insecure) { - /* this wasn't a delegation point. */ - return sec; - } else if(sec == sec_status_secure) { - /* this proved no DS. */ - *proof_ttl = ub_packed_rrset_ttl(nsec); - return sec; - } - /* if unchecked, fall through to next proof */ - } - - /* Otherwise, there is no NSEC at qname. This could be an ENT. - * (ENT=empty non terminal). If not, this is broken. */ - - /* verify NSEC rrsets in auth section */ - for(i=rep->an_numrrsets; i < rep->an_numrrsets+rep->ns_numrrsets; - i++) { - if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC)) - continue; - if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason)) { - verbose(VERB_ALGO, "NSEC for empty non-terminal " - "did not verify."); - return sec_status_bogus; - } - if(nsec_proves_nodata(rep->rrsets[i], qinfo, &wc)) { - verbose(VERB_ALGO, "NSEC for empty non-terminal " - "proved no DS."); - *proof_ttl = rrset_get_ttl(rep->rrsets[i]); - if(wc && dname_is_wild(rep->rrsets[i]->rk.dname)) - wc_nsec = rep->rrsets[i]; - valid_nsec = 1; - } - if(val_nsec_proves_name_error(rep->rrsets[i], qinfo->qname)) { - ce = nsec_closest_encloser(qinfo->qname, - rep->rrsets[i]); - } - } - if(wc && !ce) - valid_nsec = 0; - else if(wc && ce) { - /* ce and wc must match */ - if(query_dname_compare(wc, ce) != 0) - valid_nsec = 0; - else if(!wc_nsec) - valid_nsec = 0; - } - if(valid_nsec) { - if(wc) { - /* check if this is a delegation */ - *reason = "NSEC for wildcard does not prove absence of DS"; - return val_nsec_proves_no_ds(wc_nsec, qinfo); - } - /* valid nsec proves empty nonterminal */ - return sec_status_insecure; - } - - /* NSEC proof did not conclusively point to DS or no DS */ - return sec_status_unchecked; -} - -int nsec_proves_nodata(struct ub_packed_rrset_key* nsec, - struct query_info* qinfo, uint8_t** wc) -{ - log_assert(wc); - if(query_dname_compare(nsec->rk.dname, qinfo->qname) != 0) { - uint8_t* nm; - size_t ln; - - /* empty-non-terminal checking. - * Done before wildcard, because this is an exact match, - * and would prevent a wildcard from matching. */ - - /* If the nsec is proving that qname is an ENT, the nsec owner - * will be less than qname, and the next name will be a child - * domain of the qname. */ - if(!nsec_get_next(nsec, &nm, &ln)) - return 0; /* bad nsec */ - if(dname_strict_subdomain_c(nm, qinfo->qname) && - dname_canonical_compare(nsec->rk.dname, - qinfo->qname) < 0) { - return 1; /* proves ENT */ - } - - /* wildcard checking. */ - - /* If this is a wildcard NSEC, make sure that a) it was - * possible to have generated qname from the wildcard and - * b) the type map does not contain qtype. Note that this - * does NOT prove that this wildcard was the applicable - * wildcard. */ - if(dname_is_wild(nsec->rk.dname)) { - /* the purported closest encloser. */ - uint8_t* ce = nsec->rk.dname; - size_t ce_len = nsec->rk.dname_len; - dname_remove_label(&ce, &ce_len); - - /* The qname must be a strict subdomain of the - * closest encloser, for the wildcard to apply - */ - if(dname_strict_subdomain_c(qinfo->qname, ce)) { - /* here we have a matching NSEC for the qname, - * perform matching NSEC checks */ - if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) { - /* should have gotten the wildcard CNAME */ - return 0; - } - if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) && - !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { - /* wrong parentside (wildcard) NSEC used */ - return 0; - } - if(nsec_has_type(nsec, qinfo->qtype)) { - return 0; - } - *wc = ce; - return 1; - } - } else { - /* See if the next owner name covers a wildcard - * empty non-terminal. */ - while (dname_canonical_compare(nsec->rk.dname, nm) < 0) { - /* wildcard does not apply if qname below - * the name that exists under the '*' */ - if (dname_subdomain_c(qinfo->qname, nm)) - break; - /* but if it is a wildcard and qname is below - * it, then the wildcard applies. The wildcard - * is an empty nonterminal. nodata proven. */ - if (dname_is_wild(nm)) { - size_t ce_len = ln; - uint8_t* ce = nm; - dname_remove_label(&ce, &ce_len); - if(dname_strict_subdomain_c(qinfo->qname, ce)) { - *wc = ce; - return 1; - } - } - dname_remove_label(&nm, &ln); - } - } - - /* Otherwise, this NSEC does not prove ENT and is not a - * wildcard, so it does not prove NODATA. */ - return 0; - } - - /* If the qtype exists, then we should have gotten it. */ - if(nsec_has_type(nsec, qinfo->qtype)) { - return 0; - } - - /* if the name is a CNAME node, then we should have gotten the CNAME*/ - if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) { - return 0; - } - - /* If an NS set exists at this name, and NOT a SOA (so this is a - * zone cut, not a zone apex), then we should have gotten a - * referral (or we just got the wrong NSEC). - * The reverse of this check is used when qtype is DS, since that - * must use the NSEC from above the zone cut. */ - if(qinfo->qtype != LDNS_RR_TYPE_DS && - nsec_has_type(nsec, LDNS_RR_TYPE_NS) && - !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { - return 0; - } else if(qinfo->qtype == LDNS_RR_TYPE_DS && - nsec_has_type(nsec, LDNS_RR_TYPE_SOA) && - !dname_is_root(qinfo->qname)) { - return 0; - } - - return 1; -} - -int -val_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, uint8_t* qname) -{ - uint8_t* owner = nsec->rk.dname; - uint8_t* next; - size_t nlen; - if(!nsec_get_next(nsec, &next, &nlen)) - return 0; - - /* If NSEC owner == qname, then this NSEC proves that qname exists. */ - if(query_dname_compare(qname, owner) == 0) { - return 0; - } - - /* If NSEC is a parent of qname, we need to check the type map - * If the parent name has a DNAME or is a delegation point, then - * this NSEC is being misused. */ - if(dname_subdomain_c(qname, owner) && - (nsec_has_type(nsec, LDNS_RR_TYPE_DNAME) || - (nsec_has_type(nsec, LDNS_RR_TYPE_NS) - && !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) - )) { - return 0; - } - - if(query_dname_compare(owner, next) == 0) { - /* this nsec is the only nsec */ - /* zone.name NSEC zone.name, disproves everything else */ - /* but only for subdomains of that zone */ - if(dname_strict_subdomain_c(qname, next)) - return 1; - } - else if(dname_canonical_compare(owner, next) > 0) { - /* this is the last nsec, ....(bigger) NSEC zonename(smaller) */ - /* the names after the last (owner) name do not exist - * there are no names before the zone name in the zone - * but the qname must be a subdomain of the zone name(next). */ - if(dname_canonical_compare(owner, qname) < 0 && - dname_strict_subdomain_c(qname, next)) - return 1; - } else { - /* regular NSEC, (smaller) NSEC (larger) */ - if(dname_canonical_compare(owner, qname) < 0 && - dname_canonical_compare(qname, next) < 0) { - return 1; - } - } - return 0; -} - -int val_nsec_proves_insecuredelegation(struct ub_packed_rrset_key* nsec, - struct query_info* qinfo) -{ - if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) && - !nsec_has_type(nsec, LDNS_RR_TYPE_DS) && - !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { - /* see if nsec signals an insecure delegation */ - if(qinfo->qtype == LDNS_RR_TYPE_DS) { - /* if type is DS and qname is equal to nsec, then it - * is an exact match nsec, result not insecure */ - if(dname_strict_subdomain_c(qinfo->qname, - nsec->rk.dname)) - return 1; - } else { - if(dname_subdomain_c(qinfo->qname, nsec->rk.dname)) - return 1; - } - } - return 0; -} - -uint8_t* -nsec_closest_encloser(uint8_t* qname, struct ub_packed_rrset_key* nsec) -{ - uint8_t* next; - size_t nlen; - uint8_t* common1, *common2; - if(!nsec_get_next(nsec, &next, &nlen)) - return NULL; - /* longest common with owner or next name */ - common1 = dname_get_shared_topdomain(nsec->rk.dname, qname); - common2 = dname_get_shared_topdomain(next, qname); - if(dname_count_labels(common1) > dname_count_labels(common2)) - return common1; - return common2; -} - -int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec, - struct query_info* qinf, uint8_t* wc) -{ - uint8_t* ce; - /* 1) prove that qname doesn't exist and - * 2) that the correct wildcard was used - * nsec has been verified already. */ - if(!val_nsec_proves_name_error(nsec, qinf->qname)) - return 0; - /* check wildcard name */ - ce = nsec_closest_encloser(qinf->qname, nsec); - if(!ce) - return 0; - if(query_dname_compare(wc, ce) != 0) { - return 0; - } - return 1; -} - -int -val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, - size_t qnamelen) -{ - /* Determine if a NSEC record proves the non-existence of a - * wildcard that could have produced qname. */ - int labs; - int i; - uint8_t* ce = nsec_closest_encloser(qname, nsec); - uint8_t* strip; - size_t striplen; - uint8_t buf[LDNS_MAX_DOMAINLEN+3]; - if(!ce) - return 0; - /* we can subtract the closest encloser count - since that is the - * largest shared topdomain with owner and next NSEC name, - * because the NSEC is no proof for names shorter than the owner - * and next names. */ - labs = dname_count_labels(qname) - dname_count_labels(ce); - - for(i=labs; i>0; i--) { - /* i is number of labels to strip off qname, prepend * wild */ - strip = qname; - striplen = qnamelen; - dname_remove_labels(&strip, &striplen, i); - if(striplen > LDNS_MAX_DOMAINLEN-2) - continue; /* too long to prepend wildcard */ - buf[0] = 1; - buf[1] = (uint8_t)'*'; - memmove(buf+2, strip, striplen); - if(val_nsec_proves_name_error(nsec, buf)) { - return 1; - } - } - return 0; -} - -/** - * Find shared topdomain that exists - */ -static void -dlv_topdomain(struct ub_packed_rrset_key* nsec, uint8_t* qname, - uint8_t** nm, size_t* nm_len) -{ - /* make sure reply is part of nm */ - /* take shared topdomain with left of NSEC. */ - - /* because, if empty nonterminal, then right is subdomain of qname. - * and any shared topdomain would be empty nonterminals. - * - * If nxdomain, then the right is bigger, and could have an - * interesting shared topdomain, but if it does have one, it is - * an empty nonterminal. An empty nonterminal shared with the left - * one. */ - int n; - uint8_t* common = dname_get_shared_topdomain(qname, nsec->rk.dname); - n = dname_count_labels(*nm) - dname_count_labels(common); - dname_remove_labels(nm, nm_len, n); -} - -int val_nsec_check_dlv(struct query_info* qinfo, - struct reply_info* rep, uint8_t** nm, size_t* nm_len) -{ - uint8_t* next; - size_t i, nlen; - int c; - /* we should now have a NOERROR/NODATA or NXDOMAIN message */ - if(rep->an_numrrsets != 0) { - return 0; - } - /* is this NOERROR ? */ - if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) { - /* it can be a plain NSEC match - go up one more level. */ - /* or its an empty nonterminal - go up to nonempty level */ - for(i=0; i<rep->ns_numrrsets; i++) { - if(htons(rep->rrsets[i]->rk.type)!=LDNS_RR_TYPE_NSEC || - !nsec_get_next(rep->rrsets[i], &next, &nlen)) - continue; - c = dname_canonical_compare( - rep->rrsets[i]->rk.dname, qinfo->qname); - if(c == 0) { - /* plain match */ - if(nsec_has_type(rep->rrsets[i], - LDNS_RR_TYPE_DLV)) - return 0; - dname_remove_label(nm, nm_len); - return 1; - } else if(c < 0 && - dname_strict_subdomain_c(next, qinfo->qname)) { - /* ENT */ - dlv_topdomain(rep->rrsets[i], qinfo->qname, - nm, nm_len); - return 1; - } - } - return 0; - } - - /* is this NXDOMAIN ? */ - if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN) { - /* find the qname denial NSEC record. It can tell us - * a closest encloser name; or that we not need bother */ - for(i=0; i<rep->ns_numrrsets; i++) { - if(htons(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC) - continue; - if(val_nsec_proves_name_error(rep->rrsets[i], - qinfo->qname)) { - log_nametypeclass(VERB_ALGO, "topdomain on", - rep->rrsets[i]->rk.dname, - ntohs(rep->rrsets[i]->rk.type), 0); - dlv_topdomain(rep->rrsets[i], qinfo->qname, - nm, nm_len); - return 1; - } - } - return 0; - } - return 0; -} diff --git a/external/unbound/validator/val_nsec.h b/external/unbound/validator/val_nsec.h deleted file mode 100644 index c031c9a3b..000000000 --- a/external/unbound/validator/val_nsec.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * validator/val_nsec.h - validator NSEC denial of existence functions. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - * The functions help with NSEC checking, the different NSEC proofs - * for denial of existence, and proofs for presence of types. - */ - -#ifndef VALIDATOR_VAL_NSEC_H -#define VALIDATOR_VAL_NSEC_H -#include "util/data/packed_rrset.h" -struct val_env; -struct module_env; -struct ub_packed_rrset_key; -struct reply_info; -struct query_info; -struct key_entry_key; - -/** - * Check DS absence. - * There is a NODATA reply to a DS that needs checking. - * NSECs can prove this is not a delegation point, or successfully prove - * that there is no DS. Or this fails. - * - * @param env: module env for rrsig verification routines. - * @param ve: validator env for rrsig verification routines. - * @param qinfo: the DS queried for. - * @param rep: reply received. - * @param kkey: key entry to use for verification of signatures. - * @param proof_ttl: if secure, the TTL of how long this proof lasts. - * @param reason: string explaining why bogus. - * @return security status. - * SECURE: proved absence of DS. - * INSECURE: proved that this was not a delegation point. - * BOGUS: crypto bad, or no absence of DS proven. - * UNCHECKED: there was no way to prove anything (no NSECs, unknown algo). - */ -enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env, - struct val_env* ve, struct query_info* qinfo, - struct reply_info* rep, struct key_entry_key* kkey, - time_t* proof_ttl, char** reason); - -/** - * nsec typemap check, takes an NSEC-type bitmap as argument, checks for type. - * @param bitmap: pointer to the bitmap part of wireformat rdata. - * @param len: length of the bitmap, in bytes. - * @param type: the type (in host order) to check for. - * @return true if the type bit was set in the bitmap. false if not, or - * if the bitmap was malformed in some way. - */ -int nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type); - -/** - * Check if type is present in the NSEC typemap - * @param nsec: the nsec RRset. - * If there are multiple RRs, then each must have the same typemap, - * since the typemap represents the types at this domain node. - * @param type: type to check for, host order. - * @return true if present - */ -int nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type); - -/** - * Determine if a NSEC proves the NOERROR/NODATA conditions. This will also - * handle the empty non-terminal (ENT) case and partially handle the - * wildcard case. If the ownername of 'nsec' is a wildcard, the validator - * must still be provided proof that qname did not directly exist and that - * the wildcard is, in fact, *.closest_encloser. - * - * @param nsec: the nsec record to check against. - * @param qinfo: the query info. - * @param wc: if the nodata is proven for a wildcard match, the wildcard - * closest encloser is returned, else NULL (wc is unchanged). - * This closest encloser must then match the nameerror given for the - * nextcloser of qname. - * @return true if NSEC proves this. - */ -int nsec_proves_nodata(struct ub_packed_rrset_key* nsec, - struct query_info* qinfo, uint8_t** wc); - -/** - * Determine if the given NSEC proves a NameError (NXDOMAIN) for a given - * qname. - * - * @param nsec: the nsec to check - * @param qname: what was queried. - * @return true if proven. - */ -int val_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, - uint8_t* qname); - -/** - * Determine if the given NSEC proves a positive wildcard response. - * @param nsec: the nsec to check - * @param qinf: what was queried. - * @param wc: wildcard (without *. label) - * @return true if proven. - */ -int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec, - struct query_info* qinf, uint8_t* wc); - -/** - * Determine closest encloser of a query name and the NSEC that covers it - * (and thus disproved it). - * A name error must have been proven already, otherwise this will be invalid. - * @param qname: the name queried for. - * @param nsec: the nsec RRset. - * @return closest encloser dname or NULL on error (bad nsec RRset). - */ -uint8_t* nsec_closest_encloser(uint8_t* qname, - struct ub_packed_rrset_key* nsec); - -/** - * Determine if the given NSEC proves that a wildcard match does not exist. - * - * @param nsec: the nsec RRset. - * @param qname: the name queried for. - * @param qnamelen: length of qname. - * @return true if proven. - */ -int val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, - size_t qnamelen); - -/** - * Determine the DLV result, what to do with NSEC DLV reply. - * @param qinfo: what was queried for. - * @param rep: the nonpositive reply. - * @param nm: dlv lookup name, to adjust for new lookup name (if needed). - * @param nm_len: length of lookup name. - * @return 0 on error, 1 if a higher point is found. - * If the higher point is above the dlv repo anchor, the qname does - * not exist. - */ -int val_nsec_check_dlv(struct query_info* qinfo, - struct reply_info* rep, uint8_t** nm, size_t* nm_len); - -/** - * Determine if an nsec proves an insecure delegation towards the qname. - * @param nsec: nsec rrset. - * @param qinfo: what was queries for. - * @return 0 if not, 1 if an NSEC that signals an insecure delegation to - * the qname. - */ -int val_nsec_proves_insecuredelegation(struct ub_packed_rrset_key* nsec, - struct query_info* qinfo); - -#endif /* VALIDATOR_VAL_NSEC_H */ diff --git a/external/unbound/validator/val_nsec3.c b/external/unbound/validator/val_nsec3.c deleted file mode 100644 index 4d978372a..000000000 --- a/external/unbound/validator/val_nsec3.c +++ /dev/null @@ -1,1434 +0,0 @@ -/* - * validator/val_nsec3.c - validator NSEC3 denial of existence functions. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - * The functions help with NSEC3 checking, the different NSEC3 proofs - * for denial of existence, and proofs for presence of types. - */ -#include "config.h" -#include <ctype.h> -#include "validator/val_nsec3.h" -#include "validator/val_secalgo.h" -#include "validator/validator.h" -#include "validator/val_kentry.h" -#include "services/cache/rrset.h" -#include "util/regional.h" -#include "util/rbtree.h" -#include "util/module.h" -#include "util/net_help.h" -#include "util/data/packed_rrset.h" -#include "util/data/dname.h" -#include "util/data/msgreply.h" -/* we include nsec.h for the bitmap_has_type function */ -#include "validator/val_nsec.h" -#include "sldns/sbuffer.h" - -/** - * This function we get from ldns-compat or from base system - * it returns the number of data bytes stored at the target, or <0 on error. - */ -int sldns_b32_ntop_extended_hex(uint8_t const *src, size_t srclength, - char *target, size_t targsize); -/** - * This function we get from ldns-compat or from base system - * it returns the number of data bytes stored at the target, or <0 on error. - */ -int sldns_b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len, - uint8_t *target, size_t targsize); - -/** - * Closest encloser (ce) proof results - * Contains the ce and the next-closer (nc) proof. - */ -struct ce_response { - /** the closest encloser name */ - uint8_t* ce; - /** length of ce */ - size_t ce_len; - /** NSEC3 record that proved ce. rrset */ - struct ub_packed_rrset_key* ce_rrset; - /** NSEC3 record that proved ce. rr number */ - int ce_rr; - /** NSEC3 record that proved nc. rrset */ - struct ub_packed_rrset_key* nc_rrset; - /** NSEC3 record that proved nc. rr*/ - int nc_rr; -}; - -/** - * 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 */ - uint8_t* zone; - /** length of the zonename */ - size_t zone_len; - /** the list of NSEC3s to filter; array */ - 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); -} - -int -nsec3_has_optout(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_OPTOUT); -} - -/** 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; -} - -int nsec3_get_params(struct ub_packed_rrset_key* rrset, int r, - int* algo, size_t* iter, uint8_t** salt, size_t* saltlen) -{ - if(!nsec3_known_algo(rrset, r) || nsec3_unknown_flags(rrset, r)) - return 0; - if(!nsec3_get_salt(rrset, r, salt, saltlen)) - return 0; - *algo = nsec3_get_algo(rrset, r); - *iter = nsec3_get_iter(rrset, r); - return 1; -} - -int -nsec3_get_nextowner(struct ub_packed_rrset_key* rrset, int r, - uint8_t** next, size_t* nextlen) -{ - 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) { - *next = 0; - *nextlen = 0; - return 0; /* malformed */ - } - saltlen = (size_t)d->rr_data[r][2+4]; - if(d->rr_len[r] < 2+5+saltlen+1) { - *next = 0; - *nextlen = 0; - return 0; /* malformed */ - } - *nextlen = (size_t)d->rr_data[r][2+5+saltlen]; - if(d->rr_len[r] < 2+5+saltlen+1+*nextlen) { - *next = 0; - *nextlen = 0; - return 0; /* malformed */ - } - *next = d->rr_data[r]+2+5+saltlen+1; - return 1; -} - -size_t nsec3_hash_to_b32(uint8_t* hash, size_t hashlen, uint8_t* zone, - size_t zonelen, uint8_t* buf, size_t max) -{ - /* write b32 of name, leave one for length */ - int ret; - if(max < hashlen*2+1) /* quick approx of b32, as if hexb16 */ - return 0; - ret = sldns_b32_ntop_extended_hex(hash, hashlen, (char*)buf+1, max-1); - if(ret < 1) - return 0; - buf[0] = (uint8_t)ret; /* length of b32 label */ - ret++; - if(max - ret < zonelen) - return 0; - memmove(buf+ret, zone, zonelen); - return zonelen+(size_t)ret; -} - -size_t nsec3_get_nextowner_b32(struct ub_packed_rrset_key* rrset, int r, - uint8_t* buf, size_t max) -{ - uint8_t* nm, *zone; - size_t nmlen, zonelen; - if(!nsec3_get_nextowner(rrset, r, &nm, &nmlen)) - return 0; - /* append zone name; the owner name must be <b32>.zone */ - zone = rrset->rk.dname; - zonelen = rrset->rk.dname_len; - dname_remove_label(&zone, &zonelen); - return nsec3_hash_to_b32(nm, nmlen, zone, zonelen, buf, max); -} - -int -nsec3_has_type(struct ub_packed_rrset_key* rrset, int r, uint16_t type) -{ - uint8_t* bitmap; - size_t bitlen, skiplen; - struct packed_rrset_data* d = (struct packed_rrset_data*) - rrset->entry.data; - log_assert(d && r < (int)d->count); - skiplen = 2+4; - /* skip salt */ - if(d->rr_len[r] < skiplen+1) - return 0; /* malformed, too short */ - skiplen += 1+(size_t)d->rr_data[r][skiplen]; - /* skip next hashed owner */ - if(d->rr_len[r] < skiplen+1) - return 0; /* malformed, too short */ - skiplen += 1+(size_t)d->rr_data[r][skiplen]; - if(d->rr_len[r] < skiplen) - return 0; /* malformed, too short */ - bitlen = d->rr_len[r] - skiplen; - bitmap = d->rr_data[r]+skiplen; - return nsecbitmap_has_type_rdata(bitmap, bitlen, type); -} - -/** - * Iterate through NSEC3 list, per RR - * 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 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 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, initialised. - * @param rrnum: can be undefined on call, initialised. - * @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; -} - -/** - * Initialize the filter structure. - * Finds the zone by looking at available NSEC3 records and best match. - * (skips the unknown flag and unknown algo NSEC3s). - * - * @param filter: nsec3 filter structure. - * @param list: list of rrsets, an array of them. - * @param num: number of rrsets in list. - * @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, 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 NSEC3s 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 && - !dname_is_root(qinfo->qname)) - 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]; -} - -/** - * Determine if any of the NSEC3 rrs iteration count is too high, from key. - * @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 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); - - 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 */ -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); -} - -size_t -nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, - size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) -{ - size_t i, hash_len; - /* prepare buffer for first iteration */ - sldns_buffer_clear(buf); - sldns_buffer_write(buf, nm, nmlen); - query_dname_tolower(sldns_buffer_begin(buf)); - sldns_buffer_write(buf, salt, saltlen); - sldns_buffer_flip(buf); - hash_len = nsec3_hash_algo_size_supported(algo); - if(hash_len == 0) { - log_err("nsec3 hash of unknown algo %d", algo); - return 0; - } - if(hash_len > max) - return 0; - if(!secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), - sldns_buffer_limit(buf), (unsigned char*)res)) - return 0; - for(i=0; i<iter; i++) { - sldns_buffer_clear(buf); - sldns_buffer_write(buf, res, hash_len); - sldns_buffer_write(buf, salt, saltlen); - sldns_buffer_flip(buf); - if(!secalgo_nsec3_hash(algo, - (unsigned char*)sldns_buffer_begin(buf), - sldns_buffer_limit(buf), (unsigned char*)res)) - return 0; - } - return hash_len; -} - -/** perform hash of name */ -static int -nsec3_calc_hash(struct regional* region, sldns_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 */ - sldns_buffer_clear(buf); - sldns_buffer_write(buf, c->dname, c->dname_len); - query_dname_tolower(sldns_buffer_begin(buf)); - sldns_buffer_write(buf, salt, saltlen); - sldns_buffer_flip(buf); - c->hash_len = nsec3_hash_algo_size_supported(algo); - if(c->hash_len == 0) { - log_err("nsec3 hash of unknown algo %d", algo); - return -1; - } - c->hash = (uint8_t*)regional_alloc(region, c->hash_len); - if(!c->hash) - return 0; - (void)secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), - sldns_buffer_limit(buf), (unsigned char*)c->hash); - for(i=0; i<iter; i++) { - sldns_buffer_clear(buf); - sldns_buffer_write(buf, c->hash, c->hash_len); - sldns_buffer_write(buf, salt, saltlen); - sldns_buffer_flip(buf); - (void)secalgo_nsec3_hash(algo, - (unsigned char*)sldns_buffer_begin(buf), - sldns_buffer_limit(buf), (unsigned char*)c->hash); - } - return 1; -} - -/** perform b32 encoding of hash */ -static int -nsec3_calc_b32(struct regional* region, sldns_buffer* buf, - struct nsec3_cached_hash* c) -{ - int r; - sldns_buffer_clear(buf); - r = sldns_b32_ntop_extended_hex(c->hash, c->hash_len, - (char*)sldns_buffer_begin(buf), sldns_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 = regional_alloc_init(region, sldns_buffer_begin(buf), - c->b32_len); - if(!c->b32) - return 0; - return 1; -} - -int -nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_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; -#ifdef UNBOUND_DEBUG - rbnode_type* n; -#endif - 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*)regional_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; -#ifdef UNBOUND_DEBUG - n = -#else - (void) -#endif - rbtree_insert(table, &c->node); - log_assert(n); /* cannot be duplicate, just did lookup */ - *hash = c; - return 1; -} - -/** - * compare a label lowercased - */ -static int -label_compare_lower(uint8_t* lab1, uint8_t* lab2, size_t lablen) -{ - size_t i; - for(i=0; i<lablen; i++) { - if(tolower((unsigned char)*lab1) != tolower((unsigned char)*lab2)) { - if(tolower((unsigned char)*lab1) < tolower((unsigned char)*lab2)) - return -1; - return 1; - } - lab1++; - lab2++; - } - return 0; -} - -/** - * Compare a hashed name with the owner name of an NSEC3 RRset. - * @param flt: filter with zone name. - * @param hash: the hashed name. - * @param s: rrset with owner name. - * @return true if matches exactly, false if not. - */ -static int -nsec3_hash_matches_owner(struct nsec3_filter* flt, - struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s) -{ - uint8_t* nm = s->rk.dname; - /* compare, does hash of name based on params in this NSEC3 - * match the owner name of this NSEC3? - * name must be: <hashlength>base32 . zone name - * so; first label must not be root label (not zero length), - * and match the b32 encoded hash length, - * and the label content match the b32 encoded hash - * and the rest must be the zone name. - */ - if(hash->b32_len != 0 && (size_t)nm[0] == hash->b32_len && - label_compare_lower(nm+1, hash->b32, hash->b32_len) == 0 && - query_dname_compare(nm+(size_t)nm[0]+1, flt->zone) == 0) { - return 1; - } - return 0; -} - -/** - * Find matching NSEC3 - * Find the NSEC3Record that matches a hash of a name. - * @param env: module environment with temporary region and buffer. - * @param flt: the NSEC3 RR filter, contains zone name and RRs. - * @param ct: cached hashes table. - * @param nm: name to look for. - * @param nmlen: length of name. - * @param rrset: nsec3 that matches is returned here. - * @param rr: rr number in nsec3 rrset that matches. - * @return true if a matching NSEC3 is found, false if not. - */ -static int -find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, uint8_t* nm, size_t nmlen, - struct ub_packed_rrset_key** rrset, int* rr) -{ - size_t i_rs; - int i_rr; - struct ub_packed_rrset_key* s; - struct nsec3_cached_hash* hash; - int r; - - /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ - for(s=filter_first(flt, &i_rs, &i_rr); s; - s=filter_next(flt, &i_rs, &i_rr)) { - /* get name hashed for this NSEC3 RR */ - r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, - s, i_rr, nm, nmlen, &hash); - if(r == 0) { - log_err("nsec3: malloc failure"); - break; /* alloc failure */ - } else if(r < 0) - continue; /* malformed NSEC3 */ - else if(nsec3_hash_matches_owner(flt, hash, s)) { - *rrset = s; /* rrset with this name */ - *rr = i_rr; /* matches hash with these parameters */ - return 1; - } - } - *rrset = NULL; - *rr = 0; - return 0; -} - -int -nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, - struct ub_packed_rrset_key* rrset, int rr, sldns_buffer* buf) -{ - uint8_t* next, *owner; - size_t nextlen; - int len; - if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen)) - return 0; /* malformed RR proves nothing */ - - /* check the owner name is a hashed value . apex - * base32 encoded values must have equal length. - * hash_value and next hash value must have equal length. */ - if(nextlen != hash->hash_len || hash->hash_len==0||hash->b32_len==0|| - (size_t)*rrset->rk.dname != hash->b32_len || - query_dname_compare(rrset->rk.dname+1+ - (size_t)*rrset->rk.dname, zone) != 0) - return 0; /* bad lengths or owner name */ - - /* This is the "normal case: owner < next and owner < hash < next */ - if(label_compare_lower(rrset->rk.dname+1, hash->b32, - hash->b32_len) < 0 && - memcmp(hash->hash, next, nextlen) < 0) - return 1; - - /* convert owner name from text to binary */ - sldns_buffer_clear(buf); - owner = sldns_buffer_begin(buf); - len = sldns_b32_pton_extended_hex((char*)rrset->rk.dname+1, - hash->b32_len, owner, sldns_buffer_limit(buf)); - if(len<1) - return 0; /* bad owner name in some way */ - if((size_t)len != hash->hash_len || (size_t)len != nextlen) - return 0; /* wrong length */ - - /* this is the end of zone case: next <= owner && - * (hash > owner || hash < next) - * this also covers the only-apex case of next==owner. - */ - if(memcmp(next, owner, nextlen) <= 0 && - ( memcmp(hash->hash, owner, nextlen) > 0 || - memcmp(hash->hash, next, nextlen) < 0)) { - return 1; - } - return 0; -} - -/** - * findCoveringNSEC3 - * Given a name, find a covering NSEC3 from among a list of NSEC3s. - * - * @param env: module environment with temporary region and buffer. - * @param flt: the NSEC3 RR filter, contains zone name and RRs. - * @param ct: cached hashes table. - * @param nm: name to check if covered. - * @param nmlen: length of name. - * @param rrset: covering NSEC3 rrset is returned here. - * @param rr: rr of cover is returned here. - * @return true if a covering NSEC3 is found, false if not. - */ -static int -find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, uint8_t* nm, size_t nmlen, - struct ub_packed_rrset_key** rrset, int* rr) -{ - size_t i_rs; - int i_rr; - struct ub_packed_rrset_key* s; - struct nsec3_cached_hash* hash; - int r; - - /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ - for(s=filter_first(flt, &i_rs, &i_rr); s; - s=filter_next(flt, &i_rs, &i_rr)) { - /* get name hashed for this NSEC3 RR */ - r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, - s, i_rr, nm, nmlen, &hash); - if(r == 0) { - log_err("nsec3: malloc failure"); - break; /* alloc failure */ - } else if(r < 0) - continue; /* malformed NSEC3 */ - else if(nsec3_covers(flt->zone, hash, s, i_rr, - env->scratch_buffer)) { - *rrset = s; /* rrset with this name */ - *rr = i_rr; /* covers hash with these parameters */ - return 1; - } - } - *rrset = NULL; - *rr = 0; - return 0; -} - -/** - * findClosestEncloser - * Given a name and a list of NSEC3s, find the candidate closest encloser. - * This will be the first ancestor of 'name' (including itself) to have a - * matching NSEC3 RR. - * @param env: module environment with temporary region and buffer. - * @param flt: the NSEC3 RR filter, contains zone name and RRs. - * @param ct: cached hashes table. - * @param qinfo: query that is verified for. - * @param ce: closest encloser information is returned in here. - * @return true if a closest encloser candidate is found, false if not. - */ -static int -nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo, struct ce_response* ce) -{ - uint8_t* nm = qinfo->qname; - size_t nmlen = qinfo->qname_len; - - /* This scans from longest name to shortest, so the first match - * we find is the only viable candidate. */ - - /* (David:) FIXME: modify so that the NSEC3 matching the zone apex need - * not be present. (Mark Andrews idea). - * (Wouter:) But make sure you check for DNAME bit in zone apex, - * if the NSEC3 you find is the only NSEC3 in the zone, then this - * may be the case. */ - - while(dname_subdomain_c(nm, flt->zone)) { - if(find_matching_nsec3(env, flt, ct, nm, nmlen, - &ce->ce_rrset, &ce->ce_rr)) { - ce->ce = nm; - ce->ce_len = nmlen; - return 1; - } - dname_remove_label(&nm, &nmlen); - } - return 0; -} - -/** - * Given a qname and its proven closest encloser, calculate the "next - * closest" name. Basically, this is the name that is one label longer than - * the closest encloser that is still a subdomain of qname. - * - * @param qname: query name. - * @param qnamelen: length of qname. - * @param ce: closest encloser - * @param nm: result name. - * @param nmlen: length of nm. - */ -static void -next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce, - uint8_t** nm, size_t* nmlen) -{ - int strip = dname_count_labels(qname) - dname_count_labels(ce) -1; - *nm = qname; - *nmlen = qnamelen; - if(strip>0) - dname_remove_labels(nm, nmlen, strip); -} - -/** - * proveClosestEncloser - * Given a List of nsec3 RRs, find and prove the closest encloser to qname. - * @param env: module environment with temporary region and buffer. - * @param flt: the NSEC3 RR filter, contains zone name and RRs. - * @param ct: cached hashes table. - * @param qinfo: query that is verified for. - * @param prove_does_not_exist: If true, then if the closest encloser - * turns out to be qname, then null is returned. - * If set true, and the return value is true, then you can be - * certain that the ce.nc_rrset and ce.nc_rr are set properly. - * @param ce: closest encloser information is returned in here. - * @return bogus if no closest encloser could be proven. - * secure if a closest encloser could be proven, ce is set. - * insecure if the closest-encloser candidate turns out to prove - * that an insecure delegation exists above the qname. - */ -static enum sec_status -nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo, int prove_does_not_exist, - struct ce_response* ce) -{ - uint8_t* nc; - size_t nc_len; - /* robust: clean out ce, in case it gets abused later */ - memset(ce, 0, sizeof(*ce)); - - if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce)) { - verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " - "not find a candidate for the closest encloser."); - return sec_status_bogus; - } - log_nametypeclass(VERB_ALGO, "ce candidate", ce->ce, 0, 0); - - if(query_dname_compare(ce->ce, qinfo->qname) == 0) { - if(prove_does_not_exist) { - verbose(VERB_ALGO, "nsec3 proveClosestEncloser: " - "proved that qname existed, bad"); - return sec_status_bogus; - } - /* otherwise, we need to nothing else to prove that qname - * is its own closest encloser. */ - return sec_status_secure; - } - - /* If the closest encloser is actually a delegation, then the - * response should have been a referral. If it is a DNAME, then - * it should have been a DNAME response. */ - if(nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_NS) && - !nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_SOA)) { - if(!nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_DS)) { - verbose(VERB_ALGO, "nsec3 proveClosestEncloser: " - "closest encloser is insecure delegation"); - return sec_status_insecure; - } - verbose(VERB_ALGO, "nsec3 proveClosestEncloser: closest " - "encloser was a delegation, bad"); - return sec_status_bogus; - } - if(nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_DNAME)) { - verbose(VERB_ALGO, "nsec3 proveClosestEncloser: closest " - "encloser was a DNAME, bad"); - return sec_status_bogus; - } - - /* Otherwise, we need to show that the next closer name is covered. */ - next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len); - if(!find_covering_nsec3(env, flt, ct, nc, nc_len, - &ce->nc_rrset, &ce->nc_rr)) { - verbose(VERB_ALGO, "nsec3: Could not find proof that the " - "candidate encloser was the closest encloser"); - return sec_status_bogus; - } - return sec_status_secure; -} - -/** allocate a wildcard for the closest encloser */ -static uint8_t* -nsec3_ce_wildcard(struct regional* region, uint8_t* ce, size_t celen, - size_t* len) -{ - uint8_t* nm; - if(celen > LDNS_MAX_DOMAINLEN - 2) - return 0; /* too long */ - nm = (uint8_t*)regional_alloc(region, celen+2); - if(!nm) { - log_err("nsec3 wildcard: out of memory"); - return 0; /* alloc failure */ - } - nm[0] = 1; - nm[1] = (uint8_t)'*'; /* wildcard label */ - memmove(nm+2, ce, celen); - *len = celen+2; - return nm; -} - -/** Do the name error proof */ -static enum sec_status -nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo) -{ - struct ce_response ce; - uint8_t* wc; - size_t wclen; - struct ub_packed_rrset_key* wc_rrset; - int wc_rr; - enum sec_status sec; - - /* First locate and prove the closest encloser to qname. We will - * use the variant that fails if the closest encloser turns out - * to be qname. */ - sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); - if(sec != sec_status_secure) { - if(sec == sec_status_bogus) - verbose(VERB_ALGO, "nsec3 nameerror proof: failed " - "to prove a closest encloser"); - else verbose(VERB_ALGO, "nsec3 nameerror proof: closest " - "nsec3 is an insecure delegation"); - return sec; - } - log_nametypeclass(VERB_ALGO, "nsec3 namerror: proven ce=", ce.ce,0,0); - - /* At this point, we know that qname does not exist. Now we need - * to prove that the wildcard does not exist. */ - log_assert(ce.ce); - wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); - if(!wc || !find_covering_nsec3(env, flt, ct, wc, wclen, - &wc_rrset, &wc_rr)) { - verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " - "that the applicable wildcard did not exist."); - return sec_status_bogus; - } - - if(ce.nc_rrset && nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) { - verbose(VERB_ALGO, "nsec3 nameerror proof: nc has optout"); - return sec_status_insecure; - } - return sec_status_secure; -} - -enum sec_status -nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey) -{ - rbtree_type ct; - struct nsec3_filter flt; - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ - if(nsec3_iteration_count_high(ve, &flt, kkey)) - return sec_status_insecure; /* iteration count too high */ - log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", - flt.zone, 0, 0); - return nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); -} - -/* - * No code to handle qtype=NSEC3 specially. - * This existed in early drafts, but was later (-05) removed. - */ - -/** Do the nodata proof */ -static enum sec_status -nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - rbtree_type* ct, struct query_info* qinfo) -{ - struct ce_response ce; - uint8_t* wc; - size_t wclen; - struct ub_packed_rrset_key* rrset; - int rr; - enum sec_status sec; - - if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len, - &rrset, &rr)) { - /* cases 1 and 2 */ - if(nsec3_has_type(rrset, rr, qinfo->qtype)) { - verbose(VERB_ALGO, "proveNodata: Matching NSEC3 " - "proved that type existed, bogus"); - return sec_status_bogus; - } else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_CNAME)) { - verbose(VERB_ALGO, "proveNodata: Matching NSEC3 " - "proved that a CNAME existed, bogus"); - return sec_status_bogus; - } - - /* - * If type DS: filter_init zone find already found a parent - * zone, so this nsec3 is from a parent zone. - * o can be not a delegation (unusual query for normal name, - * no DS anyway, but we can verify that). - * o can be a delegation (which is the usual DS check). - * o may not have the SOA bit set (only the top of the - * zone, which must have been above the name, has that). - * Except for the root; which is checked by itself. - * - * If not type DS: matching nsec3 must not be a delegation. - */ - if(qinfo->qtype == LDNS_RR_TYPE_DS && qinfo->qname_len != 1 - && nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA) && - !dname_is_root(qinfo->qname)) { - verbose(VERB_ALGO, "proveNodata: apex NSEC3 " - "abused for no DS proof, bogus"); - return sec_status_bogus; - } else if(qinfo->qtype != LDNS_RR_TYPE_DS && - nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS) && - !nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) { - if(!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_DS)) { - verbose(VERB_ALGO, "proveNodata: matching " - "NSEC3 is insecure delegation"); - return sec_status_insecure; - } - verbose(VERB_ALGO, "proveNodata: matching " - "NSEC3 is a delegation, bogus"); - return sec_status_bogus; - } - return sec_status_secure; - } - - /* For cases 3 - 5, we need the proven closest encloser, and it - * can't match qname. Although, at this point, we know that it - * won't since we just checked that. */ - sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); - if(sec == sec_status_bogus) { - verbose(VERB_ALGO, "proveNodata: did not match qname, " - "nor found a proven closest encloser."); - return sec_status_bogus; - } else if(sec==sec_status_insecure && qinfo->qtype!=LDNS_RR_TYPE_DS){ - verbose(VERB_ALGO, "proveNodata: closest nsec3 is insecure " - "delegation."); - return sec_status_insecure; - } - - /* Case 3: removed */ - - /* Case 4: */ - log_assert(ce.ce); - wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); - if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr)) { - /* found wildcard */ - if(nsec3_has_type(rrset, rr, qinfo->qtype)) { - verbose(VERB_ALGO, "nsec3 nodata proof: matching " - "wildcard had qtype, bogus"); - return sec_status_bogus; - } else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_CNAME)) { - verbose(VERB_ALGO, "nsec3 nodata proof: matching " - "wildcard had a CNAME, bogus"); - return sec_status_bogus; - } - if(qinfo->qtype == LDNS_RR_TYPE_DS && qinfo->qname_len != 1 - && nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) { - verbose(VERB_ALGO, "nsec3 nodata proof: matching " - "wildcard for no DS proof has a SOA, bogus"); - return sec_status_bogus; - } else if(qinfo->qtype != LDNS_RR_TYPE_DS && - nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS) && - !nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) { - verbose(VERB_ALGO, "nsec3 nodata proof: matching " - "wildcard is a delegation, bogus"); - return sec_status_bogus; - } - /* everything is peachy keen, except for optout spans */ - if(ce.nc_rrset && nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) { - verbose(VERB_ALGO, "nsec3 nodata proof: matching " - "wildcard is in optout range, insecure"); - return sec_status_insecure; - } - return sec_status_secure; - } - - /* Case 5: */ - /* Due to forwarders, cnames, and other collating effects, we - * can see the ordinary unsigned data from a zone beneath an - * insecure delegation under an optout here */ - if(!ce.nc_rrset) { - verbose(VERB_ALGO, "nsec3 nodata proof: no next closer nsec3"); - return sec_status_bogus; - } - - /* We need to make sure that the covering NSEC3 is opt-out. */ - log_assert(ce.nc_rrset); - if(!nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) { - if(qinfo->qtype == LDNS_RR_TYPE_DS) - verbose(VERB_ALGO, "proveNodata: covering NSEC3 was not " - "opt-out in an opt-out DS NOERROR/NODATA case."); - else verbose(VERB_ALGO, "proveNodata: could not find matching " - "NSEC3, nor matching wildcard, nor optout NSEC3 " - "-- no more options, bogus."); - return sec_status_bogus; - } - /* RFC5155 section 9.2: if nc has optout then no AD flag set */ - return sec_status_insecure; -} - -enum sec_status -nsec3_prove_nodata(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey) -{ - rbtree_type ct; - struct nsec3_filter flt; - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ - if(nsec3_iteration_count_high(ve, &flt, kkey)) - return sec_status_insecure; /* iteration count too high */ - return nsec3_do_prove_nodata(env, &flt, &ct, qinfo); -} - -enum sec_status -nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc) -{ - rbtree_type ct; - struct nsec3_filter flt; - struct ce_response ce; - uint8_t* nc; - size_t nc_len; - size_t wclen; - (void)dname_count_size_labels(wc, &wclen); - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ - if(nsec3_iteration_count_high(ve, &flt, kkey)) - return sec_status_insecure; /* iteration count too high */ - - /* We know what the (purported) closest encloser is by just - * looking at the supposed generating wildcard. - * The *. has already been removed from the wc name. - */ - memset(&ce, 0, sizeof(ce)); - ce.ce = wc; - ce.ce_len = wclen; - - /* Now we still need to prove that the original data did not exist. - * Otherwise, we need to show that the next closer name is covered. */ - next_closer(qinfo->qname, qinfo->qname_len, ce.ce, &nc, &nc_len); - if(!find_covering_nsec3(env, &flt, &ct, nc, nc_len, - &ce.nc_rrset, &ce.nc_rr)) { - verbose(VERB_ALGO, "proveWildcard: did not find a covering " - "NSEC3 that covered the next closer name."); - return sec_status_bogus; - } - if(ce.nc_rrset && nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) { - verbose(VERB_ALGO, "proveWildcard: NSEC3 optout"); - return sec_status_insecure; - } - return sec_status_secure; -} - -/** test if list is all secure */ -static int -list_is_secure(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct key_entry_key* kkey, char** reason) -{ - struct packed_rrset_data* d; - size_t i; - for(i=0; i<num; i++) { - d = (struct packed_rrset_data*)list[i]->entry.data; - if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3)) - continue; - if(d->security == sec_status_secure) - continue; - rrset_check_sec_status(env->rrset_cache, list[i], *env->now); - if(d->security == sec_status_secure) - continue; - d->security = val_verify_rrset_entry(env, ve, list[i], kkey, - reason); - if(d->security != sec_status_secure) { - verbose(VERB_ALGO, "NSEC3 did not verify"); - return 0; - } - rrset_update_sec_status(env->rrset_cache, list[i], *env->now); - } - return 1; -} - -enum sec_status -nsec3_prove_nods(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, char** reason) -{ - rbtree_type ct; - struct nsec3_filter flt; - struct ce_response ce; - struct ub_packed_rrset_key* rrset; - int rr; - log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) { - *reason = "no valid NSEC3s"; - return sec_status_bogus; /* no valid NSEC3s, bogus */ - } - if(!list_is_secure(env, ve, list, num, kkey, reason)) - return sec_status_bogus; /* not all NSEC3 records secure */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) { - *reason = "no NSEC3 records"; - return sec_status_bogus; /* no RRs */ - } - if(nsec3_iteration_count_high(ve, &flt, kkey)) - return sec_status_insecure; /* iteration count too high */ - - /* Look for a matching NSEC3 to qname -- this is the normal - * NODATA case. */ - if(find_matching_nsec3(env, &flt, &ct, qinfo->qname, qinfo->qname_len, - &rrset, &rr)) { - /* If the matching NSEC3 has the SOA bit set, it is from - * the wrong zone (the child instead of the parent). If - * it has the DS bit set, then we were lied to. */ - if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA) && - qinfo->qname_len != 1) { - verbose(VERB_ALGO, "nsec3 provenods: NSEC3 is from" - " child zone, bogus"); - *reason = "NSEC3 from child zone"; - return sec_status_bogus; - } else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_DS)) { - verbose(VERB_ALGO, "nsec3 provenods: NSEC3 has qtype" - " DS, bogus"); - *reason = "NSEC3 has DS in bitmap"; - return sec_status_bogus; - } - /* If the NSEC3 RR doesn't have the NS bit set, then - * this wasn't a delegation point. */ - if(!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS)) - return sec_status_indeterminate; - /* Otherwise, this proves no DS. */ - return sec_status_secure; - } - - /* Otherwise, we are probably in the opt-out case. */ - if(nsec3_prove_closest_encloser(env, &flt, &ct, qinfo, 1, &ce) - != sec_status_secure) { - /* an insecure delegation *above* the qname does not prove - * anything about this qname exactly, and bogus is bogus */ - verbose(VERB_ALGO, "nsec3 provenods: did not match qname, " - "nor found a proven closest encloser."); - *reason = "no NSEC3 closest encloser"; - return sec_status_bogus; - } - - /* robust extra check */ - if(!ce.nc_rrset) { - verbose(VERB_ALGO, "nsec3 nods proof: no next closer nsec3"); - *reason = "no NSEC3 next closer"; - return sec_status_bogus; - } - - /* we had the closest encloser proof, then we need to check that the - * covering NSEC3 was opt-out -- the proveClosestEncloser step already - * checked to see if the closest encloser was a delegation or DNAME. - */ - log_assert(ce.nc_rrset); - if(!nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) { - verbose(VERB_ALGO, "nsec3 provenods: covering NSEC3 was not " - "opt-out in an opt-out DS NOERROR/NODATA case."); - *reason = "covering NSEC3 was not opt-out in an opt-out " - "DS NOERROR/NODATA case"; - return sec_status_bogus; - } - /* RFC5155 section 9.2: if nc has optout then no AD flag set */ - return sec_status_insecure; -} - -enum sec_status -nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, int* nodata) -{ - enum sec_status sec, secnx; - rbtree_type ct; - struct nsec3_filter flt; - *nodata = 0; - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ - rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ - if(nsec3_iteration_count_high(ve, &flt, kkey)) - return sec_status_insecure; /* iteration count too high */ - - /* try nxdomain and nodata after another, while keeping the - * hash cache intact */ - - secnx = nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); - if(secnx==sec_status_secure) - return sec_status_secure; - sec = nsec3_do_prove_nodata(env, &flt, &ct, qinfo); - if(sec==sec_status_secure) { - *nodata = 1; - } else if(sec == sec_status_insecure) { - *nodata = 1; - } else if(secnx == sec_status_insecure) { - sec = sec_status_insecure; - } - return sec; -} diff --git a/external/unbound/validator/val_nsec3.h b/external/unbound/validator/val_nsec3.h deleted file mode 100644 index 27e9f9eac..000000000 --- a/external/unbound/validator/val_nsec3.h +++ /dev/null @@ -1,380 +0,0 @@ -/* - * validator/val_nsec3.h - validator NSEC3 denial of existence functions. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - * The functions help with NSEC3 checking, the different NSEC3 proofs - * for denial of existence, 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 -#define VALIDATOR_VAL_NSEC3_H -#include "util/rbtree.h" -#include "util/data/packed_rrset.h" -struct val_env; -struct regional; -struct module_env; -struct ub_packed_rrset_key; -struct reply_info; -struct query_info; -struct key_entry_key; -struct sldns_buffer; - -/** - * 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 - -/** - * Determine if the set of NSEC3 records provided with a response prove NAME - * ERROR. This means that the NSEC3s prove a) the closest encloser exists, - * b) the direct child of the closest encloser towards qname doesn't exist, - * and c) *.closest encloser does not exist. - * - * @param env: module environment with temporary region and buffer. - * @param ve: validator environment, with iteration count settings. - * @param list: array of RRsets, some of which are NSEC3s. - * @param num: number of RRsets in the array to examine. - * @param qinfo: query that is verified for. - * @param kkey: key entry that signed the NSEC3s. - * @return: - * sec_status SECURE of the Name Error is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. - */ -enum sec_status -nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey); - -/** - * Determine if the NSEC3s provided in a response prove the NOERROR/NODATA - * status. There are a number of different variants to this: - * - * 1) Normal NODATA -- qname is matched to an NSEC3 record, type is not - * present. - * - * 2) ENT NODATA -- because there must be NSEC3 record for - * empty-non-terminals, this is the same as #1. - * - * 3) NSEC3 ownername NODATA -- qname matched an existing, lone NSEC3 - * ownername, but qtype was not NSEC3. NOTE: as of nsec-05, this case no - * longer exists. - * - * 4) Wildcard NODATA -- A wildcard matched the name, but not the type. - * - * 5) Opt-In DS NODATA -- the qname is covered by an opt-in span and qtype == - * DS. (or maybe some future record with the same parent-side-only property) - * - * @param env: module environment with temporary region and buffer. - * @param ve: validator environment, with iteration count settings. - * @param list: array of RRsets, some of which are NSEC3s. - * @param num: number of RRsets in the array to examine. - * @param qinfo: query that is verified for. - * @param kkey: key entry that signed the NSEC3s. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. - */ -enum sec_status -nsec3_prove_nodata(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey); - - -/** - * Prove that a positive wildcard match was appropriate (no direct match - * RRset). - * - * @param env: module environment with temporary region and buffer. - * @param ve: validator environment, with iteration count settings. - * @param list: array of RRsets, some of which are NSEC3s. - * @param num: number of RRsets in the array to examine. - * @param qinfo: query that is verified for. - * @param kkey: key entry that signed the NSEC3s. - * @param wc: The purported wildcard that matched. This is the wildcard name - * as *.wildcard.name., with the *. label already removed. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. - */ -enum sec_status -nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc); - -/** - * Prove that a DS response either had no DS, or wasn't a delegation point. - * - * Fundamentally there are two cases here: normal NODATA and Opt-In NODATA. - * - * @param env: module environment with temporary region and buffer. - * @param ve: validator environment, with iteration count settings. - * @param list: array of RRsets, some of which are NSEC3s. - * @param num: number of RRsets in the array to examine. - * @param qinfo: query that is verified for. - * @param kkey: key entry that signed the NSEC3s. - * @param reason: string for bogus result. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. - * or if there was no DS in an insecure (i.e., opt-in) way, - * INDETERMINATE if it was clear that this wasn't a delegation point. - */ -enum sec_status -nsec3_prove_nods(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, char** reason); - -/** - * Prove NXDOMAIN or NODATA. - * - * @param env: module environment with temporary region and buffer. - * @param ve: validator environment, with iteration count settings. - * @param list: array of RRsets, some of which are NSEC3s. - * @param num: number of RRsets in the array to examine. - * @param qinfo: query that is verified for. - * @param kkey: key entry that signed the NSEC3s. - * @param nodata: if return value is secure, this indicates if nodata or - * nxdomain was proven. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. - */ -enum sec_status -nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, int* nodata); - -/** - * The NSEC3 hash result storage. - * Consists of an rbtree, with these nodes in it. - * The nodes detail how a set of parameters (from nsec3 rr) plus - * a dname result in a hash. - */ -struct nsec3_cached_hash { - /** rbtree node, key is this structure */ - rbnode_type node; - /** where are the parameters for conversion, in this rrset data */ - struct ub_packed_rrset_key* nsec3; - /** where are the parameters for conversion, this RR number in data */ - int rr; - /** the name to convert */ - uint8_t* dname; - /** length of the dname */ - size_t dname_len; - /** the hash result (not base32 encoded) */ - uint8_t* hash; - /** length of hash in bytes */ - size_t hash_len; - /** the hash result in base32 encoding */ - uint8_t* b32; - /** length of base32 encoding (as a label) */ - size_t b32_len; -}; - -/** - * Rbtree for hash cache comparison function. - * @param c1: key 1. - * @param c2: key 2. - * @return: comparison code, -1, 0, 1, of the keys. - */ -int nsec3_hash_cmp(const void* c1, const void* c2); - -/** - * Obtain the hash of an owner name. - * Used internally by the nsec3 proof functions in this file. - * published to enable unit testing of hash algorithms and cache. - * - * @param table: the cache table. Must be initialised at start. - * @param region: scratch region to use for allocation. - * This region holds the tree, if you wipe the region, reinit the tree. - * @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 - * This pointer is used inside the tree, assumed region-alloced. - * @param dname_len: the length of the name. - * @param hash: the hash node is returned on success. - * @return: - * 1 on success, either from cache or newly hashed hash is returned. - * 0 on a malloc failure. - * -1 if the NSEC3 rr was badly formatted (i.e. formerr). - */ -int nsec3_hash_name(rbtree_type* table, struct regional* region, - struct sldns_buffer* buf, struct ub_packed_rrset_key* nsec3, int rr, - uint8_t* dname, size_t dname_len, struct nsec3_cached_hash** hash); - -/** - * Get next owner name, converted to base32 encoding and with the - * zone name (taken from the nsec3 owner name) appended. - * @param rrset: the NSEC3 rrset. - * @param r: the rr num of the nsec3 in the rrset. - * @param buf: buffer to store name in - * @param max: size of buffer. - * @return length of name on success. 0 on failure (buffer too short or - * bad format nsec3 record). - */ -size_t nsec3_get_nextowner_b32(struct ub_packed_rrset_key* rrset, int r, - uint8_t* buf, size_t max); - -/** - * Convert hash into base32 encoding and with the - * zone name appended. - * @param hash: hashed buffer - * @param hashlen: length of hash - * @param zone: name of zone - * @param zonelen: length of zonename. - * @param buf: buffer to store name in - * @param max: size of buffer. - * @return length of name on success. 0 on failure (buffer too short or - * bad format nsec3 record). - */ -size_t nsec3_hash_to_b32(uint8_t* hash, size_t hashlen, uint8_t* zone, - size_t zonelen, uint8_t* buf, size_t max); - -/** - * Get NSEC3 parameters out of rr. - * @param rrset: the NSEC3 rrset. - * @param r: the rr num of the nsec3 in the rrset. - * @param algo: nsec3 hash algo. - * @param iter: iteration count. - * @param salt: ptr to salt inside rdata. - * @param saltlen: length of salt. - * @return 0 if bad formatted, unknown nsec3 hash algo, or unknown flags set. - */ -int nsec3_get_params(struct ub_packed_rrset_key* rrset, int r, - int* algo, size_t* iter, uint8_t** salt, size_t* saltlen); - -/** - * Get NSEC3 hashed in a buffer - * @param buf: buffer for temp use. - * @param nm: name to hash - * @param nmlen: length of nm. - * @param algo: algo to use, must be known. - * @param iter: iterations - * @param salt: salt for nsec3 - * @param saltlen: length of salt. - * @param res: result of hash stored here. - * @param max: maximum space for result. - * @return 0 on failure, otherwise bytelength stored. - */ -size_t nsec3_get_hashed(struct sldns_buffer* buf, uint8_t* nm, size_t nmlen, - int algo, size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, - size_t max); - -/** - * see if NSEC3 RR contains given type - * @param rrset: NSEC3 rrset - * @param r: RR in rrset - * @param type: in host order to check bit for. - * @return true if bit set, false if not or error. - */ -int nsec3_has_type(struct ub_packed_rrset_key* rrset, int r, uint16_t type); - -/** - * return if nsec3 RR has the optout flag - * @param rrset: NSEC3 rrset - * @param r: RR in rrset - * @return true if optout, false on error or not optout - */ -int nsec3_has_optout(struct ub_packed_rrset_key* rrset, int r); - -/** - * Return nsec3 RR next hashed owner name - * @param rrset: NSEC3 rrset - * @param r: RR in rrset - * @param next: ptr into rdata to next owner hash - * @param nextlen: length of hash. - * @return false on malformed - */ -int nsec3_get_nextowner(struct ub_packed_rrset_key* rrset, int r, - uint8_t** next, size_t* nextlen); - -/** - * nsec3Covers - * Given a hash and a candidate NSEC3Record, determine if that NSEC3Record - * covers the hash. Covers specifically means that the hash is in between - * the owner and next hashes and does not equal either. - * - * @param zone: the zone name. - * @param hash: the hash of the name - * @param rrset: the rrset of the NSEC3. - * @param rr: which rr in the rrset. - * @param buf: temporary buffer. - * @return true if covers, false if not. - */ -int nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, - struct ub_packed_rrset_key* rrset, int rr, struct sldns_buffer* buf); - -#endif /* VALIDATOR_VAL_NSEC3_H */ diff --git a/external/unbound/validator/val_secalgo.c b/external/unbound/validator/val_secalgo.c deleted file mode 100644 index be88ff438..000000000 --- a/external/unbound/validator/val_secalgo.c +++ /dev/null @@ -1,1753 +0,0 @@ -/* - * validator/val_secalgo.c - validator security algorithm functions. - * - * Copyright (c) 2012, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - * These functions take raw data buffers, formatted for crypto verification, - * and do the library calls (for the crypto library in use). - */ -#include "config.h" -/* packed_rrset on top to define enum types (forced by c99 standard) */ -#include "util/data/packed_rrset.h" -#include "validator/val_secalgo.h" -#include "validator/val_nsec3.h" -#include "util/log.h" -#include "sldns/rrdef.h" -#include "sldns/keyraw.h" -#include "sldns/sbuffer.h" - -#if !defined(HAVE_SSL) && !defined(HAVE_NSS) && !defined(HAVE_NETTLE) -#error "Need crypto library to do digital signature cryptography" -#endif - -/* OpenSSL implementation */ -#ifdef HAVE_SSL -#ifdef HAVE_OPENSSL_ERR_H -#include <openssl/err.h> -#endif - -#ifdef HAVE_OPENSSL_RAND_H -#include <openssl/rand.h> -#endif - -#ifdef HAVE_OPENSSL_CONF_H -#include <openssl/conf.h> -#endif - -#ifdef HAVE_OPENSSL_ENGINE_H -#include <openssl/engine.h> -#endif - -/** fake DSA support for unit tests */ -int fake_dsa = 0; -/** fake SHA1 support for unit tests */ -int fake_sha1 = 0; - -/* return size of digest if supported, or 0 otherwise */ -size_t -nsec3_hash_algo_size_supported(int id) -{ - switch(id) { - case NSEC3_HASH_SHA1: - return SHA_DIGEST_LENGTH; - default: - return 0; - } -} - -/* perform nsec3 hash. return false on failure */ -int -secalgo_nsec3_hash(int algo, unsigned char* buf, size_t len, - unsigned char* res) -{ - switch(algo) { - case NSEC3_HASH_SHA1: - (void)SHA1(buf, len, res); - return 1; - default: - return 0; - } -} - -void -secalgo_hash_sha256(unsigned char* buf, size_t len, unsigned char* res) -{ - (void)SHA256(buf, len, res); -} - -/** - * Return size of DS digest according to its hash algorithm. - * @param algo: DS digest algo. - * @return size in bytes of digest, or 0 if not supported. - */ -size_t -ds_digest_size_supported(int algo) -{ - switch(algo) { - case LDNS_SHA1: -#if defined(HAVE_EVP_SHA1) && defined(USE_SHA1) - return SHA_DIGEST_LENGTH; -#else - if(fake_sha1) return 20; - return 0; -#endif -#ifdef HAVE_EVP_SHA256 - case LDNS_SHA256: - return SHA256_DIGEST_LENGTH; -#endif -#ifdef USE_GOST - case LDNS_HASH_GOST: - /* we support GOST if it can be loaded */ - (void)sldns_key_EVP_load_gost_id(); - if(EVP_get_digestbyname("md_gost94")) - return 32; - else return 0; -#endif -#ifdef USE_ECDSA - case LDNS_SHA384: - return SHA384_DIGEST_LENGTH; -#endif - default: break; - } - return 0; -} - -#ifdef USE_GOST -/** Perform GOST hash */ -static int -do_gost94(unsigned char* data, size_t len, unsigned char* dest) -{ - const EVP_MD* md = EVP_get_digestbyname("md_gost94"); - if(!md) - return 0; - return sldns_digest_evp(data, (unsigned int)len, dest, md); -} -#endif - -int -secalgo_ds_digest(int algo, unsigned char* buf, size_t len, - unsigned char* res) -{ - switch(algo) { -#if defined(HAVE_EVP_SHA1) && defined(USE_SHA1) - case LDNS_SHA1: - (void)SHA1(buf, len, res); - return 1; -#endif -#ifdef HAVE_EVP_SHA256 - case LDNS_SHA256: - (void)SHA256(buf, len, res); - return 1; -#endif -#ifdef USE_GOST - case LDNS_HASH_GOST: - if(do_gost94(buf, len, res)) - return 1; - break; -#endif -#ifdef USE_ECDSA - case LDNS_SHA384: - (void)SHA384(buf, len, res); - return 1; -#endif - default: - verbose(VERB_QUERY, "unknown DS digest algorithm %d", - algo); - break; - } - return 0; -} - -/** return true if DNSKEY algorithm id is supported */ -int -dnskey_algo_id_is_supported(int id) -{ - switch(id) { - case LDNS_RSAMD5: - /* RFC 6725 deprecates RSAMD5 */ - return 0; - case LDNS_DSA: - case LDNS_DSA_NSEC3: -#if defined(USE_DSA) && defined(USE_SHA1) - return 1; -#else - if(fake_dsa || fake_sha1) return 1; - return 0; -#endif - - case LDNS_RSASHA1: - case LDNS_RSASHA1_NSEC3: -#ifdef USE_SHA1 - return 1; -#else - if(fake_sha1) return 1; - return 0; -#endif - -#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) - case LDNS_RSASHA256: -#endif -#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2) - case LDNS_RSASHA512: -#endif -#ifdef USE_ECDSA - case LDNS_ECDSAP256SHA256: - case LDNS_ECDSAP384SHA384: -#endif -#if (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2)) || defined(USE_ECDSA) - return 1; -#endif - -#ifdef USE_GOST - case LDNS_ECC_GOST: - /* we support GOST if it can be loaded */ - return sldns_key_EVP_load_gost_id(); -#endif - default: - return 0; - } -} - -/** - * Output a libcrypto openssl error to the logfile. - * @param str: string to add to it. - * @param e: the error to output, error number from ERR_get_error(). - */ -static void -log_crypto_error(const char* str, unsigned long e) -{ - char buf[128]; - /* or use ERR_error_string if ERR_error_string_n is not avail TODO */ - ERR_error_string_n(e, buf, sizeof(buf)); - /* buf now contains */ - /* error:[error code]:[library name]:[function name]:[reason string] */ - log_err("%s crypto %s", str, buf); -} - -#ifdef USE_DSA -/** - * Setup DSA key digest in DER encoding ... - * @param sig: input is signature output alloced ptr (unless failure). - * caller must free alloced ptr if this routine returns true. - * @param len: input is initial siglen, output is output len. - * @return false on failure. - */ -static int -setup_dsa_sig(unsigned char** sig, unsigned int* len) -{ - unsigned char* orig = *sig; - unsigned int origlen = *len; - int newlen; - BIGNUM *R, *S; - DSA_SIG *dsasig; - - /* extract the R and S field from the sig buffer */ - if(origlen < 1 + 2*SHA_DIGEST_LENGTH) - return 0; - R = BN_new(); - if(!R) return 0; - (void) BN_bin2bn(orig + 1, SHA_DIGEST_LENGTH, R); - S = BN_new(); - if(!S) return 0; - (void) BN_bin2bn(orig + 21, SHA_DIGEST_LENGTH, S); - dsasig = DSA_SIG_new(); - if(!dsasig) return 0; - -#ifdef HAVE_DSA_SIG_SET0 - if(!DSA_SIG_set0(dsasig, R, S)) return 0; -#else - dsasig->r = R; - dsasig->s = S; -#endif - *sig = NULL; - newlen = i2d_DSA_SIG(dsasig, sig); - if(newlen < 0) { - DSA_SIG_free(dsasig); - free(*sig); - return 0; - } - *len = (unsigned int)newlen; - DSA_SIG_free(dsasig); - return 1; -} -#endif /* USE_DSA */ - -#ifdef USE_ECDSA -/** - * Setup the ECDSA signature in its encoding that the library wants. - * Converts from plain numbers to ASN formatted. - * @param sig: input is signature, output alloced ptr (unless failure). - * caller must free alloced ptr if this routine returns true. - * @param len: input is initial siglen, output is output len. - * @return false on failure. - */ -static int -setup_ecdsa_sig(unsigned char** sig, unsigned int* len) -{ - /* convert from two BIGNUMs in the rdata buffer, to ASN notation. - * ASN preable: 30440220 <R 32bytefor256> 0220 <S 32bytefor256> - * the '20' is the length of that field (=bnsize). -i * the '44' is the total remaining length. - * if negative, start with leading zero. - * if starts with 00s, remove them from the number. - */ - uint8_t pre[] = {0x30, 0x44, 0x02, 0x20}; - int pre_len = 4; - uint8_t mid[] = {0x02, 0x20}; - int mid_len = 2; - int raw_sig_len, r_high, s_high, r_rem=0, s_rem=0; - int bnsize = (int)((*len)/2); - unsigned char* d = *sig; - uint8_t* p; - /* if too short or not even length, fails */ - if(*len < 16 || bnsize*2 != (int)*len) - return 0; - - /* strip leading zeroes from r (but not last one) */ - while(r_rem < bnsize-1 && d[r_rem] == 0) - r_rem++; - /* strip leading zeroes from s (but not last one) */ - while(s_rem < bnsize-1 && d[bnsize+s_rem] == 0) - s_rem++; - - r_high = ((d[0+r_rem]&0x80)?1:0); - s_high = ((d[bnsize+s_rem]&0x80)?1:0); - raw_sig_len = pre_len + r_high + bnsize - r_rem + mid_len + - s_high + bnsize - s_rem; - *sig = (unsigned char*)malloc((size_t)raw_sig_len); - if(!*sig) - return 0; - p = (uint8_t*)*sig; - p[0] = pre[0]; - p[1] = (uint8_t)(raw_sig_len-2); - p[2] = pre[2]; - p[3] = (uint8_t)(bnsize + r_high - r_rem); - p += 4; - if(r_high) { - *p = 0; - p += 1; - } - memmove(p, d+r_rem, (size_t)bnsize-r_rem); - p += bnsize-r_rem; - memmove(p, mid, (size_t)mid_len-1); - p += mid_len-1; - *p = (uint8_t)(bnsize + s_high - s_rem); - p += 1; - if(s_high) { - *p = 0; - p += 1; - } - memmove(p, d+bnsize+s_rem, (size_t)bnsize-s_rem); - *len = (unsigned int)raw_sig_len; - return 1; -} -#endif /* USE_ECDSA */ - -#ifdef USE_ECDSA_EVP_WORKAROUND -static EVP_MD ecdsa_evp_256_md; -static EVP_MD ecdsa_evp_384_md; -void ecdsa_evp_workaround_init(void) -{ - /* openssl before 1.0.0 fixes RSA with the SHA256 - * hash in EVP. We create one for ecdsa_sha256 */ - ecdsa_evp_256_md = *EVP_sha256(); - ecdsa_evp_256_md.required_pkey_type[0] = EVP_PKEY_EC; - ecdsa_evp_256_md.verify = (void*)ECDSA_verify; - - ecdsa_evp_384_md = *EVP_sha384(); - ecdsa_evp_384_md.required_pkey_type[0] = EVP_PKEY_EC; - ecdsa_evp_384_md.verify = (void*)ECDSA_verify; -} -#endif /* USE_ECDSA_EVP_WORKAROUND */ - -/** - * Setup key and digest for verification. Adjust sig if necessary. - * - * @param algo: key algorithm - * @param evp_key: EVP PKEY public key to create. - * @param digest_type: digest type to use - * @param key: key to setup for. - * @param keylen: length of key. - * @return false on failure. - */ -static int -setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, - unsigned char* key, size_t keylen) -{ -#if defined(USE_DSA) && defined(USE_SHA1) - DSA* dsa; -#endif - RSA* rsa; - - switch(algo) { -#if defined(USE_DSA) && defined(USE_SHA1) - case LDNS_DSA: - case LDNS_DSA_NSEC3: - *evp_key = EVP_PKEY_new(); - if(!*evp_key) { - log_err("verify: malloc failure in crypto"); - return 0; - } - dsa = sldns_key_buf2dsa_raw(key, keylen); - if(!dsa) { - verbose(VERB_QUERY, "verify: " - "sldns_key_buf2dsa_raw failed"); - return 0; - } - if(EVP_PKEY_assign_DSA(*evp_key, dsa) == 0) { - verbose(VERB_QUERY, "verify: " - "EVP_PKEY_assign_DSA failed"); - return 0; - } -#ifdef HAVE_EVP_DSS1 - *digest_type = EVP_dss1(); -#else - *digest_type = EVP_sha1(); -#endif - - break; -#endif /* USE_DSA && USE_SHA1 */ - -#if defined(USE_SHA1) || (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2)) -#ifdef USE_SHA1 - case LDNS_RSASHA1: - case LDNS_RSASHA1_NSEC3: -#endif -#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) - case LDNS_RSASHA256: -#endif -#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2) - case LDNS_RSASHA512: -#endif - *evp_key = EVP_PKEY_new(); - if(!*evp_key) { - log_err("verify: malloc failure in crypto"); - return 0; - } - rsa = sldns_key_buf2rsa_raw(key, keylen); - if(!rsa) { - verbose(VERB_QUERY, "verify: " - "sldns_key_buf2rsa_raw SHA failed"); - return 0; - } - if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) { - verbose(VERB_QUERY, "verify: " - "EVP_PKEY_assign_RSA SHA failed"); - return 0; - } - - /* select SHA version */ -#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) - if(algo == LDNS_RSASHA256) - *digest_type = EVP_sha256(); - else -#endif -#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2) - if(algo == LDNS_RSASHA512) - *digest_type = EVP_sha512(); - else -#endif -#ifdef USE_SHA1 - *digest_type = EVP_sha1(); -#else - { verbose(VERB_QUERY, "no digest available"); return 0; } -#endif - break; -#endif /* defined(USE_SHA1) || (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2)) */ - - case LDNS_RSAMD5: - *evp_key = EVP_PKEY_new(); - if(!*evp_key) { - log_err("verify: malloc failure in crypto"); - return 0; - } - rsa = sldns_key_buf2rsa_raw(key, keylen); - if(!rsa) { - verbose(VERB_QUERY, "verify: " - "sldns_key_buf2rsa_raw MD5 failed"); - return 0; - } - if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) { - verbose(VERB_QUERY, "verify: " - "EVP_PKEY_assign_RSA MD5 failed"); - return 0; - } - *digest_type = EVP_md5(); - - break; -#ifdef USE_GOST - case LDNS_ECC_GOST: - *evp_key = sldns_gost2pkey_raw(key, keylen); - if(!*evp_key) { - verbose(VERB_QUERY, "verify: " - "sldns_gost2pkey_raw failed"); - return 0; - } - *digest_type = EVP_get_digestbyname("md_gost94"); - if(!*digest_type) { - verbose(VERB_QUERY, "verify: " - "EVP_getdigest md_gost94 failed"); - return 0; - } - break; -#endif -#ifdef USE_ECDSA - case LDNS_ECDSAP256SHA256: - *evp_key = sldns_ecdsa2pkey_raw(key, keylen, - LDNS_ECDSAP256SHA256); - if(!*evp_key) { - verbose(VERB_QUERY, "verify: " - "sldns_ecdsa2pkey_raw failed"); - return 0; - } -#ifdef USE_ECDSA_EVP_WORKAROUND - *digest_type = &ecdsa_evp_256_md; -#else - *digest_type = EVP_sha256(); -#endif - break; - case LDNS_ECDSAP384SHA384: - *evp_key = sldns_ecdsa2pkey_raw(key, keylen, - LDNS_ECDSAP384SHA384); - if(!*evp_key) { - verbose(VERB_QUERY, "verify: " - "sldns_ecdsa2pkey_raw failed"); - return 0; - } -#ifdef USE_ECDSA_EVP_WORKAROUND - *digest_type = &ecdsa_evp_384_md; -#else - *digest_type = EVP_sha384(); -#endif - break; -#endif /* USE_ECDSA */ - default: - verbose(VERB_QUERY, "verify: unknown algorithm %d", - algo); - return 0; - } - return 1; -} - -/** - * Check a canonical sig+rrset and signature against a dnskey - * @param buf: buffer with data to verify, the first rrsig part and the - * canonicalized rrset. - * @param algo: DNSKEY algorithm. - * @param sigblock: signature rdata field from RRSIG - * @param sigblock_len: length of sigblock data. - * @param key: public key data from DNSKEY RR. - * @param keylen: length of keydata. - * @param reason: bogus reason in more detail. - * @return secure if verification succeeded, bogus on crypto failure, - * unchecked on format errors and alloc failures. - */ -enum sec_status -verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock, - unsigned int sigblock_len, unsigned char* key, unsigned int keylen, - char** reason) -{ - const EVP_MD *digest_type; - EVP_MD_CTX* ctx; - int res, dofree = 0, docrypto_free = 0; - EVP_PKEY *evp_key = NULL; - -#ifndef USE_DSA - if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3) &&(fake_dsa||fake_sha1)) - return sec_status_secure; -#endif -#ifndef USE_SHA1 - if(fake_sha1 && (algo == LDNS_DSA || algo == LDNS_DSA_NSEC3 || algo == LDNS_RSASHA1 || algo == LDNS_RSASHA1_NSEC3)) - return sec_status_secure; -#endif - - if(!setup_key_digest(algo, &evp_key, &digest_type, key, keylen)) { - verbose(VERB_QUERY, "verify: failed to setup key"); - *reason = "use of key for crypto failed"; - EVP_PKEY_free(evp_key); - return sec_status_bogus; - } -#ifdef USE_DSA - /* if it is a DSA signature in bind format, convert to DER format */ - if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3) && - sigblock_len == 1+2*SHA_DIGEST_LENGTH) { - if(!setup_dsa_sig(&sigblock, &sigblock_len)) { - verbose(VERB_QUERY, "verify: failed to setup DSA sig"); - *reason = "use of key for DSA crypto failed"; - EVP_PKEY_free(evp_key); - return sec_status_bogus; - } - docrypto_free = 1; - } -#endif -#if defined(USE_ECDSA) && defined(USE_DSA) - else -#endif -#ifdef USE_ECDSA - if(algo == LDNS_ECDSAP256SHA256 || algo == LDNS_ECDSAP384SHA384) { - /* EVP uses ASN prefix on sig, which is not in the wire data */ - if(!setup_ecdsa_sig(&sigblock, &sigblock_len)) { - verbose(VERB_QUERY, "verify: failed to setup ECDSA sig"); - *reason = "use of signature for ECDSA crypto failed"; - EVP_PKEY_free(evp_key); - return sec_status_bogus; - } - dofree = 1; - } -#endif /* USE_ECDSA */ - - /* do the signature cryptography work */ -#ifdef HAVE_EVP_MD_CTX_NEW - ctx = EVP_MD_CTX_new(); -#else - ctx = (EVP_MD_CTX*)malloc(sizeof(*ctx)); - if(ctx) EVP_MD_CTX_init(ctx); -#endif - if(!ctx) { - log_err("EVP_MD_CTX_new: malloc failure"); - EVP_PKEY_free(evp_key); - if(dofree) free(sigblock); - else if(docrypto_free) OPENSSL_free(sigblock); - return sec_status_unchecked; - } - if(EVP_VerifyInit(ctx, digest_type) == 0) { - verbose(VERB_QUERY, "verify: EVP_VerifyInit failed"); - EVP_MD_CTX_destroy(ctx); - EVP_PKEY_free(evp_key); - if(dofree) free(sigblock); - else if(docrypto_free) OPENSSL_free(sigblock); - return sec_status_unchecked; - } - if(EVP_VerifyUpdate(ctx, (unsigned char*)sldns_buffer_begin(buf), - (unsigned int)sldns_buffer_limit(buf)) == 0) { - verbose(VERB_QUERY, "verify: EVP_VerifyUpdate failed"); - EVP_MD_CTX_destroy(ctx); - EVP_PKEY_free(evp_key); - if(dofree) free(sigblock); - else if(docrypto_free) OPENSSL_free(sigblock); - return sec_status_unchecked; - } - - res = EVP_VerifyFinal(ctx, sigblock, sigblock_len, evp_key); -#ifdef HAVE_EVP_MD_CTX_NEW - EVP_MD_CTX_destroy(ctx); -#else - EVP_MD_CTX_cleanup(ctx); - free(ctx); -#endif - EVP_PKEY_free(evp_key); - - if(dofree) free(sigblock); - else if(docrypto_free) OPENSSL_free(sigblock); - - if(res == 1) { - return sec_status_secure; - } else if(res == 0) { - verbose(VERB_QUERY, "verify: signature mismatch"); - *reason = "signature crypto failed"; - return sec_status_bogus; - } - - log_crypto_error("verify:", ERR_get_error()); - return sec_status_unchecked; -} - -/**************************************************/ -#elif defined(HAVE_NSS) -/* libnss implementation */ -/* nss3 */ -#include "sechash.h" -#include "pk11pub.h" -#include "keyhi.h" -#include "secerr.h" -#include "cryptohi.h" -/* nspr4 */ -#include "prerror.h" - -/* return size of digest if supported, or 0 otherwise */ -size_t -nsec3_hash_algo_size_supported(int id) -{ - switch(id) { - case NSEC3_HASH_SHA1: - return SHA1_LENGTH; - default: - return 0; - } -} - -/* perform nsec3 hash. return false on failure */ -int -secalgo_nsec3_hash(int algo, unsigned char* buf, size_t len, - unsigned char* res) -{ - switch(algo) { - case NSEC3_HASH_SHA1: - (void)HASH_HashBuf(HASH_AlgSHA1, res, buf, (unsigned long)len); - return 1; - default: - return 0; - } -} - -void -secalgo_hash_sha256(unsigned char* buf, size_t len, unsigned char* res) -{ - (void)HASH_HashBuf(HASH_AlgSHA256, res, buf, (unsigned long)len); -} - -size_t -ds_digest_size_supported(int algo) -{ - /* uses libNSS */ - switch(algo) { -#ifdef USE_SHA1 - case LDNS_SHA1: - return SHA1_LENGTH; -#endif -#ifdef USE_SHA2 - case LDNS_SHA256: - return SHA256_LENGTH; -#endif -#ifdef USE_ECDSA - case LDNS_SHA384: - return SHA384_LENGTH; -#endif - /* GOST not supported in NSS */ - case LDNS_HASH_GOST: - default: break; - } - return 0; -} - -int -secalgo_ds_digest(int algo, unsigned char* buf, size_t len, - unsigned char* res) -{ - /* uses libNSS */ - switch(algo) { -#ifdef USE_SHA1 - case LDNS_SHA1: - return HASH_HashBuf(HASH_AlgSHA1, res, buf, len) - == SECSuccess; -#endif -#if defined(USE_SHA2) - case LDNS_SHA256: - return HASH_HashBuf(HASH_AlgSHA256, res, buf, len) - == SECSuccess; -#endif -#ifdef USE_ECDSA - case LDNS_SHA384: - return HASH_HashBuf(HASH_AlgSHA384, res, buf, len) - == SECSuccess; -#endif - case LDNS_HASH_GOST: - default: - verbose(VERB_QUERY, "unknown DS digest algorithm %d", - algo); - break; - } - return 0; -} - -int -dnskey_algo_id_is_supported(int id) -{ - /* uses libNSS */ - switch(id) { - case LDNS_RSAMD5: - /* RFC 6725 deprecates RSAMD5 */ - return 0; -#if defined(USE_SHA1) || defined(USE_SHA2) -#if defined(USE_DSA) && defined(USE_SHA1) - case LDNS_DSA: - case LDNS_DSA_NSEC3: -#endif -#ifdef USE_SHA1 - case LDNS_RSASHA1: - case LDNS_RSASHA1_NSEC3: -#endif -#ifdef USE_SHA2 - case LDNS_RSASHA256: -#endif -#ifdef USE_SHA2 - case LDNS_RSASHA512: -#endif - return 1; -#endif /* SHA1 or SHA2 */ - -#ifdef USE_ECDSA - case LDNS_ECDSAP256SHA256: - case LDNS_ECDSAP384SHA384: - return PK11_TokenExists(CKM_ECDSA); -#endif - case LDNS_ECC_GOST: - default: - return 0; - } -} - -/* return a new public key for NSS */ -static SECKEYPublicKey* nss_key_create(KeyType ktype) -{ - SECKEYPublicKey* key; - PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); - if(!arena) { - log_err("out of memory, PORT_NewArena failed"); - return NULL; - } - key = PORT_ArenaZNew(arena, SECKEYPublicKey); - if(!key) { - log_err("out of memory, PORT_ArenaZNew failed"); - PORT_FreeArena(arena, PR_FALSE); - return NULL; - } - key->arena = arena; - key->keyType = ktype; - key->pkcs11Slot = NULL; - key->pkcs11ID = CK_INVALID_HANDLE; - return key; -} - -static SECKEYPublicKey* nss_buf2ecdsa(unsigned char* key, size_t len, int algo) -{ - SECKEYPublicKey* pk; - SECItem pub = {siBuffer, NULL, 0}; - SECItem params = {siBuffer, NULL, 0}; - static unsigned char param256[] = { - /* OBJECTIDENTIFIER 1.2.840.10045.3.1.7 (P-256) - * {iso(1) member-body(2) us(840) ansi-x962(10045) curves(3) prime(1) prime256v1(7)} */ - 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 - }; - static unsigned char param384[] = { - /* OBJECTIDENTIFIER 1.3.132.0.34 (P-384) - * {iso(1) identified-organization(3) certicom(132) curve(0) ansip384r1(34)} */ - 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 - }; - unsigned char buf[256+2]; /* sufficient for 2*384/8+1 */ - - /* check length, which uncompressed must be 2 bignums */ - if(algo == LDNS_ECDSAP256SHA256) { - if(len != 2*256/8) return NULL; - /* ECCurve_X9_62_PRIME_256V1 */ - } else if(algo == LDNS_ECDSAP384SHA384) { - if(len != 2*384/8) return NULL; - /* ECCurve_X9_62_PRIME_384R1 */ - } else return NULL; - - buf[0] = 0x04; /* POINT_FORM_UNCOMPRESSED */ - memmove(buf+1, key, len); - pub.data = buf; - pub.len = len+1; - if(algo == LDNS_ECDSAP256SHA256) { - params.data = param256; - params.len = sizeof(param256); - } else { - params.data = param384; - params.len = sizeof(param384); - } - - pk = nss_key_create(ecKey); - if(!pk) - return NULL; - pk->u.ec.size = (len/2)*8; - if(SECITEM_CopyItem(pk->arena, &pk->u.ec.publicValue, &pub)) { - SECKEY_DestroyPublicKey(pk); - return NULL; - } - if(SECITEM_CopyItem(pk->arena, &pk->u.ec.DEREncodedParams, ¶ms)) { - SECKEY_DestroyPublicKey(pk); - return NULL; - } - - return pk; -} - -static SECKEYPublicKey* nss_buf2dsa(unsigned char* key, size_t len) -{ - SECKEYPublicKey* pk; - uint8_t T; - uint16_t length; - uint16_t offset; - SECItem Q = {siBuffer, NULL, 0}; - SECItem P = {siBuffer, NULL, 0}; - SECItem G = {siBuffer, NULL, 0}; - SECItem Y = {siBuffer, NULL, 0}; - - if(len == 0) - return NULL; - T = (uint8_t)key[0]; - length = (64 + T * 8); - offset = 1; - - if (T > 8) { - return NULL; - } - if(len < (size_t)1 + SHA1_LENGTH + 3*length) - return NULL; - - Q.data = key+offset; - Q.len = SHA1_LENGTH; - offset += SHA1_LENGTH; - - P.data = key+offset; - P.len = length; - offset += length; - - G.data = key+offset; - G.len = length; - offset += length; - - Y.data = key+offset; - Y.len = length; - offset += length; - - pk = nss_key_create(dsaKey); - if(!pk) - return NULL; - if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.params.prime, &P)) { - SECKEY_DestroyPublicKey(pk); - return NULL; - } - if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.params.subPrime, &Q)) { - SECKEY_DestroyPublicKey(pk); - return NULL; - } - if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.params.base, &G)) { - SECKEY_DestroyPublicKey(pk); - return NULL; - } - if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.publicValue, &Y)) { - SECKEY_DestroyPublicKey(pk); - return NULL; - } - return pk; -} - -static SECKEYPublicKey* nss_buf2rsa(unsigned char* key, size_t len) -{ - SECKEYPublicKey* pk; - uint16_t exp; - uint16_t offset; - uint16_t int16; - SECItem modulus = {siBuffer, NULL, 0}; - SECItem exponent = {siBuffer, NULL, 0}; - if(len == 0) - return NULL; - if(key[0] == 0) { - if(len < 3) - return NULL; - /* the exponent is too large so it's places further */ - memmove(&int16, key+1, 2); - exp = ntohs(int16); - offset = 3; - } else { - exp = key[0]; - offset = 1; - } - - /* key length at least one */ - if(len < (size_t)offset + exp + 1) - return NULL; - - exponent.data = key+offset; - exponent.len = exp; - offset += exp; - modulus.data = key+offset; - modulus.len = (len - offset); - - pk = nss_key_create(rsaKey); - if(!pk) - return NULL; - if(SECITEM_CopyItem(pk->arena, &pk->u.rsa.modulus, &modulus)) { - SECKEY_DestroyPublicKey(pk); - return NULL; - } - if(SECITEM_CopyItem(pk->arena, &pk->u.rsa.publicExponent, &exponent)) { - SECKEY_DestroyPublicKey(pk); - return NULL; - } - return pk; -} - -/** - * Setup key and digest for verification. Adjust sig if necessary. - * - * @param algo: key algorithm - * @param evp_key: EVP PKEY public key to create. - * @param digest_type: digest type to use - * @param key: key to setup for. - * @param keylen: length of key. - * @param prefix: if returned, the ASN prefix for the hashblob. - * @param prefixlen: length of the prefix. - * @return false on failure. - */ -static int -nss_setup_key_digest(int algo, SECKEYPublicKey** pubkey, HASH_HashType* htype, - unsigned char* key, size_t keylen, unsigned char** prefix, - size_t* prefixlen) -{ - /* uses libNSS */ - - /* hash prefix for md5, RFC2537 */ - static unsigned char p_md5[] = {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, - 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10}; - /* hash prefix to prepend to hash output, from RFC3110 */ - static unsigned char p_sha1[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, - 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14}; - /* from RFC5702 */ - static unsigned char p_sha256[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, - 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; - static unsigned char p_sha512[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, - 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}; - /* from RFC6234 */ - /* for future RSASHA384 .. - static unsigned char p_sha384[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, - 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}; - */ - - switch(algo) { - -#if defined(USE_SHA1) || defined(USE_SHA2) -#if defined(USE_DSA) && defined(USE_SHA1) - case LDNS_DSA: - case LDNS_DSA_NSEC3: - *pubkey = nss_buf2dsa(key, keylen); - if(!*pubkey) { - log_err("verify: malloc failure in crypto"); - return 0; - } - *htype = HASH_AlgSHA1; - /* no prefix for DSA verification */ - break; -#endif -#ifdef USE_SHA1 - case LDNS_RSASHA1: - case LDNS_RSASHA1_NSEC3: -#endif -#ifdef USE_SHA2 - case LDNS_RSASHA256: -#endif -#ifdef USE_SHA2 - case LDNS_RSASHA512: -#endif - *pubkey = nss_buf2rsa(key, keylen); - if(!*pubkey) { - log_err("verify: malloc failure in crypto"); - return 0; - } - /* select SHA version */ -#ifdef USE_SHA2 - if(algo == LDNS_RSASHA256) { - *htype = HASH_AlgSHA256; - *prefix = p_sha256; - *prefixlen = sizeof(p_sha256); - } else -#endif -#ifdef USE_SHA2 - if(algo == LDNS_RSASHA512) { - *htype = HASH_AlgSHA512; - *prefix = p_sha512; - *prefixlen = sizeof(p_sha512); - } else -#endif -#ifdef USE_SHA1 - { - *htype = HASH_AlgSHA1; - *prefix = p_sha1; - *prefixlen = sizeof(p_sha1); - } -#else - { - verbose(VERB_QUERY, "verify: no digest algo"); - return 0; - } -#endif - - break; -#endif /* SHA1 or SHA2 */ - - case LDNS_RSAMD5: - *pubkey = nss_buf2rsa(key, keylen); - if(!*pubkey) { - log_err("verify: malloc failure in crypto"); - return 0; - } - *htype = HASH_AlgMD5; - *prefix = p_md5; - *prefixlen = sizeof(p_md5); - - break; -#ifdef USE_ECDSA - case LDNS_ECDSAP256SHA256: - *pubkey = nss_buf2ecdsa(key, keylen, - LDNS_ECDSAP256SHA256); - if(!*pubkey) { - log_err("verify: malloc failure in crypto"); - return 0; - } - *htype = HASH_AlgSHA256; - /* no prefix for DSA verification */ - break; - case LDNS_ECDSAP384SHA384: - *pubkey = nss_buf2ecdsa(key, keylen, - LDNS_ECDSAP384SHA384); - if(!*pubkey) { - log_err("verify: malloc failure in crypto"); - return 0; - } - *htype = HASH_AlgSHA384; - /* no prefix for DSA verification */ - break; -#endif /* USE_ECDSA */ - case LDNS_ECC_GOST: - default: - verbose(VERB_QUERY, "verify: unknown algorithm %d", - algo); - return 0; - } - return 1; -} - -/** - * Check a canonical sig+rrset and signature against a dnskey - * @param buf: buffer with data to verify, the first rrsig part and the - * canonicalized rrset. - * @param algo: DNSKEY algorithm. - * @param sigblock: signature rdata field from RRSIG - * @param sigblock_len: length of sigblock data. - * @param key: public key data from DNSKEY RR. - * @param keylen: length of keydata. - * @param reason: bogus reason in more detail. - * @return secure if verification succeeded, bogus on crypto failure, - * unchecked on format errors and alloc failures. - */ -enum sec_status -verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock, - unsigned int sigblock_len, unsigned char* key, unsigned int keylen, - char** reason) -{ - /* uses libNSS */ - /* large enough for the different hashes */ - unsigned char hash[HASH_LENGTH_MAX]; - unsigned char hash2[HASH_LENGTH_MAX*2]; - HASH_HashType htype = 0; - SECKEYPublicKey* pubkey = NULL; - SECItem secsig = {siBuffer, sigblock, sigblock_len}; - SECItem sechash = {siBuffer, hash, 0}; - SECStatus res; - unsigned char* prefix = NULL; /* prefix for hash, RFC3110, RFC5702 */ - size_t prefixlen = 0; - int err; - - if(!nss_setup_key_digest(algo, &pubkey, &htype, key, keylen, - &prefix, &prefixlen)) { - verbose(VERB_QUERY, "verify: failed to setup key"); - *reason = "use of key for crypto failed"; - SECKEY_DestroyPublicKey(pubkey); - return sec_status_bogus; - } - -#if defined(USE_DSA) && defined(USE_SHA1) - /* need to convert DSA, ECDSA signatures? */ - if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3)) { - if(sigblock_len == 1+2*SHA1_LENGTH) { - secsig.data ++; - secsig.len --; - } else { - SECItem* p = DSAU_DecodeDerSig(&secsig); - if(!p) { - verbose(VERB_QUERY, "verify: failed DER decode"); - *reason = "signature DER decode failed"; - SECKEY_DestroyPublicKey(pubkey); - return sec_status_bogus; - } - if(SECITEM_CopyItem(pubkey->arena, &secsig, p)) { - log_err("alloc failure in DER decode"); - SECKEY_DestroyPublicKey(pubkey); - SECITEM_FreeItem(p, PR_TRUE); - return sec_status_unchecked; - } - SECITEM_FreeItem(p, PR_TRUE); - } - } -#endif /* USE_DSA */ - - /* do the signature cryptography work */ - /* hash the data */ - sechash.len = HASH_ResultLen(htype); - if(sechash.len > sizeof(hash)) { - verbose(VERB_QUERY, "verify: hash too large for buffer"); - SECKEY_DestroyPublicKey(pubkey); - return sec_status_unchecked; - } - if(HASH_HashBuf(htype, hash, (unsigned char*)sldns_buffer_begin(buf), - (unsigned int)sldns_buffer_limit(buf)) != SECSuccess) { - verbose(VERB_QUERY, "verify: HASH_HashBuf failed"); - SECKEY_DestroyPublicKey(pubkey); - return sec_status_unchecked; - } - if(prefix) { - int hashlen = sechash.len; - if(prefixlen+hashlen > sizeof(hash2)) { - verbose(VERB_QUERY, "verify: hashprefix too large"); - SECKEY_DestroyPublicKey(pubkey); - return sec_status_unchecked; - } - sechash.data = hash2; - sechash.len = prefixlen+hashlen; - memcpy(sechash.data, prefix, prefixlen); - memmove(sechash.data+prefixlen, hash, hashlen); - } - - /* verify the signature */ - res = PK11_Verify(pubkey, &secsig, &sechash, NULL /*wincx*/); - SECKEY_DestroyPublicKey(pubkey); - - if(res == SECSuccess) { - return sec_status_secure; - } - err = PORT_GetError(); - if(err != SEC_ERROR_BAD_SIGNATURE) { - /* failed to verify */ - verbose(VERB_QUERY, "verify: PK11_Verify failed: %s", - PORT_ErrorToString(err)); - /* if it is not supported, like ECC is removed, we get, - * SEC_ERROR_NO_MODULE */ - if(err == SEC_ERROR_NO_MODULE) - return sec_status_unchecked; - /* but other errors are commonly returned - * for a bad signature from NSS. Thus we return bogus, - * not unchecked */ - *reason = "signature crypto failed"; - return sec_status_bogus; - } - verbose(VERB_QUERY, "verify: signature mismatch: %s", - PORT_ErrorToString(err)); - *reason = "signature crypto failed"; - return sec_status_bogus; -} - -#elif defined(HAVE_NETTLE) - -#include "sha.h" -#include "bignum.h" -#include "macros.h" -#include "rsa.h" -#include "dsa.h" -#ifdef HAVE_NETTLE_DSA_COMPAT_H -#include "dsa-compat.h" -#endif -#include "asn1.h" -#ifdef USE_ECDSA -#include "ecdsa.h" -#include "ecc-curve.h" -#endif - -static int -_digest_nettle(int algo, uint8_t* buf, size_t len, - unsigned char* res) -{ - switch(algo) { - case SHA1_DIGEST_SIZE: - { - struct sha1_ctx ctx; - sha1_init(&ctx); - sha1_update(&ctx, len, buf); - sha1_digest(&ctx, SHA1_DIGEST_SIZE, res); - return 1; - } - case SHA256_DIGEST_SIZE: - { - struct sha256_ctx ctx; - sha256_init(&ctx); - sha256_update(&ctx, len, buf); - sha256_digest(&ctx, SHA256_DIGEST_SIZE, res); - return 1; - } - case SHA384_DIGEST_SIZE: - { - struct sha384_ctx ctx; - sha384_init(&ctx); - sha384_update(&ctx, len, buf); - sha384_digest(&ctx, SHA384_DIGEST_SIZE, res); - return 1; - } - case SHA512_DIGEST_SIZE: - { - struct sha512_ctx ctx; - sha512_init(&ctx); - sha512_update(&ctx, len, buf); - sha512_digest(&ctx, SHA512_DIGEST_SIZE, res); - return 1; - } - default: - break; - } - return 0; -} - -/* return size of digest if supported, or 0 otherwise */ -size_t -nsec3_hash_algo_size_supported(int id) -{ - switch(id) { - case NSEC3_HASH_SHA1: - return SHA1_DIGEST_SIZE; - default: - return 0; - } -} - -/* perform nsec3 hash. return false on failure */ -int -secalgo_nsec3_hash(int algo, unsigned char* buf, size_t len, - unsigned char* res) -{ - switch(algo) { - case NSEC3_HASH_SHA1: - return _digest_nettle(SHA1_DIGEST_SIZE, (uint8_t*)buf, len, - res); - default: - return 0; - } -} - -void -secalgo_hash_sha256(unsigned char* buf, size_t len, unsigned char* res) -{ - _digest_nettle(SHA256_DIGEST_SIZE, (uint8_t*)buf, len, res); -} - -/** - * Return size of DS digest according to its hash algorithm. - * @param algo: DS digest algo. - * @return size in bytes of digest, or 0 if not supported. - */ -size_t -ds_digest_size_supported(int algo) -{ - switch(algo) { - case LDNS_SHA1: -#ifdef USE_SHA1 - return SHA1_DIGEST_SIZE; -#else - if(fake_sha1) return 20; - return 0; -#endif -#ifdef USE_SHA2 - case LDNS_SHA256: - return SHA256_DIGEST_SIZE; -#endif -#ifdef USE_ECDSA - case LDNS_SHA384: - return SHA384_DIGEST_SIZE; -#endif - /* GOST not supported */ - case LDNS_HASH_GOST: - default: - break; - } - return 0; -} - -int -secalgo_ds_digest(int algo, unsigned char* buf, size_t len, - unsigned char* res) -{ - switch(algo) { -#ifdef USE_SHA1 - case LDNS_SHA1: - return _digest_nettle(SHA1_DIGEST_SIZE, buf, len, res); -#endif -#if defined(USE_SHA2) - case LDNS_SHA256: - return _digest_nettle(SHA256_DIGEST_SIZE, buf, len, res); -#endif -#ifdef USE_ECDSA - case LDNS_SHA384: - return _digest_nettle(SHA384_DIGEST_SIZE, buf, len, res); - -#endif - case LDNS_HASH_GOST: - default: - verbose(VERB_QUERY, "unknown DS digest algorithm %d", - algo); - break; - } - return 0; -} - -int -dnskey_algo_id_is_supported(int id) -{ - /* uses libnettle */ - switch(id) { -#if defined(USE_DSA) && defined(USE_SHA1) - case LDNS_DSA: - case LDNS_DSA_NSEC3: -#endif -#ifdef USE_SHA1 - case LDNS_RSASHA1: - case LDNS_RSASHA1_NSEC3: -#endif -#ifdef USE_SHA2 - case LDNS_RSASHA256: - case LDNS_RSASHA512: -#endif -#ifdef USE_ECDSA - case LDNS_ECDSAP256SHA256: - case LDNS_ECDSAP384SHA384: -#endif - return 1; - case LDNS_RSAMD5: /* RFC 6725 deprecates RSAMD5 */ - case LDNS_ECC_GOST: - default: - return 0; - } -} - -#if defined(USE_DSA) && defined(USE_SHA1) -static char * -_verify_nettle_dsa(sldns_buffer* buf, unsigned char* sigblock, - unsigned int sigblock_len, unsigned char* key, unsigned int keylen) -{ - uint8_t digest[SHA1_DIGEST_SIZE]; - uint8_t key_t_value; - int res = 0; - size_t offset; - struct dsa_public_key pubkey; - struct dsa_signature signature; - unsigned int expected_len; - - /* Extract DSA signature from the record */ - nettle_dsa_signature_init(&signature); - /* Signature length: 41 bytes - RFC 2536 sec. 3 */ - if(sigblock_len == 41) { - if(key[0] != sigblock[0]) - return "invalid T value in DSA signature or pubkey"; - nettle_mpz_set_str_256_u(signature.r, 20, sigblock+1); - nettle_mpz_set_str_256_u(signature.s, 20, sigblock+1+20); - } else { - /* DER encoded, decode the ASN1 notated R and S bignums */ - /* SEQUENCE { r INTEGER, s INTEGER } */ - struct asn1_der_iterator i, seq; - if(asn1_der_iterator_first(&i, sigblock_len, - (uint8_t*)sigblock) != ASN1_ITERATOR_CONSTRUCTED - || i.type != ASN1_SEQUENCE) - return "malformed DER encoded DSA signature"; - /* decode this element of i using the seq iterator */ - if(asn1_der_decode_constructed(&i, &seq) != - ASN1_ITERATOR_PRIMITIVE || seq.type != ASN1_INTEGER) - return "malformed DER encoded DSA signature"; - if(!asn1_der_get_bignum(&seq, signature.r, 20*8)) - return "malformed DER encoded DSA signature"; - if(asn1_der_iterator_next(&seq) != ASN1_ITERATOR_PRIMITIVE - || seq.type != ASN1_INTEGER) - return "malformed DER encoded DSA signature"; - if(!asn1_der_get_bignum(&seq, signature.s, 20*8)) - return "malformed DER encoded DSA signature"; - if(asn1_der_iterator_next(&i) != ASN1_ITERATOR_END) - return "malformed DER encoded DSA signature"; - } - - /* Validate T values constraints - RFC 2536 sec. 2 & sec. 3 */ - key_t_value = key[0]; - if (key_t_value > 8) { - return "invalid T value in DSA pubkey"; - } - - /* Pubkey minimum length: 21 bytes - RFC 2536 sec. 2 */ - if (keylen < 21) { - return "DSA pubkey too short"; - } - - expected_len = 1 + /* T */ - 20 + /* Q */ - (64 + key_t_value*8) + /* P */ - (64 + key_t_value*8) + /* G */ - (64 + key_t_value*8); /* Y */ - if (keylen != expected_len ) { - return "invalid DSA pubkey length"; - } - - /* Extract DSA pubkey from the record */ - nettle_dsa_public_key_init(&pubkey); - offset = 1; - nettle_mpz_set_str_256_u(pubkey.q, 20, key+offset); - offset += 20; - nettle_mpz_set_str_256_u(pubkey.p, (64 + key_t_value*8), key+offset); - offset += (64 + key_t_value*8); - nettle_mpz_set_str_256_u(pubkey.g, (64 + key_t_value*8), key+offset); - offset += (64 + key_t_value*8); - nettle_mpz_set_str_256_u(pubkey.y, (64 + key_t_value*8), key+offset); - - /* Digest content of "buf" and verify its DSA signature in "sigblock"*/ - res = _digest_nettle(SHA1_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf), - (unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest); - res &= dsa_sha1_verify_digest(&pubkey, digest, &signature); - - /* Clear and return */ - nettle_dsa_signature_clear(&signature); - nettle_dsa_public_key_clear(&pubkey); - if (!res) - return "DSA signature verification failed"; - else - return NULL; -} -#endif /* USE_DSA */ - -static char * -_verify_nettle_rsa(sldns_buffer* buf, unsigned int digest_size, char* sigblock, - unsigned int sigblock_len, uint8_t* key, unsigned int keylen) -{ - uint16_t exp_len = 0; - size_t exp_offset = 0, mod_offset = 0; - struct rsa_public_key pubkey; - mpz_t signature; - int res = 0; - - /* RSA pubkey parsing as per RFC 3110 sec. 2 */ - if( keylen <= 1) { - return "null RSA key"; - } - if (key[0] != 0) { - /* 1-byte length */ - exp_len = key[0]; - exp_offset = 1; - } else { - /* 1-byte NUL + 2-bytes exponent length */ - if (keylen < 3) { - return "incorrect RSA key length"; - } - exp_len = READ_UINT16(key+1); - if (exp_len == 0) - return "null RSA exponent length"; - exp_offset = 3; - } - /* Check that we are not over-running input length */ - if (keylen < exp_offset + exp_len + 1) { - return "RSA key content shorter than expected"; - } - mod_offset = exp_offset + exp_len; - nettle_rsa_public_key_init(&pubkey); - pubkey.size = keylen - mod_offset; - nettle_mpz_set_str_256_u(pubkey.e, exp_len, &key[exp_offset]); - nettle_mpz_set_str_256_u(pubkey.n, pubkey.size, &key[mod_offset]); - - /* Digest content of "buf" and verify its RSA signature in "sigblock"*/ - nettle_mpz_init_set_str_256_u(signature, sigblock_len, (uint8_t*)sigblock); - switch (digest_size) { - case SHA1_DIGEST_SIZE: - { - uint8_t digest[SHA1_DIGEST_SIZE]; - res = _digest_nettle(SHA1_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf), - (unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest); - res &= rsa_sha1_verify_digest(&pubkey, digest, signature); - break; - } - case SHA256_DIGEST_SIZE: - { - uint8_t digest[SHA256_DIGEST_SIZE]; - res = _digest_nettle(SHA256_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf), - (unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest); - res &= rsa_sha256_verify_digest(&pubkey, digest, signature); - break; - } - case SHA512_DIGEST_SIZE: - { - uint8_t digest[SHA512_DIGEST_SIZE]; - res = _digest_nettle(SHA512_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf), - (unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest); - res &= rsa_sha512_verify_digest(&pubkey, digest, signature); - break; - } - default: - break; - } - - /* Clear and return */ - nettle_rsa_public_key_clear(&pubkey); - mpz_clear(signature); - if (!res) { - return "RSA signature verification failed"; - } else { - return NULL; - } -} - -#ifdef USE_ECDSA -static char * -_verify_nettle_ecdsa(sldns_buffer* buf, unsigned int digest_size, unsigned char* sigblock, - unsigned int sigblock_len, unsigned char* key, unsigned int keylen) -{ - int res = 0; - struct ecc_point pubkey; - struct dsa_signature signature; - - /* Always matched strength, as per RFC 6605 sec. 1 */ - if (sigblock_len != 2*digest_size || keylen != 2*digest_size) { - return "wrong ECDSA signature length"; - } - - /* Parse ECDSA signature as per RFC 6605 sec. 4 */ - nettle_dsa_signature_init(&signature); - switch (digest_size) { - case SHA256_DIGEST_SIZE: - { - uint8_t digest[SHA256_DIGEST_SIZE]; - mpz_t x, y; - nettle_ecc_point_init(&pubkey, &nettle_secp_256r1); - nettle_mpz_init_set_str_256_u(x, SHA256_DIGEST_SIZE, key); - nettle_mpz_init_set_str_256_u(y, SHA256_DIGEST_SIZE, key+SHA256_DIGEST_SIZE); - nettle_mpz_set_str_256_u(signature.r, SHA256_DIGEST_SIZE, sigblock); - nettle_mpz_set_str_256_u(signature.s, SHA256_DIGEST_SIZE, sigblock+SHA256_DIGEST_SIZE); - res = _digest_nettle(SHA256_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf), - (unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest); - res &= nettle_ecc_point_set(&pubkey, x, y); - res &= nettle_ecdsa_verify (&pubkey, SHA256_DIGEST_SIZE, digest, &signature); - mpz_clear(x); - mpz_clear(y); - break; - } - case SHA384_DIGEST_SIZE: - { - uint8_t digest[SHA384_DIGEST_SIZE]; - mpz_t x, y; - nettle_ecc_point_init(&pubkey, &nettle_secp_384r1); - nettle_mpz_init_set_str_256_u(x, SHA384_DIGEST_SIZE, key); - nettle_mpz_init_set_str_256_u(y, SHA384_DIGEST_SIZE, key+SHA384_DIGEST_SIZE); - nettle_mpz_set_str_256_u(signature.r, SHA384_DIGEST_SIZE, sigblock); - nettle_mpz_set_str_256_u(signature.s, SHA384_DIGEST_SIZE, sigblock+SHA384_DIGEST_SIZE); - res = _digest_nettle(SHA384_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf), - (unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest); - res &= nettle_ecc_point_set(&pubkey, x, y); - res &= nettle_ecdsa_verify (&pubkey, SHA384_DIGEST_SIZE, digest, &signature); - mpz_clear(x); - mpz_clear(y); - nettle_ecc_point_clear(&pubkey); - break; - } - default: - return "unknown ECDSA algorithm"; - } - - /* Clear and return */ - nettle_dsa_signature_clear(&signature); - if (!res) - return "ECDSA signature verification failed"; - else - return NULL; -} -#endif - -/** - * Check a canonical sig+rrset and signature against a dnskey - * @param buf: buffer with data to verify, the first rrsig part and the - * canonicalized rrset. - * @param algo: DNSKEY algorithm. - * @param sigblock: signature rdata field from RRSIG - * @param sigblock_len: length of sigblock data. - * @param key: public key data from DNSKEY RR. - * @param keylen: length of keydata. - * @param reason: bogus reason in more detail. - * @return secure if verification succeeded, bogus on crypto failure, - * unchecked on format errors and alloc failures. - */ -enum sec_status -verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock, - unsigned int sigblock_len, unsigned char* key, unsigned int keylen, - char** reason) -{ - unsigned int digest_size = 0; - - if (sigblock_len == 0 || keylen == 0) { - *reason = "null signature"; - return sec_status_bogus; - } - - switch(algo) { -#if defined(USE_DSA) && defined(USE_SHA1) - case LDNS_DSA: - case LDNS_DSA_NSEC3: - *reason = _verify_nettle_dsa(buf, sigblock, sigblock_len, key, keylen); - if (*reason != NULL) - return sec_status_bogus; - else - return sec_status_secure; -#endif /* USE_DSA */ - -#ifdef USE_SHA1 - case LDNS_RSASHA1: - case LDNS_RSASHA1_NSEC3: - digest_size = (digest_size ? digest_size : SHA1_DIGEST_SIZE); -#endif -#ifdef USE_SHA2 - case LDNS_RSASHA256: - digest_size = (digest_size ? digest_size : SHA256_DIGEST_SIZE); - case LDNS_RSASHA512: - digest_size = (digest_size ? digest_size : SHA512_DIGEST_SIZE); - -#endif - *reason = _verify_nettle_rsa(buf, digest_size, (char*)sigblock, - sigblock_len, key, keylen); - if (*reason != NULL) - return sec_status_bogus; - else - return sec_status_secure; - -#ifdef USE_ECDSA - case LDNS_ECDSAP256SHA256: - digest_size = (digest_size ? digest_size : SHA256_DIGEST_SIZE); - case LDNS_ECDSAP384SHA384: - digest_size = (digest_size ? digest_size : SHA384_DIGEST_SIZE); - *reason = _verify_nettle_ecdsa(buf, digest_size, sigblock, - sigblock_len, key, keylen); - if (*reason != NULL) - return sec_status_bogus; - else - return sec_status_secure; -#endif - case LDNS_RSAMD5: - case LDNS_ECC_GOST: - default: - *reason = "unable to verify signature, unknown algorithm"; - return sec_status_bogus; - } -} - -#endif /* HAVE_SSL or HAVE_NSS or HAVE_NETTLE */ diff --git a/external/unbound/validator/val_secalgo.h b/external/unbound/validator/val_secalgo.h deleted file mode 100644 index 52aaeb9f6..000000000 --- a/external/unbound/validator/val_secalgo.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * validator/val_secalgo.h - validator security algorithm functions. - * - * Copyright (c) 2012, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - * The functions take buffers with raw data and convert to library calls. - */ - -#ifndef VALIDATOR_VAL_SECALGO_H -#define VALIDATOR_VAL_SECALGO_H -struct sldns_buffer; - -/** Return size of nsec3 hash algorithm, 0 if not supported */ -size_t nsec3_hash_algo_size_supported(int id); - -/** - * Hash a single hash call of an NSEC3 hash algorithm. - * Iterations and salt are done by the caller. - * @param algo: nsec3 hash algorithm. - * @param buf: the buffer to digest - * @param len: length of buffer to digest. - * @param res: result stored here (must have sufficient space). - * @return false on failure. -*/ -int secalgo_nsec3_hash(int algo, unsigned char* buf, size_t len, - unsigned char* res); - -/** - * Calculate the sha256 hash for the data buffer into the result. - * @param buf: buffer to digest. - * @param len: length of the buffer to digest. - * @param res: result is stored here (space 256/8 bytes). - */ -void secalgo_hash_sha256(unsigned char* buf, size_t len, unsigned char* res); - -/** - * Return size of DS digest according to its hash algorithm. - * @param algo: DS digest algo. - * @return size in bytes of digest, or 0 if not supported. - */ -size_t ds_digest_size_supported(int algo); - -/** - * @param algo: the DS digest algo - * @param buf: the buffer to digest - * @param len: length of buffer to digest. - * @param res: result stored here (must have sufficient space). - * @return false on failure. - */ -int secalgo_ds_digest(int algo, unsigned char* buf, size_t len, - unsigned char* res); - -/** return true if DNSKEY algorithm id is supported */ -int dnskey_algo_id_is_supported(int id); - -/** - * Check a canonical sig+rrset and signature against a dnskey - * @param buf: buffer with data to verify, the first rrsig part and the - * canonicalized rrset. - * @param algo: DNSKEY algorithm. - * @param sigblock: signature rdata field from RRSIG - * @param sigblock_len: length of sigblock data. - * @param key: public key data from DNSKEY RR. - * @param keylen: length of keydata. - * @param reason: bogus reason in more detail. - * @return secure if verification succeeded, bogus on crypto failure, - * unchecked on format errors and alloc failures. - */ -enum sec_status verify_canonrrset(struct sldns_buffer* buf, int algo, - unsigned char* sigblock, unsigned int sigblock_len, - unsigned char* key, unsigned int keylen, char** reason); - -#endif /* VALIDATOR_VAL_SECALGO_H */ diff --git a/external/unbound/validator/val_sigcrypt.c b/external/unbound/validator/val_sigcrypt.c deleted file mode 100644 index 25278a8f3..000000000 --- a/external/unbound/validator/val_sigcrypt.c +++ /dev/null @@ -1,1451 +0,0 @@ -/* - * validator/val_sigcrypt.c - validator signature crypto functions. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - * The functions help with signature verification and checking, the - * bridging between RR wireformat data and crypto calls. - */ -#include "config.h" -#include "validator/val_sigcrypt.h" -#include "validator/val_secalgo.h" -#include "validator/validator.h" -#include "util/data/msgreply.h" -#include "util/data/msgparse.h" -#include "util/data/dname.h" -#include "util/rbtree.h" -#include "util/module.h" -#include "util/net_help.h" -#include "util/regional.h" -#include "util/config_file.h" -#include "sldns/keyraw.h" -#include "sldns/sbuffer.h" -#include "sldns/parseutil.h" -#include "sldns/wire2str.h" - -#include <ctype.h> -#if !defined(HAVE_SSL) && !defined(HAVE_NSS) && !defined(HAVE_NETTLE) -#error "Need crypto library to do digital signature cryptography" -#endif - -#ifdef HAVE_OPENSSL_ERR_H -#include <openssl/err.h> -#endif - -#ifdef HAVE_OPENSSL_RAND_H -#include <openssl/rand.h> -#endif - -#ifdef HAVE_OPENSSL_CONF_H -#include <openssl/conf.h> -#endif - -#ifdef HAVE_OPENSSL_ENGINE_H -#include <openssl/engine.h> -#endif - -/** 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; -} - -/** - * Get RR signature count - */ -static size_t -rrset_get_sigcount(struct ub_packed_rrset_key* k) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; - return d->rrsig_count; -} - -/** - * Get signature keytag value - * @param k: rrset (with signatures) - * @param sig_idx: signature index. - * @return keytag or 0 if malformed rrsig. - */ -static uint16_t -rrset_get_sig_keytag(struct ub_packed_rrset_key* k, size_t sig_idx) -{ - uint16_t t; - struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; - log_assert(sig_idx < d->rrsig_count); - if(d->rr_len[d->count + sig_idx] < 2+18) - return 0; - memmove(&t, d->rr_data[d->count + sig_idx]+2+16, 2); - return ntohs(t); -} - -/** - * Get signature signing algorithm value - * @param k: rrset (with signatures) - * @param sig_idx: signature index. - * @return algo or 0 if malformed rrsig. - */ -static int -rrset_get_sig_algo(struct ub_packed_rrset_key* k, size_t sig_idx) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; - log_assert(sig_idx < d->rrsig_count); - if(d->rr_len[d->count + sig_idx] < 2+3) - return 0; - return (int)d->rr_data[d->count + sig_idx][2+2]; -} - -/** get rdata pointer and size */ -static void -rrset_get_rdata(struct ub_packed_rrset_key* k, size_t idx, uint8_t** rdata, - size_t* len) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; - log_assert(d && idx < (d->count + d->rrsig_count)); - *rdata = d->rr_data[idx]; - *len = d->rr_len[idx]; -} - -uint16_t -dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx) -{ - uint8_t* rdata; - size_t len; - uint16_t f; - rrset_get_rdata(k, idx, &rdata, &len); - if(len < 2+2) - return 0; - memmove(&f, rdata+2, 2); - f = ntohs(f); - return f; -} - -/** - * Get DNSKEY protocol value from rdata - * @param k: DNSKEY rrset. - * @param idx: which key. - * @return protocol octet value - */ -static int -dnskey_get_protocol(struct ub_packed_rrset_key* k, size_t idx) -{ - uint8_t* rdata; - size_t len; - rrset_get_rdata(k, idx, &rdata, &len); - if(len < 2+4) - return 0; - return (int)rdata[2+2]; -} - -int -dnskey_get_algo(struct ub_packed_rrset_key* k, size_t idx) -{ - uint8_t* rdata; - size_t len; - rrset_get_rdata(k, idx, &rdata, &len); - if(len < 2+4) - return 0; - return (int)rdata[2+3]; -} - -/** get public key rdata field from a dnskey RR and do some checks */ -static void -dnskey_get_pubkey(struct ub_packed_rrset_key* k, size_t idx, - unsigned char** pk, unsigned int* pklen) -{ - uint8_t* rdata; - size_t len; - rrset_get_rdata(k, idx, &rdata, &len); - if(len < 2+5) { - *pk = NULL; - *pklen = 0; - return; - } - *pk = (unsigned char*)rdata+2+4; - *pklen = (unsigned)len-2-4; -} - -int -ds_get_key_algo(struct ub_packed_rrset_key* k, size_t idx) -{ - uint8_t* rdata; - size_t len; - rrset_get_rdata(k, idx, &rdata, &len); - if(len < 2+3) - return 0; - return (int)rdata[2+2]; -} - -int -ds_get_digest_algo(struct ub_packed_rrset_key* k, size_t idx) -{ - uint8_t* rdata; - size_t len; - rrset_get_rdata(k, idx, &rdata, &len); - if(len < 2+4) - return 0; - return (int)rdata[2+3]; -} - -uint16_t -ds_get_keytag(struct ub_packed_rrset_key* ds_rrset, size_t ds_idx) -{ - uint16_t t; - uint8_t* rdata; - size_t len; - rrset_get_rdata(ds_rrset, ds_idx, &rdata, &len); - if(len < 2+2) - return 0; - memmove(&t, rdata+2, 2); - return ntohs(t); -} - -/** - * Return pointer to the digest in a DS RR. - * @param k: DS rrset. - * @param idx: which DS. - * @param digest: digest data is returned. - * on error, this is NULL. - * @param len: length of digest is returned. - * on error, the length is 0. - */ -static void -ds_get_sigdata(struct ub_packed_rrset_key* k, size_t idx, uint8_t** digest, - size_t* len) -{ - uint8_t* rdata; - size_t rdlen; - rrset_get_rdata(k, idx, &rdata, &rdlen); - if(rdlen < 2+5) { - *digest = NULL; - *len = 0; - return; - } - *digest = rdata + 2 + 4; - *len = rdlen - 2 - 4; -} - -/** - * Return size of DS digest according to its hash algorithm. - * @param k: DS rrset. - * @param idx: which DS. - * @return size in bytes of digest, or 0 if not supported. - */ -static size_t -ds_digest_size_algo(struct ub_packed_rrset_key* k, size_t idx) -{ - return ds_digest_size_supported(ds_get_digest_algo(k, idx)); -} - -/** - * Create a DS digest for a DNSKEY entry. - * - * @param env: module environment. Uses scratch space. - * @param dnskey_rrset: DNSKEY rrset. - * @param dnskey_idx: index of RR in rrset. - * @param ds_rrset: DS rrset - * @param ds_idx: index of RR in DS rrset. - * @param digest: digest is returned in here (must be correctly sized). - * @return false on error. - */ -static int -ds_create_dnskey_digest(struct module_env* env, - struct ub_packed_rrset_key* dnskey_rrset, size_t dnskey_idx, - struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, - uint8_t* digest) -{ - sldns_buffer* b = env->scratch_buffer; - uint8_t* dnskey_rdata; - size_t dnskey_len; - rrset_get_rdata(dnskey_rrset, dnskey_idx, &dnskey_rdata, &dnskey_len); - - /* create digest source material in buffer - * digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA); - * DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key. */ - sldns_buffer_clear(b); - sldns_buffer_write(b, dnskey_rrset->rk.dname, - dnskey_rrset->rk.dname_len); - query_dname_tolower(sldns_buffer_begin(b)); - sldns_buffer_write(b, dnskey_rdata+2, dnskey_len-2); /* skip rdatalen*/ - sldns_buffer_flip(b); - - return secalgo_ds_digest(ds_get_digest_algo(ds_rrset, ds_idx), - (unsigned char*)sldns_buffer_begin(b), sldns_buffer_limit(b), - (unsigned char*)digest); -} - -int ds_digest_match_dnskey(struct module_env* env, - struct ub_packed_rrset_key* dnskey_rrset, size_t dnskey_idx, - struct ub_packed_rrset_key* ds_rrset, size_t ds_idx) -{ - uint8_t* ds; /* DS digest */ - size_t dslen; - uint8_t* digest; /* generated digest */ - size_t digestlen = ds_digest_size_algo(ds_rrset, ds_idx); - - if(digestlen == 0) { - verbose(VERB_QUERY, "DS fail: not supported, or DS RR " - "format error"); - return 0; /* not supported, or DS RR format error */ - } -#ifndef USE_SHA1 - if(fake_sha1 && ds_get_digest_algo(ds_rrset, ds_idx)==LDNS_SHA1) - return 1; -#endif - - /* check digest length in DS with length from hash function */ - ds_get_sigdata(ds_rrset, ds_idx, &ds, &dslen); - if(!ds || dslen != digestlen) { - verbose(VERB_QUERY, "DS fail: DS RR algo and digest do not " - "match each other"); - return 0; /* DS algorithm and digest do not match */ - } - - digest = regional_alloc(env->scratch, digestlen); - if(!digest) { - verbose(VERB_QUERY, "DS fail: out of memory"); - return 0; /* mem error */ - } - if(!ds_create_dnskey_digest(env, dnskey_rrset, dnskey_idx, ds_rrset, - ds_idx, digest)) { - verbose(VERB_QUERY, "DS fail: could not calc key digest"); - return 0; /* digest algo failed */ - } - if(memcmp(digest, ds, dslen) != 0) { - verbose(VERB_QUERY, "DS fail: digest is different"); - return 0; /* digest different */ - } - return 1; -} - -int -ds_digest_algo_is_supported(struct ub_packed_rrset_key* ds_rrset, - size_t ds_idx) -{ - return (ds_digest_size_algo(ds_rrset, ds_idx) != 0); -} - -int -ds_key_algo_is_supported(struct ub_packed_rrset_key* ds_rrset, - size_t ds_idx) -{ - return dnskey_algo_id_is_supported(ds_get_key_algo(ds_rrset, ds_idx)); -} - -uint16_t -dnskey_calc_keytag(struct ub_packed_rrset_key* dnskey_rrset, size_t dnskey_idx) -{ - uint8_t* data; - size_t len; - rrset_get_rdata(dnskey_rrset, dnskey_idx, &data, &len); - /* do not pass rdatalen to ldns */ - return sldns_calc_keytag_raw(data+2, len-2); -} - -int dnskey_algo_is_supported(struct ub_packed_rrset_key* dnskey_rrset, - size_t dnskey_idx) -{ - return dnskey_algo_id_is_supported(dnskey_get_algo(dnskey_rrset, - dnskey_idx)); -} - -void algo_needs_init_dnskey_add(struct algo_needs* n, - struct ub_packed_rrset_key* dnskey, uint8_t* sigalg) -{ - uint8_t algo; - size_t i, total = n->num; - size_t num = rrset_get_count(dnskey); - - for(i=0; i<num; i++) { - algo = (uint8_t)dnskey_get_algo(dnskey, i); - if(!dnskey_algo_id_is_supported((int)algo)) - continue; - if(n->needs[algo] == 0) { - n->needs[algo] = 1; - sigalg[total] = algo; - total++; - } - } - sigalg[total] = 0; - n->num = total; -} - -void algo_needs_init_list(struct algo_needs* n, uint8_t* sigalg) -{ - uint8_t algo; - size_t total = 0; - - memset(n->needs, 0, sizeof(uint8_t)*ALGO_NEEDS_MAX); - while( (algo=*sigalg++) != 0) { - log_assert(dnskey_algo_id_is_supported((int)algo)); - log_assert(n->needs[algo] == 0); - n->needs[algo] = 1; - total++; - } - n->num = total; -} - -void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds, - int fav_ds_algo, uint8_t* sigalg) -{ - uint8_t algo; - size_t i, total = 0; - size_t num = rrset_get_count(ds); - - memset(n->needs, 0, sizeof(uint8_t)*ALGO_NEEDS_MAX); - for(i=0; i<num; i++) { - if(ds_get_digest_algo(ds, i) != fav_ds_algo) - continue; - algo = (uint8_t)ds_get_key_algo(ds, i); - if(!dnskey_algo_id_is_supported((int)algo)) - continue; - log_assert(algo != 0); /* we do not support 0 and is EOS */ - if(n->needs[algo] == 0) { - n->needs[algo] = 1; - sigalg[total] = algo; - total++; - } - } - sigalg[total] = 0; - n->num = total; -} - -int algo_needs_set_secure(struct algo_needs* n, uint8_t algo) -{ - if(n->needs[algo]) { - n->needs[algo] = 0; - n->num --; - if(n->num == 0) /* done! */ - return 1; - } - return 0; -} - -void algo_needs_set_bogus(struct algo_needs* n, uint8_t algo) -{ - if(n->needs[algo]) n->needs[algo] = 2; /* need it, but bogus */ -} - -size_t algo_needs_num_missing(struct algo_needs* n) -{ - return n->num; -} - -int algo_needs_missing(struct algo_needs* n) -{ - int i; - /* first check if a needed algo was bogus - report that */ - for(i=0; i<ALGO_NEEDS_MAX; i++) - if(n->needs[i] == 2) - return 0; - /* now check which algo is missing */ - for(i=0; i<ALGO_NEEDS_MAX; i++) - if(n->needs[i] == 1) - return i; - return 0; -} - -enum sec_status -dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - uint8_t* sigalg, char** reason) -{ - enum sec_status sec; - size_t i, num; - rbtree_type* sortree = NULL; - /* make sure that for all DNSKEY algorithms there are valid sigs */ - struct algo_needs needs; - int alg; - - num = rrset_get_sigcount(rrset); - if(num == 0) { - verbose(VERB_QUERY, "rrset failed to verify due to a lack of " - "signatures"); - *reason = "no signatures"; - return sec_status_bogus; - } - - if(sigalg) { - algo_needs_init_list(&needs, sigalg); - if(algo_needs_num_missing(&needs) == 0) { - verbose(VERB_QUERY, "zone has no known algorithms"); - *reason = "zone has no known algorithms"; - return sec_status_insecure; - } - } - for(i=0; i<num; i++) { - sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset, - dnskey, i, &sortree, reason); - /* see which algorithm has been fixed up */ - if(sec == sec_status_secure) { - if(!sigalg) - return sec; /* done! */ - else if(algo_needs_set_secure(&needs, - (uint8_t)rrset_get_sig_algo(rrset, i))) - return sec; /* done! */ - } else if(sigalg && sec == sec_status_bogus) { - algo_needs_set_bogus(&needs, - (uint8_t)rrset_get_sig_algo(rrset, i)); - } - } - if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { - verbose(VERB_ALGO, "rrset failed to verify: " - "no valid signatures for %d algorithms", - (int)algo_needs_num_missing(&needs)); - algo_needs_reason(env, alg, reason, "no signatures"); - } else { - verbose(VERB_ALGO, "rrset failed to verify: " - "no valid signatures"); - } - return sec_status_bogus; -} - -void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s) -{ - char buf[256]; - sldns_lookup_table *t = sldns_lookup_by_id(sldns_algorithms, alg); - if(t&&t->name) - snprintf(buf, sizeof(buf), "%s with algorithm %s", s, t->name); - else snprintf(buf, sizeof(buf), "%s with algorithm ALG%u", s, - (unsigned)alg); - *reason = regional_strdup(env->scratch, buf); - if(!*reason) - *reason = s; -} - -enum sec_status -dnskey_verify_rrset(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - size_t dnskey_idx, char** reason) -{ - enum sec_status sec; - size_t i, num, numchecked = 0; - rbtree_type* sortree = NULL; - int buf_canon = 0; - uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx); - int algo = dnskey_get_algo(dnskey, dnskey_idx); - - num = rrset_get_sigcount(rrset); - if(num == 0) { - verbose(VERB_QUERY, "rrset failed to verify due to a lack of " - "signatures"); - *reason = "no signatures"; - return sec_status_bogus; - } - for(i=0; i<num; i++) { - /* see if sig matches keytag and algo */ - if(algo != rrset_get_sig_algo(rrset, i) || - tag != rrset_get_sig_keytag(rrset, i)) - continue; - buf_canon = 0; - sec = dnskey_verify_rrset_sig(env->scratch, - env->scratch_buffer, ve, *env->now, rrset, - dnskey, dnskey_idx, i, &sortree, &buf_canon, reason); - if(sec == sec_status_secure) - return sec; - numchecked ++; - } - verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus"); - if(!numchecked) *reason = "signature missing"; - return sec_status_bogus; -} - -enum sec_status -dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, - time_t now, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, size_t sig_idx, - struct rbtree_type** sortree, char** reason) -{ - /* find matching keys and check them */ - enum sec_status sec = sec_status_bogus; - uint16_t tag = rrset_get_sig_keytag(rrset, sig_idx); - int algo = rrset_get_sig_algo(rrset, sig_idx); - size_t i, num = rrset_get_count(dnskey); - size_t numchecked = 0; - int buf_canon = 0; - verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo); - if(!dnskey_algo_id_is_supported(algo)) { - verbose(VERB_QUERY, "verify sig: unknown algorithm"); - return sec_status_insecure; - } - - for(i=0; i<num; i++) { - /* see if key matches keytag and algo */ - if(algo != dnskey_get_algo(dnskey, i) || - tag != dnskey_calc_keytag(dnskey, i)) - continue; - numchecked ++; - - /* see if key verifies */ - sec = dnskey_verify_rrset_sig(env->scratch, - env->scratch_buffer, ve, now, rrset, dnskey, i, - sig_idx, sortree, &buf_canon, reason); - if(sec == sec_status_secure) - return sec; - } - if(numchecked == 0) { - *reason = "signatures from unknown keys"; - verbose(VERB_QUERY, "verify: could not find appropriate key"); - return sec_status_bogus; - } - return sec_status_bogus; -} - -/** - * RR entries in a canonical sorted tree of RRs - */ -struct canon_rr { - /** rbtree node, key is this structure */ - rbnode_type node; - /** rrset the RR is in */ - struct ub_packed_rrset_key* rrset; - /** which RR in the rrset */ - size_t rr_idx; -}; - -/** - * Compare two RR for canonical order, in a field-style sweep. - * @param d: rrset data - * @param desc: ldns wireformat descriptor. - * @param i: first RR to compare - * @param j: first RR to compare - * @return comparison code. - */ -static int -canonical_compare_byfield(struct packed_rrset_data* d, - const sldns_rr_descriptor* desc, size_t i, size_t j) -{ - /* sweep across rdata, keep track of some state: - * which rr field, and bytes left in field. - * current position in rdata, length left. - * are we in a dname, length left in a label. - */ - int wfi = -1; /* current wireformat rdata field (rdf) */ - int wfj = -1; - uint8_t* di = d->rr_data[i]+2; /* ptr to current rdata byte */ - uint8_t* dj = d->rr_data[j]+2; - size_t ilen = d->rr_len[i]-2; /* length left in rdata */ - size_t jlen = d->rr_len[j]-2; - int dname_i = 0; /* true if these bytes are part of a name */ - int dname_j = 0; - size_t lablen_i = 0; /* 0 for label length byte,for first byte of rdf*/ - size_t lablen_j = 0; /* otherwise remaining length of rdf or label */ - int dname_num_i = (int)desc->_dname_count; /* decreased at root label */ - int dname_num_j = (int)desc->_dname_count; - - /* loop while there are rdata bytes available for both rrs, - * and still some lowercasing needs to be done; either the dnames - * have not been reached yet, or they are currently being processed */ - while(ilen > 0 && jlen > 0 && (dname_num_i > 0 || dname_num_j > 0)) { - /* compare these two bytes */ - /* lowercase if in a dname and not a label length byte */ - if( ((dname_i && lablen_i)?(uint8_t)tolower((int)*di):*di) - != ((dname_j && lablen_j)?(uint8_t)tolower((int)*dj):*dj) - ) { - if(((dname_i && lablen_i)?(uint8_t)tolower((int)*di):*di) - < ((dname_j && lablen_j)?(uint8_t)tolower((int)*dj):*dj)) - return -1; - return 1; - } - ilen--; - jlen--; - /* bytes are equal */ - - /* advance field i */ - /* lablen 0 means that this byte is the first byte of the - * next rdata field; inspect this rdata field and setup - * to process the rest of this rdata field. - * The reason to first read the byte, then setup the rdf, - * is that we are then sure the byte is available and short - * rdata is handled gracefully (even if it is a formerr). */ - if(lablen_i == 0) { - if(dname_i) { - /* scan this dname label */ - /* capture length to lowercase */ - lablen_i = (size_t)*di; - if(lablen_i == 0) { - /* end root label */ - dname_i = 0; - dname_num_i--; - /* if dname num is 0, then the - * remainder is binary only */ - if(dname_num_i == 0) - lablen_i = ilen; - } - } else { - /* scan this rdata field */ - wfi++; - if(desc->_wireformat[wfi] - == LDNS_RDF_TYPE_DNAME) { - dname_i = 1; - lablen_i = (size_t)*di; - if(lablen_i == 0) { - dname_i = 0; - dname_num_i--; - if(dname_num_i == 0) - lablen_i = ilen; - } - } else if(desc->_wireformat[wfi] - == LDNS_RDF_TYPE_STR) - lablen_i = (size_t)*di; - else lablen_i = get_rdf_size( - desc->_wireformat[wfi]) - 1; - } - } else lablen_i--; - - /* advance field j; same as for i */ - if(lablen_j == 0) { - if(dname_j) { - lablen_j = (size_t)*dj; - if(lablen_j == 0) { - dname_j = 0; - dname_num_j--; - if(dname_num_j == 0) - lablen_j = jlen; - } - } else { - wfj++; - if(desc->_wireformat[wfj] - == LDNS_RDF_TYPE_DNAME) { - dname_j = 1; - lablen_j = (size_t)*dj; - if(lablen_j == 0) { - dname_j = 0; - dname_num_j--; - if(dname_num_j == 0) - lablen_j = jlen; - } - } else if(desc->_wireformat[wfj] - == LDNS_RDF_TYPE_STR) - lablen_j = (size_t)*dj; - else lablen_j = get_rdf_size( - desc->_wireformat[wfj]) - 1; - } - } else lablen_j--; - di++; - dj++; - } - /* end of the loop; because we advanced byte by byte; now we have - * that the rdata has ended, or that there is a binary remainder */ - /* shortest first */ - if(ilen == 0 && jlen == 0) - return 0; - if(ilen == 0) - return -1; - if(jlen == 0) - return 1; - /* binary remainder, capture comparison in wfi variable */ - if((wfi = memcmp(di, dj, (ilen<jlen)?ilen:jlen)) != 0) - return wfi; - if(ilen < jlen) - return -1; - if(jlen < ilen) - return 1; - return 0; -} - -/** - * Compare two RRs in the same RRset and determine their relative - * canonical order. - * @param rrset: the rrset in which to perform compares. - * @param i: first RR to compare - * @param j: first RR to compare - * @return 0 if RR i== RR j, -1 if <, +1 if >. - */ -static int -canonical_compare(struct ub_packed_rrset_key* rrset, size_t i, size_t j) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*) - rrset->entry.data; - const sldns_rr_descriptor* desc; - uint16_t type = ntohs(rrset->rk.type); - size_t minlen; - int c; - - if(i==j) - return 0; - - switch(type) { - /* These RR types have only a name as RDATA. - * This name has to be canonicalized.*/ - case LDNS_RR_TYPE_NS: - case LDNS_RR_TYPE_MD: - case LDNS_RR_TYPE_MF: - case LDNS_RR_TYPE_CNAME: - case LDNS_RR_TYPE_MB: - case LDNS_RR_TYPE_MG: - case LDNS_RR_TYPE_MR: - case LDNS_RR_TYPE_PTR: - case LDNS_RR_TYPE_DNAME: - /* the wireread function has already checked these - * dname's for correctness, and this double checks */ - if(!dname_valid(d->rr_data[i]+2, d->rr_len[i]-2) || - !dname_valid(d->rr_data[j]+2, d->rr_len[j]-2)) - return 0; - return query_dname_compare(d->rr_data[i]+2, - d->rr_data[j]+2); - - /* These RR types have STR and fixed size rdata fields - * before one or more name fields that need canonicalizing, - * and after that a byte-for byte remainder can be compared. - */ - /* type starts with the name; remainder is binary compared */ - case LDNS_RR_TYPE_NXT: - /* use rdata field formats */ - case LDNS_RR_TYPE_MINFO: - case LDNS_RR_TYPE_RP: - case LDNS_RR_TYPE_SOA: - case LDNS_RR_TYPE_RT: - case LDNS_RR_TYPE_AFSDB: - case LDNS_RR_TYPE_KX: - case LDNS_RR_TYPE_MX: - case LDNS_RR_TYPE_SIG: - /* RRSIG signer name has to be downcased */ - case LDNS_RR_TYPE_RRSIG: - case LDNS_RR_TYPE_PX: - case LDNS_RR_TYPE_NAPTR: - case LDNS_RR_TYPE_SRV: - desc = sldns_rr_descript(type); - log_assert(desc); - /* this holds for the types that need canonicalizing */ - log_assert(desc->_minimum == desc->_maximum); - return canonical_compare_byfield(d, desc, i, j); - - case LDNS_RR_TYPE_HINFO: /* no longer downcased */ - case LDNS_RR_TYPE_NSEC: - default: - /* For unknown RR types, or types not listed above, - * no canonicalization is needed, do binary compare */ - /* byte for byte compare, equal means shortest first*/ - minlen = d->rr_len[i]-2; - if(minlen > d->rr_len[j]-2) - minlen = d->rr_len[j]-2; - c = memcmp(d->rr_data[i]+2, d->rr_data[j]+2, minlen); - if(c!=0) - return c; - /* rdata equal, shortest is first */ - if(d->rr_len[i] < d->rr_len[j]) - return -1; - if(d->rr_len[i] > d->rr_len[j]) - return 1; - /* rdata equal, length equal */ - break; - } - return 0; -} - -int -canonical_tree_compare(const void* k1, const void* k2) -{ - struct canon_rr* r1 = (struct canon_rr*)k1; - struct canon_rr* r2 = (struct canon_rr*)k2; - log_assert(r1->rrset == r2->rrset); - return canonical_compare(r1->rrset, r1->rr_idx, r2->rr_idx); -} - -/** - * Sort RRs for rrset in canonical order. - * Does not actually canonicalize the RR rdatas. - * Does not touch rrsigs. - * @param rrset: to sort. - * @param d: rrset data. - * @param sortree: tree to sort into. - * @param rrs: rr storage. - */ -static void -canonical_sort(struct ub_packed_rrset_key* rrset, struct packed_rrset_data* d, - rbtree_type* sortree, struct canon_rr* rrs) -{ - size_t i; - /* insert into rbtree to sort and detect duplicates */ - for(i=0; i<d->count; i++) { - rrs[i].node.key = &rrs[i]; - rrs[i].rrset = rrset; - rrs[i].rr_idx = i; - if(!rbtree_insert(sortree, &rrs[i].node)) { - /* this was a duplicate */ - } - } -} - -/** - * Inser canonical owner name into buffer. - * @param buf: buffer to insert into at current position. - * @param k: rrset with its owner name. - * @param sig: signature with signer name and label count. - * must be length checked, at least 18 bytes long. - * @param can_owner: position in buffer returned for future use. - * @param can_owner_len: length of canonical owner name. - */ -static void -insert_can_owner(sldns_buffer* buf, struct ub_packed_rrset_key* k, - uint8_t* sig, uint8_t** can_owner, size_t* can_owner_len) -{ - int rrsig_labels = (int)sig[3]; - int fqdn_labels = dname_signame_label_count(k->rk.dname); - *can_owner = sldns_buffer_current(buf); - if(rrsig_labels == fqdn_labels) { - /* no change */ - sldns_buffer_write(buf, k->rk.dname, k->rk.dname_len); - query_dname_tolower(*can_owner); - *can_owner_len = k->rk.dname_len; - return; - } - log_assert(rrsig_labels < fqdn_labels); - /* *. | fqdn(rightmost rrsig_labels) */ - if(rrsig_labels < fqdn_labels) { - int i; - uint8_t* nm = k->rk.dname; - size_t len = k->rk.dname_len; - /* so skip fqdn_labels-rrsig_labels */ - for(i=0; i<fqdn_labels-rrsig_labels; i++) { - dname_remove_label(&nm, &len); - } - *can_owner_len = len+2; - sldns_buffer_write(buf, (uint8_t*)"\001*", 2); - sldns_buffer_write(buf, nm, len); - query_dname_tolower(*can_owner); - } -} - -/** - * Canonicalize Rdata in buffer. - * @param buf: buffer at position just after the rdata. - * @param rrset: rrset with type. - * @param len: length of the rdata (including rdatalen uint16). - */ -static void -canonicalize_rdata(sldns_buffer* buf, struct ub_packed_rrset_key* rrset, - size_t len) -{ - uint8_t* datstart = sldns_buffer_current(buf)-len+2; - switch(ntohs(rrset->rk.type)) { - case LDNS_RR_TYPE_NXT: - case LDNS_RR_TYPE_NS: - case LDNS_RR_TYPE_MD: - case LDNS_RR_TYPE_MF: - case LDNS_RR_TYPE_CNAME: - case LDNS_RR_TYPE_MB: - case LDNS_RR_TYPE_MG: - case LDNS_RR_TYPE_MR: - case LDNS_RR_TYPE_PTR: - case LDNS_RR_TYPE_DNAME: - /* type only has a single argument, the name */ - query_dname_tolower(datstart); - return; - case LDNS_RR_TYPE_MINFO: - case LDNS_RR_TYPE_RP: - case LDNS_RR_TYPE_SOA: - /* two names after another */ - query_dname_tolower(datstart); - query_dname_tolower(datstart + - dname_valid(datstart, len-2)); - return; - case LDNS_RR_TYPE_RT: - case LDNS_RR_TYPE_AFSDB: - case LDNS_RR_TYPE_KX: - case LDNS_RR_TYPE_MX: - /* skip fixed part */ - if(len < 2+2+1) /* rdlen, skiplen, 1byteroot */ - return; - datstart += 2; - query_dname_tolower(datstart); - return; - case LDNS_RR_TYPE_SIG: - /* downcase the RRSIG, compat with BIND (kept it from SIG) */ - case LDNS_RR_TYPE_RRSIG: - /* skip fixed part */ - if(len < 2+18+1) - return; - datstart += 18; - query_dname_tolower(datstart); - return; - case LDNS_RR_TYPE_PX: - /* skip, then two names after another */ - if(len < 2+2+1) - return; - datstart += 2; - query_dname_tolower(datstart); - query_dname_tolower(datstart + - dname_valid(datstart, len-2-2)); - return; - case LDNS_RR_TYPE_NAPTR: - if(len < 2+4) - return; - len -= 2+4; - datstart += 4; - if(len < (size_t)datstart[0]+1) /* skip text field */ - return; - len -= (size_t)datstart[0]+1; - datstart += (size_t)datstart[0]+1; - if(len < (size_t)datstart[0]+1) /* skip text field */ - return; - len -= (size_t)datstart[0]+1; - datstart += (size_t)datstart[0]+1; - if(len < (size_t)datstart[0]+1) /* skip text field */ - return; - len -= (size_t)datstart[0]+1; - datstart += (size_t)datstart[0]+1; - if(len < 1) /* check name is at least 1 byte*/ - return; - query_dname_tolower(datstart); - return; - case LDNS_RR_TYPE_SRV: - /* skip fixed part */ - if(len < 2+6+1) - return; - datstart += 6; - query_dname_tolower(datstart); - return; - - /* do not canonicalize NSEC rdata name, compat with - * from bind 9.4 signer, where it does not do so */ - case LDNS_RR_TYPE_NSEC: /* type starts with the name */ - case LDNS_RR_TYPE_HINFO: /* not downcased */ - /* A6 not supported */ - default: - /* nothing to do for unknown types */ - return; - } -} - -int rrset_canonical_equal(struct regional* region, - struct ub_packed_rrset_key* k1, struct ub_packed_rrset_key* k2) -{ - struct rbtree_type sortree1, sortree2; - struct canon_rr *rrs1, *rrs2, *p1, *p2; - struct packed_rrset_data* d1=(struct packed_rrset_data*)k1->entry.data; - struct packed_rrset_data* d2=(struct packed_rrset_data*)k2->entry.data; - struct ub_packed_rrset_key fk; - struct packed_rrset_data fd; - size_t flen[2]; - uint8_t* fdata[2]; - - /* basic compare */ - if(k1->rk.dname_len != k2->rk.dname_len || - k1->rk.flags != k2->rk.flags || - k1->rk.type != k2->rk.type || - k1->rk.rrset_class != k2->rk.rrset_class || - query_dname_compare(k1->rk.dname, k2->rk.dname) != 0) - return 0; - if(d1->ttl != d2->ttl || - d1->count != d2->count || - d1->rrsig_count != d2->rrsig_count || - d1->trust != d2->trust || - d1->security != d2->security) - return 0; - - /* init */ - memset(&fk, 0, sizeof(fk)); - memset(&fd, 0, sizeof(fd)); - fk.entry.data = &fd; - fd.count = 2; - fd.rr_len = flen; - fd.rr_data = fdata; - rbtree_init(&sortree1, &canonical_tree_compare); - rbtree_init(&sortree2, &canonical_tree_compare); - if(d1->count > RR_COUNT_MAX || d2->count > RR_COUNT_MAX) - return 1; /* protection against integer overflow */ - rrs1 = regional_alloc(region, sizeof(struct canon_rr)*d1->count); - rrs2 = regional_alloc(region, sizeof(struct canon_rr)*d2->count); - if(!rrs1 || !rrs2) return 1; /* alloc failure */ - - /* sort */ - canonical_sort(k1, d1, &sortree1, rrs1); - canonical_sort(k2, d2, &sortree2, rrs2); - - /* compare canonical-sorted RRs for canonical-equality */ - if(sortree1.count != sortree2.count) - return 0; - p1 = (struct canon_rr*)rbtree_first(&sortree1); - p2 = (struct canon_rr*)rbtree_first(&sortree2); - while(p1 != (struct canon_rr*)RBTREE_NULL && - p2 != (struct canon_rr*)RBTREE_NULL) { - flen[0] = d1->rr_len[p1->rr_idx]; - flen[1] = d2->rr_len[p2->rr_idx]; - fdata[0] = d1->rr_data[p1->rr_idx]; - fdata[1] = d2->rr_data[p2->rr_idx]; - - if(canonical_compare(&fk, 0, 1) != 0) - return 0; - p1 = (struct canon_rr*)rbtree_next(&p1->node); - p2 = (struct canon_rr*)rbtree_next(&p2->node); - } - return 1; -} - -/** - * Create canonical form of rrset in the scratch buffer. - * @param region: temporary region. - * @param buf: the buffer to use. - * @param k: the rrset to insert. - * @param sig: RRSIG rdata to include. - * @param siglen: RRSIG rdata len excluding signature field, but inclusive - * signer name length. - * @param sortree: if NULL is passed a new sorted rrset tree is built. - * Otherwise it is reused. - * @return false on alloc error. - */ -static int -rrset_canonical(struct regional* region, sldns_buffer* buf, - struct ub_packed_rrset_key* k, uint8_t* sig, size_t siglen, - struct rbtree_type** sortree) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; - uint8_t* can_owner = NULL; - size_t can_owner_len = 0; - struct canon_rr* walk; - struct canon_rr* rrs; - - if(!*sortree) { - *sortree = (struct rbtree_type*)regional_alloc(region, - sizeof(rbtree_type)); - if(!*sortree) - return 0; - if(d->count > RR_COUNT_MAX) - return 0; /* integer overflow protection */ - rrs = regional_alloc(region, sizeof(struct canon_rr)*d->count); - if(!rrs) { - *sortree = NULL; - return 0; - } - rbtree_init(*sortree, &canonical_tree_compare); - canonical_sort(k, d, *sortree, rrs); - } - - sldns_buffer_clear(buf); - sldns_buffer_write(buf, sig, siglen); - /* canonicalize signer name */ - query_dname_tolower(sldns_buffer_begin(buf)+18); - RBTREE_FOR(walk, struct canon_rr*, (*sortree)) { - /* see if there is enough space left in the buffer */ - if(sldns_buffer_remaining(buf) < can_owner_len + 2 + 2 + 4 - + d->rr_len[walk->rr_idx]) { - log_err("verify: failed to canonicalize, " - "rrset too big"); - return 0; - } - /* determine canonical owner name */ - if(can_owner) - sldns_buffer_write(buf, can_owner, can_owner_len); - else insert_can_owner(buf, k, sig, &can_owner, - &can_owner_len); - sldns_buffer_write(buf, &k->rk.type, 2); - sldns_buffer_write(buf, &k->rk.rrset_class, 2); - sldns_buffer_write(buf, sig+4, 4); - sldns_buffer_write(buf, d->rr_data[walk->rr_idx], - d->rr_len[walk->rr_idx]); - canonicalize_rdata(buf, k, d->rr_len[walk->rr_idx]); - } - sldns_buffer_flip(buf); - return 1; -} - -/** pretty print rrsig error with dates */ -static void -sigdate_error(const char* str, int32_t expi, int32_t incep, int32_t now) -{ - struct tm tm; - char expi_buf[16]; - char incep_buf[16]; - char now_buf[16]; - time_t te, ti, tn; - - if(verbosity < VERB_QUERY) - return; - te = (time_t)expi; - ti = (time_t)incep; - tn = (time_t)now; - memset(&tm, 0, sizeof(tm)); - if(gmtime_r(&te, &tm) && strftime(expi_buf, 15, "%Y%m%d%H%M%S", &tm) - &&gmtime_r(&ti, &tm) && strftime(incep_buf, 15, "%Y%m%d%H%M%S", &tm) - &&gmtime_r(&tn, &tm) && strftime(now_buf, 15, "%Y%m%d%H%M%S", &tm)) { - log_info("%s expi=%s incep=%s now=%s", str, expi_buf, - incep_buf, now_buf); - } else - log_info("%s expi=%u incep=%u now=%u", str, (unsigned)expi, - (unsigned)incep, (unsigned)now); -} - -/** check rrsig dates */ -static int -check_dates(struct val_env* ve, uint32_t unow, - uint8_t* expi_p, uint8_t* incep_p, char** reason) -{ - /* read out the dates */ - int32_t expi, incep, now; - memmove(&expi, expi_p, sizeof(expi)); - memmove(&incep, incep_p, sizeof(incep)); - expi = ntohl(expi); - incep = ntohl(incep); - - /* get current date */ - if(ve->date_override) { - if(ve->date_override == -1) { - verbose(VERB_ALGO, "date override: ignore date"); - return 1; - } - now = ve->date_override; - verbose(VERB_ALGO, "date override option %d", (int)now); - } else now = (int32_t)unow; - - /* check them */ - if(incep - expi > 0) { - sigdate_error("verify: inception after expiration, " - "signature bad", expi, incep, now); - *reason = "signature inception after expiration"; - return 0; - } - if(incep - now > 0) { - /* within skew ? (calc here to avoid calculation normally) */ - int32_t skew = (expi-incep)/10; - if(skew < ve->skew_min) skew = ve->skew_min; - if(skew > ve->skew_max) skew = ve->skew_max; - if(incep - now > skew) { - sigdate_error("verify: signature bad, current time is" - " before inception date", expi, incep, now); - *reason = "signature before inception date"; - return 0; - } - sigdate_error("verify warning suspicious signature inception " - " or bad local clock", expi, incep, now); - } - if(now - expi > 0) { - int32_t skew = (expi-incep)/10; - if(skew < ve->skew_min) skew = ve->skew_min; - if(skew > ve->skew_max) skew = ve->skew_max; - if(now - expi > skew) { - sigdate_error("verify: signature expired", expi, - incep, now); - *reason = "signature expired"; - return 0; - } - sigdate_error("verify warning suspicious signature expiration " - " or bad local clock", expi, incep, now); - } - return 1; -} - -/** adjust rrset TTL for verified rrset, compare to original TTL and expi */ -static void -adjust_ttl(struct val_env* ve, uint32_t unow, - struct ub_packed_rrset_key* rrset, uint8_t* orig_p, - uint8_t* expi_p, uint8_t* incep_p) -{ - struct packed_rrset_data* d = - (struct packed_rrset_data*)rrset->entry.data; - /* read out the dates */ - int32_t origttl, expittl, expi, incep, now; - memmove(&origttl, orig_p, sizeof(origttl)); - memmove(&expi, expi_p, sizeof(expi)); - memmove(&incep, incep_p, sizeof(incep)); - expi = ntohl(expi); - incep = ntohl(incep); - origttl = ntohl(origttl); - - /* get current date */ - if(ve->date_override) { - now = ve->date_override; - } else now = (int32_t)unow; - expittl = expi - now; - - /* so now: - * d->ttl: rrset ttl read from message or cache. May be reduced - * origttl: original TTL from signature, authoritative TTL max. - * MIN_TTL: minimum TTL from config. - * expittl: TTL until the signature expires. - * - * Use the smallest of these, but don't let origttl set the TTL - * below the minimum. - */ - if(MIN_TTL > (time_t)origttl && d->ttl > MIN_TTL) { - verbose(VERB_QUERY, "rrset TTL larger than original and minimum" - " TTL, adjusting TTL downwards to minimum ttl"); - d->ttl = MIN_TTL; - } - else if(MIN_TTL <= origttl && d->ttl > (time_t)origttl) { - verbose(VERB_QUERY, "rrset TTL larger than original TTL, " - "adjusting TTL downwards to original ttl"); - d->ttl = origttl; - } - - if(expittl > 0 && d->ttl > (time_t)expittl) { - verbose(VERB_ALGO, "rrset TTL larger than sig expiration ttl," - " adjusting TTL downwards"); - d->ttl = expittl; - } -} - -enum sec_status -dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, - struct val_env* ve, time_t now, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - size_t dnskey_idx, size_t sig_idx, - struct rbtree_type** sortree, int* buf_canon, char** reason) -{ - enum sec_status sec; - uint8_t* sig; /* RRSIG rdata */ - size_t siglen; - size_t rrnum = rrset_get_count(rrset); - uint8_t* signer; /* rrsig signer name */ - size_t signer_len; - unsigned char* sigblock; /* signature rdata field */ - unsigned int sigblock_len; - uint16_t ktag; /* DNSKEY key tag */ - unsigned char* key; /* public key rdata field */ - unsigned int keylen; - rrset_get_rdata(rrset, rrnum + sig_idx, &sig, &siglen); - /* min length of rdatalen, fixed rrsig, root signer, 1 byte sig */ - if(siglen < 2+20) { - verbose(VERB_QUERY, "verify: signature too short"); - *reason = "signature too short"; - return sec_status_bogus; - } - - if(!(dnskey_get_flags(dnskey, dnskey_idx) & DNSKEY_BIT_ZSK)) { - verbose(VERB_QUERY, "verify: dnskey without ZSK flag"); - *reason = "dnskey without ZSK flag"; - return sec_status_bogus; - } - - if(dnskey_get_protocol(dnskey, dnskey_idx) != LDNS_DNSSEC_KEYPROTO) { - /* RFC 4034 says DNSKEY PROTOCOL MUST be 3 */ - verbose(VERB_QUERY, "verify: dnskey has wrong key protocol"); - *reason = "dnskey has wrong protocolnumber"; - return sec_status_bogus; - } - - /* verify as many fields in rrsig as possible */ - signer = sig+2+18; - signer_len = dname_valid(signer, siglen-2-18); - if(!signer_len) { - verbose(VERB_QUERY, "verify: malformed signer name"); - *reason = "signer name malformed"; - return sec_status_bogus; /* signer name invalid */ - } - if(!dname_subdomain_c(rrset->rk.dname, signer)) { - verbose(VERB_QUERY, "verify: signer name is off-tree"); - *reason = "signer name off-tree"; - return sec_status_bogus; /* signer name offtree */ - } - sigblock = (unsigned char*)signer+signer_len; - if(siglen < 2+18+signer_len+1) { - verbose(VERB_QUERY, "verify: too short, no signature data"); - *reason = "signature too short, no signature data"; - return sec_status_bogus; /* sig rdf is < 1 byte */ - } - sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len); - - /* verify key dname == sig signer name */ - if(query_dname_compare(signer, dnskey->rk.dname) != 0) { - verbose(VERB_QUERY, "verify: wrong key for rrsig"); - log_nametypeclass(VERB_QUERY, "RRSIG signername is", - signer, 0, 0); - log_nametypeclass(VERB_QUERY, "the key name is", - dnskey->rk.dname, 0, 0); - *reason = "signer name mismatches key name"; - return sec_status_bogus; - } - - /* verify covered type */ - /* memcmp works because type is in network format for rrset */ - if(memcmp(sig+2, &rrset->rk.type, 2) != 0) { - verbose(VERB_QUERY, "verify: wrong type covered"); - *reason = "signature covers wrong type"; - return sec_status_bogus; - } - /* verify keytag and sig algo (possibly again) */ - if((int)sig[2+2] != dnskey_get_algo(dnskey, dnskey_idx)) { - verbose(VERB_QUERY, "verify: wrong algorithm"); - *reason = "signature has wrong algorithm"; - return sec_status_bogus; - } - ktag = htons(dnskey_calc_keytag(dnskey, dnskey_idx)); - if(memcmp(sig+2+16, &ktag, 2) != 0) { - verbose(VERB_QUERY, "verify: wrong keytag"); - *reason = "signature has wrong keytag"; - return sec_status_bogus; - } - - /* verify labels is in a valid range */ - if((int)sig[2+3] > dname_signame_label_count(rrset->rk.dname)) { - verbose(VERB_QUERY, "verify: labelcount out of range"); - *reason = "signature labelcount out of range"; - return sec_status_bogus; - } - - /* original ttl, always ok */ - - if(!*buf_canon) { - /* create rrset canonical format in buffer, ready for - * signature */ - if(!rrset_canonical(region, buf, rrset, sig+2, - 18 + signer_len, sortree)) { - log_err("verify: failed due to alloc error"); - return sec_status_unchecked; - } - *buf_canon = 1; - } - - /* check that dnskey is available */ - dnskey_get_pubkey(dnskey, dnskey_idx, &key, &keylen); - if(!key) { - verbose(VERB_QUERY, "verify: short DNSKEY RR"); - return sec_status_unchecked; - } - - /* verify */ - sec = verify_canonrrset(buf, (int)sig[2+2], - sigblock, sigblock_len, key, keylen, reason); - - if(sec == sec_status_secure) { - /* check if TTL is too high - reduce if so */ - adjust_ttl(ve, now, rrset, sig+2+4, sig+2+8, sig+2+12); - - /* verify inception, expiration dates - * Do this last so that if you ignore expired-sigs the - * rest is sure to be OK. */ - if(!check_dates(ve, now, sig+2+8, sig+2+12, reason)) { - return sec_status_bogus; - } - } - - return sec; -} diff --git a/external/unbound/validator/val_sigcrypt.h b/external/unbound/validator/val_sigcrypt.h deleted file mode 100644 index 5a975acff..000000000 --- a/external/unbound/validator/val_sigcrypt.h +++ /dev/null @@ -1,323 +0,0 @@ -/* - * validator/val_sigcrypt.h - validator signature crypto functions. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - * The functions help with signature verification and checking, the - * bridging between RR wireformat data and crypto calls. - */ - -#ifndef VALIDATOR_VAL_SIGCRYPT_H -#define VALIDATOR_VAL_SIGCRYPT_H -#include "util/data/packed_rrset.h" -struct val_env; -struct module_env; -struct ub_packed_rrset_key; -struct rbtree_type; -struct regional; -struct sldns_buffer; - -/** number of entries in algorithm needs array */ -#define ALGO_NEEDS_MAX 256 - -/** - * Storage for algorithm needs. DNSKEY algorithms. - */ -struct algo_needs { - /** the algorithms (8-bit) with each a number. - * 0: not marked. - * 1: marked 'necessary but not yet fulfilled' - * 2: marked bogus. - * Indexed by algorithm number. - */ - uint8_t needs[ALGO_NEEDS_MAX]; - /** the number of entries in the array that are unfulfilled */ - size_t num; -}; - -/** - * Initialize algo needs structure, set algos from rrset as needed. - * Results are added to an existing need structure. - * @param n: struct with storage. - * @param dnskey: algos from this struct set as necessary. DNSKEY set. - * @param sigalg: adds to signalled algorithm list too. - */ -void algo_needs_init_dnskey_add(struct algo_needs* n, - struct ub_packed_rrset_key* dnskey, uint8_t* sigalg); - -/** - * Initialize algo needs structure from a signalled algo list. - * @param n: struct with storage. - * @param sigalg: signalled algorithm list, numbers ends with 0. - */ -void algo_needs_init_list(struct algo_needs* n, uint8_t* sigalg); - -/** - * Initialize algo needs structure, set algos from rrset as needed. - * @param n: struct with storage. - * @param ds: algos from this struct set as necessary. DS set. - * @param fav_ds_algo: filter to use only this DS algo. - * @param sigalg: list of signalled algos, constructed as output, - * provide size ALGO_NEEDS_MAX+1. list of algonumbers, ends with a zero. - */ -void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds, - int fav_ds_algo, uint8_t* sigalg); - -/** - * Mark this algorithm as a success, sec_secure, and see if we are done. - * @param n: storage structure processed. - * @param algo: the algorithm processed to be secure. - * @return if true, processing has finished successfully, we are satisfied. - */ -int algo_needs_set_secure(struct algo_needs* n, uint8_t algo); - -/** - * Mark this algorithm a failure, sec_bogus. It can later be overridden - * by a success for this algorithm (with a different signature). - * @param n: storage structure processed. - * @param algo: the algorithm processed to be bogus. - */ -void algo_needs_set_bogus(struct algo_needs* n, uint8_t algo); - -/** - * See how many algorithms are missing (not bogus or secure, but not processed) - * @param n: storage structure processed. - * @return number of algorithms missing after processing. - */ -size_t algo_needs_num_missing(struct algo_needs* n); - -/** - * See which algo is missing. - * @param n: struct after processing. - * @return if 0 an algorithm was bogus, if a number, this algorithm was - * missing. So on 0, report why that was bogus, on number report a missing - * algorithm. There could be multiple missing, this reports the first one. - */ -int algo_needs_missing(struct algo_needs* n); - -/** - * Format error reason for algorithm missing. - * @param env: module env with scratch for temp storage of string. - * @param alg: DNSKEY-algorithm missing. - * @param reason: destination. - * @param s: string, appended with 'with algorithm ..'. - */ -void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s); - -/** - * Check if dnskey matches a DS digest - * Does not check dnskey-keyid footprint, just the digest. - * @param env: module environment. Uses scratch space. - * @param dnskey_rrset: DNSKEY rrset. - * @param dnskey_idx: index of RR in rrset. - * @param ds_rrset: DS rrset - * @param ds_idx: index of RR in DS rrset. - * @return true if it matches, false on error, not supported or no match. - */ -int ds_digest_match_dnskey(struct module_env* env, - struct ub_packed_rrset_key* dnskey_rrset, size_t dnskey_idx, - struct ub_packed_rrset_key* ds_rrset, size_t ds_idx); - -/** - * Get dnskey keytag, footprint value - * @param dnskey_rrset: DNSKEY rrset. - * @param dnskey_idx: index of RR in rrset. - * @return the keytag or 0 for badly formatted DNSKEYs. - */ -uint16_t dnskey_calc_keytag(struct ub_packed_rrset_key* dnskey_rrset, - size_t dnskey_idx); - -/** - * Get DS keytag, footprint value that matches the DNSKEY keytag it signs. - * @param ds_rrset: DS rrset - * @param ds_idx: index of RR in DS rrset. - * @return the keytag or 0 for badly formatted DSs. - */ -uint16_t ds_get_keytag(struct ub_packed_rrset_key* ds_rrset, size_t ds_idx); - -/** - * See if DNSKEY algorithm is supported - * @param dnskey_rrset: DNSKEY rrset. - * @param dnskey_idx: index of RR in rrset. - * @return true if supported. - */ -int dnskey_algo_is_supported(struct ub_packed_rrset_key* dnskey_rrset, - size_t dnskey_idx); - -/** - * See if DS digest algorithm is supported - * @param ds_rrset: DS rrset - * @param ds_idx: index of RR in DS rrset. - * @return true if supported. - */ -int ds_digest_algo_is_supported(struct ub_packed_rrset_key* ds_rrset, - size_t ds_idx); - -/** - * Get DS RR digest algorithm - * @param ds_rrset: DS rrset. - * @param ds_idx: which DS. - * @return algorithm or 0 if DS too short. - */ -int ds_get_digest_algo(struct ub_packed_rrset_key* ds_rrset, size_t ds_idx); - -/** - * See if DS key algorithm is supported - * @param ds_rrset: DS rrset - * @param ds_idx: index of RR in DS rrset. - * @return true if supported. - */ -int ds_key_algo_is_supported(struct ub_packed_rrset_key* ds_rrset, - size_t ds_idx); - -/** - * Get DS RR key algorithm. This value should match with the DNSKEY algo. - * @param k: DS rrset. - * @param idx: which DS. - * @return algorithm or 0 if DS too short. - */ -int ds_get_key_algo(struct ub_packed_rrset_key* k, size_t idx); - -/** - * Get DNSKEY RR signature algorithm - * @param k: DNSKEY rrset. - * @param idx: which DNSKEY RR. - * @return algorithm or 0 if DNSKEY too short. - */ -int dnskey_get_algo(struct ub_packed_rrset_key* k, size_t idx); - -/** - * Get DNSKEY RR flags - * @param k: DNSKEY rrset. - * @param idx: which DNSKEY RR. - * @return flags or 0 if DNSKEY too short. - */ -uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); - -/** - * Verify rrset against dnskey rrset. - * @param env: module environment, scratch space is used. - * @param ve: validator environment, date settings. - * @param rrset: to be validated. - * @param dnskey: DNSKEY rrset, keyset to try. - * @param sigalg: if nonNULL provide downgrade protection otherwise one - * algorithm is enough. - * @param reason: if bogus, a string returned, fixed or alloced in scratch. - * @return SECURE if one key in the set verifies one rrsig. - * UNCHECKED on allocation errors, unsupported algorithms, malformed data, - * and BOGUS on verification failures (no keys match any signatures). - */ -enum sec_status dnskeyset_verify_rrset(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason); - -/** - * verify rrset against one specific dnskey (from rrset) - * @param env: module environment, scratch space is used. - * @param ve: validator environment, date settings. - * @param rrset: to be validated. - * @param dnskey: DNSKEY rrset, keyset. - * @param dnskey_idx: which key from the rrset to try. - * @param reason: if bogus, a string returned, fixed or alloced in scratch. - * @return secure if *this* key signs any of the signatures on rrset. - * unchecked on error or and bogus on bad signature. - */ -enum sec_status dnskey_verify_rrset(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, char** reason); - -/** - * verify rrset, with dnskey rrset, for a specific rrsig in rrset - * @param env: module environment, scratch space is used. - * @param ve: validator environment, date settings. - * @param now: current time for validation (can be overridden). - * @param rrset: to be validated. - * @param dnskey: DNSKEY rrset, keyset to try. - * @param sig_idx: which signature to try to validate. - * @param sortree: reused sorted order. Stored in region. Pass NULL at start, - * and for a new rrset. - * @param reason: if bogus, a string returned, fixed or alloced in scratch. - * @return secure if any key signs *this* signature. bogus if no key signs it, - * or unchecked on error. - */ -enum sec_status dnskeyset_verify_rrset_sig(struct module_env* env, - struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, size_t sig_idx, - struct rbtree_type** sortree, char** reason); - -/** - * verify rrset, with specific dnskey(from set), for a specific rrsig - * @param region: scratch region used for temporary allocation. - * @param buf: scratch buffer used for canonicalized rrset data. - * @param ve: validator environment, date settings. - * @param now: current time for validation (can be overridden). - * @param rrset: to be validated. - * @param dnskey: DNSKEY rrset, keyset. - * @param dnskey_idx: which key from the rrset to try. - * @param sig_idx: which signature to try to validate. - * @param sortree: pass NULL at start, the sorted rrset order is returned. - * pass it again for the same rrset. - * @param buf_canon: if true, the buffer is already canonical. - * pass false at start. pass old value only for same rrset and same - * signature (but perhaps different key) for reuse. - * @param reason: if bogus, a string returned, fixed or alloced in scratch. - * @return secure if this key signs this signature. unchecked on error or - * bogus if it did not validate. - */ -enum sec_status dnskey_verify_rrset_sig(struct regional* region, - struct sldns_buffer* buf, struct val_env* ve, time_t now, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - size_t dnskey_idx, size_t sig_idx, - struct rbtree_type** sortree, int* buf_canon, char** reason); - -/** - * canonical compare for two tree entries - */ -int canonical_tree_compare(const void* k1, const void* k2); - -/** - * Compare two rrsets and see if they are the same, canonicalised. - * The rrsets are not altered. - * @param region: temporary region. - * @param k1: rrset1 - * @param k2: rrset2 - * @return true if equal. - */ -int rrset_canonical_equal(struct regional* region, - struct ub_packed_rrset_key* k1, struct ub_packed_rrset_key* k2); - -#endif /* VALIDATOR_VAL_SIGCRYPT_H */ diff --git a/external/unbound/validator/val_utils.c b/external/unbound/validator/val_utils.c deleted file mode 100644 index e3677e1d9..000000000 --- a/external/unbound/validator/val_utils.c +++ /dev/null @@ -1,1151 +0,0 @@ -/* - * validator/val_utils.c - validator utility functions. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - */ -#include "config.h" -#include "validator/val_utils.h" -#include "validator/validator.h" -#include "validator/val_kentry.h" -#include "validator/val_sigcrypt.h" -#include "validator/val_anchor.h" -#include "validator/val_nsec.h" -#include "validator/val_neg.h" -#include "services/cache/rrset.h" -#include "services/cache/dns.h" -#include "util/data/msgreply.h" -#include "util/data/packed_rrset.h" -#include "util/data/dname.h" -#include "util/net_help.h" -#include "util/module.h" -#include "util/regional.h" -#include "sldns/wire2str.h" -#include "sldns/parseutil.h" - -enum val_classification -val_classify_response(uint16_t query_flags, struct query_info* origqinf, - struct query_info* qinf, struct reply_info* rep, size_t skip) -{ - int rcode = (int)FLAGS_GET_RCODE(rep->flags); - size_t i; - - /* Normal Name Error's are easy to detect -- but don't mistake a CNAME - * chain ending in NXDOMAIN. */ - if(rcode == LDNS_RCODE_NXDOMAIN && rep->an_numrrsets == 0) - return VAL_CLASS_NAMEERROR; - - /* check for referral: nonRD query and it looks like a nodata */ - if(!(query_flags&BIT_RD) && rep->an_numrrsets == 0 && - rcode == LDNS_RCODE_NOERROR) { - /* SOA record in auth indicates it is NODATA instead. - * All validation requiring NODATA messages have SOA in - * authority section. */ - /* uses fact that answer section is empty */ - int saw_ns = 0; - for(i=0; i<rep->ns_numrrsets; i++) { - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA) - return VAL_CLASS_NODATA; - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_DS) - return VAL_CLASS_REFERRAL; - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS) - saw_ns = 1; - } - return saw_ns?VAL_CLASS_REFERRAL:VAL_CLASS_NODATA; - } - /* root referral where NS set is in the answer section */ - if(!(query_flags&BIT_RD) && rep->ns_numrrsets == 0 && - rep->an_numrrsets == 1 && rcode == LDNS_RCODE_NOERROR && - ntohs(rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_NS && - query_dname_compare(rep->rrsets[0]->rk.dname, - origqinf->qname) != 0) - return VAL_CLASS_REFERRAL; - - /* dump bad messages */ - if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) - return VAL_CLASS_UNKNOWN; - /* next check if the skip into the answer section shows no answer */ - if(skip>0 && rep->an_numrrsets <= skip) - return VAL_CLASS_CNAMENOANSWER; - - /* Next is NODATA */ - if(rcode == LDNS_RCODE_NOERROR && rep->an_numrrsets == 0) - return VAL_CLASS_NODATA; - - /* We distinguish between CNAME response and other positive/negative - * responses because CNAME answers require extra processing. */ - - /* We distinguish between ANY and CNAME or POSITIVE because - * ANY responses are validated differently. */ - if(rcode == LDNS_RCODE_NOERROR && qinf->qtype == LDNS_RR_TYPE_ANY) - return VAL_CLASS_ANY; - - /* Note that DNAMEs will be ignored here, unless qtype=DNAME. Unless - * qtype=CNAME, this will yield a CNAME response. */ - for(i=skip; i<rep->an_numrrsets; i++) { - if(rcode == LDNS_RCODE_NOERROR && - ntohs(rep->rrsets[i]->rk.type) == qinf->qtype) - return VAL_CLASS_POSITIVE; - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_CNAME) - return VAL_CLASS_CNAME; - } - log_dns_msg("validator: error. failed to classify response message: ", - qinf, rep); - return VAL_CLASS_UNKNOWN; -} - -/** Get signer name from RRSIG */ -static void -rrsig_get_signer(uint8_t* data, size_t len, uint8_t** sname, size_t* slen) -{ - /* RRSIG rdata is not allowed to be compressed, it is stored - * uncompressed in memory as well, so return a ptr to the name */ - if(len < 21) { - /* too short RRSig: - * short, byte, byte, long, long, long, short, "." is - * 2 1 1 4 4 4 2 1 = 19 - * and a skip of 18 bytes to the name. - * +2 for the rdatalen is 21 bytes len for root label */ - *sname = NULL; - *slen = 0; - return; - } - data += 20; /* skip the fixed size bits */ - len -= 20; - *slen = dname_valid(data, len); - if(!*slen) { - /* bad dname in this rrsig. */ - *sname = NULL; - return; - } - *sname = data; -} - -void -val_find_rrset_signer(struct ub_packed_rrset_key* rrset, uint8_t** sname, - size_t* slen) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*) - rrset->entry.data; - /* return signer for first signature, or NULL */ - if(d->rrsig_count == 0) { - *sname = NULL; - *slen = 0; - return; - } - /* get rrsig signer name out of the signature */ - rrsig_get_signer(d->rr_data[d->count], d->rr_len[d->count], - sname, slen); -} - -/** - * Find best signer name in this set of rrsigs. - * @param rrset: which rrsigs to look through. - * @param qinf: the query name that needs validation. - * @param signer_name: the best signer_name. Updated if a better one is found. - * @param signer_len: length of signer name. - * @param matchcount: count of current best name (starts at 0 for no match). - * Updated if match is improved. - */ -static void -val_find_best_signer(struct ub_packed_rrset_key* rrset, - struct query_info* qinf, uint8_t** signer_name, size_t* signer_len, - int* matchcount) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*) - rrset->entry.data; - uint8_t* sign; - size_t i; - int m; - for(i=d->count; i<d->count+d->rrsig_count; i++) { - sign = d->rr_data[i]+2+18; - /* look at signatures that are valid (long enough), - * and have a signer name that is a superdomain of qname, - * and then check the number of labels in the shared topdomain - * improve the match if possible */ - if(d->rr_len[i] > 2+19 && /* rdata, sig + root label*/ - dname_subdomain_c(qinf->qname, sign)) { - (void)dname_lab_cmp(qinf->qname, - dname_count_labels(qinf->qname), - sign, dname_count_labels(sign), &m); - if(m > *matchcount) { - *matchcount = m; - *signer_name = sign; - (void)dname_count_size_labels(*signer_name, - signer_len); - } - } - } -} - -void -val_find_signer(enum val_classification subtype, struct query_info* qinf, - struct reply_info* rep, size_t skip, uint8_t** signer_name, - size_t* signer_len) -{ - size_t i; - - if(subtype == VAL_CLASS_POSITIVE) { - /* check for the answer rrset */ - for(i=skip; i<rep->an_numrrsets; i++) { - if(query_dname_compare(qinf->qname, - rep->rrsets[i]->rk.dname) == 0) { - val_find_rrset_signer(rep->rrsets[i], - signer_name, signer_len); - return; - } - } - *signer_name = NULL; - *signer_len = 0; - } else if(subtype == VAL_CLASS_CNAME) { - /* check for the first signed cname/dname rrset */ - for(i=skip; i<rep->an_numrrsets; i++) { - val_find_rrset_signer(rep->rrsets[i], - signer_name, signer_len); - if(*signer_name) - return; - if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_DNAME) - break; /* only check CNAME after a DNAME */ - } - *signer_name = NULL; - *signer_len = 0; - } else if(subtype == VAL_CLASS_NAMEERROR - || subtype == VAL_CLASS_NODATA) { - /*Check to see if the AUTH section NSEC record(s) have rrsigs*/ - for(i=rep->an_numrrsets; i< - rep->an_numrrsets+rep->ns_numrrsets; i++) { - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC - || ntohs(rep->rrsets[i]->rk.type) == - LDNS_RR_TYPE_NSEC3) { - val_find_rrset_signer(rep->rrsets[i], - signer_name, signer_len); - return; - } - } - } else if(subtype == VAL_CLASS_CNAMENOANSWER) { - /* find closest superdomain signer name in authority section - * NSEC and NSEC3s */ - int matchcount = 0; - *signer_name = NULL; - *signer_len = 0; - for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep-> - ns_numrrsets; i++) { - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC - || ntohs(rep->rrsets[i]->rk.type) == - LDNS_RR_TYPE_NSEC3) { - val_find_best_signer(rep->rrsets[i], qinf, - signer_name, signer_len, &matchcount); - } - } - } else if(subtype == VAL_CLASS_ANY) { - /* check for one of the answer rrset that has signatures, - * or potentially a DNAME is in use with a different qname */ - for(i=skip; i<rep->an_numrrsets; i++) { - if(query_dname_compare(qinf->qname, - rep->rrsets[i]->rk.dname) == 0) { - val_find_rrset_signer(rep->rrsets[i], - signer_name, signer_len); - if(*signer_name) - return; - } - } - /* no answer RRSIGs with qname, try a DNAME */ - if(skip < rep->an_numrrsets && - ntohs(rep->rrsets[skip]->rk.type) == - LDNS_RR_TYPE_DNAME) { - val_find_rrset_signer(rep->rrsets[skip], - signer_name, signer_len); - if(*signer_name) - return; - } - *signer_name = NULL; - *signer_len = 0; - } else if(subtype == VAL_CLASS_REFERRAL) { - /* find keys for the item at skip */ - if(skip < rep->rrset_count) { - val_find_rrset_signer(rep->rrsets[skip], - signer_name, signer_len); - return; - } - *signer_name = NULL; - *signer_len = 0; - } else { - verbose(VERB_QUERY, "find_signer: could not find signer name" - " for unknown type response"); - *signer_name = NULL; - *signer_len = 0; - } -} - -/** 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 TTL of rrset */ -static uint32_t -rrset_get_ttl(struct ub_packed_rrset_key* rrset) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*) - rrset->entry.data; - if(!d) return 0; - return d->ttl; -} - -enum sec_status -val_verify_rrset(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, - uint8_t* sigalg, char** reason) -{ - enum sec_status sec; - struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> - entry.data; - if(d->security == sec_status_secure) { - /* re-verify all other statuses, because keyset may change*/ - log_nametypeclass(VERB_ALGO, "verify rrset cached", - rrset->rk.dname, ntohs(rrset->rk.type), - ntohs(rrset->rk.rrset_class)); - return d->security; - } - /* check in the cache if verification has already been done */ - rrset_check_sec_status(env->rrset_cache, rrset, *env->now); - if(d->security == sec_status_secure) { - log_nametypeclass(VERB_ALGO, "verify rrset from cache", - rrset->rk.dname, ntohs(rrset->rk.type), - ntohs(rrset->rk.rrset_class)); - return d->security; - } - log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, - ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); - sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason); - verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); - regional_free_all(env->scratch); - - /* update rrset security status - * only improves security status - * and bogus is set only once, even if we rechecked the status */ - if(sec > d->security) { - d->security = sec; - if(sec == sec_status_secure) - d->trust = rrset_trust_validated; - else if(sec == sec_status_bogus) { - size_t i; - /* update ttl for rrset to fixed value. */ - d->ttl = ve->bogus_ttl; - for(i=0; i<d->count+d->rrsig_count; i++) - d->rr_ttl[i] = ve->bogus_ttl; - /* leave RR specific TTL: not used for determine - * if RRset timed out and clients see proper value. */ - lock_basic_lock(&ve->bogus_lock); - ve->num_rrset_bogus++; - lock_basic_unlock(&ve->bogus_lock); - } - /* if status updated - store in cache for reuse */ - rrset_update_sec_status(env->rrset_cache, rrset, *env->now); - } - - return sec; -} - -enum sec_status -val_verify_rrset_entry(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, - char** reason) -{ - /* temporary dnskey rrset-key */ - struct ub_packed_rrset_key dnskey; - struct key_entry_data* kd = (struct key_entry_data*)kkey->entry.data; - enum sec_status sec; - dnskey.rk.type = htons(kd->rrset_type); - dnskey.rk.rrset_class = htons(kkey->key_class); - dnskey.rk.flags = 0; - dnskey.rk.dname = kkey->name; - dnskey.rk.dname_len = kkey->namelen; - dnskey.entry.key = &dnskey; - dnskey.entry.data = kd->rrset_data; - sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason); - return sec; -} - -/** verify that a DS RR hashes to a key and that key signs the set */ -static enum sec_status -verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason) -{ - enum sec_status sec = sec_status_bogus; - size_t i, num, numchecked = 0, numhashok = 0; - num = rrset_get_count(dnskey_rrset); - for(i=0; i<num; i++) { - /* Skip DNSKEYs that don't match the basic criteria. */ - if(ds_get_key_algo(ds_rrset, ds_idx) - != dnskey_get_algo(dnskey_rrset, i) - || dnskey_calc_keytag(dnskey_rrset, i) - != ds_get_keytag(ds_rrset, ds_idx)) { - continue; - } - numchecked++; - verbose(VERB_ALGO, "attempt DS match algo %d keytag %d", - ds_get_key_algo(ds_rrset, ds_idx), - ds_get_keytag(ds_rrset, ds_idx)); - - /* Convert the candidate DNSKEY into a hash using the - * same DS hash algorithm. */ - if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset, - ds_idx)) { - verbose(VERB_ALGO, "DS match attempt failed"); - continue; - } - numhashok++; - verbose(VERB_ALGO, "DS match digest ok, trying signature"); - - /* Otherwise, we have a match! Make sure that the DNSKEY - * verifies *with this key* */ - sec = dnskey_verify_rrset(env, ve, dnskey_rrset, - dnskey_rrset, i, reason); - if(sec == sec_status_secure) { - return sec; - } - /* If it didn't validate with the DNSKEY, try the next one! */ - } - if(numchecked == 0) - algo_needs_reason(env, ds_get_key_algo(ds_rrset, ds_idx), - reason, "no keys have a DS"); - else if(numhashok == 0) - *reason = "DS hash mismatches key"; - else if(!*reason) - *reason = "keyset not secured by DNSKEY that matches DS"; - return sec_status_bogus; -} - -int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset) -{ - size_t i, num = rrset_get_count(ds_rrset); - int d, digest_algo = 0; /* DS digest algo 0 is not used. */ - /* find favorite algo, for now, highest number supported */ - for(i=0; i<num; i++) { - if(!ds_digest_algo_is_supported(ds_rrset, i) || - !ds_key_algo_is_supported(ds_rrset, i)) { - continue; - } - d = ds_get_digest_algo(ds_rrset, i); - if(d > digest_algo) - digest_algo = d; - } - return digest_algo; -} - -enum sec_status -val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason) -{ - /* as long as this is false, we can consider this DS rrset to be - * equivalent to no DS rrset. */ - int has_useful_ds = 0, digest_algo, alg; - struct algo_needs needs; - size_t i, num; - enum sec_status sec; - - if(dnskey_rrset->rk.dname_len != ds_rrset->rk.dname_len || - query_dname_compare(dnskey_rrset->rk.dname, ds_rrset->rk.dname) - != 0) { - verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset " - "by name"); - *reason = "DNSKEY RRset did not match DS RRset by name"; - return sec_status_bogus; - } - - if(sigalg) { - /* harden against algo downgrade is enabled */ - digest_algo = val_favorite_ds_algo(ds_rrset); - algo_needs_init_ds(&needs, ds_rrset, digest_algo, sigalg); - } else { - /* accept any key algo, any digest algo */ - digest_algo = -1; - } - num = rrset_get_count(ds_rrset); - for(i=0; i<num; i++) { - /* Check to see if we can understand this DS. - * And check it is the strongest digest */ - if(!ds_digest_algo_is_supported(ds_rrset, i) || - !ds_key_algo_is_supported(ds_rrset, i) || - (sigalg && (ds_get_digest_algo(ds_rrset, i) != digest_algo))) { - continue; - } - - /* Once we see a single DS with a known digestID and - * algorithm, we cannot return INSECURE (with a - * "null" KeyEntry). */ - has_useful_ds = 1; - - sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, - ds_rrset, i, reason); - if(sec == sec_status_secure) { - if(!sigalg || algo_needs_set_secure(&needs, - (uint8_t)ds_get_key_algo(ds_rrset, i))) { - verbose(VERB_ALGO, "DS matched DNSKEY."); - return sec_status_secure; - } - } else if(sigalg && sec == sec_status_bogus) { - algo_needs_set_bogus(&needs, - (uint8_t)ds_get_key_algo(ds_rrset, i)); - } - } - - /* None of the DS's worked out. */ - - /* If no DSs were understandable, then this is OK. */ - if(!has_useful_ds) { - verbose(VERB_ALGO, "No usable DS records were found -- " - "treating as insecure."); - return sec_status_insecure; - } - /* If any were understandable, then it is bad. */ - verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY."); - if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { - algo_needs_reason(env, alg, reason, "missing verification of " - "DNSKEY signature"); - } - return sec_status_bogus; -} - -struct key_entry_key* -val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason) -{ - uint8_t sigalg[ALGO_NEEDS_MAX+1]; - enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, - dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason); - - if(sec == sec_status_secure) { - return key_entry_create_rrset(region, - ds_rrset->rk.dname, ds_rrset->rk.dname_len, - ntohs(ds_rrset->rk.rrset_class), dnskey_rrset, - downprot?sigalg:NULL, *env->now); - } else if(sec == sec_status_insecure) { - return key_entry_create_null(region, ds_rrset->rk.dname, - ds_rrset->rk.dname_len, - ntohs(ds_rrset->rk.rrset_class), - rrset_get_ttl(ds_rrset), *env->now); - } - return key_entry_create_bad(region, ds_rrset->rk.dname, - ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class), - BOGUS_KEY_TTL, *env->now); -} - -enum sec_status -val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ta_ds, - struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason) -{ - /* as long as this is false, we can consider this anchor to be - * equivalent to no anchor. */ - int has_useful_ta = 0, digest_algo = 0, alg; - struct algo_needs needs; - size_t i, num; - enum sec_status sec; - - if(ta_ds && (dnskey_rrset->rk.dname_len != ta_ds->rk.dname_len || - query_dname_compare(dnskey_rrset->rk.dname, ta_ds->rk.dname) - != 0)) { - verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset " - "by name"); - *reason = "DNSKEY RRset did not match DS RRset by name"; - return sec_status_bogus; - } - if(ta_dnskey && (dnskey_rrset->rk.dname_len != ta_dnskey->rk.dname_len - || query_dname_compare(dnskey_rrset->rk.dname, ta_dnskey->rk.dname) - != 0)) { - verbose(VERB_QUERY, "DNSKEY RRset did not match anchor RRset " - "by name"); - *reason = "DNSKEY RRset did not match anchor RRset by name"; - return sec_status_bogus; - } - - if(ta_ds) - digest_algo = val_favorite_ds_algo(ta_ds); - if(sigalg) { - if(ta_ds) - algo_needs_init_ds(&needs, ta_ds, digest_algo, sigalg); - else memset(&needs, 0, sizeof(needs)); - if(ta_dnskey) - algo_needs_init_dnskey_add(&needs, ta_dnskey, sigalg); - } - if(ta_ds) { - num = rrset_get_count(ta_ds); - for(i=0; i<num; i++) { - /* Check to see if we can understand this DS. - * And check it is the strongest digest */ - if(!ds_digest_algo_is_supported(ta_ds, i) || - !ds_key_algo_is_supported(ta_ds, i) || - ds_get_digest_algo(ta_ds, i) != digest_algo) - continue; - - /* Once we see a single DS with a known digestID and - * algorithm, we cannot return INSECURE (with a - * "null" KeyEntry). */ - has_useful_ta = 1; - - sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, - ta_ds, i, reason); - if(sec == sec_status_secure) { - if(!sigalg || algo_needs_set_secure(&needs, - (uint8_t)ds_get_key_algo(ta_ds, i))) { - verbose(VERB_ALGO, "DS matched DNSKEY."); - return sec_status_secure; - } - } else if(sigalg && sec == sec_status_bogus) { - algo_needs_set_bogus(&needs, - (uint8_t)ds_get_key_algo(ta_ds, i)); - } - } - } - - /* None of the DS's worked out: check the DNSKEYs. */ - if(ta_dnskey) { - num = rrset_get_count(ta_dnskey); - for(i=0; i<num; i++) { - /* Check to see if we can understand this DNSKEY */ - if(!dnskey_algo_is_supported(ta_dnskey, i)) - continue; - - /* we saw a useful TA */ - has_useful_ta = 1; - - sec = dnskey_verify_rrset(env, ve, dnskey_rrset, - ta_dnskey, i, reason); - if(sec == sec_status_secure) { - if(!sigalg || algo_needs_set_secure(&needs, - (uint8_t)dnskey_get_algo(ta_dnskey, i))) { - verbose(VERB_ALGO, "anchor matched DNSKEY."); - return sec_status_secure; - } - } else if(sigalg && sec == sec_status_bogus) { - algo_needs_set_bogus(&needs, - (uint8_t)dnskey_get_algo(ta_dnskey, i)); - } - } - } - - /* If no DSs were understandable, then this is OK. */ - if(!has_useful_ta) { - verbose(VERB_ALGO, "No usable trust anchors were found -- " - "treating as insecure."); - return sec_status_insecure; - } - /* If any were understandable, then it is bad. */ - verbose(VERB_QUERY, "Failed to match any usable anchor to a DNSKEY."); - if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { - algo_needs_reason(env, alg, reason, "missing verification of " - "DNSKEY signature"); - } - return sec_status_bogus; -} - -struct key_entry_key* -val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ta_ds_rrset, - struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot, - char** reason) -{ - uint8_t sigalg[ALGO_NEEDS_MAX+1]; - enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, - dnskey_rrset, ta_ds_rrset, ta_dnskey_rrset, - downprot?sigalg:NULL, reason); - - if(sec == sec_status_secure) { - return key_entry_create_rrset(region, - dnskey_rrset->rk.dname, dnskey_rrset->rk.dname_len, - ntohs(dnskey_rrset->rk.rrset_class), dnskey_rrset, - downprot?sigalg:NULL, *env->now); - } else if(sec == sec_status_insecure) { - return key_entry_create_null(region, dnskey_rrset->rk.dname, - dnskey_rrset->rk.dname_len, - ntohs(dnskey_rrset->rk.rrset_class), - rrset_get_ttl(dnskey_rrset), *env->now); - } - return key_entry_create_bad(region, dnskey_rrset->rk.dname, - dnskey_rrset->rk.dname_len, ntohs(dnskey_rrset->rk.rrset_class), - BOGUS_KEY_TTL, *env->now); -} - -int -val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset) -{ - size_t i; - for(i=0; i<rrset_get_count(ds_rrset); i++) { - if(ds_digest_algo_is_supported(ds_rrset, i) && - ds_key_algo_is_supported(ds_rrset, i)) - return 1; - } - if(verbosity < VERB_ALGO) - return 0; - if(rrset_get_count(ds_rrset) == 0) - verbose(VERB_ALGO, "DS is not usable"); - else { - /* report usability for the first DS RR */ - sldns_lookup_table *lt; - char herr[64], aerr[64]; - lt = sldns_lookup_by_id(sldns_hashes, - (int)ds_get_digest_algo(ds_rrset, i)); - if(lt) snprintf(herr, sizeof(herr), "%s", lt->name); - else snprintf(herr, sizeof(herr), "%d", - (int)ds_get_digest_algo(ds_rrset, i)); - lt = sldns_lookup_by_id(sldns_algorithms, - (int)ds_get_key_algo(ds_rrset, i)); - if(lt) snprintf(aerr, sizeof(aerr), "%s", lt->name); - else snprintf(aerr, sizeof(aerr), "%d", - (int)ds_get_key_algo(ds_rrset, i)); - verbose(VERB_ALGO, "DS unsupported, hash %s %s, " - "key algorithm %s %s", herr, - (ds_digest_algo_is_supported(ds_rrset, 0)? - "(supported)":"(unsupported)"), aerr, - (ds_key_algo_is_supported(ds_rrset, 0)? - "(supported)":"(unsupported)")); - } - return 0; -} - -/** get label count for a signature */ -static uint8_t -rrsig_get_labcount(struct packed_rrset_data* d, size_t sig) -{ - if(d->rr_len[sig] < 2+4) - return 0; /* bad sig length */ - return d->rr_data[sig][2+3]; -} - -int -val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> - entry.data; - uint8_t labcount; - int labdiff; - uint8_t* wn; - size_t i, wl; - if(d->rrsig_count == 0) { - return 1; - } - labcount = rrsig_get_labcount(d, d->count + 0); - /* check rest of signatures identical */ - for(i=1; i<d->rrsig_count; i++) { - if(labcount != rrsig_get_labcount(d, d->count + i)) { - return 0; - } - } - /* OK the rrsigs check out */ - /* if the RRSIG label count is shorter than the number of actual - * labels, then this rrset was synthesized from a wildcard. - * Note that the RRSIG label count doesn't count the root label. */ - wn = rrset->rk.dname; - wl = rrset->rk.dname_len; - /* skip a leading wildcard label in the dname (RFC4035 2.2) */ - if(dname_is_wild(wn)) { - wn += 2; - wl -= 2; - } - labdiff = (dname_count_labels(wn) - 1) - (int)labcount; - if(labdiff > 0) { - *wc = wn; - dname_remove_labels(wc, &wl, labdiff); - return 1; - } - return 1; -} - -int -val_chase_cname(struct query_info* qchase, struct reply_info* rep, - size_t* cname_skip) { - size_t i; - /* skip any DNAMEs, go to the CNAME for next part */ - for(i = *cname_skip; i < rep->an_numrrsets; i++) { - if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_CNAME && - query_dname_compare(qchase->qname, rep->rrsets[i]-> - rk.dname) == 0) { - qchase->qname = NULL; - get_cname_target(rep->rrsets[i], &qchase->qname, - &qchase->qname_len); - if(!qchase->qname) - return 0; /* bad CNAME rdata */ - (*cname_skip) = i+1; - return 1; - } - } - return 0; /* CNAME classified but no matching CNAME ?! */ -} - -/** see if rrset has signer name as one of the rrsig signers */ -static int -rrset_has_signer(struct ub_packed_rrset_key* rrset, uint8_t* name, size_t len) -{ - struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> - entry.data; - size_t i; - for(i = d->count; i< d->count+d->rrsig_count; i++) { - if(d->rr_len[i] > 2+18+len) { - /* at least rdatalen + signature + signame (+1 sig)*/ - if(!dname_valid(d->rr_data[i]+2+18, d->rr_len[i]-2-18)) - continue; - if(query_dname_compare(name, d->rr_data[i]+2+18) == 0) - { - return 1; - } - } - } - return 0; -} - -void -val_fill_reply(struct reply_info* chase, struct reply_info* orig, - size_t skip, uint8_t* name, size_t len, uint8_t* signer) -{ - size_t i; - int seen_dname = 0; - chase->rrset_count = 0; - chase->an_numrrsets = 0; - chase->ns_numrrsets = 0; - chase->ar_numrrsets = 0; - /* ANSWER section */ - for(i=skip; i<orig->an_numrrsets; i++) { - if(!signer) { - if(query_dname_compare(name, - orig->rrsets[i]->rk.dname) == 0) - chase->rrsets[chase->an_numrrsets++] = - orig->rrsets[i]; - } else if(seen_dname && ntohs(orig->rrsets[i]->rk.type) == - LDNS_RR_TYPE_CNAME) { - chase->rrsets[chase->an_numrrsets++] = orig->rrsets[i]; - seen_dname = 0; - } else if(rrset_has_signer(orig->rrsets[i], name, len)) { - chase->rrsets[chase->an_numrrsets++] = orig->rrsets[i]; - if(ntohs(orig->rrsets[i]->rk.type) == - LDNS_RR_TYPE_DNAME) { - seen_dname = 1; - } - } - } - /* AUTHORITY section */ - for(i = (skip > orig->an_numrrsets)?skip:orig->an_numrrsets; - i<orig->an_numrrsets+orig->ns_numrrsets; - i++) { - if(!signer) { - if(query_dname_compare(name, - orig->rrsets[i]->rk.dname) == 0) - chase->rrsets[chase->an_numrrsets+ - chase->ns_numrrsets++] = orig->rrsets[i]; - } else if(rrset_has_signer(orig->rrsets[i], name, len)) { - chase->rrsets[chase->an_numrrsets+ - chase->ns_numrrsets++] = orig->rrsets[i]; - } - } - /* ADDITIONAL section */ - for(i= (skip>orig->an_numrrsets+orig->ns_numrrsets)? - skip:orig->an_numrrsets+orig->ns_numrrsets; - i<orig->rrset_count; i++) { - if(!signer) { - if(query_dname_compare(name, - orig->rrsets[i]->rk.dname) == 0) - chase->rrsets[chase->an_numrrsets - +orig->ns_numrrsets+chase->ar_numrrsets++] - = orig->rrsets[i]; - } else if(rrset_has_signer(orig->rrsets[i], name, len)) { - chase->rrsets[chase->an_numrrsets+orig->ns_numrrsets+ - chase->ar_numrrsets++] = orig->rrsets[i]; - } - } - chase->rrset_count = chase->an_numrrsets + chase->ns_numrrsets + - chase->ar_numrrsets; -} - -void val_reply_remove_auth(struct reply_info* rep, size_t index) -{ - log_assert(index < rep->rrset_count); - log_assert(index >= rep->an_numrrsets); - log_assert(index < rep->an_numrrsets+rep->ns_numrrsets); - memmove(rep->rrsets+index, rep->rrsets+index+1, - sizeof(struct ub_packed_rrset_key*)* - (rep->rrset_count - index - 1)); - rep->ns_numrrsets--; - rep->rrset_count--; -} - -void -val_check_nonsecure(struct val_env* ve, struct reply_info* rep) -{ - size_t i; - /* authority */ - for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) { - if(((struct packed_rrset_data*)rep->rrsets[i]->entry.data) - ->security != sec_status_secure) { - /* because we want to return the authentic original - * message when presented with CD-flagged queries, - * we need to preserve AUTHORITY section data. - * However, this rrset is not signed or signed - * with the wrong keys. Validation has tried to - * verify this rrset with the keysets of import. - * But this rrset did not verify. - * Therefore the message is bogus. - */ - - /* check if authority consists of only an NS record - * which is bad, and there is an answer section with - * data. In that case, delete NS and additional to - * be lenient and make a minimal response */ - if(rep->an_numrrsets != 0 && rep->ns_numrrsets == 1 && - ntohs(rep->rrsets[i]->rk.type) - == LDNS_RR_TYPE_NS) { - verbose(VERB_ALGO, "truncate to minimal"); - rep->ns_numrrsets = 0; - rep->ar_numrrsets = 0; - rep->rrset_count = rep->an_numrrsets; - return; - } - - log_nametypeclass(VERB_QUERY, "message is bogus, " - "non secure rrset", - rep->rrsets[i]->rk.dname, - ntohs(rep->rrsets[i]->rk.type), - ntohs(rep->rrsets[i]->rk.rrset_class)); - rep->security = sec_status_bogus; - return; - } - } - /* additional */ - if(!ve->clean_additional) - return; - for(i=rep->an_numrrsets+rep->ns_numrrsets; i<rep->rrset_count; i++) { - if(((struct packed_rrset_data*)rep->rrsets[i]->entry.data) - ->security != sec_status_secure) { - /* This does not cause message invalidation. It was - * simply unsigned data in the additional. The - * RRSIG must have been truncated off the message. - * - * However, we do not want to return possible bogus - * data to clients that rely on this service for - * their authentication. - */ - /* remove this unneeded additional rrset */ - memmove(rep->rrsets+i, rep->rrsets+i+1, - sizeof(struct ub_packed_rrset_key*)* - (rep->rrset_count - i - 1)); - rep->ar_numrrsets--; - rep->rrset_count--; - i--; - } - } -} - -/** 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) -{ - size_t i; - struct packed_rrset_data* d; - for(i=0; i<rep->rrset_count; i++) { - d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; - if(d->security == sec_status_unchecked && - check_no_anchor(anchors, rep->rrsets[i]->rk.dname, - rep->rrsets[i]->rk.dname_len, - ntohs(rep->rrsets[i]->rk.rrset_class))) - { - /* mark as indeterminate */ - d->security = sec_status_indeterminate; - rrset_update_sec_status(r, rep->rrsets[i], *env->now); - } - } -} - -void -val_mark_insecure(struct reply_info* rep, uint8_t* kname, - struct rrset_cache* r, struct module_env* env) -{ - size_t i; - struct packed_rrset_data* d; - for(i=0; i<rep->rrset_count; i++) { - d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; - if(d->security == sec_status_unchecked && - dname_subdomain_c(rep->rrsets[i]->rk.dname, kname)) { - /* mark as insecure */ - d->security = sec_status_insecure; - rrset_update_sec_status(r, rep->rrsets[i], *env->now); - } - } -} - -size_t -val_next_unchecked(struct reply_info* rep, size_t skip) -{ - size_t i; - struct packed_rrset_data* d; - for(i=skip+1; i<rep->rrset_count; i++) { - d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; - if(d->security == sec_status_unchecked) { - return i; - } - } - return rep->rrset_count; -} - -const char* -val_classification_to_string(enum val_classification subtype) -{ - switch(subtype) { - case VAL_CLASS_UNTYPED: return "untyped"; - case VAL_CLASS_UNKNOWN: return "unknown"; - case VAL_CLASS_POSITIVE: return "positive"; - case VAL_CLASS_CNAME: return "cname"; - case VAL_CLASS_NODATA: return "nodata"; - case VAL_CLASS_NAMEERROR: return "nameerror"; - case VAL_CLASS_CNAMENOANSWER: return "cnamenoanswer"; - case VAL_CLASS_REFERRAL: return "referral"; - case VAL_CLASS_ANY: return "qtype_any"; - default: - return "bad_val_classification"; - } -} - -/** log a sock_list entry */ -static void -sock_list_logentry(enum verbosity_value v, const char* s, struct sock_list* p) -{ - if(p->len) - log_addr(v, s, &p->addr, p->len); - else verbose(v, "%s cache", s); -} - -void val_blacklist(struct sock_list** blacklist, struct regional* region, - struct sock_list* origin, int cross) -{ - /* debug printout */ - if(verbosity >= VERB_ALGO) { - struct sock_list* p; - for(p=*blacklist; p; p=p->next) - sock_list_logentry(VERB_ALGO, "blacklist", p); - if(!origin) - verbose(VERB_ALGO, "blacklist add: cache"); - for(p=origin; p; p=p->next) - sock_list_logentry(VERB_ALGO, "blacklist add", p); - } - /* blacklist the IPs or the cache */ - if(!origin) { - /* only add if nothing there. anything else also stops cache*/ - if(!*blacklist) - sock_list_insert(blacklist, NULL, 0, region); - } else if(!cross) - sock_list_prepend(blacklist, origin); - else sock_list_merge(blacklist, region, origin); -} - -int val_has_signed_nsecs(struct reply_info* rep, char** reason) -{ - size_t i, num_nsec = 0, num_nsec3 = 0; - struct packed_rrset_data* d; - for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) { - if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC)) - num_nsec++; - else if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC3)) - num_nsec3++; - else continue; - d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; - if(d && d->rrsig_count != 0) { - return 1; - } - } - if(num_nsec == 0 && num_nsec3 == 0) - *reason = "no DNSSEC records"; - else if(num_nsec != 0) - *reason = "no signatures over NSECs"; - else *reason = "no signatures over NSEC3s"; - return 0; -} - -struct dns_msg* -val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c, - struct regional* region, uint8_t* topname) -{ - struct dns_msg* msg; - struct query_info qinfo; - struct ub_packed_rrset_key *rrset = rrset_cache_lookup( - env->rrset_cache, nm, nmlen, LDNS_RR_TYPE_DS, c, 0, - *env->now, 0); - if(rrset) { - /* DS rrset exists. Return it to the validator immediately*/ - struct ub_packed_rrset_key* copy = packed_rrset_copy_region( - rrset, region, *env->now); - lock_rw_unlock(&rrset->entry.lock); - if(!copy) - return NULL; - msg = dns_msg_create(nm, nmlen, LDNS_RR_TYPE_DS, c, region, 1); - if(!msg) - return NULL; - msg->rep->rrsets[0] = copy; - msg->rep->rrset_count++; - msg->rep->an_numrrsets++; - return msg; - } - /* lookup in rrset and negative cache for NSEC/NSEC3 */ - qinfo.qname = nm; - qinfo.qname_len = nmlen; - qinfo.qtype = LDNS_RR_TYPE_DS; - qinfo.qclass = c; - qinfo.local_alias = NULL; - /* do not add SOA to reply message, it is going to be used internal */ - msg = val_neg_getmsg(env->neg_cache, &qinfo, region, env->rrset_cache, - env->scratch_buffer, *env->now, 0, topname); - return msg; -} diff --git a/external/unbound/validator/val_utils.h b/external/unbound/validator/val_utils.h deleted file mode 100644 index 051824aba..000000000 --- a/external/unbound/validator/val_utils.h +++ /dev/null @@ -1,410 +0,0 @@ -/* - * validator/val_utils.h - validator utility functions. - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains helper functions for the validator module. - */ - -#ifndef VALIDATOR_VAL_UTILS_H -#define VALIDATOR_VAL_UTILS_H -#include "util/data/packed_rrset.h" -struct query_info; -struct reply_info; -struct val_env; -struct module_env; -struct ub_packed_rrset_key; -struct key_entry_key; -struct regional; -struct val_anchors; -struct rrset_cache; -struct sock_list; - -/** - * Response classifications for the validator. The different types of proofs. - */ -enum val_classification { - /** Not subtyped yet. */ - VAL_CLASS_UNTYPED = 0, - /** Not a recognized subtype. */ - VAL_CLASS_UNKNOWN, - /** A positive, direct, response */ - VAL_CLASS_POSITIVE, - /** A positive response, with a CNAME/DNAME chain. */ - VAL_CLASS_CNAME, - /** A NOERROR/NODATA response. */ - VAL_CLASS_NODATA, - /** A NXDOMAIN response. */ - VAL_CLASS_NAMEERROR, - /** A CNAME/DNAME chain, and the offset is at the end of it, - * but there is no answer here, it can be NAMERROR or NODATA. */ - VAL_CLASS_CNAMENOANSWER, - /** A referral, from cache with a nonRD query. */ - VAL_CLASS_REFERRAL, - /** A response to a qtype=ANY query. */ - VAL_CLASS_ANY -}; - -/** - * Given a response, classify ANSWER responses into a subtype. - * @param query_flags: query flags for the original query. - * @param origqinf: query info. The original query name. - * @param qinf: query info. The chased query name. - * @param rep: response. The original response. - * @param skip: offset into the original response answer section. - * @return A subtype, all values possible except UNTYPED . - * Once CNAME type is returned you can increase skip. - * Then, another CNAME type, CNAME_NOANSWER or POSITIVE are possible. - */ -enum val_classification val_classify_response(uint16_t query_flags, - struct query_info* origqinf, struct query_info* qinf, - struct reply_info* rep, size_t skip); - -/** - * Given a response, determine the name of the "signer". This is primarily - * to determine if the response is, in fact, signed at all, and, if so, what - * is the name of the most pertinent keyset. - * - * @param subtype: the type from classify. - * @param qinf: query, the chased query name. - * @param rep: response to that, original response. - * @param cname_skip: how many answer rrsets have been skipped due to CNAME - * chains being chased around. - * @param signer_name: signer name, if the response is signed - * (even partially), or null if the response isn't signed. - * @param signer_len: length of signer_name of 0 if signer_name is NULL. - */ -void val_find_signer(enum val_classification subtype, - struct query_info* qinf, struct reply_info* rep, - size_t cname_skip, uint8_t** signer_name, size_t* signer_len); - -/** - * Verify RRset with keys - * @param env: module environment (scratch buffer) - * @param ve: validator environment (verification settings) - * @param rrset: what to verify - * @param keys: dnskey rrset to verify with. - * @param sigalg: if nonNULL provide downgrade protection otherwise one - * algorithm is enough. Algo list is constructed in here. - * @param reason: reason of failure. Fixed string or alloced in scratch. - * @return security status of verification. - */ -enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, - uint8_t* sigalg, char** reason); - -/** - * Verify RRset with keys from a keyset. - * @param env: module environment (scratch buffer) - * @param ve: validator environment (verification settings) - * @param rrset: what to verify - * @param kkey: key_entry to verify with. - * @param reason: reason of failure. Fixed string or alloced in scratch. - * @return security status of verification. - */ -enum sec_status val_verify_rrset_entry(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct key_entry_key* kkey, char** reason); - -/** - * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but - * returns a sec_status instead of a key_entry. - * @param env: module environment (scratch buffer) - * @param ve: validator environment (verification settings) - * @param dnskey_rrset: DNSKEY rrset to verify - * @param ds_rrset: DS rrset to verify with. - * @param sigalg: if nonNULL provide downgrade protection otherwise one - * algorithm is enough. The list of signalled algorithms is returned, - * must have enough space for ALGO_NEEDS_MAX+1. - * @param reason: reason of failure. Fixed string or alloced in scratch. - * @return: sec_status_secure if a DS matches. - * sec_status_insecure if end of trust (i.e., unknown algorithms). - * sec_status_bogus if it fails. - */ -enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason); - -/** - * Verify DNSKEYs with DS and DNSKEY rrset. Like val_verify_DNSKEY_with_DS - * but for a trust anchor. - * @param env: module environment (scratch buffer) - * @param ve: validator environment (verification settings) - * @param dnskey_rrset: DNSKEY rrset to verify - * @param ta_ds: DS rrset to verify with. - * @param ta_dnskey: DNSKEY rrset to verify with. - * @param sigalg: if nonNULL provide downgrade protection otherwise one - * algorithm is enough. The list of signalled algorithms is returned, - * must have enough space for ALGO_NEEDS_MAX+1. - * @param reason: reason of failure. Fixed string or alloced in scratch. - * @return: sec_status_secure if a DS matches. - * sec_status_insecure if end of trust (i.e., unknown algorithms). - * sec_status_bogus if it fails. - */ -enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ta_ds, - struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason); - -/** - * Verify new DNSKEYs with DS rrset. The DS contains hash values that should - * match the DNSKEY keys. - * match the DS to a DNSKEY and verify the DNSKEY rrset with that key. - * - * @param region: where to allocate key entry result. - * @param env: module environment (scratch buffer) - * @param ve: validator environment (verification settings) - * @param dnskey_rrset: DNSKEY rrset to verify - * @param ds_rrset: DS rrset to verify with. - * @param downprot: if true provide downgrade protection otherwise one - * algorithm is enough. - * @param reason: reason of failure. Fixed string or alloced in scratch. - * @return a KeyEntry. This will either contain the now trusted - * dnskey_rrset, a "null" key entry indicating that this DS - * rrset/DNSKEY pair indicate an secure end to the island of trust - * (i.e., unknown algorithms), or a "bad" KeyEntry if the dnskey - * rrset fails to verify. Note that the "null" response should - * generally only occur in a private algorithm scenario: normally - * this sort of thing is checked before fetching the matching DNSKEY - * rrset. - * if downprot is set, a key entry with an algo list is made. - */ -struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, - struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason); - - -/** - * Verify rrset with trust anchor: DS and DNSKEY rrset. - * - * @param region: where to allocate key entry result. - * @param env: module environment (scratch buffer) - * @param ve: validator environment (verification settings) - * @param dnskey_rrset: DNSKEY rrset to verify - * @param ta_ds_rrset: DS rrset to verify with. - * @param ta_dnskey_rrset: the DNSKEY rrset to verify with. - * @param downprot: if true provide downgrade protection otherwise one - * algorithm is enough. - * @param reason: reason of failure. Fixed string or alloced in scratch. - * @return a KeyEntry. This will either contain the now trusted - * dnskey_rrset, a "null" key entry indicating that this DS - * rrset/DNSKEY pair indicate an secure end to the island of trust - * (i.e., unknown algorithms), or a "bad" KeyEntry if the dnskey - * rrset fails to verify. Note that the "null" response should - * generally only occur in a private algorithm scenario: normally - * this sort of thing is checked before fetching the matching DNSKEY - * rrset. - * if downprot is set, a key entry with an algo list is made. - */ -struct key_entry_key* val_verify_new_DNSKEYs_with_ta(struct regional* region, - struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ta_ds_rrset, - struct ub_packed_rrset_key* ta_dnskey_rrset, - int downprot, char** reason); - -/** - * Determine if DS rrset is usable for validator or not. - * Returns true if the algorithms for key and DShash are supported, - * for at least one RR. - * - * @param ds_rrset: the newly received DS rrset. - * @return true or false if not usable. - */ -int val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset); - -/** - * Determine by looking at a signed RRset whether or not the RRset name was - * the result of a wildcard expansion. If so, return the name of the - * generating wildcard. - * - * @param rrset The rrset to chedck. - * @param wc: the wildcard name, if the rrset was synthesized from a wildcard. - * unchanged if not. The wildcard name, without "*." in front, is - * returned. This is a pointer into the rrset owner name. - * @return false if the signatures are inconsistent in indicating the - * wildcard status; possible spoofing of wildcard response for other - * responses is being tried. We lost the status which rrsig was verified - * after the verification routine finished, so we simply check if - * the signatures are consistent; inserting a fake signature is a denial - * of service; but in that you could also have removed the real - * signature anyway. - */ -int val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc); - -/** - * Chase the cname to the next query name. - * @param qchase: the current query name, updated to next target. - * @param rep: original message reply to look at CNAMEs. - * @param cname_skip: the skip into the answer section. Updated to skip - * DNAME and CNAME to the next part of the answer. - * @return false on error (bad rdata). - */ -int val_chase_cname(struct query_info* qchase, struct reply_info* rep, - size_t* cname_skip); - -/** - * Fill up the chased reply with the content from the original reply; - * as pointers to those rrsets. Select the part after the cname_skip into - * the answer section, NS and AR sections that are signed with same signer. - * - * @param chase: chased reply, filled up. - * @param orig: original reply. - * @param cname_skip: which part of the answer section to skip. - * The skipped part contains CNAME(and DNAME)s that have been chased. - * @param name: the signer name to look for. - * @param len: length of name. - * @param signer: signer name or NULL if an unsigned RRset is considered. - * If NULL, rrsets with the lookup name are copied over. - */ -void val_fill_reply(struct reply_info* chase, struct reply_info* orig, - size_t cname_skip, uint8_t* name, size_t len, uint8_t* signer); - -/** - * Remove rrset with index from reply, from the authority section. - * @param rep: reply to remove it from. - * @param index: rrset to remove, must be in the authority section. - */ -void val_reply_remove_auth(struct reply_info* rep, size_t index); - -/** - * Remove all unsigned or non-secure status rrsets from NS and AR sections. - * So that unsigned data does not get let through to clients, when we have - * found the data to be secure. - * - * @param ve: validator environment with cleaning options. - * @param rep: reply to dump all nonsecure stuff out of. - */ -void val_check_nonsecure(struct val_env* ve, struct reply_info* rep); - -/** - * Mark all unchecked rrset entries not below a trust anchor as indeterminate. - * Only security==unchecked rrsets are updated. - * @param rep: the reply with rrsets. - * @param anchors: the trust anchors. - * @param r: rrset cache to store updated security status into. - * @param env: module environment - */ -void val_mark_indeterminate(struct reply_info* rep, - struct val_anchors* anchors, struct rrset_cache* r, - struct module_env* env); - -/** - * Mark all unchecked rrset entries below a NULL key entry as insecure. - * Only security==unchecked rrsets are updated. - * @param rep: the reply with rrsets. - * @param kname: end of secure space name. - * @param r: rrset cache to store updated security status into. - * @param env: module environment - */ -void val_mark_insecure(struct reply_info* rep, uint8_t* kname, - struct rrset_cache* r, struct module_env* env); - -/** - * Find next unchecked rrset position, return it for skip. - * @param rep: the original reply to look into. - * @param skip: the skip now. - * @return new skip, which may be at the rep->rrset_count position to signal - * there are no unchecked items. - */ -size_t val_next_unchecked(struct reply_info* rep, size_t skip); - -/** - * Find the signer name for an RRset. - * @param rrset: the rrset. - * @param sname: signer name is returned or NULL if not signed. - * @param slen: length of sname (or 0). - */ -void val_find_rrset_signer(struct ub_packed_rrset_key* rrset, uint8_t** sname, - size_t* slen); - -/** - * Get string to denote the classification result. - * @param subtype: from classification function. - * @return static string to describe the classification. - */ -const char* val_classification_to_string(enum val_classification subtype); - -/** - * Add existing list to blacklist. - * @param blacklist: the blacklist with result - * @param region: the region where blacklist is allocated. - * Allocation failures are logged. - * @param origin: origin list to add, if NULL, a cache-entry is added to - * the blacklist to stop cache from being used. - * @param cross: if true this is a cross-qstate copy, and the 'origin' - * list is not allocated in the same region as the blacklist. - */ -void val_blacklist(struct sock_list** blacklist, struct regional* region, - struct sock_list* origin, int cross); - -/** - * check if has dnssec info, and if it has signed nsecs. gives error reason. - * @param rep: reply to check. - * @param reason: returned on fail. - * @return false if message has no signed nsecs. Can not prove negatives. - */ -int val_has_signed_nsecs(struct reply_info* rep, char** reason); - -/** - * Return algo number for favorite (best) algorithm that we support in DS. - * @param ds_rrset: the DSes in this rrset are inspected and best algo chosen. - * @return algo number or 0 if none supported. 0 is unused as algo number. - */ -int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset); - -/** - * Find DS denial message in cache. Saves new qstate allocation and allows - * the validator to use partial content which is not enough to construct a - * message for network (or user) consumption. Without SOA for example, - * which is a common occurrence in the unbound code since the referrals contain - * NSEC/NSEC3 rrs without the SOA element, thus do not allow synthesis of a - * full negative reply, but do allow synthesis of sufficient proof. - * @param env: query env with caches and time. - * @param nm: name of DS record sought. - * @param nmlen: length of name. - * @param c: class of DS RR. - * @param region: where to allocate result. - * @param topname: name of the key that is currently in use, that will get - * used to validate the result, and thus no higher entries from the - * negative cache need to be examined. - * @return a dns_msg on success. NULL on failure. - */ -struct dns_msg* val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, - uint16_t c, struct regional* region, uint8_t* topname); - -#endif /* VALIDATOR_VAL_UTILS_H */ diff --git a/external/unbound/validator/validator.c b/external/unbound/validator/validator.c deleted file mode 100644 index 81ba5fa17..000000000 --- a/external/unbound/validator/validator.c +++ /dev/null @@ -1,3079 +0,0 @@ -/* - * validator/validator.c - secure validator DNS query response module - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains a module that performs validation of DNS queries. - * According to RFC 4034. - */ -#include "config.h" -#include "validator/validator.h" -#include "validator/val_anchor.h" -#include "validator/val_kcache.h" -#include "validator/val_kentry.h" -#include "validator/val_utils.h" -#include "validator/val_nsec.h" -#include "validator/val_nsec3.h" -#include "validator/val_neg.h" -#include "validator/val_sigcrypt.h" -#include "validator/autotrust.h" -#include "services/cache/dns.h" -#include "util/data/dname.h" -#include "util/module.h" -#include "util/log.h" -#include "util/net_help.h" -#include "util/regional.h" -#include "util/config_file.h" -#include "util/fptr_wlist.h" -#include "sldns/rrdef.h" -#include "sldns/wire2str.h" - -/* forward decl for cache response and normal super inform calls of a DS */ -static void process_ds_response(struct module_qstate* qstate, - struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, - struct query_info* qinfo, struct sock_list* origin); - -/** 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 module_env* env, 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; - if(!env->anchors) - env->anchors = anchors_create(); - if(!env->anchors) { - log_err("out of memory"); - return 0; - } - if(!val_env->kcache) - val_env->kcache = key_cache_create(cfg); - if(!val_env->kcache) { - log_err("out of memory"); - return 0; - } - env->key_cache = val_env->kcache; - if(!anchors_apply_cfg(env->anchors, cfg)) { - log_err("validator: error in trustanchors config"); - return 0; - } - val_env->date_override = cfg->val_date_override; - val_env->skew_min = cfg->val_sig_skew_min; - val_env->skew_max = cfg->val_sig_skew_max; - 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; - } - if(!val_env->neg_cache) - val_env->neg_cache = val_neg_create(cfg, - val_env->nsec3_maxiter[val_env->nsec3_keyiter_count-1]); - if(!val_env->neg_cache) { - log_err("out of memory"); - return 0; - } - env->neg_cache = val_env->neg_cache; - return 1; -} - -#ifdef USE_ECDSA_EVP_WORKAROUND -void ecdsa_evp_workaround_init(void); -#endif -int -val_init(struct module_env* env, int id) -{ - struct val_env* val_env = (struct val_env*)calloc(1, - sizeof(struct val_env)); - if(!val_env) { - log_err("malloc failure"); - return 0; - } - env->modinfo[id] = (void*)val_env; - env->need_to_validate = 1; - val_env->permissive_mode = 0; - lock_basic_init(&val_env->bogus_lock); - lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus, - sizeof(val_env->num_rrset_bogus)); -#ifdef USE_ECDSA_EVP_WORKAROUND - ecdsa_evp_workaround_init(); -#endif - if(!val_apply_cfg(env, val_env, env->cfg)) { - log_err("validator: could not apply configuration settings."); - return 0; - } - - return 1; -} - -void -val_deinit(struct module_env* env, int id) -{ - struct val_env* val_env; - if(!env || !env->modinfo[id]) - return; - val_env = (struct val_env*)env->modinfo[id]; - lock_basic_destroy(&val_env->bogus_lock); - anchors_delete(env->anchors); - env->anchors = NULL; - key_cache_delete(val_env->kcache); - neg_cache_delete(val_env->neg_cache); - free(val_env->nsec3_keysize); - free(val_env->nsec3_maxiter); - free(val_env); - env->modinfo[id] = NULL; -} - -/** fill in message structure */ -static struct val_qstate* -val_new_getmsg(struct module_qstate* qstate, struct val_qstate* vq) -{ - if(!qstate->return_msg || qstate->return_rcode != LDNS_RCODE_NOERROR) { - /* create a message to verify */ - verbose(VERB_ALGO, "constructing reply for validation"); - vq->orig_msg = (struct dns_msg*)regional_alloc(qstate->region, - sizeof(struct dns_msg)); - if(!vq->orig_msg) - return NULL; - vq->orig_msg->qinfo = qstate->qinfo; - vq->orig_msg->rep = (struct reply_info*)regional_alloc( - qstate->region, sizeof(struct reply_info)); - if(!vq->orig_msg->rep) - return NULL; - memset(vq->orig_msg->rep, 0, sizeof(struct reply_info)); - vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf) - |BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD)); - vq->orig_msg->rep->qdcount = 1; - } else { - vq->orig_msg = qstate->return_msg; - } - vq->qchase = qstate->qinfo; - /* chase reply will be an edited (sub)set of the orig msg rrset ptrs */ - vq->chase_reply = regional_alloc_init(qstate->region, - vq->orig_msg->rep, - sizeof(struct reply_info) - sizeof(struct rrset_ref)); - if(!vq->chase_reply) - return NULL; - if(vq->orig_msg->rep->rrset_count > RR_COUNT_MAX) - return NULL; /* protect against integer overflow */ - vq->chase_reply->rrsets = regional_alloc_init(qstate->region, - vq->orig_msg->rep->rrsets, sizeof(struct ub_packed_rrset_key*) - * vq->orig_msg->rep->rrset_count); - if(!vq->chase_reply->rrsets) - return NULL; - vq->rrset_skip = 0; - return vq; -} - -/** allocate new validator query state */ -static struct val_qstate* -val_new(struct module_qstate* qstate, int id) -{ - struct val_qstate* vq = (struct val_qstate*)regional_alloc( - qstate->region, sizeof(*vq)); - log_assert(!qstate->minfo[id]); - if(!vq) - return NULL; - memset(vq, 0, sizeof(*vq)); - qstate->minfo[id] = vq; - vq->state = VAL_INIT_STATE; - return val_new_getmsg(qstate, vq); -} - -/** - * Exit validation with an error status - * - * @param qstate: query state - * @param id: validator id. - * @return false, for use by caller to return to stop processing. - */ -static int -val_error(struct module_qstate* qstate, int id) -{ - qstate->ext_state[id] = module_error; - qstate->return_rcode = LDNS_RCODE_SERVFAIL; - return 0; -} - -/** - * Check to see if a given response needs to go through the validation - * process. Typical reasons for this routine to return false are: CD bit was - * on in the original request, or the response is a kind of message that - * is unvalidatable (i.e., SERVFAIL, REFUSED, etc.) - * - * @param qstate: query state. - * @param ret_rc: rcode for this message (if noerror - examine ret_msg). - * @param ret_msg: return msg, can be NULL; look at rcode instead. - * @return true if the response could use validation (although this does not - * mean we can actually validate this response). - */ -static int -needs_validation(struct module_qstate* qstate, int ret_rc, - struct dns_msg* ret_msg) -{ - int rcode; - - /* If the CD bit is on in the original request, then you could think - * that we don't bother to validate anything. - * But this is signalled internally with the valrec flag. - * User queries are validated with BIT_CD to make our cache clean - * so that bogus messages get retried by the upstream also for - * downstream validators that set BIT_CD. - * For DNS64 bit_cd signals no dns64 processing, but we want to - * provide validation there too */ - /* - if(qstate->query_flags & BIT_CD) { - verbose(VERB_ALGO, "not validating response due to CD bit"); - return 0; - } - */ - if(qstate->is_valrec) { - verbose(VERB_ALGO, "not validating response, is valrec" - "(validation recursion lookup)"); - return 0; - } - - if(ret_rc != LDNS_RCODE_NOERROR || !ret_msg) - rcode = ret_rc; - else rcode = (int)FLAGS_GET_RCODE(ret_msg->rep->flags); - - if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) { - if(verbosity >= VERB_ALGO) { - char rc[16]; - rc[0]=0; - (void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc)); - verbose(VERB_ALGO, "cannot validate non-answer, rcode %s", rc); - } - return 0; - } - - /* cannot validate positive RRSIG response. (negatives can) */ - if(qstate->qinfo.qtype == LDNS_RR_TYPE_RRSIG && - rcode == LDNS_RCODE_NOERROR && ret_msg && - ret_msg->rep->an_numrrsets > 0) { - verbose(VERB_ALGO, "cannot validate RRSIG, no sigs on sigs."); - return 0; - } - return 1; -} - -/** - * Check to see if the response has already been validated. - * @param ret_msg: return msg, can be NULL - * @return true if the response has already been validated - */ -static int -already_validated(struct dns_msg* ret_msg) -{ - /* validate unchecked, and re-validate bogus messages */ - if (ret_msg && ret_msg->rep->security > sec_status_bogus) - { - verbose(VERB_ALGO, "response has already been validated: %s", - sec_status_to_string(ret_msg->rep->security)); - return 1; - } - return 0; -} - -/** - * Generate a request for DNS data. - * - * @param qstate: query state that is the parent. - * @param id: module id. - * @param name: what name to query for. - * @param namelen: length of name. - * @param qtype: query type. - * @param qclass: query class. - * @param flags: additional flags, such as the CD bit (BIT_CD), or 0. - * @return false on alloc failure. - */ -static int -generate_request(struct module_qstate* qstate, int id, uint8_t* name, - size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags) -{ - struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id]; - struct module_qstate* newq; - struct query_info ask; - int valrec; - ask.qname = name; - ask.qname_len = namelen; - ask.qtype = qtype; - ask.qclass = qclass; - ask.local_alias = NULL; - log_query_info(VERB_ALGO, "generate request", &ask); - fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); - /* enable valrec flag to avoid recursion to the same validation - * routine, this lookup is simply a lookup. DLVs need validation */ - if(qtype == LDNS_RR_TYPE_DLV) - valrec = 0; - else valrec = 1; - if(!(*qstate->env->attach_sub)(qstate, &ask, - (uint16_t)(BIT_RD|flags), 0, valrec, &newq)){ - log_err("Could not generate request: out of memory"); - return 0; - } - /* newq; validator does not need state created for that - * query, and its a 'normal' for iterator as well */ - if(newq) { - /* add our blacklist to the query blacklist */ - sock_list_merge(&newq->blacklist, newq->region, - vq->chain_blacklist); - } - qstate->ext_state[id] = module_wait_subquery; - return 1; -} - -/** - * Prime trust anchor for use. - * Generate and dispatch a priming query for the given trust anchor. - * The trust anchor can be DNSKEY or DS and does not have to be signed. - * - * @param qstate: query state. - * @param vq: validator query state. - * @param id: module id. - * @param toprime: what to prime. - * @return false on a processing error. - */ -static int -prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, - int id, struct trust_anchor* toprime) -{ - int ret = generate_request(qstate, id, toprime->name, toprime->namelen, - LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD); - if(!ret) { - log_err("Could not prime trust anchor: out of memory"); - return 0; - } - /* ignore newq; validator does not need state created for that - * 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; -} - -/** - * Validate if the ANSWER and AUTHORITY sections contain valid rrsets. - * They must be validly signed with the given key. - * Tries to validate ADDITIONAL rrsets as well, but only to check them. - * Allows unsigned CNAME after a DNAME that expands the DNAME. - * - * Note that by the time this method is called, the process of finding the - * trusted DNSKEY rrset that signs this response must already have been - * completed. - * - * @param qstate: query state. - * @param env: module env for verify. - * @param ve: validator env for verify. - * @param qchase: query that was made. - * @param chase_reply: answer to validate. - * @param key_entry: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). - * @return false if any of the rrsets in the an or ns sections of the message - * fail to verify. The message is then set to bogus. - */ -static int -validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - struct val_env* ve, struct query_info* qchase, - struct reply_info* chase_reply, struct key_entry_key* key_entry) -{ - uint8_t* sname; - size_t i, slen; - struct ub_packed_rrset_key* s; - enum sec_status sec; - int dname_seen = 0; - char* reason = NULL; - - /* validate the ANSWER section */ - for(i=0; i<chase_reply->an_numrrsets; i++) { - s = chase_reply->rrsets[i]; - /* Skip the CNAME following a (validated) DNAME. - * Because of the normalization routines in the iterator, - * there will always be an unsigned CNAME following a DNAME - * (unless qtype=DNAME). */ - if(dname_seen && ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) { - dname_seen = 0; - /* CNAME was synthesized by our own iterator */ - /* since the DNAME verified, mark the CNAME as secure */ - ((struct packed_rrset_data*)s->entry.data)->security = - sec_status_secure; - ((struct packed_rrset_data*)s->entry.data)->trust = - rrset_trust_validated; - continue; - } - - /* Verify the answer rrset */ - sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason); - /* If the (answer) rrset failed to validate, then this - * message is BAD. */ - if(sec != sec_status_secure) { - log_nametypeclass(VERB_QUERY, "validator: response " - "has failed ANSWER rrset:", s->rk.dname, - ntohs(s->rk.type), ntohs(s->rk.rrset_class)); - errinf(qstate, reason); - if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) - errinf(qstate, "for CNAME"); - else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) - errinf(qstate, "for DNAME"); - errinf_origin(qstate, qstate->reply_origin); - chase_reply->security = sec_status_bogus; - return 0; - } - - /* Notice a DNAME that should be followed by an unsigned - * CNAME. */ - if(qchase->qtype != LDNS_RR_TYPE_DNAME && - ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) { - dname_seen = 1; - } - } - - /* validate the AUTHORITY section */ - for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ - chase_reply->ns_numrrsets; i++) { - s = chase_reply->rrsets[i]; - sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason); - /* If anything in the authority section fails to be secure, - * we have a bad message. */ - if(sec != sec_status_secure) { - log_nametypeclass(VERB_QUERY, "validator: response " - "has failed AUTHORITY rrset:", s->rk.dname, - ntohs(s->rk.type), ntohs(s->rk.rrset_class)); - errinf(qstate, reason); - errinf_origin(qstate, qstate->reply_origin); - errinf_rrset(qstate, s); - chase_reply->security = sec_status_bogus; - return 0; - } - } - - /* attempt to validate the ADDITIONAL section rrsets */ - if(!ve->clean_additional) - return 1; - for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; - i<chase_reply->rrset_count; i++) { - s = chase_reply->rrsets[i]; - /* only validate rrs that have signatures with the key */ - /* leave others unchecked, those get removed later on too */ - val_find_rrset_signer(s, &sname, &slen); - if(sname && query_dname_compare(sname, key_entry->name)==0) - (void)val_verify_rrset_entry(env, ve, s, key_entry, - &reason); - /* the additional section can fail to be secure, - * it is optional, check signature in case we need - * to clean the additional section later. */ - } - - return 1; -} - -/** - * Detect wrong truncated response (say from BIND 9.6.1 that is forwarding - * and saw the NS record without signatures from a referral). - * The positive response has a mangled authority section. - * Remove that authority section and the additional section. - * @param rep: reply - * @return true if a wrongly truncated response. - */ -static int -detect_wrongly_truncated(struct reply_info* rep) -{ - size_t i; - /* only NS in authority, and it is bogus */ - if(rep->ns_numrrsets != 1 || rep->an_numrrsets == 0) - return 0; - if(ntohs(rep->rrsets[ rep->an_numrrsets ]->rk.type) != LDNS_RR_TYPE_NS) - return 0; - if(((struct packed_rrset_data*)rep->rrsets[ rep->an_numrrsets ] - ->entry.data)->security == sec_status_secure) - return 0; - /* answer section is present and secure */ - for(i=0; i<rep->an_numrrsets; i++) { - if(((struct packed_rrset_data*)rep->rrsets[ i ] - ->entry.data)->security != sec_status_secure) - return 0; - } - verbose(VERB_ALGO, "truncating to minimal response"); - return 1; -} - -/** - * For messages that are not referrals, if the chase reply contains an - * unsigned NS record in the authority section it could have been - * inserted by a (BIND) forwarder that thinks the zone is insecure, and - * that has an NS record without signatures in cache. Remove the NS - * record since the reply does not hinge on that record (in the authority - * section), but do not remove it if it removes the last record from the - * answer+authority sections. - * @param chase_reply: the chased reply, we have a key for this contents, - * so we should have signatures for these rrsets and not having - * signatures means it will be bogus. - * @param orig_reply: original reply, remove NS from there as well because - * we cannot mark the NS record as DNSSEC valid because it is not - * validated by signatures. - */ -static void -remove_spurious_authority(struct reply_info* chase_reply, - struct reply_info* orig_reply) -{ - size_t i, found = 0; - int remove = 0; - /* if no answer and only 1 auth RRset, do not remove that one */ - if(chase_reply->an_numrrsets == 0 && chase_reply->ns_numrrsets == 1) - return; - /* search authority section for unsigned NS records */ - for(i = chase_reply->an_numrrsets; - i < chase_reply->an_numrrsets+chase_reply->ns_numrrsets; i++) { - struct packed_rrset_data* d = (struct packed_rrset_data*) - chase_reply->rrsets[i]->entry.data; - if(ntohs(chase_reply->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS - && d->rrsig_count == 0) { - found = i; - remove = 1; - break; - } - } - /* see if we found the entry */ - if(!remove) return; - log_rrset_key(VERB_ALGO, "Removing spurious unsigned NS record " - "(likely inserted by forwarder)", chase_reply->rrsets[found]); - - /* find rrset in orig_reply */ - for(i = orig_reply->an_numrrsets; - i < orig_reply->an_numrrsets+orig_reply->ns_numrrsets; i++) { - if(ntohs(orig_reply->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS - && query_dname_compare(orig_reply->rrsets[i]->rk.dname, - chase_reply->rrsets[found]->rk.dname) == 0) { - /* remove from orig_msg */ - val_reply_remove_auth(orig_reply, i); - break; - } - } - /* remove rrset from chase_reply */ - val_reply_remove_auth(chase_reply, found); -} - -/** - * Given a "positive" response -- a response that contains an answer to the - * question, and no CNAME chain, validate this response. - * - * The answer and authority RRsets must already be verified as secure. - * - * @param env: module env for verify. - * @param ve: validator env for verify. - * @param qchase: query that was made. - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). - */ -static void -validate_positive_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) -{ - uint8_t* wc = NULL; - int wc_NSEC_ok = 0; - int nsec3s_seen = 0; - size_t i; - struct ub_packed_rrset_key* s; - - /* validate the ANSWER section - this will be the answer itself */ - for(i=0; i<chase_reply->an_numrrsets; i++) { - s = chase_reply->rrsets[i]; - - /* Check to see if the rrset is the result of a wildcard - * expansion. If so, an additional check will need to be - * made in the authority section. */ - if(!val_rrset_wildcard(s, &wc)) { - log_nametypeclass(VERB_QUERY, "Positive response has " - "inconsistent wildcard sigs:", s->rk.dname, - ntohs(s->rk.type), ntohs(s->rk.rrset_class)); - chase_reply->security = sec_status_bogus; - return; - } - } - - /* validate the AUTHORITY section as well - this will generally be - * the NS rrset (which could be missing, no problem) */ - for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ - chase_reply->ns_numrrsets; i++) { - s = chase_reply->rrsets[i]; - - /* If this is a positive wildcard response, and we have a - * (just verified) NSEC record, try to use it to 1) prove - * that qname doesn't exist and 2) that the correct wildcard - * was used. */ - if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { - if(val_nsec_proves_positive_wildcard(s, qchase, wc)) { - wc_NSEC_ok = 1; - } - /* if not, continue looking for proof */ - } - - /* Otherwise, if this is a positive wildcard response and - * we have NSEC3 records */ - if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { - nsec3s_seen = 1; - } - } - - /* If this was a positive wildcard response that we haven't already - * proven, and we have NSEC3 records, try to prove it using the NSEC3 - * records. */ - if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { - enum sec_status sec = nsec3_prove_wildcard(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey, wc); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "Positive wildcard response is " - "insecure"); - chase_reply->security = sec_status_insecure; - return; - } else if(sec == sec_status_secure) - wc_NSEC_ok = 1; - } - - /* If after all this, we still haven't proven the positive wildcard - * response, fail. */ - if(wc != NULL && !wc_NSEC_ok) { - verbose(VERB_QUERY, "positive response was wildcard " - "expansion and did not prove original data " - "did not exist"); - chase_reply->security = sec_status_bogus; - return; - } - - verbose(VERB_ALGO, "Successfully validated positive response"); - chase_reply->security = sec_status_secure; -} - -/** - * Validate a NOERROR/NODATA signed response -- a response that has a - * NOERROR Rcode but no ANSWER section RRsets. This consists of making - * certain that the authority section NSEC/NSEC3s proves that the qname - * does exist and the qtype doesn't. - * - * The answer and authority RRsets must already be verified as secure. - * - * @param env: module env for verify. - * @param ve: validator env for verify. - * @param qchase: query that was made. - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). - */ -static void -validate_nodata_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) -{ - /* Since we are here, there must be nothing in the ANSWER section to - * validate. */ - /* (Note: CNAME/DNAME responses will not directly get here -- - * instead, they are chased down into individual CNAME validations, - * and at the end of the cname chain a POSITIVE, or CNAME_NOANSWER - * validation.) */ - - /* validate the AUTHORITY section */ - int has_valid_nsec = 0; /* If true, then the NODATA has been proven.*/ - uint8_t* ce = NULL; /* for wildcard nodata responses. This is the - proven closest encloser. */ - uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */ - int nsec3s_seen = 0; /* nsec3s seen */ - struct ub_packed_rrset_key* s; - size_t i; - - for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ - chase_reply->ns_numrrsets; i++) { - s = chase_reply->rrsets[i]; - /* If we encounter an NSEC record, try to use it to prove - * NODATA. - * This needs to handle the ENT NODATA case. */ - if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { - if(nsec_proves_nodata(s, qchase, &wc)) { - has_valid_nsec = 1; - /* sets wc-encloser if wildcard applicable */ - } - if(val_nsec_proves_name_error(s, qchase->qname)) { - ce = nsec_closest_encloser(qchase->qname, s); - } - if(val_nsec_proves_insecuredelegation(s, qchase)) { - verbose(VERB_ALGO, "delegation is insecure"); - chase_reply->security = sec_status_insecure; - return; - } - } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { - nsec3s_seen = 1; - } - } - - /* check to see if we have a wildcard NODATA proof. */ - - /* The wildcard NODATA is 1 NSEC proving that qname does not exist - * (and also proving what the closest encloser is), and 1 NSEC - * showing the matching wildcard, which must be *.closest_encloser. */ - if(wc && !ce) - has_valid_nsec = 0; - else if(wc && ce) { - if(query_dname_compare(wc, ce) != 0) { - has_valid_nsec = 0; - } - } - - if(!has_valid_nsec && nsec3s_seen) { - enum sec_status sec = nsec3_prove_nodata(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "NODATA response is insecure"); - chase_reply->security = sec_status_insecure; - return; - } else if(sec == sec_status_secure) - has_valid_nsec = 1; - } - - if(!has_valid_nsec) { - verbose(VERB_QUERY, "NODATA response failed to prove NODATA " - "status with NSEC/NSEC3"); - if(verbosity >= VERB_ALGO) - log_dns_msg("Failed NODATA", qchase, chase_reply); - chase_reply->security = sec_status_bogus; - return; - } - - verbose(VERB_ALGO, "successfully validated NODATA response."); - chase_reply->security = sec_status_secure; -} - -/** - * Validate a NAMEERROR signed response -- a response that has a NXDOMAIN - * Rcode. - * This consists of making certain that the authority section NSEC proves - * that the qname doesn't exist and the covering wildcard also doesn't exist.. - * - * The answer and authority RRsets must have already been verified as secure. - * - * @param env: module env for verify. - * @param ve: validator env for verify. - * @param qchase: query that was made. - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). - * @param rcode: adjusted RCODE, in case of RCODE/proof mismatch leniency. - */ -static void -validate_nameerror_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey, int* rcode) -{ - int has_valid_nsec = 0; - int has_valid_wnsec = 0; - int nsec3s_seen = 0; - struct ub_packed_rrset_key* s; - size_t i; - - for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ - chase_reply->ns_numrrsets; i++) { - s = chase_reply->rrsets[i]; - if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { - if(val_nsec_proves_name_error(s, qchase->qname)) - has_valid_nsec = 1; - if(val_nsec_proves_no_wc(s, qchase->qname, - qchase->qname_len)) - has_valid_wnsec = 1; - if(val_nsec_proves_insecuredelegation(s, qchase)) { - verbose(VERB_ALGO, "delegation is insecure"); - chase_reply->security = sec_status_insecure; - return; - } - } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) - nsec3s_seen = 1; - } - - if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen) { - /* use NSEC3 proof, both answer and auth rrsets, in case - * NSEC3s end up in the answer (due to qtype=NSEC3 or so) */ - chase_reply->security = nsec3_prove_nameerror(env, ve, - chase_reply->rrsets, chase_reply->an_numrrsets+ - chase_reply->ns_numrrsets, qchase, kkey); - if(chase_reply->security != sec_status_secure) { - verbose(VERB_QUERY, "NameError response failed nsec, " - "nsec3 proof was %s", sec_status_to_string( - chase_reply->security)); - return; - } - has_valid_nsec = 1; - has_valid_wnsec = 1; - } - - /* If the message fails to prove either condition, it is bogus. */ - if(!has_valid_nsec) { - verbose(VERB_QUERY, "NameError response has failed to prove: " - "qname does not exist"); - chase_reply->security = sec_status_bogus; - /* Be lenient with RCODE in NSEC NameError responses */ - validate_nodata_response(env, ve, qchase, chase_reply, kkey); - if (chase_reply->security == sec_status_secure) - *rcode = LDNS_RCODE_NOERROR; - return; - } - - if(!has_valid_wnsec) { - verbose(VERB_QUERY, "NameError response has failed to prove: " - "covering wildcard does not exist"); - chase_reply->security = sec_status_bogus; - /* Be lenient with RCODE in NSEC NameError responses */ - validate_nodata_response(env, ve, qchase, chase_reply, kkey); - if (chase_reply->security == sec_status_secure) - *rcode = LDNS_RCODE_NOERROR; - return; - } - - /* Otherwise, we consider the message secure. */ - verbose(VERB_ALGO, "successfully validated NAME ERROR response."); - chase_reply->security = sec_status_secure; -} - -/** - * Given a referral response, validate rrsets and take least trusted rrset - * as the current validation status. - * - * Note that by the time this method is called, the process of finding the - * trusted DNSKEY rrset that signs this response must already have been - * completed. - * - * @param chase_reply: answer to validate. - */ -static void -validate_referral_response(struct reply_info* chase_reply) -{ - size_t i; - enum sec_status s; - /* message security equals lowest rrset security */ - chase_reply->security = sec_status_secure; - for(i=0; i<chase_reply->rrset_count; i++) { - s = ((struct packed_rrset_data*)chase_reply->rrsets[i] - ->entry.data)->security; - if(s < chase_reply->security) - chase_reply->security = s; - } - verbose(VERB_ALGO, "validated part of referral response as %s", - sec_status_to_string(chase_reply->security)); -} - -/** - * Given an "ANY" response -- a response that contains an answer to a - * qtype==ANY question, with answers. This does no checking that all - * types are present. - * - * NOTE: it may be possible to get parent-side delegation point records - * here, which won't all be signed. Right now, this routine relies on the - * upstream iterative resolver to not return these responses -- instead - * treating them as referrals. - * - * NOTE: RFC 4035 is silent on this issue, so this may change upon - * clarification. Clarification draft -05 says to not check all types are - * present. - * - * Note that by the time this method is called, the process of finding the - * trusted DNSKEY rrset that signs this response must already have been - * completed. - * - * @param env: module env for verify. - * @param ve: validator env for verify. - * @param qchase: query that was made. - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). - */ -static void -validate_any_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) -{ - /* all answer and auth rrsets already verified */ - /* but check if a wildcard response is given, then check NSEC/NSEC3 - * for qname denial to see if wildcard is applicable */ - uint8_t* wc = NULL; - int wc_NSEC_ok = 0; - int nsec3s_seen = 0; - size_t i; - struct ub_packed_rrset_key* s; - - if(qchase->qtype != LDNS_RR_TYPE_ANY) { - log_err("internal error: ANY validation called for non-ANY"); - chase_reply->security = sec_status_bogus; - return; - } - - /* validate the ANSWER section - this will be the answer itself */ - for(i=0; i<chase_reply->an_numrrsets; i++) { - s = chase_reply->rrsets[i]; - - /* Check to see if the rrset is the result of a wildcard - * expansion. If so, an additional check will need to be - * made in the authority section. */ - if(!val_rrset_wildcard(s, &wc)) { - log_nametypeclass(VERB_QUERY, "Positive ANY response" - " has inconsistent wildcard sigs:", - s->rk.dname, ntohs(s->rk.type), - ntohs(s->rk.rrset_class)); - chase_reply->security = sec_status_bogus; - return; - } - } - - /* if it was a wildcard, check for NSEC/NSEC3s in both answer - * and authority sections (NSEC may be moved to the ANSWER section) */ - if(wc != NULL) - for(i=0; i<chase_reply->an_numrrsets+chase_reply->ns_numrrsets; - i++) { - s = chase_reply->rrsets[i]; - - /* If this is a positive wildcard response, and we have a - * (just verified) NSEC record, try to use it to 1) prove - * that qname doesn't exist and 2) that the correct wildcard - * was used. */ - if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { - if(val_nsec_proves_positive_wildcard(s, qchase, wc)) { - wc_NSEC_ok = 1; - } - /* if not, continue looking for proof */ - } - - /* Otherwise, if this is a positive wildcard response and - * we have NSEC3 records */ - if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { - nsec3s_seen = 1; - } - } - - /* If this was a positive wildcard response that we haven't already - * proven, and we have NSEC3 records, try to prove it using the NSEC3 - * records. */ - if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { - /* look both in answer and auth section for NSEC3s */ - enum sec_status sec = nsec3_prove_wildcard(env, ve, - chase_reply->rrsets, - chase_reply->an_numrrsets+chase_reply->ns_numrrsets, - qchase, kkey, wc); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "Positive ANY wildcard response is " - "insecure"); - chase_reply->security = sec_status_insecure; - return; - } else if(sec == sec_status_secure) - wc_NSEC_ok = 1; - } - - /* If after all this, we still haven't proven the positive wildcard - * response, fail. */ - if(wc != NULL && !wc_NSEC_ok) { - verbose(VERB_QUERY, "positive ANY response was wildcard " - "expansion and did not prove original data " - "did not exist"); - chase_reply->security = sec_status_bogus; - return; - } - - verbose(VERB_ALGO, "Successfully validated positive ANY response"); - chase_reply->security = sec_status_secure; -} - -/** - * Validate CNAME response, or DNAME+CNAME. - * This is just like a positive proof, except that this is about a - * DNAME+CNAME. Possible wildcard proof. - * Difference with positive proof is that this routine refuses - * wildcarded DNAMEs. - * - * The answer and authority rrsets must already be verified as secure. - * - * @param env: module env for verify. - * @param ve: validator env for verify. - * @param qchase: query that was made. - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). - */ -static void -validate_cname_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) -{ - uint8_t* wc = NULL; - int wc_NSEC_ok = 0; - int nsec3s_seen = 0; - size_t i; - struct ub_packed_rrset_key* s; - - /* validate the ANSWER section - this will be the CNAME (+DNAME) */ - for(i=0; i<chase_reply->an_numrrsets; i++) { - s = chase_reply->rrsets[i]; - - /* Check to see if the rrset is the result of a wildcard - * expansion. If so, an additional check will need to be - * made in the authority section. */ - if(!val_rrset_wildcard(s, &wc)) { - log_nametypeclass(VERB_QUERY, "Cname response has " - "inconsistent wildcard sigs:", s->rk.dname, - ntohs(s->rk.type), ntohs(s->rk.rrset_class)); - chase_reply->security = sec_status_bogus; - return; - } - - /* Refuse wildcarded DNAMEs rfc 4597. - * Do not follow a wildcarded DNAME because - * its synthesized CNAME expansion is underdefined */ - if(qchase->qtype != LDNS_RR_TYPE_DNAME && - ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME && wc) { - log_nametypeclass(VERB_QUERY, "cannot validate a " - "wildcarded DNAME:", s->rk.dname, - ntohs(s->rk.type), ntohs(s->rk.rrset_class)); - chase_reply->security = sec_status_bogus; - return; - } - - /* If we have found a CNAME, stop looking for one. - * The iterator has placed the CNAME chain in correct - * order. */ - if (ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) { - break; - } - } - - /* AUTHORITY section */ - for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ - chase_reply->ns_numrrsets; i++) { - s = chase_reply->rrsets[i]; - - /* If this is a positive wildcard response, and we have a - * (just verified) NSEC record, try to use it to 1) prove - * that qname doesn't exist and 2) that the correct wildcard - * was used. */ - if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { - if(val_nsec_proves_positive_wildcard(s, qchase, wc)) { - wc_NSEC_ok = 1; - } - /* if not, continue looking for proof */ - } - - /* Otherwise, if this is a positive wildcard response and - * we have NSEC3 records */ - if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { - nsec3s_seen = 1; - } - } - - /* If this was a positive wildcard response that we haven't already - * proven, and we have NSEC3 records, try to prove it using the NSEC3 - * records. */ - if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { - enum sec_status sec = nsec3_prove_wildcard(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey, wc); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "wildcard CNAME response is " - "insecure"); - chase_reply->security = sec_status_insecure; - return; - } else if(sec == sec_status_secure) - wc_NSEC_ok = 1; - } - - /* If after all this, we still haven't proven the positive wildcard - * response, fail. */ - if(wc != NULL && !wc_NSEC_ok) { - verbose(VERB_QUERY, "CNAME response was wildcard " - "expansion and did not prove original data " - "did not exist"); - chase_reply->security = sec_status_bogus; - return; - } - - verbose(VERB_ALGO, "Successfully validated CNAME response"); - chase_reply->security = sec_status_secure; -} - -/** - * Validate CNAME NOANSWER response, no more data after a CNAME chain. - * This can be a NODATA or a NAME ERROR case, but not both at the same time. - * We don't know because the rcode has been set to NOERROR by the CNAME. - * - * The answer and authority rrsets must already be verified as secure. - * - * @param env: module env for verify. - * @param ve: validator env for verify. - * @param qchase: query that was made. - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). - */ -static void -validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, - struct key_entry_key* kkey) -{ - int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/ - uint8_t* ce = NULL; /* for wildcard nodata responses. This is the - proven closest encloser. */ - uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */ - int nxdomain_valid_nsec = 0; /* if true, namerror has been proven */ - int nxdomain_valid_wnsec = 0; - int nsec3s_seen = 0; /* nsec3s seen */ - struct ub_packed_rrset_key* s; - size_t i; - - /* the AUTHORITY section */ - for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ - chase_reply->ns_numrrsets; i++) { - s = chase_reply->rrsets[i]; - - /* If we encounter an NSEC record, try to use it to prove - * NODATA. This needs to handle the ENT NODATA case. - * Also try to prove NAMEERROR, and absence of a wildcard */ - if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { - if(nsec_proves_nodata(s, qchase, &wc)) { - nodata_valid_nsec = 1; - /* set wc encloser if wildcard applicable */ - } - if(val_nsec_proves_name_error(s, qchase->qname)) { - ce = nsec_closest_encloser(qchase->qname, s); - nxdomain_valid_nsec = 1; - } - if(val_nsec_proves_no_wc(s, qchase->qname, - qchase->qname_len)) - nxdomain_valid_wnsec = 1; - if(val_nsec_proves_insecuredelegation(s, qchase)) { - verbose(VERB_ALGO, "delegation is insecure"); - chase_reply->security = sec_status_insecure; - return; - } - } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { - nsec3s_seen = 1; - } - } - - /* check to see if we have a wildcard NODATA proof. */ - - /* The wildcard NODATA is 1 NSEC proving that qname does not exists - * (and also proving what the closest encloser is), and 1 NSEC - * showing the matching wildcard, which must be *.closest_encloser. */ - if(wc && !ce) - nodata_valid_nsec = 0; - else if(wc && ce) { - if(query_dname_compare(wc, ce) != 0) { - nodata_valid_nsec = 0; - } - } - if(nxdomain_valid_nsec && !nxdomain_valid_wnsec) { - /* name error is missing wildcard denial proof */ - nxdomain_valid_nsec = 0; - } - - if(nodata_valid_nsec && nxdomain_valid_nsec) { - verbose(VERB_QUERY, "CNAMEchain to noanswer proves that name " - "exists and not exists, bogus"); - chase_reply->security = sec_status_bogus; - return; - } - if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) { - int nodata; - enum sec_status sec = nsec3_prove_nxornodata(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, - chase_reply->ns_numrrsets, qchase, kkey, &nodata); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "CNAMEchain to noanswer response " - "is insecure"); - chase_reply->security = sec_status_insecure; - return; - } else if(sec == sec_status_secure) { - if(nodata) - nodata_valid_nsec = 1; - else nxdomain_valid_nsec = 1; - } - } - - if(!nodata_valid_nsec && !nxdomain_valid_nsec) { - verbose(VERB_QUERY, "CNAMEchain to noanswer response failed " - "to prove status with NSEC/NSEC3"); - if(verbosity >= VERB_ALGO) - log_dns_msg("Failed CNAMEnoanswer", qchase, chase_reply); - chase_reply->security = sec_status_bogus; - return; - } - - if(nodata_valid_nsec) - verbose(VERB_ALGO, "successfully validated CNAME chain to a " - "NODATA response."); - else verbose(VERB_ALGO, "successfully validated CNAME chain to a " - "NAMEERROR response."); - chase_reply->security = sec_status_secure; -} - -/** - * Process init state for validator. - * Process the INIT state. First tier responses start in the INIT state. - * This is where they are vetted for validation suitability, and the initial - * key search is done. - * - * Currently, events the come through this routine will be either promoted - * to FINISHED/CNAME_RESP (no validation needed), FINDKEY (next step to - * validation), or will be (temporarily) retired and a new priming request - * event will be generated. - * - * @param qstate: query state. - * @param vq: validator query state. - * @param ve: validator shared global environment. - * @param id: module id. - * @return true if the event should be processed further on return, false if - * not. - */ -static int -processInit(struct module_qstate* qstate, struct val_qstate* vq, - struct val_env* ve, int id) -{ - 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); - if(vq->restart_count > VAL_MAX_RESTART_COUNT) { - verbose(VERB_ALGO, "restart count exceeded"); - return val_error(qstate, id); - } - verbose(VERB_ALGO, "validator classification %s", - val_classification_to_string(subtype)); - if(subtype == VAL_CLASS_REFERRAL && - vq->rrset_skip < vq->orig_msg->rep->rrset_count) { - /* referral uses the rrset name as qchase, to find keys for - * that rrset */ - vq->qchase.qname = vq->orig_msg->rep-> - rrsets[vq->rrset_skip]->rk.dname; - vq->qchase.qname_len = vq->orig_msg->rep-> - rrsets[vq->rrset_skip]->rk.dname_len; - vq->qchase.qtype = ntohs(vq->orig_msg->rep-> - rrsets[vq->rrset_skip]->rk.type); - vq->qchase.qclass = ntohs(vq->orig_msg->rep-> - rrsets[vq->rrset_skip]->rk.rrset_class); - } - lookup_name = vq->qchase.qname; - lookup_len = vq->qchase.qname_len; - /* for type DS look at the parent side for keys/trustanchor */ - /* also for NSEC not at apex */ - if(vq->qchase.qtype == LDNS_RR_TYPE_DS || - (vq->qchase.qtype == LDNS_RR_TYPE_NSEC && - vq->orig_msg->rep->rrset_count > vq->rrset_skip && - ntohs(vq->orig_msg->rep->rrsets[vq->rrset_skip]->rk.type) == - LDNS_RR_TYPE_NSEC && - !(vq->orig_msg->rep->rrsets[vq->rrset_skip]-> - rk.flags&PACKED_RRSET_NSEC_AT_APEX))) { - dname_remove_label(&lookup_name, &lookup_len); - } - - val_mark_indeterminate(vq->chase_reply, qstate->env->anchors, - qstate->env->rrset_cache, qstate->env); - vq->key_entry = NULL; - vq->empty_DS_name = NULL; - vq->ds_rrset = 0; - anchor = anchors_lookup(qstate->env->anchors, - lookup_name, lookup_len, vq->qchase.qclass); - - /* Determine the signer/lookup name */ - val_find_signer(subtype, &vq->qchase, vq->orig_msg->rep, - vq->rrset_skip, &vq->signer_name, &vq->signer_len); - if(vq->signer_name != NULL && - !dname_subdomain_c(lookup_name, vq->signer_name)) { - log_nametypeclass(VERB_ALGO, "this signer name is not a parent " - "of lookupname, omitted", vq->signer_name, 0, 0); - vq->signer_name = NULL; - } - if(vq->signer_name == NULL) { - log_nametypeclass(VERB_ALGO, "no signer, using", lookup_name, - 0, 0); - } else { - lookup_name = vq->signer_name; - lookup_len = vq->signer_len; - log_nametypeclass(VERB_ALGO, "signer is", lookup_name, 0, 0); - } - - /* for NXDOMAIN it could be signed by a parent of the trust anchor */ - if(subtype == VAL_CLASS_NAMEERROR && vq->signer_name && - 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; - vq->state = VAL_FINISHED_STATE; - return 1; - } - verbose(VERB_ALGO, "trust anchor NXDOMAIN by signed parent"); - } else if(subtype == VAL_CLASS_POSITIVE && - qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY && - query_dname_compare(lookup_name, qstate->qinfo.qname) == 0) { - /* is a DNSKEY so lookup a bit higher since we want to - * get it from a parent or from trustanchor */ - dname_remove_label(&lookup_name, &lookup_len); - } - - if(vq->rrset_skip > 0 || subtype == VAL_CLASS_CNAME || - subtype == VAL_CLASS_REFERRAL) { - /* extract this part of orig_msg into chase_reply for - * the eventual VALIDATE stage */ - val_fill_reply(vq->chase_reply, vq->orig_msg->rep, - vq->rrset_skip, lookup_name, lookup_len, - vq->signer_name); - if(verbosity >= VERB_ALGO) - log_dns_msg("chased extract", &vq->qchase, - vq->chase_reply); - } - - vq->key_entry = key_cache_obtain(ve->kcache, lookup_name, lookup_len, - vq->qchase.qclass, qstate->region, *qstate->env->now); - - /* there is no key(from DLV) and no trust anchor */ - 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 */ - vq->state = VAL_FINISHED_STATE; - return 1; - } - /* 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 || (anchor && - dname_strict_subdomain_c(anchor->name, vq->key_entry->name))) { - /* trust anchor is an 'unsigned' trust anchor */ - if(anchor && anchor->numDS == 0 && anchor->numDNSKEY == 0) { - vq->chase_reply->security = sec_status_insecure; - 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; - return 1; - } - /* fire off a trust anchor priming query. */ - verbose(VERB_DETAIL, "prime 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; - } - 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. */ - vq->chase_reply->security = sec_status_insecure; - val_mark_insecure(vq->chase_reply, vq->key_entry->name, - qstate->env->rrset_cache, qstate->env); - /* go to finished state to cache this result */ - vq->state = VAL_FINISHED_STATE; - return 1; - } else if(key_entry_isbad(vq->key_entry)) { - /* key is bad, chain is bad, reply is bogus */ - errinf_dname(qstate, "key for validation", vq->key_entry->name); - errinf(qstate, "is marked as invalid"); - if(key_entry_get_reason(vq->key_entry)) { - errinf(qstate, "because of a previous"); - errinf(qstate, key_entry_get_reason(vq->key_entry)); - } - /* no retries, stop bothering the authority until timeout */ - vq->restart_count = VAL_MAX_RESTART_COUNT; - vq->chase_reply->security = sec_status_bogus; - vq->state = VAL_FINISHED_STATE; - return 1; - } - - /* otherwise, we have our "closest" cached key -- continue - * processing in the next state. */ - vq->state = VAL_FINDKEY_STATE; - return 1; -} - -/** - * Process the FINDKEY state. Generally this just calculates the next name - * to query and either issues a DS or a DNSKEY query. It will check to see - * if the correct key has already been reached, in which case it will - * advance the event to the next state. - * - * @param qstate: query state. - * @param vq: validator query state. - * @param id: module id. - * @return true if the event should be processed further on return, false if - * not. - */ -static int -processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) -{ - uint8_t* target_key_name, *current_key_name; - size_t target_key_len; - int strip_lab; - - log_query_info(VERB_ALGO, "validator: FindKey", &vq->qchase); - /* We know that state.key_entry is not 0 or bad key -- if it were, - * then previous processing should have directed this event to - * a different state. - * It could be an isnull key, which signals that a DLV was just - * done and the DNSKEY after the DLV failed with dnssec-retry state - * and the DNSKEY has to be performed again. */ - log_assert(vq->key_entry && !key_entry_isbad(vq->key_entry)); - if(key_entry_isnull(vq->key_entry)) { - if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, - vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, - vq->qchase.qclass, BIT_CD)) { - log_err("mem error generating DNSKEY request"); - return val_error(qstate, id); - } - return 0; - } - - target_key_name = vq->signer_name; - target_key_len = vq->signer_len; - if(!target_key_name) { - target_key_name = vq->qchase.qname; - target_key_len = vq->qchase.qname_len; - } - - current_key_name = vq->key_entry->name; - - /* If our current key entry matches our target, then we are done. */ - if(query_dname_compare(target_key_name, current_key_name) == 0) { - vq->state = VAL_VALIDATE_STATE; - return 1; - } - - if(vq->empty_DS_name) { - /* if the last empty nonterminal/emptyDS name we detected is - * below the current key, use that name to make progress - * along the chain of trust */ - if(query_dname_compare(target_key_name, - vq->empty_DS_name) == 0) { - /* do not query for empty_DS_name again */ - verbose(VERB_ALGO, "Cannot retrieve DS for signature"); - errinf(qstate, "no signatures"); - errinf_origin(qstate, qstate->reply_origin); - vq->chase_reply->security = sec_status_bogus; - vq->state = VAL_FINISHED_STATE; - return 1; - } - current_key_name = vq->empty_DS_name; - } - - log_nametypeclass(VERB_ALGO, "current keyname", current_key_name, - LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN); - log_nametypeclass(VERB_ALGO, "target keyname", target_key_name, - LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN); - /* assert we are walking down the DNS tree */ - if(!dname_subdomain_c(target_key_name, current_key_name)) { - verbose(VERB_ALGO, "bad signer name"); - vq->chase_reply->security = sec_status_bogus; - vq->state = VAL_FINISHED_STATE; - return 1; - } - /* so this value is >= -1 */ - strip_lab = dname_count_labels(target_key_name) - - dname_count_labels(current_key_name) - 1; - log_assert(strip_lab >= -1); - verbose(VERB_ALGO, "striplab %d", strip_lab); - if(strip_lab > 0) { - dname_remove_labels(&target_key_name, &target_key_len, - strip_lab); - } - log_nametypeclass(VERB_ALGO, "next keyname", target_key_name, - LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN); - - /* The next step is either to query for the next DS, or to query - * for the next DNSKEY. */ - if(vq->ds_rrset) - log_nametypeclass(VERB_ALGO, "DS RRset", vq->ds_rrset->rk.dname, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN); - else verbose(VERB_ALGO, "No DS RRset"); - - if(vq->ds_rrset && query_dname_compare(vq->ds_rrset->rk.dname, - vq->key_entry->name) != 0) { - if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, - vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, - vq->qchase.qclass, BIT_CD)) { - log_err("mem error generating DNSKEY request"); - return val_error(qstate, id); - } - return 0; - } - - if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname, - target_key_name) != 0) { - /* check if there is a cache entry : pick up an NSEC if - * there is no DS, check if that NSEC has DS-bit unset, and - * thus can disprove the secure delegation we seek. - * We can then use that NSEC even in the absence of a SOA - * record that would be required by the iterator to supply - * a completely protocol-correct response. - * Uses negative cache for NSEC3 lookup of DS responses. */ - /* only if cache not blacklisted, of course */ - struct dns_msg* msg; - if(!qstate->blacklist && !vq->chain_blacklist && - (msg=val_find_DS(qstate->env, target_key_name, - target_key_len, vq->qchase.qclass, qstate->region, - vq->key_entry->name)) ) { - verbose(VERB_ALGO, "Process cached DS response"); - process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, - msg, &msg->qinfo, NULL); - return 1; /* continue processing ds-response results */ - } - if(!generate_request(qstate, id, target_key_name, - target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass, - BIT_CD)) { - log_err("mem error generating DS request"); - return val_error(qstate, id); - } - return 0; - } - - /* Otherwise, it is time to query for the DNSKEY */ - if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, - vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, - vq->qchase.qclass, BIT_CD)) { - log_err("mem error generating DNSKEY request"); - return val_error(qstate, id); - } - - return 0; -} - -/** - * Process the VALIDATE stage, the init and findkey stages are finished, - * and the right keys are available to validate the response. - * Or, there are no keys available, in order to invalidate the response. - * - * After validation, the status is recorded in the message and rrsets, - * and finished state is started. - * - * @param qstate: query state. - * @param vq: validator query state. - * @param ve: validator shared global environment. - * @param id: module id. - * @return true if the event should be processed further on return, false if - * not. - */ -static int -processValidate(struct module_qstate* qstate, struct val_qstate* vq, - struct val_env* ve, int id) -{ - enum val_classification subtype; - int rcode; - - if(!vq->key_entry) { - verbose(VERB_ALGO, "validate: no key entry, failed"); - return val_error(qstate, id); - } - - /* This is the default next state. */ - vq->state = VAL_FINISHED_STATE; - - /* Unsigned responses must be underneath a "null" key entry.*/ - if(key_entry_isnull(vq->key_entry)) { - verbose(VERB_DETAIL, "Verified that %sresponse is INSECURE", - vq->signer_name?"":"unsigned "); - vq->chase_reply->security = sec_status_insecure; - val_mark_insecure(vq->chase_reply, vq->key_entry->name, - qstate->env->rrset_cache, qstate->env); - key_cache_insert(ve->kcache, vq->key_entry, qstate); - return 1; - } - - if(key_entry_isbad(vq->key_entry)) { - log_nametypeclass(VERB_DETAIL, "Could not establish a chain " - "of trust to keys for", vq->key_entry->name, - LDNS_RR_TYPE_DNSKEY, vq->key_entry->key_class); - vq->chase_reply->security = sec_status_bogus; - errinf(qstate, "while building chain of trust"); - if(vq->restart_count >= VAL_MAX_RESTART_COUNT) - key_cache_insert(ve->kcache, vq->key_entry, qstate); - return 1; - } - - /* signerName being null is the indicator that this response was - * unsigned */ - if(vq->signer_name == NULL) { - log_query_info(VERB_ALGO, "processValidate: state has no " - "signer name", &vq->qchase); - verbose(VERB_DETAIL, "Could not establish validation of " - "INSECURE status of unsigned response."); - errinf(qstate, "no signatures"); - errinf_origin(qstate, qstate->reply_origin); - vq->chase_reply->security = sec_status_bogus; - return 1; - } - subtype = val_classify_response(qstate->query_flags, &qstate->qinfo, - &vq->qchase, vq->orig_msg->rep, vq->rrset_skip); - if(subtype != VAL_CLASS_REFERRAL) - remove_spurious_authority(vq->chase_reply, vq->orig_msg->rep); - - /* check signatures in the message; - * answer and authority must be valid, additional is only checked. */ - if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase, - vq->chase_reply, vq->key_entry)) { - /* workaround bad recursor out there that truncates (even - * with EDNS4k) to 512 by removing RRSIG from auth section - * for positive replies*/ - if((subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_ANY - || subtype == VAL_CLASS_CNAME) && - detect_wrongly_truncated(vq->orig_msg->rep)) { - /* truncate the message some more */ - vq->orig_msg->rep->ns_numrrsets = 0; - vq->orig_msg->rep->ar_numrrsets = 0; - vq->orig_msg->rep->rrset_count = - vq->orig_msg->rep->an_numrrsets; - vq->chase_reply->ns_numrrsets = 0; - vq->chase_reply->ar_numrrsets = 0; - vq->chase_reply->rrset_count = - vq->chase_reply->an_numrrsets; - qstate->errinf = NULL; - } - else { - verbose(VERB_DETAIL, "Validate: message contains " - "bad rrsets"); - return 1; - } - } - - switch(subtype) { - case VAL_CLASS_POSITIVE: - verbose(VERB_ALGO, "Validating a positive response"); - validate_positive_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); - verbose(VERB_DETAIL, "validate(positive): %s", - sec_status_to_string( - vq->chase_reply->security)); - break; - - case VAL_CLASS_NODATA: - verbose(VERB_ALGO, "Validating a nodata response"); - validate_nodata_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); - verbose(VERB_DETAIL, "validate(nodata): %s", - sec_status_to_string( - vq->chase_reply->security)); - break; - - case VAL_CLASS_NAMEERROR: - rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags); - verbose(VERB_ALGO, "Validating a nxdomain response"); - validate_nameerror_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry, &rcode); - verbose(VERB_DETAIL, "validate(nxdomain): %s", - sec_status_to_string( - vq->chase_reply->security)); - FLAGS_SET_RCODE(vq->orig_msg->rep->flags, rcode); - FLAGS_SET_RCODE(vq->chase_reply->flags, rcode); - break; - - case VAL_CLASS_CNAME: - verbose(VERB_ALGO, "Validating a cname response"); - validate_cname_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); - verbose(VERB_DETAIL, "validate(cname): %s", - sec_status_to_string( - vq->chase_reply->security)); - break; - - case VAL_CLASS_CNAMENOANSWER: - verbose(VERB_ALGO, "Validating a cname noanswer " - "response"); - validate_cname_noanswer_response(qstate->env, ve, - &vq->qchase, vq->chase_reply, vq->key_entry); - verbose(VERB_DETAIL, "validate(cname_noanswer): %s", - sec_status_to_string( - vq->chase_reply->security)); - break; - - case VAL_CLASS_REFERRAL: - verbose(VERB_ALGO, "Validating a referral response"); - validate_referral_response(vq->chase_reply); - verbose(VERB_DETAIL, "validate(referral): %s", - sec_status_to_string( - vq->chase_reply->security)); - break; - - case VAL_CLASS_ANY: - verbose(VERB_ALGO, "Validating a positive ANY " - "response"); - validate_any_response(qstate->env, ve, &vq->qchase, - vq->chase_reply, vq->key_entry); - verbose(VERB_DETAIL, "validate(positive_any): %s", - sec_status_to_string( - vq->chase_reply->security)); - break; - - default: - log_err("validate: unhandled response subtype: %d", - subtype); - } - if(vq->chase_reply->security == sec_status_bogus) { - if(subtype == VAL_CLASS_POSITIVE) - errinf(qstate, "wildcard"); - else errinf(qstate, val_classification_to_string(subtype)); - errinf(qstate, "proof failed"); - errinf_origin(qstate, qstate->reply_origin); - } - - return 1; -} - -/** - * Init DLV check. - * DLV is going to be decommissioned, but the code is still here for some time. - * - * Called when a query is determined by other trust anchors to be insecure - * (or indeterminate). Then we look if there is a key in the DLV. - * Performs aggressive negative cache check to see if there is no key. - * Otherwise, spawns a DLV query, and changes to the DLV wait state. - * - * @param qstate: query state. - * @param vq: validator query state. - * @param ve: validator shared global environment. - * @param id: module id. - * @return true if there is no DLV. - * false: processing is finished for the validator operate(). - * This function may exit in three ways: - * o no DLV (aggressive cache), so insecure. (true) - * o error - stop processing (false) - * o DLV lookup was started, stop processing (false) - */ -static int -val_dlv_init(struct module_qstate* qstate, struct val_qstate* vq, - struct val_env* ve, int id) -{ - uint8_t* nm; - size_t nm_len; - /* there must be a DLV configured */ - log_assert(qstate->env->anchors->dlv_anchor); - /* this bool is true to avoid looping in the DLV checks */ - log_assert(vq->dlv_checked); - - /* init the DLV lookup variables */ - vq->dlv_lookup_name = NULL; - vq->dlv_lookup_name_len = 0; - vq->dlv_insecure_at = NULL; - vq->dlv_insecure_at_len = 0; - - /* Determine the name for which we want to lookup DLV. - * This name is for the current message, or - * for the current RRset for CNAME, referral subtypes. - * If there is a signer, use that, otherwise the domain name */ - if(vq->signer_name) { - nm = vq->signer_name; - nm_len = vq->signer_len; - } else { - /* use qchase */ - nm = vq->qchase.qname; - nm_len = vq->qchase.qname_len; - if(vq->qchase.qtype == LDNS_RR_TYPE_DS) - dname_remove_label(&nm, &nm_len); - } - log_nametypeclass(VERB_ALGO, "DLV init look", nm, LDNS_RR_TYPE_DS, - vq->qchase.qclass); - log_assert(nm && nm_len); - /* sanity check: no DLV lookups below the DLV anchor itself. - * Like, an securely insecure delegation there makes no sense. */ - if(dname_subdomain_c(nm, qstate->env->anchors->dlv_anchor->name)) { - verbose(VERB_ALGO, "DLV lookup within DLV repository denied"); - return 1; - } - /* concat name (minus root label) + dlv name */ - vq->dlv_lookup_name_len = nm_len - 1 + - qstate->env->anchors->dlv_anchor->namelen; - vq->dlv_lookup_name = regional_alloc(qstate->region, - vq->dlv_lookup_name_len); - if(!vq->dlv_lookup_name) { - log_err("Out of memory preparing DLV lookup"); - return val_error(qstate, id); - } - memmove(vq->dlv_lookup_name, nm, nm_len-1); - memmove(vq->dlv_lookup_name+nm_len-1, - qstate->env->anchors->dlv_anchor->name, - qstate->env->anchors->dlv_anchor->namelen); - log_nametypeclass(VERB_ALGO, "DLV name", vq->dlv_lookup_name, - LDNS_RR_TYPE_DLV, vq->qchase.qclass); - - /* determine where the insecure point was determined, the DLV must - * be equal or below that to continue building the trust chain - * down. May be NULL if no trust chain was built yet */ - nm = NULL; - if(vq->key_entry && key_entry_isnull(vq->key_entry)) { - nm = vq->key_entry->name; - nm_len = vq->key_entry->namelen; - } - if(nm) { - vq->dlv_insecure_at_len = nm_len - 1 + - qstate->env->anchors->dlv_anchor->namelen; - vq->dlv_insecure_at = regional_alloc(qstate->region, - vq->dlv_insecure_at_len); - if(!vq->dlv_insecure_at) { - log_err("Out of memory preparing DLV lookup"); - return val_error(qstate, id); - } - memmove(vq->dlv_insecure_at, nm, nm_len-1); - memmove(vq->dlv_insecure_at+nm_len-1, - qstate->env->anchors->dlv_anchor->name, - qstate->env->anchors->dlv_anchor->namelen); - log_nametypeclass(VERB_ALGO, "insecure_at", - vq->dlv_insecure_at, 0, vq->qchase.qclass); - } - - /* If we can find the name in the aggressive negative cache, - * give up; insecure is the answer */ - while(val_neg_dlvlookup(ve->neg_cache, vq->dlv_lookup_name, - vq->dlv_lookup_name_len, vq->qchase.qclass, - qstate->env->rrset_cache, *qstate->env->now)) { - /* go up */ - dname_remove_label(&vq->dlv_lookup_name, - &vq->dlv_lookup_name_len); - /* too high? */ - if(!dname_subdomain_c(vq->dlv_lookup_name, - qstate->env->anchors->dlv_anchor->name)) { - verbose(VERB_ALGO, "ask above dlv repo"); - return 1; /* Above the repo is insecure */ - } - /* above chain of trust? */ - if(vq->dlv_insecure_at && !dname_subdomain_c( - vq->dlv_lookup_name, vq->dlv_insecure_at)) { - verbose(VERB_ALGO, "ask above insecure endpoint"); - return 1; - } - } - - /* perform a lookup for the DLV; with validation */ - vq->state = VAL_DLVLOOKUP_STATE; - if(!generate_request(qstate, id, vq->dlv_lookup_name, - vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV, - vq->qchase.qclass, 0)) { - return val_error(qstate, id); - } - - /* Find the closest encloser DLV from the repository. - * then that is used to build another chain of trust - * This may first require a query 'too low' that has NSECs in - * the answer, from which we determine the closest encloser DLV. - * When determine the closest encloser, skip empty nonterminals, - * since we want a nonempty node in the DLV repository. */ - - return 0; -} - -/** - * The Finished state. The validation status (good or bad) has been determined. - * - * @param qstate: query state. - * @param vq: validator query state. - * @param ve: validator shared global environment. - * @param id: module id. - * @return true if the event should be processed further on return, false if - * not. - */ -static int -processFinished(struct module_qstate* qstate, struct val_qstate* vq, - struct val_env* ve, int id) -{ - enum val_classification subtype = val_classify_response( - qstate->query_flags, &qstate->qinfo, &vq->qchase, - vq->orig_msg->rep, vq->rrset_skip); - - /* if the result is insecure or indeterminate and we have not - * checked the DLV yet, check the DLV */ - if((vq->chase_reply->security == sec_status_insecure || - vq->chase_reply->security == sec_status_indeterminate) && - qstate->env->anchors->dlv_anchor && !vq->dlv_checked) { - vq->dlv_checked = 1; - if(!val_dlv_init(qstate, vq, ve, id)) - return 0; - } - - /* store overall validation result in orig_msg */ - if(vq->rrset_skip == 0) - vq->orig_msg->rep->security = vq->chase_reply->security; - else if(subtype != VAL_CLASS_REFERRAL || - vq->rrset_skip < vq->orig_msg->rep->an_numrrsets + - vq->orig_msg->rep->ns_numrrsets) { - /* ignore sec status of additional section if a referral - * type message skips there and - * use the lowest security status as end result. */ - if(vq->chase_reply->security < vq->orig_msg->rep->security) - vq->orig_msg->rep->security = - vq->chase_reply->security; - } - - if(subtype == VAL_CLASS_REFERRAL) { - /* for a referral, move to next unchecked rrset and check it*/ - vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep, - vq->rrset_skip); - if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) { - /* and restart for this rrset */ - verbose(VERB_ALGO, "validator: go to next rrset"); - vq->chase_reply->security = sec_status_unchecked; - vq->dlv_checked = 0; /* can do DLV for this RR */ - vq->state = VAL_INIT_STATE; - return 1; - } - /* referral chase is done */ - } - if(vq->chase_reply->security != sec_status_bogus && - subtype == VAL_CLASS_CNAME) { - /* chase the CNAME; process next part of the message */ - if(!val_chase_cname(&vq->qchase, vq->orig_msg->rep, - &vq->rrset_skip)) { - verbose(VERB_ALGO, "validator: failed to chase CNAME"); - vq->orig_msg->rep->security = sec_status_bogus; - } else { - /* restart process for new qchase at rrset_skip */ - log_query_info(VERB_ALGO, "validator: chased to", - &vq->qchase); - vq->chase_reply->security = sec_status_unchecked; - vq->dlv_checked = 0; /* can do DLV for this RR */ - vq->state = VAL_INIT_STATE; - return 1; - } - } - - if(vq->orig_msg->rep->security == sec_status_secure) { - /* If the message is secure, check that all rrsets are - * secure (i.e. some inserted RRset for CNAME chain with - * a different signer name). And drop additional rrsets - * that are not secure (if clean-additional option is set) */ - /* this may cause the msg to be marked bogus */ - val_check_nonsecure(ve, vq->orig_msg->rep); - if(vq->orig_msg->rep->security == sec_status_secure) { - log_query_info(VERB_DETAIL, "validation success", - &qstate->qinfo); - } - } - - /* if the result is bogus - set message ttl to bogus ttl to avoid - * endless bogus revalidation */ - if(vq->orig_msg->rep->security == sec_status_bogus) { - /* see if we can try again to fetch data */ - if(vq->restart_count < VAL_MAX_RESTART_COUNT) { - int restart_count = vq->restart_count+1; - verbose(VERB_ALGO, "validation failed, " - "blacklist and retry to fetch data"); - val_blacklist(&qstate->blacklist, qstate->region, - qstate->reply_origin, 0); - qstate->reply_origin = NULL; - qstate->errinf = NULL; - memset(vq, 0, sizeof(*vq)); - vq->restart_count = restart_count; - vq->state = VAL_INIT_STATE; - verbose(VERB_ALGO, "pass back to next module"); - qstate->ext_state[id] = module_restart_next; - return 0; - } - - vq->orig_msg->rep->ttl = ve->bogus_ttl; - vq->orig_msg->rep->prefetch_ttl = - PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl); - if(qstate->env->cfg->val_log_level >= 1 && - !qstate->env->cfg->val_log_squelch) { - if(qstate->env->cfg->val_log_level < 2) - log_query_info(0, "validation failure", - &qstate->qinfo); - else { - char* err = errinf_to_str(qstate); - if(err) log_info("%s", err); - free(err); - } - } - /* If we are in permissive mode, bogus gets indeterminate */ - if(ve->permissive_mode) - vq->orig_msg->rep->security = sec_status_indeterminate; - } - - /* store results in cache */ - if(qstate->query_flags&BIT_RD) { - /* if secure, this will override cache anyway, no need - * to check if from parentNS */ - if(!qstate->no_cache_store) { - if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, - vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, NULL, - qstate->query_flags)) { - log_err("out of memory caching validator results"); - } - } - } else { - /* for a referral, store the verified RRsets */ - /* and this does not get prefetched, so no leeway */ - if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, - vq->orig_msg->rep, 1, 0, 0, NULL, - qstate->query_flags)) { - log_err("out of memory caching validator results"); - } - } - qstate->return_rcode = LDNS_RCODE_NOERROR; - qstate->return_msg = vq->orig_msg; - qstate->ext_state[id] = module_finished; - return 0; -} - -/** - * The DLVLookup state. Process DLV lookups. - * - * @param qstate: query state. - * @param vq: validator query state. - * @param ve: validator shared global environment. - * @param id: module id. - * @return true if the event should be processed further on return, false if - * not. - */ -static int -processDLVLookup(struct module_qstate* qstate, struct val_qstate* vq, - struct val_env* ve, int id) -{ - /* see if this we are ready to continue normal resolution */ - /* we may need more DLV lookups */ - if(vq->dlv_status==dlv_error) - verbose(VERB_ALGO, "DLV woke up with status dlv_error"); - else if(vq->dlv_status==dlv_success) - verbose(VERB_ALGO, "DLV woke up with status dlv_success"); - else if(vq->dlv_status==dlv_ask_higher) - verbose(VERB_ALGO, "DLV woke up with status dlv_ask_higher"); - else if(vq->dlv_status==dlv_there_is_no_dlv) - verbose(VERB_ALGO, "DLV woke up with status dlv_there_is_no_dlv"); - else verbose(VERB_ALGO, "DLV woke up with status unknown"); - - if(vq->dlv_status == dlv_error) { - verbose(VERB_QUERY, "failed DLV lookup"); - return val_error(qstate, id); - } else if(vq->dlv_status == dlv_success) { - uint8_t* nm; - size_t nmlen; - /* chain continues with DNSKEY, continue in FINDKEY */ - vq->state = VAL_FINDKEY_STATE; - - /* strip off the DLV suffix from the name; could result in . */ - log_assert(dname_subdomain_c(vq->ds_rrset->rk.dname, - qstate->env->anchors->dlv_anchor->name)); - nmlen = vq->ds_rrset->rk.dname_len - - qstate->env->anchors->dlv_anchor->namelen + 1; - nm = regional_alloc_init(qstate->region, - vq->ds_rrset->rk.dname, nmlen); - if(!nm) { - log_err("Out of memory in DLVLook"); - return val_error(qstate, id); - } - nm[nmlen-1] = 0; - - vq->ds_rrset->rk.dname = nm; - vq->ds_rrset->rk.dname_len = nmlen; - - /* create a nullentry for the key so the dnskey lookup - * can be retried after a validation failure for it */ - vq->key_entry = key_entry_create_null(qstate->region, - nm, nmlen, vq->qchase.qclass, 0, 0); - if(!vq->key_entry) { - log_err("Out of memory in DLVLook"); - return val_error(qstate, id); - } - - if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, - vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, - vq->qchase.qclass, BIT_CD)) { - log_err("mem error generating DNSKEY request"); - return val_error(qstate, id); - } - return 0; - } else if(vq->dlv_status == dlv_there_is_no_dlv) { - /* continue with the insecure result we got */ - vq->state = VAL_FINISHED_STATE; - return 1; - } - log_assert(vq->dlv_status == dlv_ask_higher); - - /* ask higher, make sure we stay in DLV repo, below dlv_at */ - if(!dname_subdomain_c(vq->dlv_lookup_name, - qstate->env->anchors->dlv_anchor->name)) { - /* just like, there is no DLV */ - verbose(VERB_ALGO, "ask above dlv repo"); - vq->state = VAL_FINISHED_STATE; - return 1; - } - if(vq->dlv_insecure_at && !dname_subdomain_c(vq->dlv_lookup_name, - vq->dlv_insecure_at)) { - /* already checked a chain lower than dlv_lookup_name */ - verbose(VERB_ALGO, "ask above insecure endpoint"); - log_nametypeclass(VERB_ALGO, "enpt", vq->dlv_insecure_at, 0, 0); - vq->state = VAL_FINISHED_STATE; - return 1; - } - - /* check negative cache before making new request */ - if(val_neg_dlvlookup(ve->neg_cache, vq->dlv_lookup_name, - vq->dlv_lookup_name_len, vq->qchase.qclass, - qstate->env->rrset_cache, *qstate->env->now)) { - /* does not exist, go up one (go higher). */ - dname_remove_label(&vq->dlv_lookup_name, - &vq->dlv_lookup_name_len); - /* limit number of labels, limited number of recursion */ - return processDLVLookup(qstate, vq, ve, id); - } - - if(!generate_request(qstate, id, vq->dlv_lookup_name, - vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV, - vq->qchase.qclass, 0)) { - return val_error(qstate, id); - } - - return 0; -} - -/** - * Handle validator state. - * If a method returns true, the next state is started. If false, then - * processing will stop. - * @param qstate: query state. - * @param vq: validator query state. - * @param ve: validator shared global environment. - * @param id: module id. - */ -static void -val_handle(struct module_qstate* qstate, struct val_qstate* vq, - struct val_env* ve, int id) -{ - int cont = 1; - while(cont) { - verbose(VERB_ALGO, "val handle processing q with state %s", - val_state_to_string(vq->state)); - switch(vq->state) { - case VAL_INIT_STATE: - cont = processInit(qstate, vq, ve, id); - break; - case VAL_FINDKEY_STATE: - cont = processFindKey(qstate, vq, id); - break; - case VAL_VALIDATE_STATE: - cont = processValidate(qstate, vq, ve, id); - break; - case VAL_FINISHED_STATE: - cont = processFinished(qstate, vq, ve, id); - break; - case VAL_DLVLOOKUP_STATE: - cont = processDLVLookup(qstate, vq, ve, id); - break; - default: - log_warn("validator: invalid state %d", - vq->state); - cont = 0; - break; - } - } -} - -void -val_operate(struct module_qstate* qstate, enum module_ev event, int id, - struct outbound_entry* outbound) -{ - struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; - struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id]; - verbose(VERB_QUERY, "validator[module %d] operate: extstate:%s " - "event:%s", id, strextstate(qstate->ext_state[id]), - strmodulevent(event)); - log_query_info(VERB_QUERY, "validator operate: query", - &qstate->qinfo); - if(vq && qstate->qinfo.qname != vq->qchase.qname) - log_query_info(VERB_QUERY, "validator operate: chased to", - &vq->qchase); - (void)outbound; - if(event == module_event_new || - (event == module_event_pass && vq == NULL)) { - - /* pass request to next module, to get it */ - verbose(VERB_ALGO, "validator: pass to next module"); - qstate->ext_state[id] = module_wait_module; - return; - } - if(event == module_event_moddone) { - /* check if validation is needed */ - verbose(VERB_ALGO, "validator: nextmodule returned"); - - if(!needs_validation(qstate, qstate->return_rcode, - qstate->return_msg)) { - /* no need to validate this */ - if(qstate->return_msg) - qstate->return_msg->rep->security = - sec_status_indeterminate; - qstate->ext_state[id] = module_finished; - return; - } - if(already_validated(qstate->return_msg)) { - qstate->ext_state[id] = module_finished; - return; - } - /* qclass ANY should have validation result from spawned - * queries. If we get here, it is bogus or an internal error */ - if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) { - verbose(VERB_ALGO, "cannot validate classANY: bogus"); - if(qstate->return_msg) - qstate->return_msg->rep->security = - sec_status_bogus; - qstate->ext_state[id] = module_finished; - return; - } - /* create state to start validation */ - qstate->ext_state[id] = module_error; /* override this */ - if(!vq) { - vq = val_new(qstate, id); - if(!vq) { - log_err("validator: malloc failure"); - qstate->ext_state[id] = module_error; - return; - } - } else if(!vq->orig_msg) { - if(!val_new_getmsg(qstate, vq)) { - log_err("validator: malloc failure"); - qstate->ext_state[id] = module_error; - return; - } - } - val_handle(qstate, vq, ve, id); - return; - } - if(event == module_event_pass) { - qstate->ext_state[id] = module_error; /* override this */ - /* continue processing, since val_env exists */ - val_handle(qstate, vq, ve, id); - return; - } - log_err("validator: bad event %s", strmodulevent(event)); - qstate->ext_state[id] = module_error; - return; -} - -/** - * Evaluate the response to a priming request. - * - * @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. - * @return new key entry or NULL on allocation failure. - * The key entry will either contain a validated DNSKEY rrset, or - * represent a Null key (query failed, but validation did not), or a - * Bad key (validation failed). - */ -static struct key_entry_key* -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 key_entry_key* kkey = NULL; - enum sec_status sec = sec_status_unchecked; - char* reason = NULL; - int downprot = qstate->env->cfg->harden_algo_downgrade; - - if(!dnskey_rrset) { - log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " - "could not fetch DNSKEY rrset", - ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); - if(qstate->env->cfg->harden_dnssec_stripped) { - errinf(qstate, "no DNSKEY rrset"); - kkey = key_entry_create_bad(qstate->region, ta->name, - ta->namelen, ta->dclass, BOGUS_KEY_TTL, - *qstate->env->now); - } else kkey = key_entry_create_null(qstate->region, ta->name, - ta->namelen, ta->dclass, NULL_KEY_TTL, - *qstate->env->now); - if(!kkey) { - log_err("out of memory: allocate fail prime key"); - return NULL; - } - return kkey; - } - /* attempt to verify with trust anchor DS and DNSKEY */ - kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve, - dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot, - &reason); - if(!kkey) { - log_err("out of memory: verifying prime TA"); - return NULL; - } - if(key_entry_isgood(kkey)) - sec = sec_status_secure; - else - sec = sec_status_bogus; - verbose(VERB_DETAIL, "validate keys with anchor(DS): %s", - sec_status_to_string(sec)); - - if(sec != sec_status_secure) { - log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " - "DNSKEY rrset is not secure", - ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); - /* NOTE: in this case, we should probably reject the trust - * anchor for longer, perhaps forever. */ - if(qstate->env->cfg->harden_dnssec_stripped) { - errinf(qstate, reason); - kkey = key_entry_create_bad(qstate->region, ta->name, - ta->namelen, ta->dclass, BOGUS_KEY_TTL, - *qstate->env->now); - } else kkey = key_entry_create_null(qstate->region, ta->name, - ta->namelen, ta->dclass, NULL_KEY_TTL, - *qstate->env->now); - if(!kkey) { - log_err("out of memory: allocate null prime key"); - return NULL; - } - return kkey; - } - - log_nametypeclass(VERB_DETAIL, "Successfully primed trust anchor", - ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); - return kkey; -} - -/** - * In inform supers, with the resulting message and rcode and the current - * keyset in the super state, validate the DS response, returning a KeyEntry. - * - * @param qstate: query state that is validating and asked for a DS. - * @param vq: validator query state - * @param id: module id. - * @param rcode: rcode result value. - * @param msg: result message (if rcode is OK). - * @param qinfo: from the sub query state, query info. - * @param ke: the key entry to return. It returns - * is_bad if the DS response fails to validate, is_null if the - * DS response indicated an end to secure space, is_good if the DS - * validated. It returns ke=NULL if the DS response indicated that the - * request wasn't a delegation point. - * @return 0 on servfail error (malloc failure). - */ -static int -ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, - struct key_entry_key** ke) -{ - struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; - char* reason = NULL; - enum val_classification subtype; - if(rcode != LDNS_RCODE_NOERROR) { - char rc[16]; - rc[0]=0; - (void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc)); - /* errors here pretty much break validation */ - verbose(VERB_DETAIL, "DS response was error, thus bogus"); - errinf(qstate, rc); - errinf(qstate, "no DS"); - goto return_bogus; - } - - subtype = val_classify_response(BIT_RD, qinfo, qinfo, msg->rep, 0); - if(subtype == VAL_CLASS_POSITIVE) { - struct ub_packed_rrset_key* ds; - enum sec_status sec; - ds = reply_find_answer_rrset(qinfo, msg->rep); - /* If there was no DS rrset, then we have mis-classified - * this message. */ - if(!ds) { - log_warn("internal error: POSITIVE DS response was " - "missing DS."); - errinf(qstate, "no DS record"); - goto return_bogus; - } - /* Verify only returns BOGUS or SECURE. If the rrset is - * bogus, then we are done. */ - sec = val_verify_rrset_entry(qstate->env, ve, ds, - vq->key_entry, &reason); - if(sec != sec_status_secure) { - verbose(VERB_DETAIL, "DS rrset in DS response did " - "not verify"); - errinf(qstate, reason); - goto return_bogus; - } - - /* If the DS rrset validates, we still have to make sure - * that they are usable. */ - if(!val_dsset_isusable(ds)) { - /* If they aren't usable, then we treat it like - * there was no DS. */ - *ke = key_entry_create_null(qstate->region, - qinfo->qname, qinfo->qname_len, qinfo->qclass, - ub_packed_rrset_ttl(ds), *qstate->env->now); - return (*ke) != NULL; - } - - /* Otherwise, we return the positive response. */ - log_query_info(VERB_DETAIL, "validated DS", qinfo); - *ke = key_entry_create_rrset(qstate->region, - qinfo->qname, qinfo->qname_len, qinfo->qclass, ds, - NULL, *qstate->env->now); - return (*ke) != NULL; - } else if(subtype == VAL_CLASS_NODATA || - subtype == VAL_CLASS_NAMEERROR) { - /* NODATA means that the qname exists, but that there was - * no DS. This is a pretty normal case. */ - time_t proof_ttl = 0; - enum sec_status sec; - - /* make sure there are NSECs or NSEC3s with signatures */ - if(!val_has_signed_nsecs(msg->rep, &reason)) { - verbose(VERB_ALGO, "no NSECs: %s", reason); - errinf(qstate, reason); - goto return_bogus; - } - - /* For subtype Name Error. - * attempt ANS 2.8.1.0 compatibility where it sets rcode - * to nxdomain, but really this is an Nodata/Noerror response. - * Find and prove the empty nonterminal in that case */ - - /* Try to prove absence of the DS with NSEC */ - sec = val_nsec_prove_nodata_dsreply( - qstate->env, ve, qinfo, msg->rep, vq->key_entry, - &proof_ttl, &reason); - switch(sec) { - case sec_status_secure: - verbose(VERB_DETAIL, "NSEC RRset for the " - "referral proved no DS."); - *ke = key_entry_create_null(qstate->region, - qinfo->qname, qinfo->qname_len, - qinfo->qclass, proof_ttl, - *qstate->env->now); - return (*ke) != NULL; - case sec_status_insecure: - verbose(VERB_DETAIL, "NSEC RRset for the " - "referral proved not a delegation point"); - *ke = NULL; - return 1; - case sec_status_bogus: - verbose(VERB_DETAIL, "NSEC RRset for the " - "referral did not prove no DS."); - errinf(qstate, reason); - goto return_bogus; - case sec_status_unchecked: - default: - /* NSEC proof did not work, try next */ - break; - } - - sec = nsec3_prove_nods(qstate->env, ve, - msg->rep->rrsets + msg->rep->an_numrrsets, - msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason); - switch(sec) { - case sec_status_insecure: - /* case insecure also continues to unsigned - * space. If nsec3-iter-count too high or - * optout, then treat below as unsigned */ - case sec_status_secure: - verbose(VERB_DETAIL, "NSEC3s for the " - "referral proved no DS."); - *ke = key_entry_create_null(qstate->region, - qinfo->qname, qinfo->qname_len, - qinfo->qclass, proof_ttl, - *qstate->env->now); - return (*ke) != NULL; - case sec_status_indeterminate: - verbose(VERB_DETAIL, "NSEC3s for the " - "referral proved no delegation"); - *ke = NULL; - return 1; - case sec_status_bogus: - verbose(VERB_DETAIL, "NSEC3s for the " - "referral did not prove no DS."); - errinf(qstate, reason); - goto return_bogus; - case sec_status_unchecked: - default: - /* NSEC3 proof did not work */ - break; - } - - /* Apparently, no available NSEC/NSEC3 proved NODATA, so - * this is BOGUS. */ - verbose(VERB_DETAIL, "DS %s ran out of options, so return " - "bogus", val_classification_to_string(subtype)); - errinf(qstate, "no DS but also no proof of that"); - goto return_bogus; - } else if(subtype == VAL_CLASS_CNAME || - subtype == VAL_CLASS_CNAMENOANSWER) { - /* if the CNAME matches the exact name we want and is signed - * properly, then also, we are sure that no DS exists there, - * much like a NODATA proof */ - enum sec_status sec; - struct ub_packed_rrset_key* cname; - cname = reply_find_rrset_section_an(msg->rep, qinfo->qname, - qinfo->qname_len, LDNS_RR_TYPE_CNAME, qinfo->qclass); - if(!cname) { - errinf(qstate, "validator classified CNAME but no " - "CNAME of the queried name for DS"); - goto return_bogus; - } - if(((struct packed_rrset_data*)cname->entry.data)->rrsig_count - == 0) { - if(msg->rep->an_numrrsets != 0 && ntohs(msg->rep-> - rrsets[0]->rk.type)==LDNS_RR_TYPE_DNAME) { - errinf(qstate, "DS got DNAME answer"); - } else { - errinf(qstate, "DS got unsigned CNAME answer"); - } - goto return_bogus; - } - sec = val_verify_rrset_entry(qstate->env, ve, cname, - vq->key_entry, &reason); - if(sec == sec_status_secure) { - verbose(VERB_ALGO, "CNAME validated, " - "proof that DS does not exist"); - /* and that it is not a referral point */ - *ke = NULL; - return 1; - } - errinf(qstate, "CNAME in DS response was not secure."); - errinf(qstate, reason); - goto return_bogus; - } else { - verbose(VERB_QUERY, "Encountered an unhandled type of " - "DS response, thus bogus."); - errinf(qstate, "no DS and"); - if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) { - char rc[16]; - rc[0]=0; - (void)sldns_wire2str_rcode_buf((int)FLAGS_GET_RCODE( - msg->rep->flags), rc, sizeof(rc)); - errinf(qstate, rc); - } else errinf(qstate, val_classification_to_string(subtype)); - errinf(qstate, "message fails to prove that"); - goto return_bogus; - } -return_bogus: - *ke = key_entry_create_bad(qstate->region, qinfo->qname, - qinfo->qname_len, qinfo->qclass, - BOGUS_KEY_TTL, *qstate->env->now); - return (*ke) != NULL; -} - -/** - * Process DS response. Called from inform_supers. - * Because it is in inform_supers, the mesh itself is busy doing callbacks - * for a state that is to be deleted soon; don't touch the mesh; instead - * set a state in the super, as the super will be reactivated soon. - * Perform processing to determine what state to set in the super. - * - * @param qstate: query state that is validating and asked for a DS. - * @param vq: validator query state - * @param id: module id. - * @param rcode: rcode result value. - * @param msg: result message (if rcode is OK). - * @param qinfo: from the sub query state, query info. - * @param origin: the origin of msg. - */ -static void -process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, - int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, - struct sock_list* origin) -{ - struct key_entry_key* dske = NULL; - uint8_t* olds = vq->empty_DS_name; - vq->empty_DS_name = NULL; - if(!ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske)) { - log_err("malloc failure in process_ds_response"); - vq->key_entry = NULL; /* make it error */ - vq->state = VAL_VALIDATE_STATE; - return; - } - if(dske == NULL) { - vq->empty_DS_name = regional_alloc_init(qstate->region, - qinfo->qname, qinfo->qname_len); - if(!vq->empty_DS_name) { - log_err("malloc failure in empty_DS_name"); - vq->key_entry = NULL; /* make it error */ - vq->state = VAL_VALIDATE_STATE; - return; - } - vq->empty_DS_len = qinfo->qname_len; - vq->chain_blacklist = NULL; - /* ds response indicated that we aren't on a delegation point. - * Keep the forState.state on FINDKEY. */ - } else if(key_entry_isgood(dske)) { - vq->ds_rrset = key_entry_get_rrset(dske, qstate->region); - if(!vq->ds_rrset) { - log_err("malloc failure in process DS"); - vq->key_entry = NULL; /* make it error */ - vq->state = VAL_VALIDATE_STATE; - return; - } - vq->chain_blacklist = NULL; /* fresh blacklist for next part*/ - /* Keep the forState.state on FINDKEY. */ - } else if(key_entry_isbad(dske) - && vq->restart_count < VAL_MAX_RESTART_COUNT) { - vq->empty_DS_name = olds; - val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1); - qstate->errinf = NULL; - vq->restart_count++; - } else { - if(key_entry_isbad(dske)) { - errinf_origin(qstate, origin); - errinf_dname(qstate, "for DS", qinfo->qname); - } - /* NOTE: the reason for the DS to be not good (that is, - * either bad or null) should have been logged by - * dsResponseToKE. */ - vq->key_entry = dske; - /* The FINDKEY phase has ended, so move on. */ - vq->state = VAL_VALIDATE_STATE; - } -} - -/** - * Process DNSKEY response. Called from inform_supers. - * Sets the key entry in the state. - * Because it is in inform_supers, the mesh itself is busy doing callbacks - * for a state that is to be deleted soon; don't touch the mesh; instead - * set a state in the super, as the super will be reactivated soon. - * Perform processing to determine what state to set in the super. - * - * @param qstate: query state that is validating and asked for a DNSKEY. - * @param vq: validator query state - * @param id: module id. - * @param rcode: rcode result value. - * @param msg: result message (if rcode is OK). - * @param qinfo: from the sub query state, query info. - * @param origin: the origin of msg. - */ -static void -process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, - int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, - struct sock_list* origin) -{ - struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; - struct key_entry_key* old = vq->key_entry; - struct ub_packed_rrset_key* dnskey = NULL; - int downprot; - char* reason = NULL; - - if(rcode == LDNS_RCODE_NOERROR) - dnskey = reply_find_answer_rrset(qinfo, msg->rep); - - if(dnskey == NULL) { - /* bad response */ - verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to " - "DNSKEY query."); - if(vq->restart_count < VAL_MAX_RESTART_COUNT) { - val_blacklist(&vq->chain_blacklist, qstate->region, - origin, 1); - qstate->errinf = NULL; - vq->restart_count++; - return; - } - vq->key_entry = key_entry_create_bad(qstate->region, - qinfo->qname, qinfo->qname_len, qinfo->qclass, - BOGUS_KEY_TTL, *qstate->env->now); - if(!vq->key_entry) { - log_err("alloc failure in missing dnskey response"); - /* key_entry is NULL for failure in Validate */ - } - errinf(qstate, "No DNSKEY record"); - errinf_origin(qstate, origin); - errinf_dname(qstate, "for key", qinfo->qname); - vq->state = VAL_VALIDATE_STATE; - return; - } - if(!vq->ds_rrset) { - log_err("internal error: no DS rrset for new DNSKEY response"); - vq->key_entry = NULL; - vq->state = VAL_VALIDATE_STATE; - return; - } - downprot = qstate->env->cfg->harden_algo_downgrade; - vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env, - ve, dnskey, vq->ds_rrset, downprot, &reason); - - if(!vq->key_entry) { - log_err("out of memory in verify new DNSKEYs"); - vq->state = VAL_VALIDATE_STATE; - return; - } - /* If the key entry isBad or isNull, then we can move on to the next - * state. */ - if(!key_entry_isgood(vq->key_entry)) { - if(key_entry_isbad(vq->key_entry)) { - if(vq->restart_count < VAL_MAX_RESTART_COUNT) { - val_blacklist(&vq->chain_blacklist, - qstate->region, origin, 1); - qstate->errinf = NULL; - vq->restart_count++; - vq->key_entry = old; - return; - } - verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, " - "thus bogus."); - errinf(qstate, reason); - errinf_origin(qstate, origin); - errinf_dname(qstate, "for key", qinfo->qname); - } - vq->chain_blacklist = NULL; - vq->state = VAL_VALIDATE_STATE; - return; - } - vq->chain_blacklist = NULL; - qstate->errinf = NULL; - - /* The DNSKEY validated, so cache it as a trusted key rrset. */ - key_cache_insert(ve->kcache, vq->key_entry, qstate); - - /* If good, we stay in the FINDKEY state. */ - log_query_info(VERB_DETAIL, "validated DNSKEY", qinfo); -} - -/** - * Process prime response - * Sets the key entry in the state. - * - * @param qstate: query state that is validating and primed a trust anchor. - * @param vq: validator query state - * @param id: module id. - * @param rcode: rcode result value. - * @param msg: result message (if rcode is OK). - * @param origin: the origin of msg. - */ -static void -process_prime_response(struct module_qstate* qstate, struct val_qstate* vq, - int id, int rcode, struct dns_msg* msg, struct sock_list* origin) -{ - 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. */ - 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) { - if(key_entry_isbad(vq->key_entry) - && vq->restart_count < VAL_MAX_RESTART_COUNT) { - val_blacklist(&vq->chain_blacklist, qstate->region, - origin, 1); - qstate->errinf = NULL; - vq->restart_count++; - vq->key_entry = NULL; - vq->state = VAL_INIT_STATE; - return; - } - vq->chain_blacklist = NULL; - errinf_origin(qstate, origin); - errinf_dname(qstate, "for trust anchor", ta->name); - /* store the freshly primed entry in the cache */ - key_cache_insert(ve->kcache, vq->key_entry, qstate); - } - - /* If the result of the prime is a null key, skip the FINDKEY state.*/ - if(!vq->key_entry || key_entry_isnull(vq->key_entry) || - key_entry_isbad(vq->key_entry)) { - vq->state = VAL_VALIDATE_STATE; - } - /* the qstate will be reactivated after inform_super is done */ -} - -/** - * Process DLV response. Called from inform_supers. - * Because it is in inform_supers, the mesh itself is busy doing callbacks - * for a state that is to be deleted soon; don't touch the mesh; instead - * set a state in the super, as the super will be reactivated soon. - * Perform processing to determine what state to set in the super. - * - * @param qstate: query state that is validating and asked for a DLV. - * @param vq: validator query state - * @param id: module id. - * @param rcode: rcode result value. - * @param msg: result message (if rcode is OK). - * @param qinfo: from the sub query state, query info. - */ -static void -process_dlv_response(struct module_qstate* qstate, struct val_qstate* vq, - int id, int rcode, struct dns_msg* msg, struct query_info* qinfo) -{ - struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; - - verbose(VERB_ALGO, "process dlv response to super"); - if(rcode != LDNS_RCODE_NOERROR) { - /* lookup failed, set in vq to give up */ - vq->dlv_status = dlv_error; - verbose(VERB_ALGO, "response is error"); - return; - } - if(msg->rep->security != sec_status_secure) { - vq->dlv_status = dlv_error; - verbose(VERB_ALGO, "response is not secure, %s", - sec_status_to_string(msg->rep->security)); - return; - } - /* was the lookup a success? validated DLV? */ - if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NOERROR && - msg->rep->an_numrrsets == 1 && - msg->rep->security == sec_status_secure && - ntohs(msg->rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_DLV && - ntohs(msg->rep->rrsets[0]->rk.rrset_class) == qinfo->qclass && - query_dname_compare(msg->rep->rrsets[0]->rk.dname, - vq->dlv_lookup_name) == 0) { - /* yay! it is just like a DS */ - vq->ds_rrset = (struct ub_packed_rrset_key*) - regional_alloc_init(qstate->region, - msg->rep->rrsets[0], sizeof(*vq->ds_rrset)); - if(!vq->ds_rrset) { - log_err("out of memory in process_dlv"); - return; - } - vq->ds_rrset->entry.key = vq->ds_rrset; - vq->ds_rrset->rk.dname = (uint8_t*)regional_alloc_init( - qstate->region, vq->ds_rrset->rk.dname, - vq->ds_rrset->rk.dname_len); - if(!vq->ds_rrset->rk.dname) { - log_err("out of memory in process_dlv"); - vq->dlv_status = dlv_error; - return; - } - vq->ds_rrset->entry.data = regional_alloc_init(qstate->region, - vq->ds_rrset->entry.data, - packed_rrset_sizeof(vq->ds_rrset->entry.data)); - if(!vq->ds_rrset->entry.data) { - log_err("out of memory in process_dlv"); - vq->dlv_status = dlv_error; - return; - } - packed_rrset_ptr_fixup(vq->ds_rrset->entry.data); - /* make vq do a DNSKEY query next up */ - vq->dlv_status = dlv_success; - return; - } - /* store NSECs into negative cache */ - val_neg_addreply(ve->neg_cache, msg->rep); - - /* was the lookup a failure? - * if we have to go up into the DLV for a higher DLV anchor - * then set this in the vq, so it can make queries when activated. - * See if the NSECs indicate that we should look for higher DLV - * or, that there is no DLV securely */ - if(!val_nsec_check_dlv(qinfo, msg->rep, &vq->dlv_lookup_name, - &vq->dlv_lookup_name_len)) { - vq->dlv_status = dlv_error; - verbose(VERB_ALGO, "nsec error"); - return; - } - if(!dname_subdomain_c(vq->dlv_lookup_name, - qstate->env->anchors->dlv_anchor->name)) { - vq->dlv_status = dlv_there_is_no_dlv; - return; - } - vq->dlv_status = dlv_ask_higher; -} - -/* - * inform validator super. - * - * @param qstate: query state that finished. - * @param id: module id. - * @param super: the qstate to inform. - */ -void -val_inform_super(struct module_qstate* qstate, int id, - struct module_qstate* super) -{ - struct val_qstate* vq = (struct val_qstate*)super->minfo[id]; - log_query_info(VERB_ALGO, "validator: inform_super, sub is", - &qstate->qinfo); - log_query_info(VERB_ALGO, "super is", &super->qinfo); - if(!vq) { - verbose(VERB_ALGO, "super: has no validator state"); - return; - } - if(vq->wait_prime_ta) { - vq->wait_prime_ta = 0; - process_prime_response(super, vq, id, qstate->return_rcode, - qstate->return_msg, qstate->reply_origin); - return; - } - if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) { - process_ds_response(super, vq, id, qstate->return_rcode, - qstate->return_msg, &qstate->qinfo, - qstate->reply_origin); - return; - } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) { - process_dnskey_response(super, vq, id, qstate->return_rcode, - qstate->return_msg, &qstate->qinfo, - qstate->reply_origin); - return; - } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DLV) { - process_dlv_response(super, vq, id, qstate->return_rcode, - qstate->return_msg, &qstate->qinfo); - return; - } - log_err("internal error in validator: no inform_supers possible"); -} - -void -val_clear(struct module_qstate* qstate, int id) -{ - if(!qstate) - return; - /* everything is allocated in the region, so assign NULL */ - qstate->minfo[id] = NULL; -} - -size_t -val_get_mem(struct module_env* env, int id) -{ - struct val_env* ve = (struct val_env*)env->modinfo[id]; - if(!ve) - return 0; - return sizeof(*ve) + key_cache_get_mem(ve->kcache) + - val_neg_get_mem(ve->neg_cache) + - sizeof(size_t)*2*ve->nsec3_keyiter_count; -} - -/** - * The validator function block - */ -static struct module_func_block val_block = { - "validator", - &val_init, &val_deinit, &val_operate, &val_inform_super, &val_clear, - &val_get_mem -}; - -struct module_func_block* -val_get_funcblock(void) -{ - return &val_block; -} - -const char* -val_state_to_string(enum val_state state) -{ - switch(state) { - case VAL_INIT_STATE: return "VAL_INIT_STATE"; - case VAL_FINDKEY_STATE: return "VAL_FINDKEY_STATE"; - case VAL_VALIDATE_STATE: return "VAL_VALIDATE_STATE"; - case VAL_FINISHED_STATE: return "VAL_FINISHED_STATE"; - case VAL_DLVLOOKUP_STATE: return "VAL_DLVLOOKUP_STATE"; - } - return "UNKNOWN VALIDATOR STATE"; -} - diff --git a/external/unbound/validator/validator.h b/external/unbound/validator/validator.h deleted file mode 100644 index 23d307242..000000000 --- a/external/unbound/validator/validator.h +++ /dev/null @@ -1,294 +0,0 @@ -/* - * validator/validator.h - secure validator DNS query response module - * - * Copyright (c) 2007, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This file contains a module that performs validation of DNS queries. - * According to RFC 4034. - */ - -#ifndef VALIDATOR_VALIDATOR_H -#define VALIDATOR_VALIDATOR_H -#include "util/module.h" -#include "util/data/msgreply.h" -#include "validator/val_utils.h" -struct val_anchors; -struct key_cache; -struct key_entry_key; -struct val_neg_cache; -struct config_strlist; - -/** - * This is the TTL to use when a trust anchor fails to prime. A trust anchor - * will be primed no more often than this interval. Used when harden- - * dnssec-stripped is off and the trust anchor fails. - */ -#define NULL_KEY_TTL 60 /* seconds */ - -/** - * TTL for bogus key entries. When a DS or DNSKEY fails in the chain of - * trust the entire zone for that name is blacked out for this TTL. - */ -#define BOGUS_KEY_TTL 60 /* seconds */ - -/** max number of query restarts, number of IPs to probe */ -#define VAL_MAX_RESTART_COUNT 5 - -/** - * Global state for the validator. - */ -struct val_env { - /** key cache; these are validated keys. trusted keys only - * end up here after being primed. */ - struct key_cache* kcache; - - /** aggressive negative cache. index into NSECs in rrset cache. */ - struct val_neg_cache* neg_cache; - - /** for debug testing a fixed validation date can be entered. - * if 0, current time is used for rrsig validation */ - int32_t date_override; - - /** clock skew min for signatures */ - int32_t skew_min; - - /** clock skew max for signatures */ - int32_t skew_max; - - /** TTL for bogus data; used instead of untrusted TTL from data. - * Bogus data will not be verified more often than this interval. - * seconds. */ - uint32_t bogus_ttl; - - /** If set, the validator should clean the additional section of - * secure messages. - */ - int clean_additional; - - /** - * If set, the validator will not make messages bogus, instead - * indeterminate is issued, so that no clients receive SERVFAIL. - * This allows an operator to run validation 'shadow' without - * 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; - - /** lock on bogus counter */ - lock_basic_type bogus_lock; - /** number of times rrsets marked bogus */ - size_t num_rrset_bogus; -}; - -/** - * State of the validator for a query. - */ -enum val_state { - /** initial state for validation */ - VAL_INIT_STATE = 0, - /** find the proper keys for validation, follow trust chain */ - VAL_FINDKEY_STATE, - /** validate the answer, using found key entry */ - VAL_VALIDATE_STATE, - /** finish up */ - VAL_FINISHED_STATE, - /** DLV lookup state, processing DLV queries */ - VAL_DLVLOOKUP_STATE -}; - -/** - * Per query state for the validator module. - */ -struct val_qstate { - /** - * State of the validator module. - */ - enum val_state state; - - /** - * The original message we have been given to validate. - */ - struct dns_msg* orig_msg; - - /** - * The query restart count - */ - int restart_count; - /** The blacklist saved for chainoftrust elements */ - struct sock_list* chain_blacklist; - - /** - * The query name we have chased to; qname after following CNAMEs - */ - struct query_info qchase; - - /** - * The chased reply, extract from original message. Can be: - * o CNAME - * o DNAME + CNAME - * o answer - * plus authority, additional (nsecs) that have same signature. - */ - struct reply_info* chase_reply; - - /** - * The cname skip value; the number of rrsets that have been skipped - * due to chasing cnames. This is the offset into the - * orig_msg->rep->rrsets array, into the answer section. - * starts at 0 - for the full original message. - * if it is >0 - qchase followed the cname, chase_reply setup to be - * that message and relevant authority rrsets. - * - * The skip is also used for referral messages, where it will - * range from 0, over the answer, authority and additional sections. - */ - size_t rrset_skip; - - /** 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; - - /** domain name for empty nonterminal detection */ - uint8_t* empty_DS_name; - /** length of empty_DS_name */ - size_t empty_DS_len; - - /** the current key entry */ - struct key_entry_key* key_entry; - - /** subtype */ - enum val_classification subtype; - - /** signer name */ - uint8_t* signer_name; - /** length of signer_name */ - size_t signer_len; - - /** true if this state is waiting to prime a trust anchor */ - int wait_prime_ta; - - /** have we already checked the DLV? */ - int dlv_checked; - /** The name for which the DLV is looked up. For the current message - * or for the current RRset (for CNAME, REFERRAL types). - * If there is signer name, that may be it, else a domain name */ - uint8_t* dlv_lookup_name; - /** length of dlv lookup name */ - size_t dlv_lookup_name_len; - /** Name at which chain of trust stopped with insecure, starting DLV - * DLV must result in chain going further down */ - uint8_t* dlv_insecure_at; - /** length of dlv insecure point name */ - size_t dlv_insecure_at_len; - /** status of DLV lookup. Indication to VAL_DLV_STATE what to do */ - enum dlv_status { - dlv_error, /* server failure */ - dlv_success, /* got a DLV */ - dlv_ask_higher, /* ask again */ - dlv_there_is_no_dlv /* got no DLV, sure of it */ - } dlv_status; -}; - -/** - * Get the validator function block. - * @return: function block with function pointers to validator methods. - */ -struct module_func_block* val_get_funcblock(void); - -/** - * Get validator state as a string - * @param state: to convert - * @return constant string that is printable. - */ -const char* val_state_to_string(enum val_state state); - -/** validator init */ -int val_init(struct module_env* env, int id); - -/** validator deinit */ -void val_deinit(struct module_env* env, int id); - -/** validator operate on a query */ -void val_operate(struct module_qstate* qstate, enum module_ev event, int id, - struct outbound_entry* outbound); - -/** - * inform validator super. - * - * @param qstate: query state that finished. - * @param id: module id. - * @param super: the qstate to inform. - */ -void val_inform_super(struct module_qstate* qstate, int id, - struct module_qstate* super); - -/** validator cleanup query state */ -void val_clear(struct module_qstate* qstate, int id); - -/** - * Debug helper routine that assists worker in determining memory in - * use. - * @param env: module environment - * @param id: module id. - * @return memory in use in bytes. - */ -size_t val_get_mem(struct module_env* env, int id); - -#endif /* VALIDATOR_VALIDATOR_H */ |