diff --git a/lib/dns/Makefile.am b/lib/dns/Makefile.am index d5e96faa5e..fb91857fc3 100644 --- a/lib/dns/Makefile.am +++ b/lib/dns/Makefile.am @@ -121,6 +121,7 @@ libdns_la_HEADERS = \ include/dns/sdlz.h \ include/dns/secalg.h \ include/dns/secproto.h \ + include/dns/skr.h \ include/dns/soa.h \ include/dns/ssu.h \ include/dns/stats.h \ @@ -238,6 +239,7 @@ libdns_la_SOURCES = \ rrl.c \ rriterator.c \ sdlz.c \ + skr.c \ soa.c \ ssu.c \ ssu_external.c \ diff --git a/lib/dns/include/dns/skr.h b/lib/dns/include/dns/skr.h new file mode 100644 index 0000000000..872d936e28 --- /dev/null +++ b/lib/dns/include/dns/skr.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file dns/skr.h + * \brief + * A skr is a convenience type representing a Signed Key Response (SKR), + * determining which DNSKEY, CDS, CDNSKEY and corresponding signatures + * should be published at a given time. It is based on terminology used in + * https://www.iana.org/dnssec/archive/files/draft-icann-dnssec-keymgmt-01.txt + */ + +#include + +#include +#include + +ISC_LANG_BEGINDECLS + +#define DNS_SKR_MAGIC ISC_MAGIC('S', 'K', 'R', '-') +#define DNS_SKR_VALID(t) ISC_MAGIC_VALID(t, DNS_SKR_MAGIC) + +#define DNS_SKRBUNDLE_MAGIC ISC_MAGIC('S', 'K', 'R', 'B') +#define DNS_SKRBUNDLE_VALID(t) ISC_MAGIC_VALID(t, DNS_SKRBUNDLE_MAGIC) + +typedef struct dns_skrbundle dns_skrbundle_t; +typedef ISC_LIST(dns_skrbundle_t) dns_skrbundlelist_t; + +/* Stores a Signed Key Response (SKR) */ +struct dns_skr { + unsigned int magic; + isc_mem_t *mctx; + char *filename; + isc_time_t loadtime; + dns_skrbundlelist_t bundles; + isc_refcount_t references; +}; + +struct dns_skrbundle { + unsigned int magic; + isc_stdtime_t inception; + dns_diff_t diff; + ISC_LINK(dns_skrbundle_t) link; +}; + +void +dns_skrbundle_create(isc_mem_t *mctx, dns_name_t *name, + dns_rdataclass_t rdclass, isc_stdtime_t inception, + dns_skrbundle_t **bp); +/*%< + * Create a single bundle. + * + * Requires: + * \li *bp != NULL && *bp == NULL + */ + +void +dns_skrbundle_addtuple(dns_skrbundle_t *bundle, dns_difftuple_t **tuple); +/*%< + * Add a single tuple to a key bundle. + * + * \li 'bundle' is a valid bundle + * \li '*tuple' is a valid tuple + */ + +isc_result_t +dns_skrbundle_getsig(dns_skrbundle_t *bundle, dst_key_t *key, + dns_rdatatype_t covering_type, dns_rdata_t *sigrdata); +/*%< + * Retrieve the RRSIG rdata for 'covering_type' generated by 'key' from the + * given 'bundle'. + * + * Requires: + * \li 'bundle' is a valid bundle + * + * Returns: + * \li a possible error if we fail to convert the rdata to a struct + * \li ISC_R_SUCCESS if the signature is found + * \li ISC_R_NOTFOUND otherwise + */ + +void +dns_skr_create(isc_mem_t *mctx, const char *filename, dns_name_t *origin, + dns_rdataclass_t rdclass, dns_skr_t **skrp); +/*%< + * Create a SKR. + * + * Requires: + * \li mctx != NULL + * \li *skrp != NULL && *skrp == NULL + */ + +void +dns_skr_addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep); +/*%< + * Add a single bundle to a SKR. + * + * Requires: + * \li 'skr' is a valid SKR + * \li 'bundle' is a valid bundle + */ + +dns_skrbundle_t * +dns_skr_lookup(dns_skr_t *skr, isc_stdtime_t time, uint32_t sigval); +/*%< + * Look up the currently active bundle. The active bundle is the one which + * inception time is prior to 'time' and the next bundle inception is after + " 'time'. In case of the last bundle in the SKR, 'time' is expected to be + * lower than the last bundle inception time plus 'sigval'. + * + * Requires: + * \li 'skr' is a valid SKR + * + * Returns: + * \li The currently active bundle, or NULL if no such bundle is found. + */ + +void +dns_skr_attach(dns_skr_t *source, dns_skr_t **targetp); +/*%< + * Attach '*targetp' to 'source'. + * + * Requires: + * + *\li 'source' is a valid SKR. + * + *\li 'targetp' points to a NULL dns_skr_t *. + * + * Ensures: + * + *\li *targetp is attached to source. + */ + +void +dns_skr_detach(dns_skr_t **skrp); +/*%< + * Detach SKR. + * + * Requires: + * + *\li 'skrp' points to a valid dns_skr_t * + * + * Ensures: + * + *\li *skrp is NULL. + */ + +void +dns_skr_destroy(dns_skr_t *skr); +/*%< + * Destroy a SKR. + * + * Requires: + * \li 'skr' is a valid SKR + */ + +ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 7784d126f6..c4353aef4c 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -150,6 +150,7 @@ typedef struct dns_qpnode dns_qpnode_t; typedef uint8_t dns_secalg_t; typedef uint8_t dns_secproto_t; typedef struct dns_signature dns_signature_t; +typedef struct dns_skr dns_skr_t; typedef struct dns_slabheader dns_slabheader_t; typedef ISC_LIST(dns_slabheader_t) dns_slabheaderlist_t; typedef struct dns_sortlist_arg dns_sortlist_arg_t; diff --git a/lib/dns/skr.c b/lib/dns/skr.c new file mode 100644 index 0000000000..0926cc7854 --- /dev/null +++ b/lib/dns/skr.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include + +void +dns_skrbundle_create(isc_mem_t *mctx, dns_name_t *name, + dns_rdataclass_t rdclass, isc_stdtime_t inception, + dns_skrbundle_t **bp) { + dns_skrbundle_t *b; + + REQUIRE(bp != NULL && *bp == NULL); + + UNUSED(name); + UNUSED(rdclass); + + b = isc_mem_get(mctx, sizeof(*b)); + b->magic = DNS_SKRBUNDLE_MAGIC; + b->inception = inception; + dns_diff_init(mctx, &b->diff); + + ISC_LINK_INIT(b, link); + + *bp = b; +} + +void +dns_skrbundle_addtuple(dns_skrbundle_t *bundle, dns_difftuple_t **tuple) { + REQUIRE(DNS_DIFFTUPLE_VALID(*tuple)); + REQUIRE(DNS_SKRBUNDLE_VALID(bundle)); + REQUIRE(DNS_DIFF_VALID(&bundle->diff)); + + dns_diff_append(&bundle->diff, tuple); +} + +isc_result_t +dns_skrbundle_getsig(dns_skrbundle_t *bundle, dst_key_t *key, + dns_rdatatype_t covering_type, dns_rdata_t *sigrdata) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(DNS_SKRBUNDLE_VALID(bundle)); + REQUIRE(DNS_DIFF_VALID(&bundle->diff)); + + dns_difftuple_t *tuple = ISC_LIST_HEAD(bundle->diff.tuples); + while (tuple != NULL) { + dns_rdata_rrsig_t rrsig; + + if (tuple->op != DNS_DIFFOP_ADDRESIGN) { + tuple = ISC_LIST_NEXT(tuple, link); + continue; + } + INSIST(tuple->rdata.type == dns_rdatatype_rrsig); + + result = dns_rdata_tostruct(&tuple->rdata, &rrsig, NULL); + if (result != ISC_R_SUCCESS) { + return (result); + } + + /* + * Check if covering type matches, and if the signature is + * generated by 'key'. + */ + if (rrsig.covered == covering_type && + rrsig.keyid == dst_key_id(key)) + { + dns_rdata_clone(&tuple->rdata, sigrdata); + return (ISC_R_SUCCESS); + } + + tuple = ISC_LIST_NEXT(tuple, link); + } + + return (ISC_R_NOTFOUND); +} + +void +dns_skr_create(isc_mem_t *mctx, const char *filename, dns_name_t *origin, + dns_rdataclass_t rdclass, dns_skr_t **skrp) { + isc_time_t now; + dns_skr_t *skr = NULL; + + REQUIRE(skrp != NULL && *skrp == NULL); + REQUIRE(mctx != NULL); + + UNUSED(origin); + UNUSED(rdclass); + + now = isc_time_now(); + skr = isc_mem_get(mctx, sizeof(*skr)); + *skr = (dns_skr_t){ + .magic = DNS_SKR_MAGIC, + .filename = isc_mem_strdup(mctx, filename), + .loadtime = now, + }; + /* + * A list is not the best structure to store bundles that + * we need to look up, but we don't expect many bundles + * per SKR so it is acceptable for now. + */ + ISC_LIST_INIT(skr->bundles); + + isc_mem_attach(mctx, &skr->mctx); + isc_refcount_init(&skr->references, 1); + *skrp = skr; +} + +void +dns_skr_addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep) { + REQUIRE(DNS_SKR_VALID(skr)); + REQUIRE(DNS_SKRBUNDLE_VALID(*bundlep)); + + ISC_LIST_APPEND(skr->bundles, *bundlep, link); + *bundlep = NULL; +} + +dns_skrbundle_t * +dns_skr_lookup(dns_skr_t *skr, isc_stdtime_t time, uint32_t sigval) { + dns_skrbundle_t *b, *next; + + REQUIRE(DNS_SKR_VALID(skr)); + + for (b = ISC_LIST_HEAD(skr->bundles); b != NULL; b = next) { + next = ISC_LIST_NEXT(b, link); + if (next == NULL) { + isc_stdtime_t expired = b->inception + sigval; + if (b->inception <= time && time < expired) { + return (b); + } + return (NULL); + } + if (b->inception <= time && time < next->inception) { + return (b); + } + } + + return (NULL); +} + +void +dns_skr_attach(dns_skr_t *source, dns_skr_t **targetp) { + REQUIRE(DNS_SKR_VALID(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + isc_refcount_increment(&source->references); + *targetp = source; +} + +void +dns_skr_detach(dns_skr_t **skrp) { + REQUIRE(skrp != NULL && DNS_SKR_VALID(*skrp)); + + dns_skr_t *skr = *skrp; + *skrp = NULL; + + if (isc_refcount_decrement(&skr->references) == 1) { + dns_skr_destroy(skr); + } +} + +void +dns_skr_destroy(dns_skr_t *skr) { + dns_skrbundle_t *b, *next; + + REQUIRE(DNS_SKR_VALID(skr)); + + for (b = ISC_LIST_HEAD(skr->bundles); b != NULL; b = next) { + next = ISC_LIST_NEXT(b, link); + ISC_LIST_UNLINK(skr->bundles, b, link); + dns_diff_clear(&b->diff); + isc_mem_put(skr->mctx, b, sizeof(*b)); + } + INSIST(ISC_LIST_EMPTY(skr->bundles)); + + isc_mem_free(skr->mctx, skr->filename); + isc_mem_putanddetach(&skr->mctx, skr, sizeof(*skr)); +}