Track updating TA's with root DNSKEY rrset

This commit is contained in:
Willem Toorop 2017-09-20 10:30:13 +02:00
parent e2ffaf3e07
commit 34d35f9e79
8 changed files with 364 additions and 13 deletions

View File

@ -45,9 +45,10 @@
#include "gldns/parseutil.h"
#include "gldns/gbuffer.h"
#include "gldns/str2wire.h"
#include "gldns/wire2str.h"
#include "gldns/pkthdr.h"
#include "gldns/keyraw.h"
#include "general.h"
#include "rr-iter.h"
#include "util-internal.h"
/* get key usage out of its extension, returns 0 if no key_usage extension */
@ -741,7 +742,6 @@ void _getdns_context_equip_with_anchor(
else if (!verify_CA || !*verify_CA)
DEBUG_ANCHOR("NOTICE: Trust anchor verification explicitely "
"disabled by empty verify CA\n");
return;
else if ((r = getdns_context_get_trust_anchor_verify_email(
context, ".", &verify_email)))
@ -1612,4 +1612,305 @@ void _getdns_start_fetching_ta(getdns_context *context, getdns_eventloop *loop)
context->trust_anchors_source = GETDNS_TASRC_FETCHING;
}
static int _uint16_cmp(const void *a, const void *b)
{ return (int)*(uint16_t *)a - (int)*(uint16_t *)b; }
static int _uint8x16_cmp(const void *a, const void *b)
{ return memcmp(a, b, RRSIG_RDATA_LEN); }
static void
_getdns_init_ksks(_getdns_ksks *ksks, _getdns_rrset *dnskey_set)
{
_getdns_rrtype_iter *rr, rr_space;
_getdns_rrsig_iter *rrsig, rrsig_space;
assert(ksks);
assert(dnskey_set);
assert(dnskey_set->rr_type == GETDNS_RRTYPE_DNSKEY);
ksks->n = 0;
for ( rr = _getdns_rrtype_iter_init(&rr_space, dnskey_set)
; rr && ksks->n < MAX_KSKS
; rr = _getdns_rrtype_iter_next(rr)) {
if (rr->rr_i.nxt - rr->rr_i.rr_type < 12
|| !(rr->rr_i.rr_type[11] & 1))
continue; /* Not a KSK */
ksks->ids[ksks->n++] = gldns_calc_keytag_raw(
rr->rr_i.rr_type + 10,
rr->rr_i.nxt - rr->rr_i.rr_type - 10);
}
qsort(ksks->ids, ksks->n, sizeof(uint16_t), _uint16_cmp);
ksks->n_rrsigs = 0;
for ( rrsig = _getdns_rrsig_iter_init(&rrsig_space, dnskey_set)
; rrsig && ksks->n_rrsigs < MAX_KSKS
; rrsig = _getdns_rrsig_iter_next(rrsig)) {
if (rrsig->rr_i.nxt - rrsig->rr_i.rr_type < 28)
continue;
(void) memcpy(ksks->rrsigs[ksks->n_rrsigs++],
rrsig->rr_i.rr_type + 12, RRSIG_RDATA_LEN);
}
qsort(ksks->rrsigs, ksks->n_rrsigs, RRSIG_RDATA_LEN, _uint8x16_cmp);
}
static int
_getdns_ksks_equal(_getdns_ksks *a, _getdns_ksks *b)
{
return a == b
|| ( a != NULL && b != NULL
&& a->n == b->n
&& memcmp(a->ids, b->ids, a->n * sizeof(uint16_t)) == 0
&& a->n_rrsigs == b->n_rrsigs
&& memcmp(a->rrsigs, b->rrsigs, a->n_rrsigs * RRSIG_RDATA_LEN) == 0);
}
static void _getdns_context_read_root_ksk(getdns_context *context)
{
FILE *fp;
struct gldns_file_parse_state pst;
size_t len, dname_len;
uint8_t buf_spc[4096], *buf = buf_spc, *ptr = buf_spc;
size_t buf_sz = sizeof(buf_spc);
_getdns_rrset root_dnskey;
uint8_t *root_dname = (uint8_t *)"\00";
if (!(fp = _getdns_context_get_priv_fp(context, "root.key")))
return;
for (;;) {
size_t n_rrs = 0;
*pst.origin = 0;
pst.origin_len = 1;
*pst.prev_rr = 0;
pst.prev_rr_len = 1;
pst.default_ttl = 0;
pst.lineno = 1;
(void) memset(buf, 0, 12);
ptr += 12;
while (!feof(fp)) {
len = buf + buf_sz - ptr;
dname_len = 0;
if (gldns_fp2wire_rr_buf(fp, ptr, &len, &dname_len, &pst))
break;
if ((ptr += len) > buf + buf_sz)
break;
if (len)
n_rrs += 1;
if (dname_len && dname_len < sizeof(pst.prev_rr)) {
memcpy(pst.prev_rr, ptr, dname_len);
pst.prev_rr_len = dname_len;
}
}
if (ptr <= buf + buf_sz) {
gldns_write_uint16(buf + GLDNS_ANCOUNT_OFF, n_rrs);
break;
}
rewind(fp);
if (buf == buf_spc)
buf_sz = 65536;
else {
GETDNS_FREE(context->mf, buf);
buf_sz *= 2;
}
if (!(buf = GETDNS_XMALLOC(context->mf, uint8_t, buf_sz))) {
DEBUG_ANCHOR("ERROR %s(): Memory error\n", __FUNC__);
break;;
}
ptr = buf;
};
fclose(fp);
if (!buf)
return;
root_dnskey.name = root_dname;
root_dnskey.rr_class = GETDNS_RRCLASS_IN;
root_dnskey.rr_type = GETDNS_RRTYPE_DNSKEY;
root_dnskey.pkt = buf;
root_dnskey.pkt_len = ptr - buf;
root_dnskey.sections = SECTION_ANSWER;
_getdns_init_ksks(&context->root_ksk, &root_dnskey);
if (buf && buf != buf_spc)
GETDNS_FREE(context->mf, buf);
}
void
_getdns_context_update_root_ksk(
getdns_context *context, _getdns_rrset *dnskey_set)
{
_getdns_ksks root_ksk_seen;
_getdns_rrtype_iter *rr, rr_space;
_getdns_rrsig_iter *rrsig, rrsig_space;
char str_spc[4096], *str_buf, *str_pos;
int sz_needed;
int remaining;
size_t str_sz = 0;
getdns_bindata root_key_bd;
_getdns_init_ksks(&root_ksk_seen, dnskey_set);
if (_getdns_ksks_equal(&context->root_ksk, &root_ksk_seen))
return; /* root DNSKEY rrset already known */
/* Try to read root DNSKEY rrset from root.key */
_getdns_context_read_root_ksk(context);
if (_getdns_ksks_equal(&context->root_ksk, &root_ksk_seen))
return; /* root DNSKEY rrset same as the safed one */
/* Different root DNSKEY rrset. Perhaps because of failure to read
* from disk. If we cannot write to our appdata directory, bail out
*/
if (context->can_write_appdata == PROP_UNABLE)
return;
/* We might be able to write or we do not know whether we can write
* to the appdata directory. In the latter we'll try to write to
* find out. The section below converts the wireformat DNSKEY rrset
* to presentationformat.
*/
str_pos = str_buf = str_spc;
remaining = sizeof(str_spc);
for (;;) {
for ( rr = _getdns_rrtype_iter_init(&rr_space, dnskey_set)
; rr ; rr = _getdns_rrtype_iter_next(rr)) {
sz_needed = gldns_wire2str_rr_buf((uint8_t *)rr->rr_i.pos,
rr->rr_i.nxt - rr->rr_i.pos, str_pos,
(size_t)(remaining > 0 ? remaining : 0));
str_pos += sz_needed;
remaining -= sz_needed;
}
for ( rrsig = _getdns_rrsig_iter_init(&rrsig_space, dnskey_set)
; rrsig
; rrsig = _getdns_rrsig_iter_next(rrsig)) {
sz_needed = gldns_wire2str_rr_buf((uint8_t *)rrsig->rr_i.pos,
rrsig->rr_i.nxt - rrsig->rr_i.pos, str_pos,
(size_t)(remaining > 0 ? remaining : 0));
str_pos += sz_needed;
remaining -= sz_needed;
}
if (remaining > 0) {
*str_pos = 0;
if (str_buf == str_spc)
str_sz = sizeof(str_spc) - remaining;
break;
}
if (str_buf != str_spc) {
DEBUG_ANCHOR("ERROR %s(): Buffer size determination "
"error\n", __FUNC__);
if (str_buf)
GETDNS_FREE(context->mf, str_buf);
return;
}
if (!(str_pos = str_buf = GETDNS_XMALLOC( context->mf, char,
(str_sz = sizeof(str_spc) - remaining) + 1))) {
DEBUG_ANCHOR("ERROR %s(): Memory error\n", __FUNC__);
return;
}
remaining = str_sz + 1;
DEBUG_ANCHOR("Retrying with buf size: %d\n", remaining);
};
/* Write presentation format DNSKEY rrset to "root.key" file */
root_key_bd.size = str_sz;
root_key_bd.data = (void *)str_buf;
if (_getdns_context_write_priv_file(
context, "root.key", &root_key_bd)) {
size_t i;
/* A new "root.key" file was written. When they contain
* key_id's which are not in "root-anchors.xml", then update
* "root-anchors.xml".
*/
for (i = 0; i < context->root_ksk.n; i++) {
_getdns_rrset_iter tas_iter_spc, *ta;
for ( ta = _getdns_rrset_iter_init(&tas_iter_spc
, context->trust_anchors
, context->trust_anchors_len
, SECTION_ANSWER)
; ta ; ta = _getdns_rrset_iter_next(ta)) {
_getdns_rrtype_iter *rr, rr_space;
_getdns_rrset *rrset;
if (!(rrset = _getdns_rrset_iter_value(ta)))
continue;
if (*rrset->name != '\0')
continue; /* Not a root anchor */
if (rrset->rr_type == GETDNS_RRTYPE_DS) {
for ( rr = _getdns_rrtype_iter_init(
&rr_space, rrset)
; rr
; rr = _getdns_rrtype_iter_next(rr)
) {
if (rr->rr_i.nxt -
rr->rr_i.rr_type < 12)
continue;
DEBUG_ANCHOR("DS with id: %d\n"
, (int)gldns_read_uint16(rr->rr_i.rr_type + 10));
if (gldns_read_uint16(
rr->rr_i.rr_type + 10) ==
context->root_ksk.ids[i])
break;
}
if (rr)
break;
continue;
}
if (rrset->rr_type != GETDNS_RRTYPE_DNSKEY)
continue;
for ( rr = _getdns_rrtype_iter_init(&rr_space
, rrset)
; rr ; rr = _getdns_rrtype_iter_next(rr)) {
if (rr->rr_i.nxt-rr->rr_i.rr_type < 12
|| !(rr->rr_i.rr_type[11] & 1))
continue; /* Not a KSK */
if (gldns_calc_keytag_raw(
rr->rr_i.rr_type + 10,
rr->rr_i.nxt-rr->rr_i.rr_type - 10)
== context->root_ksk.ids[i])
break;
}
if (rr)
break;
}
if (!ta) {
DEBUG_ANCHOR("NOTICE %s(): Key with id %d "
"*not* found in TA.\n"
"\"root-anchors.xml\" need "
"updating.\n", __FUNC__
, context->root_ksk.ids[i]);
context->trust_anchors_source =
GETDNS_TASRC_XML_UPDATE;
break;
}
DEBUG_ANCHOR("DEBUG %s(): Key with id %d found in TA\n"
, __FUNC__, context->root_ksk.ids[i]);
}
}
if (str_buf && str_buf != str_spc)
GETDNS_FREE(context->mf, str_buf);
}
/* anchor.c */

View File

@ -37,10 +37,23 @@
#include "getdns/getdns.h"
#include "getdns/getdns_extra.h"
#include <time.h>
#include "rr-iter.h"
void _getdns_context_equip_with_anchor(getdns_context *context, uint64_t *now_ms);
void _getdns_start_fetching_ta(getdns_context *context, getdns_eventloop *loop);
#define MAX_KSKS 16
#define RRSIG_RDATA_LEN 16
typedef struct _getdns_ksks {
size_t n;
uint16_t ids[MAX_KSKS];
size_t n_rrsigs;
uint8_t rrsigs[MAX_KSKS][RRSIG_RDATA_LEN];
} _getdns_ksks;
void _getdns_context_update_root_ksk(
getdns_context *context, _getdns_rrset *dnskey_set);
#endif
/* anchor.h */

View File

@ -1500,6 +1500,8 @@ getdns_context_create_with_extended_memory_functions(
= _getdns_default_root_anchor_verify_email;
result->root_anchor_verify_CA = _getdns_default_root_anchor_verify_CA;
(void) memset(&result->root_ksk, 0, sizeof(result->root_ksk));
(void) memset(&result->a, 0, sizeof(result->a));
(void) memset(&result->aaaa, 0, sizeof(result->aaaa));
result->a.fd = -1;
@ -4654,13 +4656,13 @@ static size_t _getdns_get_appdata(char *path)
return 0;
}
uint8_t *_getdns_context_get_priv_file(getdns_context *context,
const char *fn, uint8_t *buf, size_t buf_len, size_t *file_sz)
FILE *_getdns_context_get_priv_fp(getdns_context *context, const char *fn)
{
char path[PATH_MAX];
FILE *f = NULL;
size_t len;
(void) context;
if (!(len = _getdns_get_appdata(path)))
DEBUG_ANCHOR("ERROR %s(): Could nog get application data path\n"
, __FUNC__);
@ -4675,6 +4677,17 @@ uint8_t *_getdns_context_get_priv_file(getdns_context *context,
DEBUG_ANCHOR("ERROR %s(): Opening \"%s\": %s\n"
, __FUNC__, path, strerror(errno));
return f;
}
uint8_t *_getdns_context_get_priv_file(getdns_context *context,
const char *fn, uint8_t *buf, size_t buf_len, size_t *file_sz)
{
FILE *f = NULL;
if (!(f = _getdns_context_get_priv_fp(context, fn)))
; /* pass */
else if ((*file_sz = fread(buf, 1, buf_len, f)) < (buf_len - 1) && feof(f)) {
buf[*file_sz] = 0;
(void) fclose(f);
@ -4682,19 +4695,19 @@ uint8_t *_getdns_context_get_priv_file(getdns_context *context,
}
else if (fseek(f, 0, SEEK_END) < 0)
DEBUG_ANCHOR("ERROR %s(): Determining size of \"%s\": %s\n"
, __FUNC__, path, strerror(errno));
, __FUNC__, fn, strerror(errno));
else if (!(buf = GETDNS_XMALLOC(
context->mf, uint8_t, (buf_len = ftell(f) + 1))))
DEBUG_ANCHOR("ERROR %s(): Allocating %d memory for \"%s\"\n"
, __FUNC__, (int)buf_len, path);
, __FUNC__, (int)buf_len, fn);
else {
rewind(f);
if ((*file_sz = fread(buf, 1, buf_len, f)) >= buf_len || !feof(f)) {
GETDNS_FREE(context->mf, buf);
DEBUG_ANCHOR("ERROR %s(): Reading \"%s\": %s\n"
, __FUNC__, path, strerror(errno));
, __FUNC__, fn, strerror(errno));
}
else {
buf[*file_sz] = 0;

View File

@ -49,6 +49,7 @@
#include "util/lruhash.h"
#endif
#include "rr-iter.h"
#include "anchor.h"
struct getdns_dns_req;
struct ub_ctx;
@ -99,6 +100,7 @@ typedef enum getdns_tasrc {
GETDNS_TASRC_APP,
GETDNS_TASRC_FETCHING,
GETDNS_TASRC_XML,
GETDNS_TASRC_XML_UPDATE,
GETDNS_TASRC_FAILED
} getdns_tasrc;
@ -341,6 +343,8 @@ struct getdns_context {
const char *root_anchor_verify_CA;
const char *root_anchor_verify_email;
_getdns_ksks root_ksk;
getdns_upstreams *upstreams;
uint16_t limit_outstanding_queries;
uint32_t dnssec_allowed_skew;
@ -545,6 +549,7 @@ void _getdns_upstreams_dereference(getdns_upstreams *upstreams);
void _getdns_upstream_shutdown(getdns_upstream *upstream);
FILE *_getdns_context_get_priv_fp(getdns_context *context, const char *fn);
uint8_t *_getdns_context_get_priv_file(getdns_context *context,
const char *fn, uint8_t *buf, size_t buf_len, size_t *file_sz);

View File

@ -2998,6 +2998,11 @@ static void append_empty_ds2val_chain_list(
if (_getdns_list_append_this_dict(val_chain_list, rr_dict))
getdns_dict_destroy(rr_dict);
}
static inline chain_node *_to_the_root(chain_node *node)
{
while (node->parent) node = node->parent;
return node;
}
static void check_chain_complete(chain_head *chain)
{
@ -3063,6 +3068,16 @@ static void check_chain_complete(chain_head *chain)
, context->trust_anchors_len
, SECTION_ANSWER));
#endif
if (context->trust_anchors_source != GETDNS_TASRC_XML)
; /* pass */
/* Find root key or query for it in full recursion... */
else if (!(head = chain) || !(node = _to_the_root(head->parent)))
; /* pass */
else if (node->dnskey.name && *node->dnskey.name == 0)
_getdns_context_update_root_ksk(context, &node->dnskey);
#ifdef DNSSEC_ROADBLOCK_AVOIDANCE
if ( dnsreq->dnssec_roadblock_avoidance
&& !dnsreq->avoid_dnssec_roadblocks

View File

@ -581,11 +581,15 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop,
req->internal_cb = internal_cb;
req->is_sync_request = loop == &context->sync_eventloop.loop;
if (req->dnssec_return_status &&
context->trust_anchors_source == GETDNS_TASRC_NONE) {
_getdns_context_equip_with_anchor(context, &now_ms);
if (context->trust_anchors_source == GETDNS_TASRC_NONE)
if (req->dnssec_return_status) {
if (context->trust_anchors_source == GETDNS_TASRC_XML_UPDATE)
_getdns_start_fetching_ta(context, loop);
else if (context->trust_anchors_source == GETDNS_TASRC_NONE) {
_getdns_context_equip_with_anchor(context, &now_ms);
if (context->trust_anchors_source == GETDNS_TASRC_NONE)
_getdns_start_fetching_ta(context, loop);
}
}
/* Set up the context assuming we won't use the specified namespaces.
This is (currently) identical to setting up a pure DNS namespace */

View File

@ -95,7 +95,7 @@ gldns_rr_dnskey_key_size_raw(const unsigned char* keydata,
}
}
uint16_t gldns_calc_keytag_raw(uint8_t* key, size_t keysize)
uint16_t gldns_calc_keytag_raw(const uint8_t* key, size_t keysize)
{
if(keysize < 4) {
return 0;

View File

@ -44,7 +44,7 @@ size_t gldns_rr_dnskey_key_size_raw(const unsigned char *keydata,
* \param[in] keysize length of key data.
* \return the keytag
*/
uint16_t gldns_calc_keytag_raw(uint8_t* key, size_t keysize);
uint16_t gldns_calc_keytag_raw(const uint8_t* key, size_t keysize);
#if GLDNS_BUILD_CONFIG_HAVE_SSL
/**