mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-01-01 12:29:36 -05:00
autotrust
git-svn-id: file:///svn/unbound/trunk@1765 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
c42056b9aa
commit
a8dccbdd40
5 changed files with 220 additions and 22 deletions
|
|
@ -1,6 +1,7 @@
|
|||
17 August 2009: Wouter
|
||||
- Fix so that servers are only blacklisted if they fail to reply
|
||||
to 16 queries in a row and the timeout gets above 2 minutes.
|
||||
- autotrust work, split up DS verification of DNSKEYs.
|
||||
|
||||
14 August 2009: Wouter
|
||||
- unbound-control lookup prints out infra cache information, like RTT.
|
||||
|
|
|
|||
|
|
@ -41,7 +41,10 @@
|
|||
#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"
|
||||
|
|
@ -211,6 +214,7 @@ parse_comments(char* str, struct autr_ta* ta)
|
|||
"considered NOW", str, ldns_calc_keytag(ta->rr));
|
||||
free(str);
|
||||
*/
|
||||
/* cannot use event base timeptr, because not inited yet */
|
||||
ta->last_change = (uint32_t)time(NULL);
|
||||
}
|
||||
else
|
||||
|
|
@ -297,6 +301,23 @@ autr_tp_create(struct val_anchors* anchors, ldns_rr* rr)
|
|||
return tp;
|
||||
}
|
||||
|
||||
/** delete assembled rrsets */
|
||||
static void
|
||||
autr_rrset_delete(struct trust_anchor* tp)
|
||||
{
|
||||
if(tp->ds_rrset) {
|
||||
free(tp->ds_rrset->rk.dname);
|
||||
free(tp->ds_rrset->entry.data);
|
||||
free(tp->ds_rrset);
|
||||
}
|
||||
if(tp->dnskey_rrset) {
|
||||
free(tp->dnskey_rrset->rk.dname);
|
||||
free(tp->dnskey_rrset->entry.data);
|
||||
free(tp->dnskey_rrset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void autr_point_delete(struct trust_anchor* tp)
|
||||
{
|
||||
if(!tp)
|
||||
|
|
@ -304,6 +325,7 @@ void autr_point_delete(struct trust_anchor* tp)
|
|||
lock_unprotect(&tp->lock, tp);
|
||||
lock_unprotect(&tp->lock, tp->autr);
|
||||
lock_basic_destroy(&tp->lock);
|
||||
autr_rrset_delete(tp);
|
||||
free(tp->autr);
|
||||
free(tp->name);
|
||||
free(tp);
|
||||
|
|
@ -379,9 +401,9 @@ add_trustanchor_frm_str(struct val_anchors* anchors, char* str,
|
|||
* @param anchors: all points.
|
||||
* @param str: comments line
|
||||
* @param fname: filename
|
||||
* @return false on failure.
|
||||
* @return false on failure, otherwise the tp read.
|
||||
*/
|
||||
static int
|
||||
static struct trust_anchor*
|
||||
load_trustanchor(struct val_anchors* anchors, char* str, const char* fname)
|
||||
{
|
||||
struct autr_ta* ta = NULL;
|
||||
|
|
@ -389,11 +411,11 @@ load_trustanchor(struct val_anchors* anchors, char* str, const char* fname)
|
|||
|
||||
ta = add_trustanchor_frm_str(anchors, str, &tp);
|
||||
if(!ta)
|
||||
return 0;
|
||||
return NULL;
|
||||
lock_basic_lock(&tp->lock);
|
||||
if(!parse_comments(str, ta)) {
|
||||
lock_basic_unlock(&tp->lock);
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
if (rr_is_dnskey_sep(ta->rr)) {
|
||||
if (ta->s == AUTR_STATE_VALID)
|
||||
|
|
@ -406,11 +428,63 @@ load_trustanchor(struct val_anchors* anchors, char* str, const char* fname)
|
|||
tp->autr->file = strdup(fname);
|
||||
if(!tp->autr->file) {
|
||||
lock_basic_unlock(&tp->lock);
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
lock_basic_unlock(&tp->lock);
|
||||
return 1;
|
||||
return tp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble the trust anchors into DS and DNSKEY packed rrsets.
|
||||
* Read the ldns_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)
|
||||
{
|
||||
ldns_rr_list* ds, *dnskey;
|
||||
struct autr_ta* ta;
|
||||
|
||||
ds = ldns_rr_list_new();
|
||||
dnskey = ldns_rr_list_new();
|
||||
if(!ds || !dnskey) {
|
||||
ldns_rr_list_free(ds);
|
||||
ldns_rr_list_free(dnskey);
|
||||
return 0;
|
||||
}
|
||||
for(ta = tp->autr->keys; ta; ta = ta->next) {
|
||||
if(ldns_rr_get_type(ta->rr) == LDNS_RR_TYPE_DS) {
|
||||
if(!ldns_rr_list_push_rr(ds, ta->rr)) {
|
||||
ldns_rr_list_free(ds);
|
||||
ldns_rr_list_free(dnskey);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if(!ldns_rr_list_push_rr(dnskey, ta->rr)) {
|
||||
ldns_rr_list_free(ds);
|
||||
ldns_rr_list_free(dnskey);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* make packed rrset keys - malloced with no ID number, they
|
||||
* are not in the cache */
|
||||
|
||||
/* make packed rrset data */
|
||||
|
||||
/* assign the data to replace the old */
|
||||
|
||||
/* free the old data */
|
||||
autr_rrset_delete(tp);
|
||||
tp->ds_rrset = NULL;
|
||||
tp->dnskey_rrset = NULL;
|
||||
|
||||
ldns_rr_list_free(ds);
|
||||
ldns_rr_list_free(dnskey);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int autr_read_file(struct val_anchors* anchors, const char* nm)
|
||||
|
|
@ -421,6 +495,8 @@ int autr_read_file(struct val_anchors* anchors, const char* nm)
|
|||
int line_nr = 0;
|
||||
/* single line */
|
||||
char line[10240];
|
||||
/* trust point being read */
|
||||
struct trust_anchor *tp = NULL, *tp2;
|
||||
|
||||
if (!(fd = fopen(nm, "r"))) {
|
||||
log_err("unable to open %s for reading: %s",
|
||||
|
|
@ -434,14 +510,30 @@ int autr_read_file(struct val_anchors* anchors, const char* nm)
|
|||
line_nr++;
|
||||
if (!str_contains_data(line, ';'))
|
||||
continue; /* empty lines allowed */
|
||||
if (!load_trustanchor(anchors, line, nm)) {
|
||||
if (!(tp2=load_trustanchor(anchors, line, nm))) {
|
||||
log_err("failed to load trust anchor from %s "
|
||||
"at line %i, skipping", nm, line_nr);
|
||||
/* try to do the rest */
|
||||
continue;
|
||||
}
|
||||
if(tp && tp != tp2) {
|
||||
log_err("file %s has mismatching data inside", nm);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
tp = tp2;
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
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);
|
||||
autr_assemble(tp);
|
||||
lock_basic_unlock(&tp->lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -453,11 +545,83 @@ void autr_write_file(struct trust_anchor* tp)
|
|||
/* write anchors */
|
||||
}
|
||||
|
||||
/** 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)
|
||||
{
|
||||
if(tp->ds_rrset) {
|
||||
/* verify with ds, any will do to prime autotrust */
|
||||
enum sec_status sec = val_verify_DNSKEY_with_DS(
|
||||
env, ve, rrset, tp->ds_rrset);
|
||||
verbose(VERB_ALGO, "autotrust: validate DNSKEY with DS: %s",
|
||||
sec_status_to_string(sec));
|
||||
if(sec == sec_status_secure) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if(tp->dnskey_rrset) {
|
||||
/* verify with keys */
|
||||
enum sec_status sec = val_verify_rrset(env, ve, rrset,
|
||||
tp->dnskey_rrset);
|
||||
verbose(VERB_ALGO, "autotrust: DNSKEY is %s",
|
||||
sec_status_to_string(sec));
|
||||
if(sec == sec_status_secure) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int autr_process_prime(struct module_env* env, struct val_env* ve,
|
||||
struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset)
|
||||
{
|
||||
struct val_anchors* anchors = env->anchors;
|
||||
log_assert(tp->autr);
|
||||
/* autotrust update trust anchors */
|
||||
/* note: tp is locked */
|
||||
|
||||
/* query_dnskeys(): */
|
||||
tp->autr->last_queried = *env->now;
|
||||
|
||||
log_nametypeclass(VERB_ALGO, "autotrust process for",
|
||||
tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass);
|
||||
if(!dnskey_rrset) {
|
||||
verbose(VERB_ALGO, "autotrust: no dnskey rrset");
|
||||
tp->autr->query_failed += 1;
|
||||
return 1; /* trust point exists */
|
||||
}
|
||||
/* 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.");
|
||||
tp->autr->query_failed += 1;
|
||||
return 1; /* trust point exists */
|
||||
}
|
||||
|
||||
tp->autr->query_failed = 0;
|
||||
|
||||
/* update_events():
|
||||
* - find minimum rrsig expiration interval
|
||||
* - add new trust anchors to the data structure
|
||||
* - note which trust anchors are seen this probe.
|
||||
* - note revoked (selfsigned) anchors.
|
||||
* Set trustpoint query_interval and retry_time.
|
||||
*/
|
||||
/* update_events(env, ve, tp, dnskey_rrset); */
|
||||
|
||||
/* do_statetable():
|
||||
* - for every SEP key do the 5011 statetable.
|
||||
* - remove missing trustanchors (if too many).
|
||||
*/
|
||||
/* do_statetable(env, tp); */
|
||||
|
||||
autr_assemble(tp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ init_parents(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 */
|
||||
lock_basic_lock(&anchors->lock);
|
||||
RBTREE_FOR(node, struct trust_anchor*, anchors->tree) {
|
||||
lock_basic_lock(&node->lock);
|
||||
|
|
@ -1057,6 +1059,8 @@ anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg)
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -416,9 +416,9 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
|
|||
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,
|
||||
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)
|
||||
{
|
||||
/* as long as this is false, we can consider this DS rrset to be
|
||||
|
|
@ -433,13 +433,11 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env,
|
|||
!= 0) {
|
||||
verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset "
|
||||
"by name");
|
||||
return key_entry_create_bad(region, ds_rrset->rk.dname,
|
||||
ds_rrset->rk.dname_len,
|
||||
ntohs(ds_rrset->rk.rrset_class));
|
||||
return sec_status_bogus;
|
||||
}
|
||||
|
||||
num = rrset_get_count(ds_rrset);
|
||||
/* find favority algo, for now, highest number supported */
|
||||
/* 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)) {
|
||||
|
|
@ -467,10 +465,7 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env,
|
|||
ds_rrset, i);
|
||||
if(sec == sec_status_secure) {
|
||||
verbose(VERB_ALGO, "DS matched DNSKEY.");
|
||||
return key_entry_create_rrset(region,
|
||||
ds_rrset->rk.dname, ds_rrset->rk.dname_len,
|
||||
ntohs(ds_rrset->rk.rrset_class), dnskey_rrset,
|
||||
*env->now);
|
||||
return sec_status_secure;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -480,13 +475,32 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env,
|
|||
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.");
|
||||
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)
|
||||
{
|
||||
enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve,
|
||||
dnskey_rrset, ds_rrset);
|
||||
|
||||
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,
|
||||
*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);
|
||||
}
|
||||
/* If any were understandable, then it is bad. */
|
||||
verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY.");
|
||||
}
|
||||
return key_entry_create_bad(region, ds_rrset->rk.dname,
|
||||
ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,21 @@ 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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Verify new DNSKEYs with DS rrset. The DS contains hash values that should
|
||||
* match the DNSKEY keys.
|
||||
|
|
|
|||
Loading…
Reference in a new issue