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.
This commit is contained in:
Matthijs Mekking 2024-06-19 11:09:29 +02:00
parent afe093258c
commit 445722d2bf
4 changed files with 359 additions and 0 deletions

View file

@ -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 \

168
lib/dns/include/dns/skr.h Normal file
View file

@ -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 <isc/stdtime.h>
#include <dns/diff.h>
#include <dns/types.h>
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

View file

@ -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;

188
lib/dns/skr.c Normal file
View file

@ -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 <dns/skr.h>
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));
}