From 445722d2bf4bbe15af4e4940ffb0d4dc2d2c5a8b Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 19 Jun 2024 11:09:29 +0200 Subject: [PATCH] Add code to store SKR This added source code stores SKR data. It is loosely based on: https://www.iana.org/dnssec/archive/files/draft-icann-dnssec-keymgmt-01.txt A SKR contains a list of signed DNSKEY RRsets. Each change in data should be stored in a separate bundle. So if the RRSIG is refreshed that means it is stored in the next bundle. Likewise, if there is a new ZSK pre-published, it is in the next bundle. In addition (not mentioned in the draft), each bundle may contain signed CDS and CDNSKEY RRsets. Each bundle has an inception time. These will determine when we need to re-sign or re-key the zone. --- lib/dns/Makefile.am | 2 + lib/dns/include/dns/skr.h | 168 ++++++++++++++++++++++++++++++++ lib/dns/include/dns/types.h | 1 + lib/dns/skr.c | 188 ++++++++++++++++++++++++++++++++++++ 4 files changed, 359 insertions(+) create mode 100644 lib/dns/include/dns/skr.h create mode 100644 lib/dns/skr.c 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)); +}