Implement SKR import

When 'rndc skr import' is called, read the file contents and store the
data in the zone's skr structure.
This commit is contained in:
Matthijs Mekking 2024-06-19 12:17:31 +02:00
parent 445722d2bf
commit 037382c4a5
5 changed files with 357 additions and 39 deletions

View file

@ -16715,8 +16715,15 @@ named_server_skr(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
goto cleanup;
}
CHECK(putstr(text, "import command not implemented"));
CHECK(putnull(text));
result = dns_zone_import_skr(zone, skrfile);
if (result != ISC_R_SUCCESS) {
CHECK(putstr(text, "import failed: "));
CHECK(putstr(text, isc_result_totext(result)));
CHECK(putnull(text));
} else {
/* Schedule a rekey */
dns_zone_rekey(zone, false);
}
cleanup:
if (zone != NULL) {

View file

@ -54,26 +54,6 @@ struct dns_skrbundle {
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);
@ -101,14 +81,15 @@ dns_skr_create(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
* \li *skrp != NULL && *skrp == NULL
*/
void
dns_skr_addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep);
isc_result_t
dns_skr_read(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
dns_rdataclass_t rdclass, dns_ttl_t dnskeyttl, dns_skr_t **skrp);
/*%<
* Add a single bundle to a SKR.
* Read a SKR from 'filename'.
*
* Requires:
* \li 'skr' is a valid SKR
* \li 'bundle' is a valid bundle
* \li mctx != NULL
* \li *skrp != NULL && *skrp == NULL
*/
dns_skrbundle_t *

View file

@ -34,6 +34,7 @@
#include <dns/masterdump.h>
#include <dns/rdatastruct.h>
#include <dns/rpz.h>
#include <dns/skr.h>
#include <dns/types.h>
#include <dns/xfrin.h>
#include <dns/zt.h>
@ -2750,6 +2751,19 @@ dns_zone_check_dnskey_nsec3(dns_zone_t *zone, dns_db_t *db,
* 'false' if the zone would have NSEC only DNSKEYs and an NSEC3 chain.
*/
isc_result_t
dns_zone_import_skr(dns_zone_t *zone, const char *file);
/**<
* Import a Signed Key Response (SKR) from file.
*
* Requires:
* \li 'zone' to be a valid zone.
* \li 'file' is not NULL.
*
* Returns:
* \li ISC_R_SUCCESS if there were no errors loading the SKR.
*/
#if DNS_ZONE_TRACE
#define dns_zone_ref(ptr) dns_zone__ref(ptr, __func__, __FILE__, __LINE__)
#define dns_zone_unref(ptr) dns_zone__unref(ptr, __func__, __FILE__, __LINE__)

View file

@ -13,19 +13,132 @@
/*! \file */
#include <dns/skr.h>
#include <isc/lex.h>
#include <isc/log.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) {
#include <dns/callbacks.h>
#include <dns/fixedname.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdatatype.h>
#include <dns/skr.h>
#include <dns/time.h>
#include <dns/ttl.h>
#define CHECK(op) \
do { \
result = (op); \
if (result != ISC_R_SUCCESS) \
goto failure; \
} while (0)
#define READLINE(lex, opt, token)
#define NEXTTOKEN(lex, opt, token) \
{ \
ret = isc_lex_gettoken(lex, opt, token); \
if (ret != ISC_R_SUCCESS) \
goto cleanup; \
}
#define BADTOKEN() \
{ \
ret = ISC_R_UNEXPECTEDTOKEN; \
goto cleanup; \
}
#define TOKENSIZ (8 * 1024)
#define STR(t) ((t).value.as_textregion.base)
static isc_result_t
parse_rr(isc_lex_t *lex, isc_mem_t *mctx, char *owner, dns_name_t *origin,
dns_rdataclass_t rdclass, isc_buffer_t *buf, dns_ttl_t *ttl,
dns_rdatatype_t *rdtype, dns_rdata_t **rdata) {
dns_rdatacallbacks_t callbacks;
dns_fixedname_t dfname;
dns_name_t *dname = NULL;
dns_rdataclass_t clas;
isc_buffer_t b;
isc_token_t token;
unsigned int opt = ISC_LEXOPT_EOL;
isc_result_t ret = ISC_R_SUCCESS;
isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
/* Read the domain name */
if (!strcmp(owner, "@")) {
BADTOKEN();
}
dname = dns_fixedname_initname(&dfname);
isc_buffer_init(&b, owner, strlen(owner));
isc_buffer_add(&b, strlen(owner));
ret = dns_name_fromtext(dname, &b, dns_rootname, 0, NULL);
if (ret != ISC_R_SUCCESS) {
return (ret);
}
if (dns_name_compare(dname, origin) != 0) {
return (DNS_R_BADOWNERNAME);
}
isc_buffer_clear(&b);
/* Read the next word: either TTL, class, or type */
NEXTTOKEN(lex, opt, &token);
if (token.type != isc_tokentype_string) {
BADTOKEN();
}
/* If it's a TTL, read the next one */
ret = dns_ttl_fromtext(&token.value.as_textregion, ttl);
if (ret == ISC_R_SUCCESS) {
NEXTTOKEN(lex, opt, &token);
}
if (token.type != isc_tokentype_string) {
BADTOKEN();
}
/* If it's a class, read the next one */
ret = dns_rdataclass_fromtext(&clas, &token.value.as_textregion);
if (ret == ISC_R_SUCCESS) {
if (clas != rdclass) {
BADTOKEN();
}
NEXTTOKEN(lex, opt, &token);
}
if (token.type != isc_tokentype_string) {
BADTOKEN();
}
/* Must be the record type */
ret = dns_rdatatype_fromtext(rdtype, &token.value.as_textregion);
if (ret != ISC_R_SUCCESS) {
BADTOKEN();
}
switch (*rdtype) {
case dns_rdatatype_dnskey:
case dns_rdatatype_cdnskey:
case dns_rdatatype_cds:
case dns_rdatatype_rrsig:
/* Allowed record types */
break;
default:
BADTOKEN();
}
dns_rdatacallbacks_init(&callbacks);
ret = dns_rdata_fromtext(*rdata, rdclass, *rdtype, lex, dname, 0, mctx,
buf, &callbacks);
cleanup:
isc_lex_setcomments(lex, 0);
return (ret);
}
static void
skrbundle_create(isc_mem_t *mctx, 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;
@ -36,8 +149,8 @@ dns_skrbundle_create(isc_mem_t *mctx, dns_name_t *name,
*bp = b;
}
void
dns_skrbundle_addtuple(dns_skrbundle_t *bundle, dns_difftuple_t **tuple) {
static void
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));
@ -116,8 +229,8 @@ dns_skr_create(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
*skrp = skr;
}
void
dns_skr_addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep) {
static void
addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep) {
REQUIRE(DNS_SKR_VALID(skr));
REQUIRE(DNS_SKRBUNDLE_VALID(*bundlep));
@ -125,6 +238,159 @@ dns_skr_addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep) {
*bundlep = NULL;
}
isc_result_t
dns_skr_read(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
dns_rdataclass_t rdclass, dns_ttl_t dnskeyttl, dns_skr_t **skrp) {
isc_result_t result;
dns_skrbundle_t *bundle = NULL;
char bundlebuf[1024];
uint32_t bundle_id;
isc_lex_t *lex = NULL;
isc_lexspecials_t specials;
isc_token_t token;
unsigned int opt = ISC_LEXOPT_EOL;
REQUIRE(DNS_SKR_VALID(*skrp));
isc_lex_create(mctx, TOKENSIZ, &lex);
memset(specials, 0, sizeof(specials));
specials['('] = 1;
specials[')'] = 1;
specials['"'] = 1;
isc_lex_setspecials(lex, specials);
result = isc_lex_openfile(lex, filename);
if (result != ISC_R_SUCCESS) {
isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
ISC_LOG_ERROR, "unable to open ksr file %s: %s",
filename, isc_result_totext(result));
isc_lex_destroy(&lex);
return (result);
}
for (result = isc_lex_gettoken(lex, opt, &token);
result == ISC_R_SUCCESS;
result = isc_lex_gettoken(lex, opt, &token))
{
if (token.type == isc_tokentype_eol) {
continue;
}
if (token.type != isc_tokentype_string) {
CHECK(DNS_R_SYNTAX);
}
if (strcmp(STR(token), ";;") == 0) {
/* New bundle */
CHECK(isc_lex_gettoken(lex, opt, &token));
if (token.type != isc_tokentype_string ||
strcmp(STR(token), "SignedKeyResponse") != 0)
{
CHECK(DNS_R_SYNTAX);
}
/* Version */
CHECK(isc_lex_gettoken(lex, opt, &token));
if (token.type != isc_tokentype_string ||
strcmp(STR(token), "1.0") != 0)
{
CHECK(DNS_R_SYNTAX);
}
/* Date and time of bundle */
CHECK(isc_lex_gettoken(lex, opt, &token));
if (token.type != isc_tokentype_string) {
CHECK(DNS_R_SYNTAX);
}
if (strcmp(STR(token), "generated") == 0) {
/* Final bundle */
goto readline;
}
if (token.type != isc_tokentype_string) {
CHECK(DNS_R_SYNTAX);
}
/* Add previous bundle */
if (bundle != NULL) {
addbundle(*skrp, &bundle);
}
/* Create new bundle */
sscanf(STR(token), "%s", bundlebuf);
CHECK(dns_time32_fromtext(bundlebuf, &bundle_id));
bundle = NULL;
skrbundle_create(mctx, (isc_stdtime_t)bundle_id,
&bundle);
readline:
/* Read remainder of header line */
do {
CHECK(isc_lex_gettoken(lex, opt, &token));
} while (token.type != isc_tokentype_eol);
} else {
isc_buffer_t buf;
dns_rdata_t *rdata = NULL;
u_char rdatabuf[DST_KEY_MAXSIZE];
dns_rdatatype_t rdtype;
/* Parse record */
rdata = isc_mem_get(mctx, sizeof(*rdata));
dns_rdata_init(rdata);
isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf));
result = parse_rr(lex, mctx, STR(token), origin,
rdclass, &buf, &dnskeyttl, &rdtype,
&rdata);
if (result != ISC_R_SUCCESS) {
isc_log_write(
DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(1),
"read skr file %s(%lu) parse rr "
"failed: %s",
filename, isc_lex_getsourceline(lex),
isc_result_totext(result));
isc_mem_put(mctx, rdata, sizeof(*rdata));
goto failure;
}
/* Create new diff tuple */
dns_diffop_t op = (rdtype == dns_rdatatype_rrsig)
? DNS_DIFFOP_ADDRESIGN
: DNS_DIFFOP_ADD;
dns_difftuple_t *tuple = NULL;
dns_difftuple_create((*skrp)->mctx, op, origin,
dnskeyttl, rdata, &tuple);
skrbundle_addtuple(bundle, &tuple);
INSIST(tuple == NULL);
isc_mem_put(mctx, rdata, sizeof(*rdata));
}
}
if (result != ISC_R_EOF) {
CHECK(DNS_R_SYNTAX);
}
result = ISC_R_SUCCESS;
/* Add final bundle */
if (bundle != NULL) {
addbundle(*skrp, &bundle);
}
failure:
if (result != ISC_R_SUCCESS) {
isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
ISC_LOG_DEBUG(1),
"read skr file %s(%lu) failed: %s", filename,
isc_lex_getsourceline(lex),
isc_result_totext(result));
}
/* Clean up */
isc_lex_destroy(&lex);
return (result);
}
dns_skrbundle_t *
dns_skr_lookup(dns_skr_t *skr, isc_stdtime_t time, uint32_t sigval) {
dns_skrbundle_t *b, *next;

View file

@ -79,11 +79,13 @@
#include <dns/request.h>
#include <dns/resolver.h>
#include <dns/rriterator.h>
#include <dns/skr.h>
#include <dns/soa.h>
#include <dns/ssu.h>
#include <dns/stats.h>
#include <dns/time.h>
#include <dns/tsig.h>
#include <dns/ttl.h>
#include <dns/update.h>
#include <dns/xfrin.h>
#include <dns/zone.h>
@ -499,6 +501,12 @@ struct dns_zone {
dns_update_state_t *rss_state;
isc_stats_t *gluecachestats;
/*%
* Offline KSK signed key responses.
*/
dns_skr_t *skr;
dns_skrbundle_t *skrbundle;
};
#define zonediff_init(z, d) \
@ -1273,6 +1281,10 @@ zone_free(dns_zone_t *zone) {
if (!ISC_LIST_EMPTY(zone->checkds_ok)) {
clear_keylist(&zone->checkds_ok, zone->mctx);
}
if (zone->skr != NULL) {
zone->skrbundle = NULL;
dns_skr_detach(&zone->skr);
}
zone->journalsize = -1;
if (zone->journal != NULL) {
@ -5731,6 +5743,21 @@ dns_zone_getkasp(dns_zone_t *zone) {
return (kasp);
}
static void
dns_zone_setskr(dns_zone_t *zone, dns_skr_t *skr) {
REQUIRE(DNS_ZONE_VALID(zone));
LOCK_ZONE(zone);
zone->skrbundle = NULL;
if (zone->skr != NULL) {
dns_skr_detach(&zone->skr);
}
if (skr != NULL) {
dns_skr_attach(skr, &zone->skr);
}
UNLOCK_ZONE(zone);
}
void
dns_zone_setoption(dns_zone_t *zone, dns_zoneopt_t option, bool value) {
REQUIRE(DNS_ZONE_VALID(zone));
@ -24219,3 +24246,26 @@ dns_zone_makedb(dns_zone_t *zone, dns_db_t **dbp) {
return (ISC_R_SUCCESS);
}
isc_result_t
dns_zone_import_skr(dns_zone_t *zone, const char *file) {
dns_skr_t *skr = NULL;
isc_result_t result;
REQUIRE(DNS_ZONE_VALID(zone));
REQUIRE(zone->kasp != NULL);
REQUIRE(file != NULL);
dns_skr_create(zone->mctx, file, &zone->origin, zone->rdclass, &skr);
CHECK(dns_skr_read(zone->mctx, file, &zone->origin, zone->rdclass,
dns_kasp_dnskeyttl(zone->kasp), &skr));
dns_zone_setskr(zone, skr);
dnssec_log(zone, ISC_LOG_DEBUG(1), "imported skr file %s", file);
failure:
dns_skr_detach(&skr);
return (result);
}