summaryrefslogtreecommitdiffstats
path: root/bin/dnssec/dnssec-importkey.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--bin/dnssec/dnssec-importkey.c485
1 files changed, 485 insertions, 0 deletions
diff --git a/bin/dnssec/dnssec-importkey.c b/bin/dnssec/dnssec-importkey.c
new file mode 100644
index 0000000..8a776c1
--- /dev/null
+++ b/bin/dnssec/dnssec-importkey.c
@@ -0,0 +1,485 @@
+/*
+ * 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 <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/callbacks.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/ds.h>
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+
+#include <dst/dst.h>
+
+#if USE_PKCS11
+#include <pk11/result.h>
+#endif /* if USE_PKCS11 */
+
+#include "dnssectool.h"
+
+const char *program = "dnssec-importkey";
+
+static dns_rdataclass_t rdclass;
+static dns_fixedname_t fixed;
+static dns_name_t *name = NULL;
+static isc_mem_t *mctx = NULL;
+static bool setpub = false, setdel = false;
+static bool setttl = false;
+static isc_stdtime_t pub = 0, del = 0;
+static dns_ttl_t ttl = 0;
+static isc_stdtime_t syncadd = 0, syncdel = 0;
+static bool setsyncadd = false;
+static bool setsyncdel = false;
+
+static isc_result_t
+initname(char *setname) {
+ isc_result_t result;
+ isc_buffer_t buf;
+
+ name = dns_fixedname_initname(&fixed);
+
+ isc_buffer_init(&buf, setname, strlen(setname));
+ isc_buffer_add(&buf, strlen(setname));
+ result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
+ return (result);
+}
+
+static void
+db_load_from_stream(dns_db_t *db, FILE *fp) {
+ isc_result_t result;
+ dns_rdatacallbacks_t callbacks;
+
+ dns_rdatacallbacks_init(&callbacks);
+ result = dns_db_beginload(db, &callbacks);
+ if (result != ISC_R_SUCCESS) {
+ fatal("dns_db_beginload failed: %s", isc_result_totext(result));
+ }
+
+ result = dns_master_loadstream(fp, name, name, rdclass, 0, &callbacks,
+ mctx);
+ if (result != ISC_R_SUCCESS) {
+ fatal("can't load from input: %s", isc_result_totext(result));
+ }
+
+ result = dns_db_endload(db, &callbacks);
+ if (result != ISC_R_SUCCESS) {
+ fatal("dns_db_endload failed: %s", isc_result_totext(result));
+ }
+}
+
+static isc_result_t
+loadset(const char *filename, dns_rdataset_t *rdataset) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ char setname[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(name, setname, sizeof(setname));
+
+ result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0,
+ NULL, &db);
+ if (result != ISC_R_SUCCESS) {
+ fatal("can't create database");
+ }
+
+ if (strcmp(filename, "-") == 0) {
+ db_load_from_stream(db, stdin);
+ filename = "input";
+ } else {
+ result = dns_db_load(db, filename, dns_masterformat_text,
+ DNS_MASTER_NOTTL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) {
+ fatal("can't load %s: %s", filename,
+ isc_result_totext(result));
+ }
+ }
+
+ result = dns_db_findnode(db, name, false, &node);
+ if (result != ISC_R_SUCCESS) {
+ fatal("can't find %s node in %s", setname, filename);
+ }
+
+ result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, 0,
+ rdataset, NULL);
+
+ if (result == ISC_R_NOTFOUND) {
+ fatal("no DNSKEY RR for %s in %s", setname, filename);
+ } else if (result != ISC_R_SUCCESS) {
+ fatal("dns_db_findrdataset");
+ }
+
+ if (node != NULL) {
+ dns_db_detachnode(db, &node);
+ }
+ if (db != NULL) {
+ dns_db_detach(&db);
+ }
+ return (result);
+}
+
+static void
+loadkey(char *filename, unsigned char *key_buf, unsigned int key_buf_size,
+ dns_rdata_t *rdata) {
+ isc_result_t result;
+ dst_key_t *key = NULL;
+ isc_buffer_t keyb;
+ isc_region_t r;
+
+ dns_rdata_init(rdata);
+
+ isc_buffer_init(&keyb, key_buf, key_buf_size);
+
+ result = dst_key_fromnamedfile(filename, NULL, DST_TYPE_PUBLIC, mctx,
+ &key);
+ if (result != ISC_R_SUCCESS) {
+ fatal("invalid keyfile name %s: %s", filename,
+ isc_result_totext(result));
+ }
+
+ if (verbose > 2) {
+ char keystr[DST_KEY_FORMATSIZE];
+
+ dst_key_format(key, keystr, sizeof(keystr));
+ fprintf(stderr, "%s: %s\n", program, keystr);
+ }
+
+ result = dst_key_todns(key, &keyb);
+ if (result != ISC_R_SUCCESS) {
+ fatal("can't decode key");
+ }
+
+ isc_buffer_usedregion(&keyb, &r);
+ dns_rdata_fromregion(rdata, dst_key_class(key), dns_rdatatype_dnskey,
+ &r);
+
+ rdclass = dst_key_class(key);
+
+ name = dns_fixedname_initname(&fixed);
+ dns_name_copynf(dst_key_name(key), name);
+
+ dst_key_free(&key);
+}
+
+static void
+emit(const char *dir, dns_rdata_t *rdata) {
+ isc_result_t result;
+ char keystr[DST_KEY_FORMATSIZE];
+ char pubname[1024];
+ char priname[1024];
+ isc_buffer_t buf;
+ dst_key_t *key = NULL, *tmp = NULL;
+
+ isc_buffer_init(&buf, rdata->data, rdata->length);
+ isc_buffer_add(&buf, rdata->length);
+ result = dst_key_fromdns(name, rdclass, &buf, mctx, &key);
+ if (result != ISC_R_SUCCESS) {
+ fatal("dst_key_fromdns: %s", isc_result_totext(result));
+ }
+
+ isc_buffer_init(&buf, pubname, sizeof(pubname));
+ result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Failed to build public key filename: %s",
+ isc_result_totext(result));
+ }
+ isc_buffer_init(&buf, priname, sizeof(priname));
+ result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Failed to build private key filename: %s",
+ isc_result_totext(result));
+ }
+
+ result = dst_key_fromfile(
+ dst_key_name(key), dst_key_id(key), dst_key_alg(key),
+ DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, dir, mctx, &tmp);
+ if (result == ISC_R_SUCCESS) {
+ if (dst_key_isprivate(tmp) && !dst_key_isexternal(tmp)) {
+ fatal("Private key already exists in %s", priname);
+ }
+ dst_key_free(&tmp);
+ }
+
+ dst_key_setexternal(key, true);
+ if (setpub) {
+ dst_key_settime(key, DST_TIME_PUBLISH, pub);
+ }
+ if (setdel) {
+ dst_key_settime(key, DST_TIME_DELETE, del);
+ }
+ if (setsyncadd) {
+ dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncadd);
+ }
+ if (setsyncdel) {
+ dst_key_settime(key, DST_TIME_SYNCDELETE, syncdel);
+ }
+
+ if (setttl) {
+ dst_key_setttl(key, ttl);
+ }
+
+ result = dst_key_tofile(key, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, dir);
+ if (result != ISC_R_SUCCESS) {
+ dst_key_format(key, keystr, sizeof(keystr));
+ fatal("Failed to write key %s: %s", keystr,
+ isc_result_totext(result));
+ }
+ printf("%s\n", pubname);
+
+ isc_buffer_clear(&buf);
+ result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Failed to build private key filename: %s",
+ isc_result_totext(result));
+ }
+ printf("%s\n", priname);
+ dst_key_free(&key);
+}
+
+ISC_PLATFORM_NORETURN_PRE static void
+usage(void) ISC_PLATFORM_NORETURN_POST;
+
+static void
+usage(void) {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, " %s options [-K dir] keyfile\n\n", program);
+ fprintf(stderr, " %s options -f file [keyname]\n\n", program);
+ fprintf(stderr, "Version: %s\n", VERSION);
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -f file: read key from zone file\n");
+ fprintf(stderr, " -K <directory>: directory in which to store "
+ "the key files\n");
+ fprintf(stderr, " -L ttl: set default key TTL\n");
+ fprintf(stderr, " -v <verbose level>\n");
+ fprintf(stderr, " -V: print version information\n");
+ fprintf(stderr, " -h: print usage and exit\n");
+ fprintf(stderr, "Timing options:\n");
+ fprintf(stderr, " -P date/[+-]offset/none: set/unset key "
+ "publication date\n");
+ fprintf(stderr, " -P sync date/[+-]offset/none: set/unset "
+ "CDS and CDNSKEY publication date\n");
+ fprintf(stderr, " -D date/[+-]offset/none: set/unset key "
+ "deletion date\n");
+ fprintf(stderr, " -D sync date/[+-]offset/none: set/unset "
+ "CDS and CDNSKEY deletion date\n");
+
+ exit(-1);
+}
+
+int
+main(int argc, char **argv) {
+ char *classname = NULL;
+ char *filename = NULL, *dir = NULL, *namestr;
+ char *endp;
+ int ch;
+ isc_result_t result;
+ isc_log_t *log = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata;
+ isc_stdtime_t now;
+
+ dns_rdata_init(&rdata);
+ isc_stdtime_get(&now);
+
+ if (argc == 1) {
+ usage();
+ }
+
+ isc_mem_create(&mctx);
+
+#if USE_PKCS11
+ pk11_result_register();
+#endif /* if USE_PKCS11 */
+ dns_result_register();
+
+ isc_commandline_errprint = false;
+
+#define CMDLINE_FLAGS "D:f:hK:L:P:v:V"
+ while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
+ switch (ch) {
+ case 'D':
+ /* -Dsync ? */
+ if (isoptarg("sync", argv, usage)) {
+ if (setsyncdel) {
+ fatal("-D sync specified more than "
+ "once");
+ }
+
+ syncdel = strtotime(isc_commandline_argument,
+ now, now, &setsyncdel);
+ break;
+ }
+ /* -Ddnskey ? */
+ (void)isoptarg("dnskey", argv, usage);
+ if (setdel) {
+ fatal("-D specified more than once");
+ }
+
+ del = strtotime(isc_commandline_argument, now, now,
+ &setdel);
+ break;
+ case 'K':
+ dir = isc_commandline_argument;
+ if (strlen(dir) == 0U) {
+ fatal("directory must be non-empty string");
+ }
+ break;
+ case 'L':
+ ttl = strtottl(isc_commandline_argument);
+ setttl = true;
+ break;
+ case 'P':
+ /* -Psync ? */
+ if (isoptarg("sync", argv, usage)) {
+ if (setsyncadd) {
+ fatal("-P sync specified more than "
+ "once");
+ }
+
+ syncadd = strtotime(isc_commandline_argument,
+ now, now, &setsyncadd);
+ break;
+ }
+ /* -Pdnskey ? */
+ (void)isoptarg("dnskey", argv, usage);
+ if (setpub) {
+ fatal("-P specified more than once");
+ }
+
+ pub = strtotime(isc_commandline_argument, now, now,
+ &setpub);
+ break;
+ case 'f':
+ filename = isc_commandline_argument;
+ break;
+ case 'v':
+ verbose = strtol(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0') {
+ fatal("-v must be followed by a number");
+ }
+ break;
+ case '?':
+ if (isc_commandline_option != '?') {
+ fprintf(stderr, "%s: invalid argument -%c\n",
+ program, isc_commandline_option);
+ }
+ FALLTHROUGH;
+ case 'h':
+ /* Does not return. */
+ usage();
+
+ case 'V':
+ /* Does not return. */
+ version(program);
+
+ default:
+ fprintf(stderr, "%s: unhandled option -%c\n", program,
+ isc_commandline_option);
+ exit(1);
+ }
+ }
+
+ rdclass = strtoclass(classname);
+
+ if (argc < isc_commandline_index + 1 && filename == NULL) {
+ fatal("the key file name was not specified");
+ }
+ if (argc > isc_commandline_index + 1) {
+ fatal("extraneous arguments");
+ }
+
+ result = dst_lib_init(mctx, NULL);
+ if (result != ISC_R_SUCCESS) {
+ fatal("could not initialize dst: %s",
+ isc_result_totext(result));
+ }
+
+ setup_logging(mctx, &log);
+
+ dns_rdataset_init(&rdataset);
+
+ if (filename != NULL) {
+ if (argc < isc_commandline_index + 1) {
+ /* using filename as zone name */
+ namestr = filename;
+ } else {
+ namestr = argv[isc_commandline_index];
+ }
+
+ result = initname(namestr);
+ if (result != ISC_R_SUCCESS) {
+ fatal("could not initialize name %s", namestr);
+ }
+
+ result = loadset(filename, &rdataset);
+
+ if (result != ISC_R_SUCCESS) {
+ fatal("could not load DNSKEY set: %s\n",
+ isc_result_totext(result));
+ }
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(&rdataset, &rdata);
+ emit(dir, &rdata);
+ }
+ } else {
+ unsigned char key_buf[DST_KEY_MAXSIZE];
+
+ loadkey(argv[isc_commandline_index], key_buf, DST_KEY_MAXSIZE,
+ &rdata);
+
+ emit(dir, &rdata);
+ }
+
+ if (dns_rdataset_isassociated(&rdataset)) {
+ dns_rdataset_disassociate(&rdataset);
+ }
+ cleanup_logging(&log);
+ dst_lib_destroy();
+ if (verbose > 10) {
+ isc_mem_stats(mctx, stdout);
+ }
+ isc_mem_destroy(&mctx);
+
+ fflush(stdout);
+ if (ferror(stdout)) {
+ fprintf(stderr, "write error\n");
+ return (1);
+ } else {
+ return (0);
+ }
+}