]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Implement SKR import
authorMatthijs Mekking <matthijs@isc.org>
Wed, 19 Jun 2024 10:17:31 +0000 (12:17 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Thu, 22 Aug 2024 06:21:52 +0000 (08:21 +0200)
When 'rndc skr import' is called, read the file contents and store the
data in the zone's skr structure.

bin/named/server.c
lib/dns/include/dns/skr.h
lib/dns/include/dns/zone.h
lib/dns/skr.c
lib/dns/zone.c

index 6addb6108ad54ceb705e5c56e7d68220a7faa704..eacd3ac88cd2cb68d864b33e246a2f245ea99136 100644 (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) {
index 872d936e2852ba29b5a6e9764601898f5c5a9947..6b2c3c71f92742a7c37f82a89b42038d794ce01f 100644 (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 *
index 5bbcaaa552829986e2887865f69a9643b2aac52f..35a80df65c8c58dff502086282add1911c94e994 100644 (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__)
index 0926cc78549ac517cd4a51b4abfb9e3909401fbb..6ba7b9a88b2566140c7c58877cb8cec70fedd8a3 100644 (file)
 
 /*! \file */
 
+#include <isc/lex.h>
+#include <isc/log.h>
+
+#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;                    \
+       }
 
-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;
index b68383eb22e26c887de0c7d240e4ea8efb5e95a7..7dbab379ae7ebaf002ee6c5f8d92f63e9573a0a2 100644 (file)
 #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);
+}