From: Matthijs Mekking Date: Wed, 19 Jun 2024 10:17:31 +0000 (+0200) Subject: Implement SKR import X-Git-Tag: v9.21.1~23^2~7 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=037382c4a5d41ebc99faee268f47a8a638e5d2fb;p=thirdparty%2Fbind9.git Implement SKR import When 'rndc skr import' is called, read the file contents and store the data in the zone's skr structure. --- diff --git a/bin/named/server.c b/bin/named/server.c index 6addb6108ad..eacd3ac88cd 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -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) { diff --git a/lib/dns/include/dns/skr.h b/lib/dns/include/dns/skr.h index 872d936e285..6b2c3c71f92 100644 --- a/lib/dns/include/dns/skr.h +++ b/lib/dns/include/dns/skr.h @@ -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 * diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 5bbcaaa5528..35a80df65c8 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -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__) diff --git a/lib/dns/skr.c b/lib/dns/skr.c index 0926cc78549..6ba7b9a88b2 100644 --- a/lib/dns/skr.c +++ b/lib/dns/skr.c @@ -13,19 +13,132 @@ /*! \file */ +#include +#include + +#include +#include +#include +#include +#include #include +#include +#include + +#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; \ + } -void -dns_skrbundle_create(isc_mem_t *mctx, dns_name_t *name, - dns_rdataclass_t rdclass, isc_stdtime_t inception, - dns_skrbundle_t **bp) { +#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; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index b68383eb22e..7dbab379ae7 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -79,11 +79,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -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); +}