summaryrefslogtreecommitdiffstats
path: root/bin/dnssec
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 07:24:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 07:24:22 +0000
commit45d6379135504814ab723b57f0eb8be23393a51d (patch)
treed4f2ec4acca824a8446387a758b0ce4238a4dffa /bin/dnssec
parentInitial commit. (diff)
downloadbind9-45d6379135504814ab723b57f0eb8be23393a51d.tar.xz
bind9-45d6379135504814ab723b57f0eb8be23393a51d.zip
Adding upstream version 1:9.16.44.upstream/1%9.16.44
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--bin/dnssec/Makefile.in112
-rw-r--r--bin/dnssec/dnssec-cds.c1314
-rw-r--r--bin/dnssec/dnssec-cds.rst203
-rw-r--r--bin/dnssec/dnssec-dsfromkey.c568
-rw-r--r--bin/dnssec/dnssec-dsfromkey.rst144
-rw-r--r--bin/dnssec/dnssec-importkey.c485
-rw-r--r--bin/dnssec/dnssec-importkey.rst113
-rw-r--r--bin/dnssec/dnssec-keyfromlabel.c782
-rw-r--r--bin/dnssec/dnssec-keyfromlabel.rst262
-rw-r--r--bin/dnssec/dnssec-keygen.c1315
-rw-r--r--bin/dnssec/dnssec-keygen.rst318
-rw-r--r--bin/dnssec/dnssec-revoke.c278
-rw-r--r--bin/dnssec/dnssec-revoke.rst71
-rw-r--r--bin/dnssec/dnssec-settime.c985
-rw-r--r--bin/dnssec/dnssec-settime.rst231
-rw-r--r--bin/dnssec/dnssec-signzone.c4197
-rw-r--r--bin/dnssec/dnssec-signzone.rst396
-rw-r--r--bin/dnssec/dnssec-verify.c366
-rw-r--r--bin/dnssec/dnssec-verify.rst98
-rw-r--r--bin/dnssec/dnssectool.c594
-rw-r--r--bin/dnssec/dnssectool.h119
-rw-r--r--bin/dnssec/win32/cds.vcxproj.filters.in18
-rw-r--r--bin/dnssec/win32/cds.vcxproj.in121
-rw-r--r--bin/dnssec/win32/cds.vcxproj.user3
-rw-r--r--bin/dnssec/win32/dnssectool.vcxproj.filters.in27
-rw-r--r--bin/dnssec/win32/dnssectool.vcxproj.in118
-rw-r--r--bin/dnssec/win32/dnssectool.vcxproj.user3
-rw-r--r--bin/dnssec/win32/dsfromkey.vcxproj.filters.in18
-rw-r--r--bin/dnssec/win32/dsfromkey.vcxproj.in147
-rw-r--r--bin/dnssec/win32/dsfromkey.vcxproj.user3
-rw-r--r--bin/dnssec/win32/importkey.vcxproj.filters.in18
-rw-r--r--bin/dnssec/win32/importkey.vcxproj.in121
-rw-r--r--bin/dnssec/win32/importkey.vcxproj.user3
-rw-r--r--bin/dnssec/win32/keyfromlabel.vcxproj.filters.in18
-rw-r--r--bin/dnssec/win32/keyfromlabel.vcxproj.in121
-rw-r--r--bin/dnssec/win32/keyfromlabel.vcxproj.user3
-rw-r--r--bin/dnssec/win32/keygen.vcxproj.filters.in18
-rw-r--r--bin/dnssec/win32/keygen.vcxproj.in121
-rw-r--r--bin/dnssec/win32/keygen.vcxproj.user3
-rw-r--r--bin/dnssec/win32/revoke.vcxproj.filters.in18
-rw-r--r--bin/dnssec/win32/revoke.vcxproj.in121
-rw-r--r--bin/dnssec/win32/revoke.vcxproj.user3
-rw-r--r--bin/dnssec/win32/settime.vcxproj.filters.in18
-rw-r--r--bin/dnssec/win32/settime.vcxproj.in121
-rw-r--r--bin/dnssec/win32/settime.vcxproj.user3
-rw-r--r--bin/dnssec/win32/signzone.vcxproj.filters.in18
-rw-r--r--bin/dnssec/win32/signzone.vcxproj.in121
-rw-r--r--bin/dnssec/win32/signzone.vcxproj.user3
-rw-r--r--bin/dnssec/win32/verify.vcxproj.filters.in18
-rw-r--r--bin/dnssec/win32/verify.vcxproj.in121
-rw-r--r--bin/dnssec/win32/verify.vcxproj.user3
51 files changed, 14403 insertions, 0 deletions
diff --git a/bin/dnssec/Makefile.in b/bin/dnssec/Makefile.in
new file mode 100644
index 0000000..f49f775
--- /dev/null
+++ b/bin/dnssec/Makefile.in
@@ -0,0 +1,112 @@
+# 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.
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+VERSION=@BIND9_VERSION@
+
+@BIND9_MAKE_INCLUDES@
+
+CINCLUDES = ${DNS_INCLUDES} ${ISC_INCLUDES} ${ISCCFG_INCLUDES} \
+ ${OPENSSL_CFLAGS}
+
+CDEFINES = -DVERSION=\"${VERSION}\" -DNAMED_CONFFILE=\"${sysconfdir}/named.conf\"
+CWARNINGS =
+
+DNSLIBS = ../../lib/dns/libdns.@A@ @NO_LIBTOOL_DNSLIBS@
+ISCCFGLIBS = ../../lib/isccfg/libisccfg.@A@
+ISCLIBS = ../../lib/isc/libisc.@A@ @NO_LIBTOOL_ISCLIBS@
+ISCNOSYMLIBS = ../../lib/isc/libisc-nosymtbl.@A@ @NO_LIBTOOL_ISCLIBS@
+
+DNSDEPLIBS = ../../lib/dns/libdns.@A@
+ISCDEPLIBS = ../../lib/isc/libisc.@A@
+ISCCFGDEPLIBS = ../../lib/isccfg/libisccfg.@A@
+
+DEPLIBS = ${DNSDEPLIBS} ${ISCCFGDEPLIBS} ${ISCDEPLIBS}
+
+LIBS = ${DNSLIBS} ${ISCCFGLIBS} ${ISCLIBS} @LIBS@
+
+NOSYMLIBS = ${DNSLIBS} ${ISCCFGLIBS} ${ISCNOSYMLIBS} @LIBS@
+
+# Alphabetically
+TARGETS = dnssec-cds@EXEEXT@ dnssec-dsfromkey@EXEEXT@ \
+ dnssec-importkey@EXEEXT@ dnssec-keyfromlabel@EXEEXT@ \
+ dnssec-keygen@EXEEXT@ dnssec-revoke@EXEEXT@ \
+ dnssec-settime@EXEEXT@ dnssec-signzone@EXEEXT@ \
+ dnssec-verify@EXEEXT@
+
+OBJS = dnssectool.@O@
+
+SRCS = dnssec-cds.c dnssec-dsfromkey.c dnssec-importkey.c \
+ dnssec-keyfromlabel.c dnssec-keygen.c dnssec-revoke.c \
+ dnssec-settime.c dnssec-signzone.c dnssec-verify.c \
+ dnssectool.c
+
+@BIND9_MAKE_RULES@
+
+dnssec-cds@EXEEXT@: dnssec-cds.@O@ ${OBJS} ${DEPLIBS}
+ export BASEOBJS="dnssec-cds.@O@ ${OBJS}"; \
+ ${FINALBUILDCMD}
+
+dnssec-dsfromkey@EXEEXT@: dnssec-dsfromkey.@O@ ${OBJS} ${DEPLIBS}
+ export BASEOBJS="dnssec-dsfromkey.@O@ ${OBJS}"; \
+ ${FINALBUILDCMD}
+
+dnssec-keyfromlabel@EXEEXT@: dnssec-keyfromlabel.@O@ ${OBJS} ${DEPLIBS}
+ export BASEOBJS="dnssec-keyfromlabel.@O@ ${OBJS}"; \
+ ${FINALBUILDCMD}
+
+dnssec-keygen@EXEEXT@: dnssec-keygen.@O@ ${OBJS} ${DEPLIBS}
+ export BASEOBJS="dnssec-keygen.@O@ ${OBJS}"; \
+ ${FINALBUILDCMD}
+
+dnssec-signzone.@O@: dnssec-signzone.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -DVERSION=\"${VERSION}\" \
+ -c ${srcdir}/dnssec-signzone.c
+
+dnssec-signzone@EXEEXT@: dnssec-signzone.@O@ ${OBJS} ${DEPLIBS}
+ export BASEOBJS="dnssec-signzone.@O@ ${OBJS}"; \
+ ${FINALBUILDCMD}
+
+dnssec-verify.@O@: dnssec-verify.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -DVERSION=\"${VERSION}\" \
+ -c ${srcdir}/dnssec-verify.c
+
+dnssec-verify@EXEEXT@: dnssec-verify.@O@ ${OBJS} ${DEPLIBS}
+ export BASEOBJS="dnssec-verify.@O@ ${OBJS}"; \
+ ${FINALBUILDCMD}
+
+dnssec-revoke@EXEEXT@: dnssec-revoke.@O@ ${OBJS} ${DEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ dnssec-revoke.@O@ ${OBJS} ${LIBS}
+
+dnssec-settime@EXEEXT@: dnssec-settime.@O@ ${OBJS} ${DEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ dnssec-settime.@O@ ${OBJS} ${LIBS}
+
+dnssec-importkey@EXEEXT@: dnssec-importkey.@O@ ${OBJS} ${DEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ dnssec-importkey.@O@ ${OBJS} ${LIBS}
+
+installdirs:
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${sbindir}
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man8
+
+install:: ${TARGETS} installdirs
+ for t in ${TARGETS}; do ${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} $$t ${DESTDIR}${sbindir} || exit 1; done
+
+uninstall::
+ for t in ${TARGETS}; do ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${sbindir}/$$t || exit 1; done
+
+clean distclean::
+ rm -f ${TARGETS}
diff --git a/bin/dnssec/dnssec-cds.c b/bin/dnssec/dnssec-cds.c
new file mode 100644
index 0000000..15f9f4e
--- /dev/null
+++ b/bin/dnssec/dnssec-cds.c
@@ -0,0 +1,1314 @@
+/*
+ * 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.
+ */
+
+/*
+ * Written by Tony Finch <dot@dotat.at> <fanf2@cam.ac.uk>
+ * at Cambridge University Information Services
+ */
+
+/*! \file */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/serial.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/callbacks.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/dnssec.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/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/time.h>
+
+#include <dst/dst.h>
+
+#if USE_PKCS11
+#include <pk11/result.h>
+#endif /* if USE_PKCS11 */
+
+#include "dnssectool.h"
+
+const char *program = "dnssec-cds";
+
+/*
+ * Infrastructure
+ */
+static isc_log_t *lctx = NULL;
+static isc_mem_t *mctx = NULL;
+
+/*
+ * The domain we are working on
+ */
+static const char *namestr = NULL;
+static dns_fixedname_t fixed;
+static dns_name_t *name = NULL;
+static dns_rdataclass_t rdclass = dns_rdataclass_in;
+
+static const char *startstr = NULL; /* from which we derive notbefore */
+static isc_stdtime_t notbefore = 0; /* restrict sig inception times */
+static dns_rdata_rrsig_t oldestsig; /* for recording inception time */
+
+static int nkey; /* number of child zone DNSKEY records */
+
+/*
+ * The validation strategy of this program is top-down.
+ *
+ * We start with an implicitly trusted authoritative dsset.
+ *
+ * The child DNSKEY RRset is scanned to find out which keys are
+ * authenticated by DS records, and the result is recorded in a key
+ * table as described later in this comment.
+ *
+ * The key table is used up to three times to verify the signatures on
+ * the child DNSKEY, CDNSKEY, and CDS RRsets. In this program, only keys
+ * that have matching DS records are used for validating signatures.
+ *
+ * For replay attack protection, signatures are ignored if their inception
+ * time is before the previously recorded inception time. We use the earliest
+ * signature so that another run of dnssec-cds with the same records will
+ * still accept all the signatures.
+ *
+ * A key table is an array of nkey keyinfo structures, like
+ *
+ * keyinfo_t key_tbl[nkey];
+ *
+ * Each key is decoded into more useful representations, held in
+ * keyinfo->rdata
+ * keyinfo->dst
+ *
+ * If a key has no matching DS record then keyinfo->dst is NULL.
+ *
+ * The key algorithm and ID are saved in keyinfo->algo and
+ * keyinfo->tag for quicky skipping DS and RRSIG records that can't
+ * match.
+ */
+typedef struct keyinfo {
+ dns_rdata_t rdata;
+ dst_key_t *dst;
+ dns_secalg_t algo;
+ dns_keytag_t tag;
+} keyinfo_t;
+
+/* A replaceable function that can generate a DS RRset from some input */
+typedef isc_result_t
+ds_maker_func_t(dns_rdatalist_t *dslist, isc_buffer_t *buf, dns_rdata_t *rdata);
+
+static dns_rdataset_t cdnskey_set, cdnskey_sig;
+static dns_rdataset_t cds_set, cds_sig;
+static dns_rdataset_t dnskey_set, dnskey_sig;
+static dns_rdataset_t old_ds_set, new_ds_set;
+
+static keyinfo_t *old_key_tbl, *new_key_tbl;
+
+isc_buffer_t *new_ds_buf = NULL; /* backing store for new_ds_set */
+
+static void
+verbose_time(int level, const char *msg, isc_stdtime_t time) {
+ isc_result_t result;
+ isc_buffer_t timebuf;
+ char timestr[32];
+
+ if (verbose < level) {
+ return;
+ }
+
+ isc_buffer_init(&timebuf, timestr, sizeof(timestr));
+ result = dns_time64_totext(time, &timebuf);
+ check_result(result, "dns_time64_totext()");
+ isc_buffer_putuint8(&timebuf, 0);
+ if (verbose < 3) {
+ vbprintf(level, "%s %s\n", msg, timestr);
+ } else {
+ vbprintf(level, "%s %s (%" PRIu32 ")\n", msg, timestr, time);
+ }
+}
+
+static void
+initname(char *setname) {
+ isc_result_t result;
+ isc_buffer_t buf;
+
+ name = dns_fixedname_initname(&fixed);
+ namestr = setname;
+
+ isc_buffer_init(&buf, setname, strlen(setname));
+ isc_buffer_add(&buf, strlen(setname));
+ result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ fatal("could not initialize name %s", setname);
+ }
+}
+
+static void
+findset(dns_db_t *db, dns_dbnode_t *node, dns_rdatatype_t type,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) {
+ isc_result_t result;
+
+ dns_rdataset_init(rdataset);
+ if (sigrdataset != NULL) {
+ dns_rdataset_init(sigrdataset);
+ }
+ result = dns_db_findrdataset(db, node, NULL, type, 0, 0, rdataset,
+ sigrdataset);
+ if (result != ISC_R_NOTFOUND) {
+ check_result(result, "dns_db_findrdataset()");
+ }
+}
+
+static void
+freeset(dns_rdataset_t *rdataset) {
+ if (dns_rdataset_isassociated(rdataset)) {
+ dns_rdataset_disassociate(rdataset);
+ }
+}
+
+static void
+freelist(dns_rdataset_t *rdataset) {
+ dns_rdatalist_t *rdlist;
+ dns_rdata_t *rdata;
+
+ if (!dns_rdataset_isassociated(rdataset)) {
+ return;
+ }
+
+ dns_rdatalist_fromrdataset(rdataset, &rdlist);
+
+ for (rdata = ISC_LIST_HEAD(rdlist->rdata); rdata != NULL;
+ rdata = ISC_LIST_HEAD(rdlist->rdata))
+ {
+ ISC_LIST_UNLINK(rdlist->rdata, rdata, link);
+ isc_mem_put(mctx, rdata, sizeof(*rdata));
+ }
+ isc_mem_put(mctx, rdlist, sizeof(*rdlist));
+ dns_rdataset_disassociate(rdataset);
+}
+
+static void
+free_all_sets(void) {
+ freeset(&cdnskey_set);
+ freeset(&cdnskey_sig);
+ freeset(&cds_set);
+ freeset(&cds_sig);
+ freeset(&dnskey_set);
+ freeset(&dnskey_sig);
+ freeset(&old_ds_set);
+ freelist(&new_ds_set);
+ if (new_ds_buf != NULL) {
+ isc_buffer_free(&new_ds_buf);
+ }
+}
+
+static void
+load_db(const char *filename, dns_db_t **dbp, dns_dbnode_t **nodep) {
+ isc_result_t result;
+
+ result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0,
+ NULL, dbp);
+ check_result(result, "dns_db_create()");
+
+ result = dns_db_load(*dbp, filename, dns_masterformat_text,
+ DNS_MASTER_HINT);
+ if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) {
+ fatal("can't load %s: %s", filename, isc_result_totext(result));
+ }
+
+ result = dns_db_findnode(*dbp, name, false, nodep);
+ if (result != ISC_R_SUCCESS) {
+ fatal("can't find %s node in %s", namestr, filename);
+ }
+}
+
+static void
+free_db(dns_db_t **dbp, dns_dbnode_t **nodep) {
+ dns_db_detachnode(*dbp, nodep);
+ dns_db_detach(dbp);
+}
+
+static void
+load_child_sets(const char *file) {
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+
+ load_db(file, &db, &node);
+ findset(db, node, dns_rdatatype_dnskey, &dnskey_set, &dnskey_sig);
+ findset(db, node, dns_rdatatype_cdnskey, &cdnskey_set, &cdnskey_sig);
+ findset(db, node, dns_rdatatype_cds, &cds_set, &cds_sig);
+ free_db(&db, &node);
+}
+
+static void
+get_dsset_name(char *filename, size_t size, const char *path,
+ const char *suffix) {
+ isc_result_t result;
+ isc_buffer_t buf;
+ size_t len;
+
+ isc_buffer_init(&buf, filename, size);
+
+ len = strlen(path);
+
+ /* allow room for a trailing slash */
+ if (isc_buffer_availablelength(&buf) <= len) {
+ fatal("%s: pathname too long", path);
+ }
+ isc_buffer_putstr(&buf, path);
+
+ if (isc_file_isdirectory(path) == ISC_R_SUCCESS) {
+ const char *prefix = "dsset-";
+
+ if (path[len - 1] != '/') {
+ isc_buffer_putstr(&buf, "/");
+ }
+
+ if (isc_buffer_availablelength(&buf) < strlen(prefix)) {
+ fatal("%s: pathname too long", path);
+ }
+ isc_buffer_putstr(&buf, prefix);
+
+ result = dns_name_tofilenametext(name, false, &buf);
+ check_result(result, "dns_name_tofilenametext()");
+ if (isc_buffer_availablelength(&buf) == 0) {
+ fatal("%s: pathname too long", path);
+ }
+ }
+ /* allow room for a trailing nul */
+ if (isc_buffer_availablelength(&buf) <= strlen(suffix)) {
+ fatal("%s: pathname too long", path);
+ }
+ isc_buffer_putstr(&buf, suffix);
+ isc_buffer_putuint8(&buf, 0);
+}
+
+static void
+load_parent_set(const char *path) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ isc_time_t modtime;
+ char filename[PATH_MAX + 1];
+
+ get_dsset_name(filename, sizeof(filename), path, "");
+
+ result = isc_file_getmodtime(filename, &modtime);
+ if (result != ISC_R_SUCCESS) {
+ fatal("could not get modification time of %s: %s", filename,
+ isc_result_totext(result));
+ }
+ notbefore = isc_time_seconds(&modtime);
+ if (startstr != NULL) {
+ isc_stdtime_t now;
+ isc_stdtime_get(&now);
+ notbefore = strtotime(startstr, now, notbefore, NULL);
+ }
+ verbose_time(1, "child records must not be signed before", notbefore);
+
+ load_db(filename, &db, &node);
+ findset(db, node, dns_rdatatype_ds, &old_ds_set, NULL);
+
+ if (!dns_rdataset_isassociated(&old_ds_set)) {
+ fatal("could not find DS records for %s in %s", namestr,
+ filename);
+ }
+
+ free_db(&db, &node);
+}
+
+#define MAX_CDS_RDATA_TEXT_SIZE DNS_RDATA_MAXLENGTH * 2
+
+static isc_buffer_t *
+formatset(dns_rdataset_t *rdataset) {
+ isc_result_t result;
+ isc_buffer_t *buf = NULL;
+ dns_master_style_t *style = NULL;
+ unsigned int styleflags;
+
+ styleflags = (rdataset->ttl == 0) ? DNS_STYLEFLAG_NO_TTL : 0;
+
+ /*
+ * This style is for consistency with the output of dnssec-dsfromkey
+ * which just separates fields with spaces. The huge tab stop width
+ * eliminates any tab characters.
+ */
+ result = dns_master_stylecreate(&style, styleflags, 0, 0, 0, 0, 0,
+ 1000000, 0, mctx);
+ check_result(result, "dns_master_stylecreate2 failed");
+
+ isc_buffer_allocate(mctx, &buf, MAX_CDS_RDATA_TEXT_SIZE);
+ result = dns_master_rdatasettotext(name, rdataset, style, NULL, buf);
+
+ if ((result == ISC_R_SUCCESS) && isc_buffer_availablelength(buf) < 1) {
+ result = ISC_R_NOSPACE;
+ }
+
+ check_result(result, "dns_rdataset_totext()");
+
+ isc_buffer_putuint8(buf, 0);
+
+ dns_master_styledestroy(&style, mctx);
+
+ return (buf);
+}
+
+static void
+write_parent_set(const char *path, const char *inplace, bool nsupdate,
+ dns_rdataset_t *rdataset) {
+ isc_result_t result;
+ isc_buffer_t *buf = NULL;
+ isc_region_t r;
+ isc_time_t filetime;
+ char backname[PATH_MAX + 1];
+ char filename[PATH_MAX + 1];
+ char tmpname[PATH_MAX + 1];
+ FILE *fp = NULL;
+
+ if (nsupdate && inplace == NULL) {
+ return;
+ }
+
+ buf = formatset(rdataset);
+ isc_buffer_usedregion(buf, &r);
+
+ /*
+ * Try to ensure a write error doesn't make a zone go insecure!
+ */
+ if (inplace == NULL) {
+ printf("%s", (char *)r.base);
+ isc_buffer_free(&buf);
+ if (fflush(stdout) == EOF) {
+ fatal("error writing to stdout: %s", strerror(errno));
+ }
+ return;
+ }
+
+ if (inplace[0] != '\0') {
+ get_dsset_name(backname, sizeof(backname), path, inplace);
+ }
+ get_dsset_name(filename, sizeof(filename), path, "");
+ get_dsset_name(tmpname, sizeof(tmpname), path, "-XXXXXXXXXX");
+
+ result = isc_file_openunique(tmpname, &fp);
+ if (result != ISC_R_SUCCESS) {
+ fatal("open %s: %s", tmpname, isc_result_totext(result));
+ }
+ fprintf(fp, "%s", (char *)r.base);
+ isc_buffer_free(&buf);
+ if (fclose(fp) == EOF) {
+ int err = errno;
+ isc_file_remove(tmpname);
+ fatal("error writing to %s: %s", tmpname, strerror(err));
+ }
+
+ isc_time_set(&filetime, oldestsig.timesigned, 0);
+ result = isc_file_settime(tmpname, &filetime);
+ if (result != ISC_R_SUCCESS) {
+ isc_file_remove(tmpname);
+ fatal("can't set modification time of %s: %s", tmpname,
+ isc_result_totext(result));
+ }
+
+ if (inplace[0] != '\0') {
+ isc_file_rename(filename, backname);
+ }
+ isc_file_rename(tmpname, filename);
+}
+
+typedef enum { LOOSE, TIGHT } strictness_t;
+
+/*
+ * Find out if any (C)DS record matches a particular (C)DNSKEY.
+ */
+static bool
+match_key_dsset(keyinfo_t *ki, dns_rdataset_t *dsset, strictness_t strictness) {
+ isc_result_t result;
+ unsigned char dsbuf[DNS_DS_BUFFERSIZE];
+
+ for (result = dns_rdataset_first(dsset); result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(dsset))
+ {
+ dns_rdata_ds_t ds;
+ dns_rdata_t dsrdata = DNS_RDATA_INIT;
+ dns_rdata_t newdsrdata = DNS_RDATA_INIT;
+ bool c;
+
+ dns_rdataset_current(dsset, &dsrdata);
+ result = dns_rdata_tostruct(&dsrdata, &ds, NULL);
+ check_result(result, "dns_rdata_tostruct(DS)");
+
+ if (ki->tag != ds.key_tag || ki->algo != ds.algorithm) {
+ continue;
+ }
+
+ result = dns_ds_buildrdata(name, &ki->rdata, ds.digest_type,
+ dsbuf, &newdsrdata);
+ if (result != ISC_R_SUCCESS) {
+ vbprintf(3,
+ "dns_ds_buildrdata("
+ "keytag=%d, algo=%d, digest=%d): %s\n",
+ ds.key_tag, ds.algorithm, ds.digest_type,
+ dns_result_totext(result));
+ continue;
+ }
+ /* allow for both DS and CDS */
+ c = dsrdata.type != dns_rdatatype_ds;
+ dsrdata.type = dns_rdatatype_ds;
+ if (dns_rdata_compare(&dsrdata, &newdsrdata) == 0) {
+ vbprintf(1, "found matching %s %d %d %d\n",
+ c ? "CDS" : "DS", ds.key_tag, ds.algorithm,
+ ds.digest_type);
+ return (true);
+ } else if (strictness == TIGHT) {
+ vbprintf(0,
+ "key does not match %s %d %d %d "
+ "when it looks like it should\n",
+ c ? "CDS" : "DS", ds.key_tag, ds.algorithm,
+ ds.digest_type);
+ return (false);
+ }
+ }
+
+ vbprintf(1, "no matching %s for %s %d %d\n",
+ dsset->type == dns_rdatatype_cds ? "CDS" : "DS",
+ ki->rdata.type == dns_rdatatype_cdnskey ? "CDNSKEY" : "DNSKEY",
+ ki->tag, ki->algo);
+
+ return (false);
+}
+
+/*
+ * Find which (C)DNSKEY records match a (C)DS RRset.
+ * This creates a keyinfo_t key_tbl[nkey] array.
+ */
+static keyinfo_t *
+match_keyset_dsset(dns_rdataset_t *keyset, dns_rdataset_t *dsset,
+ strictness_t strictness) {
+ isc_result_t result;
+ keyinfo_t *keytable;
+ int i;
+
+ nkey = dns_rdataset_count(keyset);
+
+ keytable = isc_mem_get(mctx, sizeof(keyinfo_t) * nkey);
+
+ for (result = dns_rdataset_first(keyset), i = 0;
+ result == ISC_R_SUCCESS; result = dns_rdataset_next(keyset), i++)
+ {
+ keyinfo_t *ki;
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_t *keyrdata;
+ isc_region_t r;
+
+ INSIST(i < nkey);
+ ki = &keytable[i];
+ keyrdata = &ki->rdata;
+
+ dns_rdata_init(keyrdata);
+ dns_rdataset_current(keyset, keyrdata);
+
+ result = dns_rdata_tostruct(keyrdata, &dnskey, NULL);
+ check_result(result, "dns_rdata_tostruct(DNSKEY)");
+ ki->algo = dnskey.algorithm;
+
+ dns_rdata_toregion(keyrdata, &r);
+ ki->tag = dst_region_computeid(&r);
+
+ ki->dst = NULL;
+ if (!match_key_dsset(ki, dsset, strictness)) {
+ continue;
+ }
+
+ result = dns_dnssec_keyfromrdata(name, keyrdata, mctx,
+ &ki->dst);
+ if (result != ISC_R_SUCCESS) {
+ vbprintf(3,
+ "dns_dnssec_keyfromrdata("
+ "keytag=%d, algo=%d): %s\n",
+ ki->tag, ki->algo, dns_result_totext(result));
+ }
+ }
+
+ return (keytable);
+}
+
+static void
+free_keytable(keyinfo_t **keytable_p) {
+ keyinfo_t *keytable = *keytable_p;
+ *keytable_p = NULL;
+ keyinfo_t *ki;
+ int i;
+
+ for (i = 0; i < nkey; i++) {
+ ki = &keytable[i];
+ if (ki->dst != NULL) {
+ dst_key_free(&ki->dst);
+ }
+ }
+
+ isc_mem_put(mctx, keytable, sizeof(keyinfo_t) * nkey);
+}
+
+/*
+ * Find out which keys have signed an RRset. Keys that do not match a
+ * DS record are skipped.
+ *
+ * The return value is an array with nkey elements, one for each key,
+ * either zero if the key was skipped or did not sign the RRset, or
+ * otherwise the key algorithm. This is used by the signature coverage
+ * check functions below.
+ */
+static dns_secalg_t *
+matching_sigs(keyinfo_t *keytbl, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigset) {
+ isc_result_t result;
+ dns_secalg_t *algo;
+ int i;
+
+ algo = isc_mem_get(mctx, nkey);
+ memset(algo, 0, nkey);
+
+ for (result = dns_rdataset_first(sigset); result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(sigset))
+ {
+ dns_rdata_t sigrdata = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t sig;
+
+ dns_rdataset_current(sigset, &sigrdata);
+ result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
+ check_result(result, "dns_rdata_tostruct(RRSIG)");
+
+ /*
+ * Replay attack protection: check against current age limit
+ */
+ if (isc_serial_lt(sig.timesigned, notbefore)) {
+ vbprintf(1, "skip RRSIG by key %d: too old\n",
+ sig.keyid);
+ continue;
+ }
+
+ for (i = 0; i < nkey; i++) {
+ keyinfo_t *ki = &keytbl[i];
+ if (sig.keyid != ki->tag || sig.algorithm != ki->algo ||
+ !dns_name_equal(&sig.signer, name))
+ {
+ continue;
+ }
+ if (ki->dst == NULL) {
+ vbprintf(1,
+ "skip RRSIG by key %d:"
+ " no matching (C)DS\n",
+ sig.keyid);
+ continue;
+ }
+
+ result = dns_dnssec_verify(name, rdataset, ki->dst,
+ false, 0, mctx, &sigrdata,
+ NULL);
+
+ if (result != ISC_R_SUCCESS &&
+ result != DNS_R_FROMWILDCARD)
+ {
+ vbprintf(1,
+ "skip RRSIG by key %d:"
+ " verification failed: %s\n",
+ sig.keyid, isc_result_totext(result));
+ continue;
+ }
+
+ vbprintf(1, "found RRSIG by key %d\n", ki->tag);
+ algo[i] = sig.algorithm;
+
+ /*
+ * Replay attack protection: work out next age limit,
+ * only after the signature has been verified
+ */
+ if (oldestsig.timesigned == 0 ||
+ isc_serial_lt(sig.timesigned, oldestsig.timesigned))
+ {
+ verbose_time(2, "this is the oldest so far",
+ sig.timesigned);
+ oldestsig = sig;
+ }
+ }
+ }
+
+ return (algo);
+}
+
+/*
+ * Consume the result of matching_sigs(). When checking records
+ * fetched from the child zone, any working signature is enough.
+ */
+static bool
+signed_loose(dns_secalg_t *algo) {
+ bool ok = false;
+ int i;
+ for (i = 0; i < nkey; i++) {
+ if (algo[i] != 0) {
+ ok = true;
+ }
+ }
+ isc_mem_put(mctx, algo, nkey);
+ return (ok);
+}
+
+/*
+ * Consume the result of matching_sigs(). To ensure that the new DS
+ * RRset does not break the chain of trust to the DNSKEY RRset, every
+ * key algorithm in the DS RRset must have a signature in the DNSKEY
+ * RRset.
+ */
+static bool
+signed_strict(dns_rdataset_t *dsset, dns_secalg_t *algo) {
+ isc_result_t result;
+ bool all_ok = true;
+
+ for (result = dns_rdataset_first(dsset); result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(dsset))
+ {
+ dns_rdata_t dsrdata = DNS_RDATA_INIT;
+ dns_rdata_ds_t ds;
+ bool ds_ok;
+ int i;
+
+ dns_rdataset_current(dsset, &dsrdata);
+ result = dns_rdata_tostruct(&dsrdata, &ds, NULL);
+ check_result(result, "dns_rdata_tostruct(DS)");
+
+ ds_ok = false;
+ for (i = 0; i < nkey; i++) {
+ if (algo[i] == ds.algorithm) {
+ ds_ok = true;
+ }
+ }
+ if (!ds_ok) {
+ vbprintf(0,
+ "missing signature for algorithm %d "
+ "(key %d)\n",
+ ds.algorithm, ds.key_tag);
+ all_ok = false;
+ }
+ }
+
+ isc_mem_put(mctx, algo, nkey);
+ return (all_ok);
+}
+
+static dns_rdata_t *
+rdata_get(void) {
+ dns_rdata_t *rdata;
+
+ rdata = isc_mem_get(mctx, sizeof(*rdata));
+ dns_rdata_init(rdata);
+
+ return (rdata);
+}
+
+static isc_result_t
+rdata_put(isc_result_t result, dns_rdatalist_t *rdlist, dns_rdata_t *rdata) {
+ if (result == ISC_R_SUCCESS) {
+ ISC_LIST_APPEND(rdlist->rdata, rdata, link);
+ } else {
+ isc_mem_put(mctx, rdata, sizeof(*rdata));
+ }
+
+ return (result);
+}
+
+/*
+ * This basically copies the rdata into the buffer, but going via the
+ * unpacked struct has the side-effect of changing the rdatatype. The
+ * dns_rdata_cds_t and dns_rdata_ds_t types are aliases.
+ */
+static isc_result_t
+ds_from_cds(dns_rdatalist_t *dslist, isc_buffer_t *buf, dns_rdata_t *cds) {
+ isc_result_t result;
+ dns_rdata_ds_t ds;
+ dns_rdata_t *rdata;
+
+ REQUIRE(buf != NULL);
+
+ rdata = rdata_get();
+
+ result = dns_rdata_tostruct(cds, &ds, NULL);
+ check_result(result, "dns_rdata_tostruct(CDS)");
+ ds.common.rdtype = dns_rdatatype_ds;
+
+ result = dns_rdata_fromstruct(rdata, rdclass, dns_rdatatype_ds, &ds,
+ buf);
+
+ return (rdata_put(result, dslist, rdata));
+}
+
+static isc_result_t
+ds_from_cdnskey(dns_rdatalist_t *dslist, isc_buffer_t *buf,
+ dns_rdata_t *cdnskey) {
+ isc_result_t result;
+ unsigned i, n;
+
+ REQUIRE(buf != NULL);
+
+ n = sizeof(dtype) / sizeof(dtype[0]);
+ for (i = 0; i < n; i++) {
+ if (dtype[i] != 0) {
+ dns_rdata_t *rdata;
+ isc_region_t r;
+
+ isc_buffer_availableregion(buf, &r);
+ if (r.length < DNS_DS_BUFFERSIZE) {
+ return (ISC_R_NOSPACE);
+ }
+
+ rdata = rdata_get();
+ result = dns_ds_buildrdata(name, cdnskey, dtype[i],
+ r.base, rdata);
+ if (result == ISC_R_SUCCESS) {
+ isc_buffer_add(buf, DNS_DS_BUFFERSIZE);
+ }
+
+ result = rdata_put(result, dslist, rdata);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+make_new_ds_set(ds_maker_func_t *ds_from_rdata, uint32_t ttl,
+ dns_rdataset_t *rdset) {
+ unsigned int size = 16;
+ for (;;) {
+ isc_result_t result;
+ dns_rdatalist_t *dslist;
+
+ dslist = isc_mem_get(mctx, sizeof(*dslist));
+
+ dns_rdatalist_init(dslist);
+ dslist->rdclass = rdclass;
+ dslist->type = dns_rdatatype_ds;
+ dslist->ttl = ttl;
+
+ dns_rdataset_init(&new_ds_set);
+ result = dns_rdatalist_tordataset(dslist, &new_ds_set);
+ check_result(result, "dns_rdatalist_tordataset(dslist)");
+
+ isc_buffer_allocate(mctx, &new_ds_buf, size);
+
+ for (result = dns_rdataset_first(rdset);
+ result == ISC_R_SUCCESS; result = dns_rdataset_next(rdset))
+ {
+ isc_result_t tresult;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(rdset, &rdata);
+
+ tresult = ds_from_rdata(dslist, new_ds_buf, &rdata);
+ if (tresult == ISC_R_NOSPACE) {
+ vbprintf(20, "DS list buffer size %u\n", size);
+ freelist(&new_ds_set);
+ isc_buffer_free(&new_ds_buf);
+ size *= 2;
+ break;
+ }
+
+ check_result(tresult, "ds_from_rdata()");
+ }
+
+ if (result == ISC_R_NOMORE) {
+ break;
+ }
+ }
+}
+
+static int
+rdata_cmp(const void *rdata1, const void *rdata2) {
+ return (dns_rdata_compare((const dns_rdata_t *)rdata1,
+ (const dns_rdata_t *)rdata2));
+}
+
+/*
+ * Ensure that every key identified by the DS RRset has the same set of
+ * digest types.
+ */
+static bool
+consistent_digests(dns_rdataset_t *dsset) {
+ isc_result_t result;
+ dns_rdata_t *arrdata;
+ dns_rdata_ds_t *ds;
+ dns_keytag_t key_tag;
+ dns_secalg_t algorithm;
+ bool match;
+ int i, j, n, d;
+
+ /*
+ * First sort the dsset. DS rdata fields are tag, algorithm, digest,
+ * so sorting them brings together all the records for each key.
+ */
+
+ n = dns_rdataset_count(dsset);
+
+ arrdata = isc_mem_get(mctx, n * sizeof(dns_rdata_t));
+
+ for (result = dns_rdataset_first(dsset), i = 0; result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(dsset), i++)
+ {
+ dns_rdata_init(&arrdata[i]);
+ dns_rdataset_current(dsset, &arrdata[i]);
+ }
+
+ qsort(arrdata, n, sizeof(dns_rdata_t), rdata_cmp);
+
+ /*
+ * Convert sorted arrdata to more accessible format
+ */
+ ds = isc_mem_get(mctx, n * sizeof(dns_rdata_ds_t));
+
+ for (i = 0; i < n; i++) {
+ result = dns_rdata_tostruct(&arrdata[i], &ds[i], NULL);
+ check_result(result, "dns_rdata_tostruct(DS)");
+ }
+
+ /*
+ * Count number of digest types (d) for first key
+ */
+ key_tag = ds[0].key_tag;
+ algorithm = ds[0].algorithm;
+ for (d = 0, i = 0; i < n; i++, d++) {
+ if (ds[i].key_tag != key_tag || ds[i].algorithm != algorithm) {
+ break;
+ }
+ }
+
+ /*
+ * Check subsequent keys match the first one
+ */
+ match = true;
+ while (i < n) {
+ key_tag = ds[i].key_tag;
+ algorithm = ds[i].algorithm;
+ for (j = 0; j < d && i + j < n; j++) {
+ if (ds[i + j].key_tag != key_tag ||
+ ds[i + j].algorithm != algorithm ||
+ ds[i + j].digest_type != ds[j].digest_type)
+ {
+ match = false;
+ }
+ }
+ i += d;
+ }
+
+ /*
+ * Done!
+ */
+ isc_mem_put(mctx, ds, n * sizeof(dns_rdata_ds_t));
+ isc_mem_put(mctx, arrdata, n * sizeof(dns_rdata_t));
+
+ return (match);
+}
+
+static void
+print_diff(const char *cmd, dns_rdataset_t *rdataset) {
+ isc_buffer_t *buf;
+ isc_region_t r;
+ unsigned char *nl;
+ size_t len;
+
+ buf = formatset(rdataset);
+ isc_buffer_usedregion(buf, &r);
+
+ while ((nl = memchr(r.base, '\n', r.length)) != NULL) {
+ len = nl - r.base + 1;
+ printf("update %s %.*s", cmd, (int)len, (char *)r.base);
+ isc_region_consume(&r, len);
+ }
+
+ isc_buffer_free(&buf);
+}
+
+static void
+update_diff(const char *cmd, uint32_t ttl, dns_rdataset_t *addset,
+ dns_rdataset_t *delset) {
+ isc_result_t result;
+ dns_db_t *db;
+ dns_dbnode_t *node;
+ dns_dbversion_t *ver;
+ dns_rdataset_t diffset;
+ uint32_t save;
+
+ db = NULL;
+ result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0,
+ NULL, &db);
+ check_result(result, "dns_db_create()");
+
+ ver = NULL;
+ result = dns_db_newversion(db, &ver);
+ check_result(result, "dns_db_newversion()");
+
+ node = NULL;
+ result = dns_db_findnode(db, name, true, &node);
+ check_result(result, "dns_db_findnode()");
+
+ dns_rdataset_init(&diffset);
+
+ result = dns_db_addrdataset(db, node, ver, 0, addset, DNS_DBADD_MERGE,
+ NULL);
+ check_result(result, "dns_db_addrdataset()");
+
+ result = dns_db_subtractrdataset(db, node, ver, delset, 0, &diffset);
+ if (result == DNS_R_UNCHANGED) {
+ save = addset->ttl;
+ addset->ttl = ttl;
+ print_diff(cmd, addset);
+ addset->ttl = save;
+ } else if (result != DNS_R_NXRRSET) {
+ check_result(result, "dns_db_subtractrdataset()");
+ diffset.ttl = ttl;
+ print_diff(cmd, &diffset);
+ dns_rdataset_disassociate(&diffset);
+ }
+
+ dns_db_detachnode(db, &node);
+ dns_db_closeversion(db, &ver, false);
+ dns_db_detach(&db);
+}
+
+static void
+nsdiff(uint32_t ttl, dns_rdataset_t *oldset, dns_rdataset_t *newset) {
+ if (ttl == 0) {
+ vbprintf(1, "warning: no TTL in nsupdate script\n");
+ }
+ update_diff("add", ttl, newset, oldset);
+ update_diff("del", 0, oldset, newset);
+ if (verbose > 0) {
+ printf("show\nsend\nanswer\n");
+ } else {
+ printf("send\n");
+ }
+ if (fflush(stdout) == EOF) {
+ fatal("write stdout: %s", strerror(errno));
+ }
+}
+
+ISC_PLATFORM_NORETURN_PRE static void
+usage(void) ISC_PLATFORM_NORETURN_POST;
+
+static void
+usage(void) {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr,
+ " %s options [options] -f <file> -d <path> <domain>\n",
+ program);
+ fprintf(stderr, "Version: %s\n", VERSION);
+ fprintf(stderr, "Options:\n"
+ " -a <algorithm> digest algorithm (SHA-1 / "
+ "SHA-256 / SHA-384)\n"
+ " -c <class> of domain (default IN)\n"
+ " -D prefer CDNSKEY records instead "
+ "of CDS\n"
+ " -d <file|dir> where to find parent dsset- "
+ "file\n"
+ " -f <file> child DNSKEY+CDNSKEY+CDS+RRSIG "
+ "records\n"
+ " -i[extension] update dsset- file in place\n"
+ " -s <start-time> oldest permitted child "
+ "signatures\n"
+ " -u emit nsupdate script\n"
+ " -T <ttl> TTL of DS records\n"
+ " -V print version\n"
+ " -v <verbosity>\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[]) {
+ const char *child_path = NULL;
+ const char *ds_path = NULL;
+ const char *inplace = NULL;
+ isc_result_t result;
+ bool prefer_cdnskey = false;
+ bool nsupdate = false;
+ uint32_t ttl = 0;
+ int ch;
+ char *endp;
+
+ isc_mem_create(&mctx);
+
+#if USE_PKCS11
+ pk11_result_register();
+#endif /* if USE_PKCS11 */
+ dns_result_register();
+
+ isc_commandline_errprint = false;
+
+#define OPTIONS "a:c:Dd:f:i:ms:T:uv:V"
+ while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) {
+ switch (ch) {
+ case 'a':
+ add_dtype(strtodsdigest(isc_commandline_argument));
+ break;
+ case 'c':
+ rdclass = strtoclass(isc_commandline_argument);
+ break;
+ case 'D':
+ prefer_cdnskey = true;
+ break;
+ case 'd':
+ ds_path = isc_commandline_argument;
+ break;
+ case 'f':
+ child_path = isc_commandline_argument;
+ break;
+ case 'i':
+ /*
+ * This is a bodge to make the argument optional,
+ * so that it works just like sed(1).
+ */
+ if (isc_commandline_argument ==
+ argv[isc_commandline_index - 1])
+ {
+ isc_commandline_index--;
+ inplace = "";
+ } else {
+ inplace = isc_commandline_argument;
+ }
+ break;
+ case 'm':
+ isc_mem_debugging = ISC_MEM_DEBUGTRACE |
+ ISC_MEM_DEBUGRECORD;
+ break;
+ case 's':
+ startstr = isc_commandline_argument;
+ break;
+ case 'T':
+ ttl = strtottl(isc_commandline_argument);
+ break;
+ case 'u':
+ nsupdate = true;
+ break;
+ case 'V':
+ /* Does not return. */
+ version(program);
+ break;
+ case 'v':
+ verbose = strtoul(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0') {
+ fatal("-v must be followed by a number");
+ }
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argv += isc_commandline_index;
+ argc -= isc_commandline_index;
+
+ if (argc != 1) {
+ usage();
+ }
+ initname(argv[0]);
+
+ /*
+ * Default digest type if none specified.
+ */
+ if (dtype[0] == 0) {
+ dtype[0] = DNS_DSDIGEST_SHA256;
+ }
+
+ setup_logging(mctx, &lctx);
+
+ result = dst_lib_init(mctx, NULL);
+ if (result != ISC_R_SUCCESS) {
+ fatal("could not initialize dst: %s",
+ isc_result_totext(result));
+ }
+
+ if (ds_path == NULL) {
+ fatal("missing -d DS pathname");
+ }
+ load_parent_set(ds_path);
+
+ /*
+ * Preserve the TTL if it wasn't overridden.
+ */
+ if (ttl == 0) {
+ ttl = old_ds_set.ttl;
+ }
+
+ if (child_path == NULL) {
+ fatal("path to file containing child data must be specified");
+ }
+
+ load_child_sets(child_path);
+
+ /*
+ * Check child records have accompanying RRSIGs and DNSKEYs
+ */
+
+ if (!dns_rdataset_isassociated(&dnskey_set) ||
+ !dns_rdataset_isassociated(&dnskey_sig))
+ {
+ fatal("could not find signed DNSKEY RRset for %s", namestr);
+ }
+
+ if (dns_rdataset_isassociated(&cdnskey_set) &&
+ !dns_rdataset_isassociated(&cdnskey_sig))
+ {
+ fatal("missing RRSIG CDNSKEY records for %s", namestr);
+ }
+ if (dns_rdataset_isassociated(&cds_set) &&
+ !dns_rdataset_isassociated(&cds_sig))
+ {
+ fatal("missing RRSIG CDS records for %s", namestr);
+ }
+
+ vbprintf(1, "which child DNSKEY records match parent DS records?\n");
+ old_key_tbl = match_keyset_dsset(&dnskey_set, &old_ds_set, LOOSE);
+
+ /*
+ * We have now identified the keys that are allowed to authenticate
+ * the DNSKEY RRset (RFC 4035 section 5.2 bullet 2), and CDNSKEY and
+ * CDS RRsets (RFC 7344 section 4.1 bullet 2).
+ */
+
+ vbprintf(1, "verify DNSKEY signature(s)\n");
+ if (!signed_loose(matching_sigs(old_key_tbl, &dnskey_set, &dnskey_sig)))
+ {
+ fatal("could not validate child DNSKEY RRset for %s", namestr);
+ }
+
+ if (dns_rdataset_isassociated(&cdnskey_set)) {
+ vbprintf(1, "verify CDNSKEY signature(s)\n");
+ if (!signed_loose(matching_sigs(old_key_tbl, &cdnskey_set,
+ &cdnskey_sig)))
+ {
+ fatal("could not validate child CDNSKEY RRset for %s",
+ namestr);
+ }
+ }
+ if (dns_rdataset_isassociated(&cds_set)) {
+ vbprintf(1, "verify CDS signature(s)\n");
+ if (!signed_loose(
+ matching_sigs(old_key_tbl, &cds_set, &cds_sig)))
+ {
+ fatal("could not validate child CDS RRset for %s",
+ namestr);
+ }
+ }
+
+ free_keytable(&old_key_tbl);
+
+ /*
+ * Report the result of the replay attack protection checks
+ * used for the output file timestamp
+ */
+ if (oldestsig.timesigned != 0 && verbose > 0) {
+ char type[32];
+ dns_rdatatype_format(oldestsig.covered, type, sizeof(type));
+ verbose_time(1, "child signature inception time",
+ oldestsig.timesigned);
+ vbprintf(2, "from RRSIG %s by key %d\n", type, oldestsig.keyid);
+ }
+
+ /*
+ * Successfully do nothing if there's neither CDNSKEY nor CDS
+ * RFC 7344 section 4.1 first paragraph
+ */
+ if (!dns_rdataset_isassociated(&cdnskey_set) &&
+ !dns_rdataset_isassociated(&cds_set))
+ {
+ vbprintf(1, "%s has neither CDS nor CDNSKEY records\n",
+ namestr);
+ write_parent_set(ds_path, inplace, nsupdate, &old_ds_set);
+ exit(0);
+ }
+
+ /*
+ * Make DS records from the CDS or CDNSKEY records
+ * Prefer CDS if present, unless run with -D
+ */
+ if (prefer_cdnskey && dns_rdataset_isassociated(&cdnskey_set)) {
+ make_new_ds_set(ds_from_cdnskey, ttl, &cdnskey_set);
+ } else if (dns_rdataset_isassociated(&cds_set)) {
+ make_new_ds_set(ds_from_cds, ttl, &cds_set);
+ } else {
+ make_new_ds_set(ds_from_cdnskey, ttl, &cdnskey_set);
+ }
+
+ /*
+ * Now we have a candidate DS RRset, we need to check it
+ * won't break the delegation.
+ */
+ vbprintf(1, "which child DNSKEY records match new DS records?\n");
+ new_key_tbl = match_keyset_dsset(&dnskey_set, &new_ds_set, TIGHT);
+
+ if (!consistent_digests(&new_ds_set)) {
+ fatal("CDS records at %s do not cover each key "
+ "with the same set of digest types",
+ namestr);
+ }
+
+ vbprintf(1, "verify DNSKEY signature(s)\n");
+ if (!signed_strict(&new_ds_set, matching_sigs(new_key_tbl, &dnskey_set,
+ &dnskey_sig)))
+ {
+ fatal("could not validate child DNSKEY RRset "
+ "with new DS records for %s",
+ namestr);
+ }
+
+ free_keytable(&new_key_tbl);
+
+ /*
+ * OK, it's all good!
+ */
+ if (nsupdate) {
+ nsdiff(ttl, &old_ds_set, &new_ds_set);
+ }
+
+ write_parent_set(ds_path, inplace, nsupdate, &new_ds_set);
+
+ free_all_sets();
+ cleanup_logging(&lctx);
+ dst_lib_destroy();
+ if (verbose > 10) {
+ isc_mem_stats(mctx, stdout);
+ }
+ isc_mem_destroy(&mctx);
+
+ exit(0);
+}
diff --git a/bin/dnssec/dnssec-cds.rst b/bin/dnssec/dnssec-cds.rst
new file mode 100644
index 0000000..4ba77fb
--- /dev/null
+++ b/bin/dnssec/dnssec-cds.rst
@@ -0,0 +1,203 @@
+.. 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.
+
+.. highlight: console
+
+.. _man_dnssec-cds:
+
+dnssec-cds - change DS records for a child zone based on CDS/CDNSKEY
+--------------------------------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`dnssec-cds` [**-a** alg...] [**-c** class] [**-D**] {**-d** dsset-file} {**-f** child-file} [**-i**[extension]] [**-s** start-time] [**-T** ttl] [**-u**] [**-v** level] [**-V**] {domain}
+
+Description
+~~~~~~~~~~~
+
+The ``dnssec-cds`` command changes DS records at a delegation point
+based on CDS or CDNSKEY records published in the child zone. If both CDS
+and CDNSKEY records are present in the child zone, the CDS is preferred.
+This enables a child zone to inform its parent of upcoming changes to
+its key-signing keys (KSKs); by polling periodically with ``dnssec-cds``, the
+parent can keep the DS records up-to-date and enable automatic rolling
+of KSKs.
+
+Two input files are required. The ``-f child-file`` option specifies a
+file containing the child's CDS and/or CDNSKEY records, plus RRSIG and
+DNSKEY records so that they can be authenticated. The ``-d path`` option
+specifies the location of a file containing the current DS records. For
+example, this could be a ``dsset-`` file generated by
+``dnssec-signzone``, or the output of ``dnssec-dsfromkey``, or the
+output of a previous run of ``dnssec-cds``.
+
+The ``dnssec-cds`` command uses special DNSSEC validation logic
+specified by :rfc:`7344`. It requires that the CDS and/or CDNSKEY records
+be validly signed by a key represented in the existing DS records. This
+is typically the pre-existing KSK.
+
+For protection against replay attacks, the signatures on the child
+records must not be older than they were on a previous run of
+``dnssec-cds``. Their age is obtained from the modification time of the
+``dsset-`` file, or from the ``-s`` option.
+
+To protect against breaking the delegation, ``dnssec-cds`` ensures that
+the DNSKEY RRset can be verified by every key algorithm in the new DS
+RRset, and that the same set of keys are covered by every DS digest
+type.
+
+By default, replacement DS records are written to the standard output;
+with the ``-i`` option the input file is overwritten in place. The
+replacement DS records are the same as the existing records, when no
+change is required. The output can be empty if the CDS/CDNSKEY records
+specify that the child zone wants to be insecure.
+
+.. warning::
+
+ Be careful not to delete the DS records when ``dnssec-cds`` fails!
+
+Alternatively, ``dnssec-cds -u`` writes an ``nsupdate`` script to the
+standard output. The ``-u`` and ``-i`` options can be used together to
+maintain a ``dsset-`` file as well as emit an ``nsupdate`` script.
+
+Options
+~~~~~~~
+
+``-a algorithm``
+ This option specifies a digest algorithm to use when converting CDNSKEY records to
+ DS records. This option can be repeated, so that multiple DS records
+ are created for each CDNSKEY record. This option has no effect when
+ using CDS records.
+
+ The algorithm must be one of SHA-1, SHA-256, or SHA-384. These values
+ are case-insensitive, and the hyphen may be omitted. If no algorithm
+ is specified, the default is SHA-256.
+
+``-c class``
+ This option specifies the DNS class of the zones.
+
+``-D``
+ This option generates DS records from CDNSKEY records if both CDS and CDNSKEY
+ records are present in the child zone. By default CDS records are
+ preferred.
+
+``-d path``
+ This specifies the location of the parent DS records. The path can be the name of a file
+ containing the DS records; if it is a directory, ``dnssec-cds``
+ looks for a ``dsset-`` file for the domain inside the directory.
+
+ To protect against replay attacks, child records are rejected if they
+ were signed earlier than the modification time of the ``dsset-``
+ file. This can be adjusted with the ``-s`` option.
+
+``-f child-file``
+ This option specifies the file containing the child's CDS and/or CDNSKEY records, plus its
+ DNSKEY records and the covering RRSIG records, so that they can be
+ authenticated.
+
+ The examples below describe how to generate this file.
+
+``-iextension``
+ This option updates the ``dsset-`` file in place, instead of writing DS records to
+ the standard output.
+
+ There must be no space between the ``-i`` and the extension. If
+ no extension is provided, the old ``dsset-`` is discarded. If an
+ extension is present, a backup of the old ``dsset-`` file is kept
+ with the extension appended to its filename.
+
+ To protect against replay attacks, the modification time of the
+ ``dsset-`` file is set to match the signature inception time of the
+ child records, provided that it is later than the file's current
+ modification time.
+
+``-s start-time``
+ This option specifies the date and time after which RRSIG records become
+ acceptable. This can be either an absolute or a relative time. An
+ absolute start time is indicated by a number in YYYYMMDDHHMMSS
+ notation; 20170827133700 denotes 13:37:00 UTC on August 27th, 2017. A
+ time relative to the ``dsset-`` file is indicated with ``-N``, which is N
+ seconds before the file modification time. A time relative to the
+ current time is indicated with ``now+N``.
+
+ If no start-time is specified, the modification time of the
+ ``dsset-`` file is used.
+
+``-T ttl``
+ This option specifies a TTL to be used for new DS records. If not specified, the
+ default is the TTL of the old DS records. If they had no explicit TTL,
+ the new DS records also have no explicit TTL.
+
+``-u``
+ This option writes an ``nsupdate`` script to the standard output, instead of
+ printing the new DS reords. The output is empty if no change is
+ needed.
+
+ Note: The TTL of new records needs to be specified: it can be done in the
+ original ``dsset-`` file, with the ``-T`` option, or using the
+ ``nsupdate`` ``ttl`` command.
+
+``-V``
+ This option prints version information.
+
+``-v level``
+ This option sets the debugging level. Level 1 is intended to be usefully verbose
+ for general users; higher levels are intended for developers.
+
+``domain``
+ This indicates the name of the delegation point/child zone apex.
+
+Exit Status
+~~~~~~~~~~~
+
+The ``dnssec-cds`` command exits 0 on success, or non-zero if an error
+occurred.
+
+If successful, the DS records may or may not need to be
+changed.
+
+Examples
+~~~~~~~~
+
+Before running ``dnssec-signzone``, ensure that the delegations
+are up-to-date by running ``dnssec-cds`` on every ``dsset-`` file.
+
+To fetch the child records required by ``dnssec-cds``, invoke
+``dig`` as in the script below. It is acceptable if the ``dig`` fails, since
+``dnssec-cds`` performs all the necessary checking.
+
+::
+
+ for f in dsset-*
+ do
+ d=${f#dsset-}
+ dig +dnssec +noall +answer $d DNSKEY $d CDNSKEY $d CDS |
+ dnssec-cds -i -f /dev/stdin -d $f $d
+ done
+
+When the parent zone is automatically signed by ``named``,
+``dnssec-cds`` can be used with ``nsupdate`` to maintain a delegation as follows.
+The ``dsset-`` file allows the script to avoid having to fetch and
+validate the parent DS records, and it maintains the replay attack
+protection time.
+
+::
+
+ dig +dnssec +noall +answer $d DNSKEY $d CDNSKEY $d CDS |
+ dnssec-cds -u -i -f /dev/stdin -d $f $d |
+ nsupdate -l
+
+See Also
+~~~~~~~~
+
+:manpage:`dig(1)`, :manpage:`dnssec-settime(8)`, :manpage:`dnssec-signzone(8)`, :manpage:`nsupdate(1)`, BIND 9 Administrator
+Reference Manual, :rfc:`7344`.
diff --git a/bin/dnssec/dnssec-dsfromkey.c b/bin/dnssec/dnssec-dsfromkey.c
new file mode 100644
index 0000000..404239e
--- /dev/null
+++ b/bin/dnssec/dnssec-dsfromkey.c
@@ -0,0 +1,568 @@
+/*
+ * 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 <inttypes.h>
+#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-dsfromkey";
+
+static dns_rdataclass_t rdclass;
+static dns_fixedname_t fixed;
+static dns_name_t *name = NULL;
+static isc_mem_t *mctx = NULL;
+static uint32_t ttl;
+static bool emitttl = 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, 0);
+ 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 isc_result_t
+loadkeyset(char *dirname, dns_rdataset_t *rdataset) {
+ isc_result_t result;
+ char filename[PATH_MAX + 1];
+ isc_buffer_t buf;
+
+ dns_rdataset_init(rdataset);
+
+ isc_buffer_init(&buf, filename, sizeof(filename));
+ if (dirname != NULL) {
+ /* allow room for a trailing slash */
+ if (strlen(dirname) >= isc_buffer_availablelength(&buf)) {
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_putstr(&buf, dirname);
+ if (dirname[strlen(dirname) - 1] != '/') {
+ isc_buffer_putstr(&buf, "/");
+ }
+ }
+
+ if (isc_buffer_availablelength(&buf) < 7) {
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_putstr(&buf, "keyset-");
+
+ result = dns_name_tofilenametext(name, false, &buf);
+ check_result(result, "dns_name_tofilenametext()");
+ if (isc_buffer_availablelength(&buf) == 0) {
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_putuint8(&buf, 0);
+
+ return (loadset(filename, rdataset));
+}
+
+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("can't load %s.key: %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
+logkey(dns_rdata_t *rdata) {
+ isc_result_t result;
+ dst_key_t *key = NULL;
+ isc_buffer_t buf;
+ char keystr[DST_KEY_FORMATSIZE];
+
+ 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) {
+ return;
+ }
+
+ dst_key_format(key, keystr, sizeof(keystr));
+ fprintf(stderr, "%s: %s\n", program, keystr);
+
+ dst_key_free(&key);
+}
+
+static void
+emit(dns_dsdigest_t dt, bool showall, bool cds, dns_rdata_t *rdata) {
+ isc_result_t result;
+ unsigned char buf[DNS_DS_BUFFERSIZE];
+ char text_buf[DST_KEY_MAXTEXTSIZE];
+ char name_buf[DNS_NAME_MAXWIRE];
+ char class_buf[10];
+ isc_buffer_t textb, nameb, classb;
+ isc_region_t r;
+ dns_rdata_t ds;
+ dns_rdata_dnskey_t dnskey;
+
+ isc_buffer_init(&textb, text_buf, sizeof(text_buf));
+ isc_buffer_init(&nameb, name_buf, sizeof(name_buf));
+ isc_buffer_init(&classb, class_buf, sizeof(class_buf));
+
+ dns_rdata_init(&ds);
+
+ result = dns_rdata_tostruct(rdata, &dnskey, NULL);
+ if (result != ISC_R_SUCCESS) {
+ fatal("can't convert DNSKEY");
+ }
+
+ if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) {
+ return;
+ }
+
+ if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && !showall) {
+ return;
+ }
+
+ result = dns_ds_buildrdata(name, rdata, dt, buf, &ds);
+ if (result != ISC_R_SUCCESS) {
+ fatal("can't build record");
+ }
+
+ result = dns_name_totext(name, false, &nameb);
+ if (result != ISC_R_SUCCESS) {
+ fatal("can't print name");
+ }
+
+ result = dns_rdata_tofmttext(&ds, (dns_name_t *)NULL, 0, 0, 0, "",
+ &textb);
+
+ if (result != ISC_R_SUCCESS) {
+ fatal("can't print rdata");
+ }
+
+ result = dns_rdataclass_totext(rdclass, &classb);
+ if (result != ISC_R_SUCCESS) {
+ fatal("can't print class");
+ }
+
+ isc_buffer_usedregion(&nameb, &r);
+ printf("%.*s ", (int)r.length, r.base);
+
+ if (emitttl) {
+ printf("%u ", ttl);
+ }
+
+ isc_buffer_usedregion(&classb, &r);
+ printf("%.*s", (int)r.length, r.base);
+
+ if (cds) {
+ printf(" CDS ");
+ } else {
+ printf(" DS ");
+ }
+
+ isc_buffer_usedregion(&textb, &r);
+ printf("%.*s\n", (int)r.length, r.base);
+}
+
+static void
+emits(bool showall, bool cds, dns_rdata_t *rdata) {
+ unsigned i, n;
+
+ n = sizeof(dtype) / sizeof(dtype[0]);
+ for (i = 0; i < n; i++) {
+ if (dtype[i] != 0) {
+ emit(dtype[i], showall, cds, rdata);
+ }
+ }
+}
+
+ISC_PLATFORM_NORETURN_PRE static void
+usage(void) ISC_PLATFORM_NORETURN_POST;
+
+static void
+usage(void) {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, " %s [options] keyfile\n\n", program);
+ fprintf(stderr, " %s [options] -f zonefile [zonename]\n\n", program);
+ fprintf(stderr, " %s [options] -s dnsname\n\n", program);
+ fprintf(stderr, " %s [-h|-V]\n\n", program);
+ fprintf(stderr, "Version: %s\n", VERSION);
+ fprintf(stderr, "Options:\n"
+ " -1: digest algorithm SHA-1\n"
+ " -2: digest algorithm SHA-256\n"
+ " -a algorithm: digest algorithm (SHA-1, SHA-256 or "
+ "SHA-384)\n"
+ " -A: include all keys in DS set, not just KSKs (-f "
+ "only)\n"
+ " -c class: rdata class for DS set (default IN) (-f "
+ "or -s only)\n"
+ " -C: print CDS records\n"
+ " -f zonefile: read keys from a zone file\n"
+ " -h: print help information\n"
+ " -K directory: where to find key or keyset files\n"
+ " -s: read keys from keyset-<dnsname> file\n"
+ " -T: TTL of output records (omitted by default)\n"
+ " -v level: verbosity\n"
+ " -V: print version information\n");
+ fprintf(stderr, "Output: DS or CDS RRs\n");
+
+ exit(-1);
+}
+
+int
+main(int argc, char **argv) {
+ char *classname = NULL;
+ char *filename = NULL, *dir = NULL, *namestr;
+ char *endp, *arg1;
+ int ch;
+ bool cds = false;
+ bool usekeyset = false;
+ bool showall = false;
+ isc_result_t result;
+ isc_log_t *log = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata;
+
+ dns_rdata_init(&rdata);
+
+ 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 OPTIONS "12Aa:Cc:d:Ff:K:l:sT:v:hV"
+ while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) {
+ switch (ch) {
+ case '1':
+ add_dtype(DNS_DSDIGEST_SHA1);
+ break;
+ case '2':
+ add_dtype(DNS_DSDIGEST_SHA256);
+ break;
+ case 'A':
+ showall = true;
+ break;
+ case 'a':
+ add_dtype(strtodsdigest(isc_commandline_argument));
+ break;
+ case 'C':
+ cds = true;
+ break;
+ case 'c':
+ classname = isc_commandline_argument;
+ break;
+ case 'd':
+ fprintf(stderr,
+ "%s: the -d option is deprecated; "
+ "use -K\n",
+ program);
+ /* fall through */
+ case 'K':
+ dir = isc_commandline_argument;
+ if (strlen(dir) == 0U) {
+ fatal("directory must be non-empty string");
+ }
+ break;
+ case 'f':
+ filename = isc_commandline_argument;
+ break;
+ case 'l':
+ fatal("-l option (DLV lookaside) is obsolete");
+ break;
+ case 's':
+ usekeyset = true;
+ break;
+ case 'T':
+ emitttl = true;
+ ttl = strtottl(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 'F':
+ /* Reserved for FIPS mode */
+ FALLTHROUGH;
+ 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 (usekeyset && filename != NULL) {
+ fatal("cannot use both -s and -f");
+ }
+
+ /* When not using -f, -A is implicit */
+ if (filename == NULL) {
+ showall = true;
+ }
+
+ /* Default digest type if none specified. */
+ if (dtype[0] == 0) {
+ dtype[0] = DNS_DSDIGEST_SHA256;
+ }
+
+ /*
+ * Use local variable arg1 so that clang can correctly analyse
+ * reachable paths rather than 'argc < isc_commandline_index + 1'.
+ */
+ arg1 = argv[isc_commandline_index];
+ if (arg1 == NULL && filename == NULL) {
+ fatal("the key file name was not specified");
+ }
+ if (arg1 != NULL && argv[isc_commandline_index + 1] != NULL) {
+ 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 (usekeyset || filename != NULL) {
+ if (arg1 == NULL) {
+ /* using file name as the zone name */
+ namestr = filename;
+ } else {
+ namestr = arg1;
+ }
+
+ result = initname(namestr);
+ if (result != ISC_R_SUCCESS) {
+ fatal("could not initialize name %s", namestr);
+ }
+
+ if (usekeyset) {
+ result = loadkeyset(dir, &rdataset);
+ } else {
+ INSIST(filename != NULL);
+ 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);
+
+ if (verbose > 2) {
+ logkey(&rdata);
+ }
+
+ emits(showall, cds, &rdata);
+ }
+ } else {
+ unsigned char key_buf[DST_KEY_MAXSIZE];
+
+ loadkey(arg1, key_buf, DST_KEY_MAXSIZE, &rdata);
+
+ emits(showall, cds, &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);
+ }
+}
diff --git a/bin/dnssec/dnssec-dsfromkey.rst b/bin/dnssec/dnssec-dsfromkey.rst
new file mode 100644
index 0000000..6396733
--- /dev/null
+++ b/bin/dnssec/dnssec-dsfromkey.rst
@@ -0,0 +1,144 @@
+.. 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.
+
+.. highlight: console
+
+.. _man_dnssec-dsfromkey:
+
+dnssec-dsfromkey - DNSSEC DS RR generation tool
+-----------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`dnssec-dsfromkey` [ **-1** | **-2** | **-a** alg ] [ **-C** ] [**-T** TTL] [**-v** level] [**-K** directory] {keyfile}
+
+:program:`dnssec-dsfromkey` [ **-1** | **-2** | **-a** alg ] [ **-C** ] [**-T** TTL] [**-v** level] [**-c** class] [**-A**] {**-f** file} [dnsname]
+
+:program:`dnssec-dsfromkey` [ **-1** | **-2** | **-a** alg ] [ **-C** ] [**-T** TTL] [**-v** level] [**-c** class] [**-K** directory] {**-s**} {dnsname}
+
+:program:`dnssec-dsfromkey` [ **-h** | **-V** ]
+
+Description
+~~~~~~~~~~~
+
+The ``dnssec-dsfromkey`` command outputs DS (Delegation Signer) resource records
+(RRs), or CDS (Child DS) RRs with the ``-C`` option.
+
+By default, only KSKs are converted (keys with flags = 257). The
+``-A`` option includes ZSKs (flags = 256). Revoked keys are never
+included.
+
+The input keys can be specified in a number of ways:
+
+By default, ``dnssec-dsfromkey`` reads a key file named in the format
+``Knnnn.+aaa+iiiii.key``, as generated by ``dnssec-keygen``.
+
+With the ``-f file`` option, ``dnssec-dsfromkey`` reads keys from a zone
+file or partial zone file (which can contain just the DNSKEY records).
+
+With the ``-s`` option, ``dnssec-dsfromkey`` reads a ``keyset-`` file,
+as generated by ``dnssec-keygen`` ``-C``.
+
+Options
+~~~~~~~
+
+``-1``
+ This option is an abbreviation for ``-a SHA1``.
+
+``-2``
+ This option is an abbreviation for ``-a SHA-256``.
+
+``-a algorithm``
+ This option specifies a digest algorithm to use when converting DNSKEY records to
+ DS records. This option can be repeated, so that multiple DS records
+ are created for each DNSKEY record.
+
+ The algorithm must be one of SHA-1, SHA-256, or SHA-384. These values
+ are case-insensitive, and the hyphen may be omitted. If no algorithm
+ is specified, the default is SHA-256.
+
+``-A``
+ This option indicates that ZSKs are to be included when generating DS records. Without this option, only
+ keys which have the KSK flag set are converted to DS records and
+ printed. This option is only useful in ``-f`` zone file mode.
+
+``-c class``
+ This option specifies the DNS class; the default is IN. This option is only useful in ``-s`` keyset
+ or ``-f`` zone file mode.
+
+``-C``
+ This option generates CDS records rather than DS records.
+
+``-f file``
+ This option sets zone file mode, in which the final dnsname argument of ``dnssec-dsfromkey`` is the
+ DNS domain name of a zone whose master file can be read from
+ ``file``. If the zone name is the same as ``file``, then it may be
+ omitted.
+
+ If ``file`` is ``-``, then the zone data is read from the standard
+ input. This makes it possible to use the output of the ``dig``
+ command as input, as in:
+
+ ``dig dnskey example.com | dnssec-dsfromkey -f - example.com``
+
+``-h``
+ This option prints usage information.
+
+``-K directory``
+ This option tells BIND 9 to look for key files or ``keyset-`` files in ``directory``.
+
+``-s``
+ This option enables keyset mode, in which the final dnsname argument from ``dnssec-dsfromkey`` is the DNS
+ domain name used to locate a ``keyset-`` file.
+
+``-T TTL``
+ This option specifies the TTL of the DS records. By default the TTL is omitted.
+
+``-v level``
+ This option sets the debugging level.
+
+``-V``
+ This option prints version information.
+
+Example
+~~~~~~~
+
+To build the SHA-256 DS RR from the ``Kexample.com.+003+26160`` keyfile,
+issue the following command:
+
+``dnssec-dsfromkey -2 Kexample.com.+003+26160``
+
+The command returns something similar to:
+
+``example.com. IN DS 26160 5 2 3A1EADA7A74B8D0BA86726B0C227AA85AB8BBD2B2004F41A868A54F0C5EA0B94``
+
+Files
+~~~~~
+
+The keyfile can be designated by the key identification
+``Knnnn.+aaa+iiiii`` or the full file name ``Knnnn.+aaa+iiiii.key``, as
+generated by ``dnssec-keygen``.
+
+The keyset file name is built from the ``directory``, the string
+``keyset-``, and the ``dnsname``.
+
+Caveat
+~~~~~~
+
+A keyfile error may return "file not found," even if the file exists.
+
+See Also
+~~~~~~~~
+
+:manpage:`dnssec-keygen(8)`, :manpage:`dnssec-signzone(8)`, BIND 9 Administrator Reference Manual,
+:rfc:`3658` (DS RRs), :rfc:`4509` (SHA-256 for DS RRs),
+:rfc:`6605` (SHA-384 for DS RRs), :rfc:`7344` (CDS and CDNSKEY RRs).
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);
+ }
+}
diff --git a/bin/dnssec/dnssec-importkey.rst b/bin/dnssec/dnssec-importkey.rst
new file mode 100644
index 0000000..9e58fce
--- /dev/null
+++ b/bin/dnssec/dnssec-importkey.rst
@@ -0,0 +1,113 @@
+.. 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.
+
+.. highlight: console
+
+.. _man_dnssec-importkey:
+
+dnssec-importkey - import DNSKEY records from external systems so they can be managed
+-------------------------------------------------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`dnssec-importkey` [**-K** directory] [**-L** ttl] [**-P** date/offset] [**-P** sync date/offset] [**-D** date/offset] [**-D** sync date/offset] [**-h**] [**-v** level] [**-V**] {keyfile}
+
+:program:`dnssec-importkey` {**-f** filename} [**-K** directory] [**-L** ttl] [**-P** date/offset] [**-P** sync date/offset] [**-D** date/offset] [**-D** sync date/offset] [**-h**] [**-v** level] [**-V**] [dnsname]
+
+Description
+~~~~~~~~~~~
+
+``dnssec-importkey`` reads a public DNSKEY record and generates a pair
+of .key/.private files. The DNSKEY record may be read from an
+existing .key file, in which case a corresponding .private file is
+generated, or it may be read from any other file or from the standard
+input, in which case both .key and .private files are generated.
+
+The newly created .private file does *not* contain private key data, and
+cannot be used for signing. However, having a .private file makes it
+possible to set publication (``-P``) and deletion (``-D``) times for the
+key, which means the public key can be added to and removed from the
+DNSKEY RRset on schedule even if the true private key is stored offline.
+
+Options
+~~~~~~~
+
+``-f filename``
+ This option indicates the zone file mode. Instead of a public keyfile name, the argument is the
+ DNS domain name of a zone master file, which can be read from
+ ``filename``. If the domain name is the same as ``filename``, then it may be
+ omitted.
+
+ If ``filename`` is set to ``"-"``, then the zone data is read from the
+ standard input.
+
+``-K directory``
+ This option sets the directory in which the key files are to reside.
+
+``-L ttl``
+ This option sets the default TTL to use for this key when it is converted into a
+ DNSKEY RR. This is the TTL used when the key is imported into a zone,
+ unless there was already a DNSKEY RRset in
+ place, in which case the existing TTL takes precedence. Setting the default TTL to ``0`` or ``none``
+ removes it from the key.
+
+``-h``
+ This option emits a usage message and exits.
+
+``-v level``
+ This option sets the debugging level.
+
+``-V``
+ This option prints version information.
+
+Timing Options
+~~~~~~~~~~~~~~
+
+Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS. If the
+argument begins with a ``+`` or ``-``, it is interpreted as an offset from
+the present time. For convenience, if such an offset is followed by one
+of the suffixes ``y``, ``mo``, ``w``, ``d``, ``h``, or ``mi``, then the offset is
+computed in years (defined as 365 24-hour days, ignoring leap years),
+months (defined as 30 24-hour days), weeks, days, hours, or minutes,
+respectively. Without a suffix, the offset is computed in seconds. To
+explicitly prevent a date from being set, use ``none`` or ``never``.
+
+``-P date/offset``
+ This option sets the date on which a key is to be published to the zone. After
+ that date, the key is included in the zone but is not used
+ to sign it.
+
+``-P sync date/offset``
+ This option sets the date on which CDS and CDNSKEY records that match this key
+ are to be published to the zone.
+
+``-D date/offset``
+ This option sets the date on which the key is to be deleted. After that date, the
+ key is no longer included in the zone. (However, it may remain in the key
+ repository.)
+
+``-D sync date/offset``
+ This option sets the date on which the CDS and CDNSKEY records that match this
+ key are to be deleted.
+
+Files
+~~~~~
+
+A keyfile can be designed by the key identification ``Knnnn.+aaa+iiiii``
+or the full file name ``Knnnn.+aaa+iiiii.key``, as generated by
+``dnssec-keygen``.
+
+See Also
+~~~~~~~~
+
+:manpage:`dnssec-keygen(8)`, :manpage:`dnssec-signzone(8)`, BIND 9 Administrator Reference Manual,
+:rfc:`5011`.
diff --git a/bin/dnssec/dnssec-keyfromlabel.c b/bin/dnssec/dnssec-keyfromlabel.c
new file mode 100644
index 0000000..26d8352
--- /dev/null
+++ b/bin/dnssec/dnssec-keyfromlabel.c
@@ -0,0 +1,782 @@
+/*
+ * 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 <ctype.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/region.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <pk11/site.h>
+
+#include <dns/dnssec.h>
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/result.h>
+#include <dns/secalg.h>
+
+#include <dst/dst.h>
+
+#if USE_PKCS11
+#include <pk11/result.h>
+#endif /* if USE_PKCS11 */
+
+#include "dnssectool.h"
+
+#define MAX_RSA 4096 /* should be long enough... */
+
+const char *program = "dnssec-keyfromlabel";
+
+ISC_PLATFORM_NORETURN_PRE static void
+usage(void) ISC_PLATFORM_NORETURN_POST;
+
+static void
+usage(void) {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, " %s -l label [options] name\n\n", program);
+ fprintf(stderr, "Version: %s\n", VERSION);
+ fprintf(stderr, "Required options:\n");
+ fprintf(stderr, " -l label: label of the key pair\n");
+ fprintf(stderr, " name: owner of the key\n");
+ fprintf(stderr, "Other options:\n");
+ fprintf(stderr, " -a algorithm: \n"
+ " DH | RSASHA1 |\n"
+ " NSEC3RSASHA1 |\n"
+ " RSASHA256 | RSASHA512 |\n"
+ " ECDSAP256SHA256 | ECDSAP384SHA384 |\n"
+ " ED25519 | ED448\n");
+ fprintf(stderr, " -3: use NSEC3-capable algorithm\n");
+ fprintf(stderr, " -c class (default: IN)\n");
+ fprintf(stderr, " -E <engine>:\n");
+#if USE_PKCS11
+ fprintf(stderr,
+ " path to PKCS#11 provider library "
+ "(default is %s)\n",
+ PK11_LIB_LOCATION);
+#else /* if USE_PKCS11 */
+ fprintf(stderr, " name of an OpenSSL engine to use\n");
+#endif /* if USE_PKCS11 */
+ fprintf(stderr, " -f keyflag: KSK | REVOKE\n");
+ fprintf(stderr, " -K directory: directory in which to place "
+ "key files\n");
+ fprintf(stderr, " -k: generate a TYPE=KEY key\n");
+ fprintf(stderr, " -L ttl: default key TTL\n");
+ fprintf(stderr, " -n nametype: ZONE | HOST | ENTITY | USER | "
+ "OTHER\n");
+ fprintf(stderr, " (DNSKEY generation defaults to ZONE\n");
+ fprintf(stderr, " -p protocol: default: 3 [dnssec]\n");
+ fprintf(stderr, " -t type: "
+ "AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF "
+ "(default: AUTHCONF)\n");
+ fprintf(stderr, " -y: permit keys that might collide\n");
+ fprintf(stderr, " -v verbose level\n");
+ fprintf(stderr, " -V: print version information\n");
+ fprintf(stderr, "Date options:\n");
+ fprintf(stderr, " -P date/[+-]offset: set key publication date\n");
+ fprintf(stderr, " -P sync date/[+-]offset: set CDS and CDNSKEY "
+ "publication date\n");
+ fprintf(stderr, " -A date/[+-]offset: set key activation date\n");
+ fprintf(stderr, " -R date/[+-]offset: set key revocation date\n");
+ fprintf(stderr, " -I date/[+-]offset: set key inactivation date\n");
+ fprintf(stderr, " -D date/[+-]offset: set key deletion date\n");
+ fprintf(stderr, " -D sync date/[+-]offset: set CDS and CDNSKEY "
+ "deletion date\n");
+ fprintf(stderr, " -G: generate key only; do not set -P or -A\n");
+ fprintf(stderr, " -C: generate a backward-compatible key, omitting"
+ " all dates\n");
+ fprintf(stderr, " -S <key>: generate a successor to an existing "
+ "key\n");
+ fprintf(stderr, " -i <interval>: prepublication interval for "
+ "successor key "
+ "(default: 30 days)\n");
+ fprintf(stderr, "Output:\n");
+ fprintf(stderr, " K<name>+<alg>+<id>.key, "
+ "K<name>+<alg>+<id>.private\n");
+
+ exit(-1);
+}
+
+int
+main(int argc, char **argv) {
+ char *algname = NULL, *freeit = NULL;
+ char *nametype = NULL, *type = NULL;
+ const char *directory = NULL;
+ const char *predecessor = NULL;
+ dst_key_t *prevkey = NULL;
+ const char *engine = NULL;
+ char *classname = NULL;
+ char *endp;
+ dst_key_t *key = NULL;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ uint16_t flags = 0, kskflag = 0, revflag = 0;
+ dns_secalg_t alg;
+ bool oldstyle = false;
+ isc_mem_t *mctx = NULL;
+ int ch;
+ int protocol = -1, signatory = 0;
+ isc_result_t ret;
+ isc_textregion_t r;
+ char filename[255];
+ isc_buffer_t buf;
+ isc_log_t *log = NULL;
+ dns_rdataclass_t rdclass;
+ int options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC;
+ char *label = NULL;
+ dns_ttl_t ttl = 0;
+ isc_stdtime_t publish = 0, activate = 0, revoke = 0;
+ isc_stdtime_t inactive = 0, deltime = 0;
+ isc_stdtime_t now;
+ int prepub = -1;
+ bool setpub = false, setact = false;
+ bool setrev = false, setinact = false;
+ bool setdel = false, setttl = false;
+ bool unsetpub = false, unsetact = false;
+ bool unsetrev = false, unsetinact = false;
+ bool unsetdel = false;
+ bool genonly = false;
+ bool use_nsec3 = false;
+ bool avoid_collisions = true;
+ bool exact;
+ unsigned char c;
+ isc_stdtime_t syncadd = 0, syncdel = 0;
+ bool unsetsyncadd = false, setsyncadd = false;
+ bool unsetsyncdel = false, setsyncdel = false;
+
+ 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;
+
+ isc_stdtime_get(&now);
+
+#define CMDLINE_FLAGS "3A:a:Cc:D:E:Ff:GhI:i:kK:L:l:n:P:p:R:S:t:v:Vy"
+ while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
+ switch (ch) {
+ case '3':
+ use_nsec3 = true;
+ break;
+ case 'a':
+ algname = isc_commandline_argument;
+ break;
+ case 'C':
+ oldstyle = true;
+ break;
+ case 'c':
+ classname = isc_commandline_argument;
+ break;
+ case 'E':
+ engine = isc_commandline_argument;
+ break;
+ case 'f':
+ c = (unsigned char)(isc_commandline_argument[0]);
+ if (toupper(c) == 'K') {
+ kskflag = DNS_KEYFLAG_KSK;
+ } else if (toupper(c) == 'R') {
+ revflag = DNS_KEYFLAG_REVOKE;
+ } else {
+ fatal("unknown flag '%s'",
+ isc_commandline_argument);
+ }
+ break;
+ case 'K':
+ directory = isc_commandline_argument;
+ ret = try_dir(directory);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("cannot open directory %s: %s", directory,
+ isc_result_totext(ret));
+ }
+ break;
+ case 'k':
+ options |= DST_TYPE_KEY;
+ break;
+ case 'L':
+ ttl = strtottl(isc_commandline_argument);
+ setttl = true;
+ break;
+ case 'l':
+ label = isc_mem_strdup(mctx, isc_commandline_argument);
+ break;
+ case 'n':
+ nametype = isc_commandline_argument;
+ break;
+ case 'p':
+ protocol = strtol(isc_commandline_argument, &endp, 10);
+ if (*endp != '\0' || protocol < 0 || protocol > 255) {
+ fatal("-p must be followed by a number "
+ "[0..255]");
+ }
+ break;
+ case 't':
+ type = 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 'y':
+ avoid_collisions = false;
+ break;
+ case 'G':
+ genonly = true;
+ break;
+ case 'P':
+ /* -Psync ? */
+ if (isoptarg("sync", argv, usage)) {
+ if (unsetsyncadd || setsyncadd) {
+ fatal("-P sync specified more than "
+ "once");
+ }
+
+ syncadd = strtotime(isc_commandline_argument,
+ now, now, &setsyncadd);
+ unsetsyncadd = !setsyncadd;
+ break;
+ }
+ /* -Pdnskey ? */
+ (void)isoptarg("dnskey", argv, usage);
+ if (setpub || unsetpub) {
+ fatal("-P specified more than once");
+ }
+
+ publish = strtotime(isc_commandline_argument, now, now,
+ &setpub);
+ unsetpub = !setpub;
+ break;
+ case 'A':
+ if (setact || unsetact) {
+ fatal("-A specified more than once");
+ }
+
+ activate = strtotime(isc_commandline_argument, now, now,
+ &setact);
+ unsetact = !setact;
+ break;
+ case 'R':
+ if (setrev || unsetrev) {
+ fatal("-R specified more than once");
+ }
+
+ revoke = strtotime(isc_commandline_argument, now, now,
+ &setrev);
+ unsetrev = !setrev;
+ break;
+ case 'I':
+ if (setinact || unsetinact) {
+ fatal("-I specified more than once");
+ }
+
+ inactive = strtotime(isc_commandline_argument, now, now,
+ &setinact);
+ unsetinact = !setinact;
+ break;
+ case 'D':
+ /* -Dsync ? */
+ if (isoptarg("sync", argv, usage)) {
+ if (unsetsyncdel || setsyncdel) {
+ fatal("-D sync specified more than "
+ "once");
+ }
+
+ syncdel = strtotime(isc_commandline_argument,
+ now, now, &setsyncdel);
+ unsetsyncdel = !setsyncdel;
+ break;
+ }
+ /* -Ddnskey ? */
+ (void)isoptarg("dnskey", argv, usage);
+ if (setdel || unsetdel) {
+ fatal("-D specified more than once");
+ }
+
+ deltime = strtotime(isc_commandline_argument, now, now,
+ &setdel);
+ unsetdel = !setdel;
+ break;
+ case 'S':
+ predecessor = isc_commandline_argument;
+ break;
+ case 'i':
+ prepub = strtottl(isc_commandline_argument);
+ break;
+ case 'F':
+ /* Reserved for FIPS mode */
+ FALLTHROUGH;
+ 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);
+ }
+ }
+
+ ret = dst_lib_init(mctx, engine);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("could not initialize dst: %s", isc_result_totext(ret));
+ }
+
+ setup_logging(mctx, &log);
+
+ if (predecessor == NULL) {
+ if (label == NULL) {
+ fatal("the key label was not specified");
+ }
+ if (argc < isc_commandline_index + 1) {
+ fatal("the key name was not specified");
+ }
+ if (argc > isc_commandline_index + 1) {
+ fatal("extraneous arguments");
+ }
+
+ name = dns_fixedname_initname(&fname);
+ isc_buffer_init(&buf, argv[isc_commandline_index],
+ strlen(argv[isc_commandline_index]));
+ isc_buffer_add(&buf, strlen(argv[isc_commandline_index]));
+ ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("invalid key name %s: %s",
+ argv[isc_commandline_index],
+ isc_result_totext(ret));
+ }
+
+ if (strchr(label, ':') == NULL) {
+ char *l;
+ int len;
+
+ len = strlen(label) + 8;
+ l = isc_mem_allocate(mctx, len);
+ snprintf(l, len, "pkcs11:%s", label);
+ isc_mem_free(mctx, label);
+ label = l;
+ }
+
+ if (algname == NULL) {
+ fatal("no algorithm specified");
+ }
+
+ r.base = algname;
+ r.length = strlen(algname);
+ ret = dns_secalg_fromtext(&alg, &r);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("unknown algorithm %s", algname);
+ }
+ if (alg == DST_ALG_DH) {
+ options |= DST_TYPE_KEY;
+ }
+
+ if (use_nsec3) {
+ switch (alg) {
+ case DST_ALG_RSASHA1:
+ alg = DST_ALG_NSEC3RSASHA1;
+ break;
+ case DST_ALG_NSEC3RSASHA1:
+ case DST_ALG_RSASHA256:
+ case DST_ALG_RSASHA512:
+ case DST_ALG_ECDSA256:
+ case DST_ALG_ECDSA384:
+ case DST_ALG_ED25519:
+ case DST_ALG_ED448:
+ break;
+ default:
+ fatal("%s is incompatible with NSEC3; "
+ "do not use the -3 option",
+ algname);
+ }
+ }
+
+ if (type != NULL && (options & DST_TYPE_KEY) != 0) {
+ if (strcasecmp(type, "NOAUTH") == 0) {
+ flags |= DNS_KEYTYPE_NOAUTH;
+ } else if (strcasecmp(type, "NOCONF") == 0) {
+ flags |= DNS_KEYTYPE_NOCONF;
+ } else if (strcasecmp(type, "NOAUTHCONF") == 0) {
+ flags |= (DNS_KEYTYPE_NOAUTH |
+ DNS_KEYTYPE_NOCONF);
+ } else if (strcasecmp(type, "AUTHCONF") == 0) {
+ /* nothing */
+ } else {
+ fatal("invalid type %s", type);
+ }
+ }
+
+ if (!oldstyle && prepub > 0) {
+ if (setpub && setact && (activate - prepub) < publish) {
+ fatal("Activation and publication dates "
+ "are closer together than the\n\t"
+ "prepublication interval.");
+ }
+
+ if (!setpub && !setact) {
+ setpub = setact = true;
+ publish = now;
+ activate = now + prepub;
+ } else if (setpub && !setact) {
+ setact = true;
+ activate = publish + prepub;
+ } else if (setact && !setpub) {
+ setpub = true;
+ publish = activate - prepub;
+ }
+
+ if ((activate - prepub) < now) {
+ fatal("Time until activation is shorter "
+ "than the\n\tprepublication interval.");
+ }
+ }
+ } else {
+ char keystr[DST_KEY_FORMATSIZE];
+ isc_stdtime_t when;
+ int major, minor;
+
+ if (prepub == -1) {
+ prepub = (30 * 86400);
+ }
+
+ if (algname != NULL) {
+ fatal("-S and -a cannot be used together");
+ }
+ if (nametype != NULL) {
+ fatal("-S and -n cannot be used together");
+ }
+ if (type != NULL) {
+ fatal("-S and -t cannot be used together");
+ }
+ if (setpub || unsetpub) {
+ fatal("-S and -P cannot be used together");
+ }
+ if (setact || unsetact) {
+ fatal("-S and -A cannot be used together");
+ }
+ if (use_nsec3) {
+ fatal("-S and -3 cannot be used together");
+ }
+ if (oldstyle) {
+ fatal("-S and -C cannot be used together");
+ }
+ if (genonly) {
+ fatal("-S and -G cannot be used together");
+ }
+
+ ret = dst_key_fromnamedfile(predecessor, directory,
+ DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
+ mctx, &prevkey);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("Invalid keyfile %s: %s", predecessor,
+ isc_result_totext(ret));
+ }
+ if (!dst_key_isprivate(prevkey)) {
+ fatal("%s is not a private key", predecessor);
+ }
+
+ name = dst_key_name(prevkey);
+ alg = dst_key_alg(prevkey);
+ flags = dst_key_flags(prevkey);
+
+ dst_key_format(prevkey, keystr, sizeof(keystr));
+ dst_key_getprivateformat(prevkey, &major, &minor);
+ if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) {
+ fatal("Key %s has incompatible format version %d.%d\n\t"
+ "It is not possible to generate a successor key.",
+ keystr, major, minor);
+ }
+
+ ret = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &when);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("Key %s has no activation date.\n\t"
+ "You must use dnssec-settime -A to set one "
+ "before generating a successor.",
+ keystr);
+ }
+
+ ret = dst_key_gettime(prevkey, DST_TIME_INACTIVE, &activate);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("Key %s has no inactivation date.\n\t"
+ "You must use dnssec-settime -I to set one "
+ "before generating a successor.",
+ keystr);
+ }
+
+ publish = activate - prepub;
+ if (publish < now) {
+ fatal("Key %s becomes inactive\n\t"
+ "sooner than the prepublication period "
+ "for the new key ends.\n\t"
+ "Either change the inactivation date with "
+ "dnssec-settime -I,\n\t"
+ "or use the -i option to set a shorter "
+ "prepublication interval.",
+ keystr);
+ }
+
+ ret = dst_key_gettime(prevkey, DST_TIME_DELETE, &when);
+ if (ret != ISC_R_SUCCESS) {
+ fprintf(stderr,
+ "%s: WARNING: Key %s has no removal "
+ "date;\n\t it will remain in the zone "
+ "indefinitely after rollover.\n\t "
+ "You can use dnssec-settime -D to "
+ "change this.\n",
+ program, keystr);
+ }
+
+ setpub = setact = true;
+ }
+
+ if (nametype == NULL) {
+ if ((options & DST_TYPE_KEY) != 0) { /* KEY */
+ fatal("no nametype specified");
+ }
+ flags |= DNS_KEYOWNER_ZONE; /* DNSKEY */
+ } else if (strcasecmp(nametype, "zone") == 0) {
+ flags |= DNS_KEYOWNER_ZONE;
+ } else if ((options & DST_TYPE_KEY) != 0) { /* KEY */
+ if (strcasecmp(nametype, "host") == 0 ||
+ strcasecmp(nametype, "entity") == 0)
+ {
+ flags |= DNS_KEYOWNER_ENTITY;
+ } else if (strcasecmp(nametype, "user") == 0) {
+ flags |= DNS_KEYOWNER_USER;
+ } else {
+ fatal("invalid KEY nametype %s", nametype);
+ }
+ } else if (strcasecmp(nametype, "other") != 0) { /* DNSKEY */
+ fatal("invalid DNSKEY nametype %s", nametype);
+ }
+
+ rdclass = strtoclass(classname);
+
+ if (directory == NULL) {
+ directory = ".";
+ }
+
+ if ((options & DST_TYPE_KEY) != 0) { /* KEY */
+ flags |= signatory;
+ } else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */
+ flags |= kskflag;
+ flags |= revflag;
+ }
+
+ if (protocol == -1) {
+ protocol = DNS_KEYPROTO_DNSSEC;
+ } else if ((options & DST_TYPE_KEY) == 0 &&
+ protocol != DNS_KEYPROTO_DNSSEC)
+ {
+ fatal("invalid DNSKEY protocol: %d", protocol);
+ }
+
+ if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
+ if ((flags & DNS_KEYFLAG_SIGNATORYMASK) != 0) {
+ fatal("specified null key with signing authority");
+ }
+ }
+
+ if ((flags & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE &&
+ alg == DNS_KEYALG_DH)
+ {
+ fatal("a key with algorithm '%s' cannot be a zone key",
+ algname);
+ }
+
+ isc_buffer_init(&buf, filename, sizeof(filename) - 1);
+
+ /* associate the key */
+ ret = dst_key_fromlabel(name, alg, flags, protocol, rdclass,
+#if USE_PKCS11
+ "pkcs11",
+#else /* if USE_PKCS11 */
+ engine,
+#endif /* if USE_PKCS11 */
+ label, NULL, mctx, &key);
+
+ if (ret != ISC_R_SUCCESS) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ char algstr[DNS_SECALG_FORMATSIZE];
+ dns_name_format(name, namestr, sizeof(namestr));
+ dns_secalg_format(alg, algstr, sizeof(algstr));
+ fatal("failed to get key %s/%s: %s", namestr, algstr,
+ isc_result_totext(ret));
+ UNREACHABLE();
+ exit(-1);
+ }
+
+ /*
+ * Set key timing metadata (unless using -C)
+ *
+ * Publish and activation dates are set to "now" by default, but
+ * can be overridden. Creation date is always set to "now".
+ */
+ if (!oldstyle) {
+ dst_key_settime(key, DST_TIME_CREATED, now);
+
+ if (genonly && (setpub || setact)) {
+ fatal("cannot use -G together with -P or -A options");
+ }
+
+ if (setpub) {
+ dst_key_settime(key, DST_TIME_PUBLISH, publish);
+ } else if (setact) {
+ dst_key_settime(key, DST_TIME_PUBLISH, activate);
+ } else if (!genonly && !unsetpub) {
+ dst_key_settime(key, DST_TIME_PUBLISH, now);
+ }
+
+ if (setact) {
+ dst_key_settime(key, DST_TIME_ACTIVATE, activate);
+ } else if (!genonly && !unsetact) {
+ dst_key_settime(key, DST_TIME_ACTIVATE, now);
+ }
+
+ if (setrev) {
+ if (kskflag == 0) {
+ fprintf(stderr,
+ "%s: warning: Key is "
+ "not flagged as a KSK, but -R "
+ "was used. Revoking a ZSK is "
+ "legal, but undefined.\n",
+ program);
+ }
+ dst_key_settime(key, DST_TIME_REVOKE, revoke);
+ }
+
+ if (setinact) {
+ dst_key_settime(key, DST_TIME_INACTIVE, inactive);
+ }
+
+ if (setdel) {
+ dst_key_settime(key, DST_TIME_DELETE, deltime);
+ }
+ if (setsyncadd) {
+ dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncadd);
+ }
+ if (setsyncdel) {
+ dst_key_settime(key, DST_TIME_SYNCDELETE, syncdel);
+ }
+ } else {
+ if (setpub || setact || setrev || setinact || setdel ||
+ unsetpub || unsetact || unsetrev || unsetinact ||
+ unsetdel || genonly || setsyncadd || setsyncdel)
+ {
+ fatal("cannot use -C together with "
+ "-P, -A, -R, -I, -D, or -G options");
+ }
+ /*
+ * Compatibility mode: Private-key-format
+ * should be set to 1.2.
+ */
+ dst_key_setprivateformat(key, 1, 2);
+ }
+
+ /* Set default key TTL */
+ if (setttl) {
+ dst_key_setttl(key, ttl);
+ }
+
+ /*
+ * Do not overwrite an existing key. Warn LOUDLY if there
+ * is a risk of ID collision due to this key or another key
+ * being revoked.
+ */
+ if (key_collision(key, name, directory, mctx, &exact)) {
+ isc_buffer_clear(&buf);
+ ret = dst_key_buildfilename(key, 0, directory, &buf);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("dst_key_buildfilename returned: %s\n",
+ isc_result_totext(ret));
+ }
+ if (exact) {
+ fatal("%s: %s already exists\n", program, filename);
+ }
+
+ if (avoid_collisions) {
+ fatal("%s: %s could collide with another key upon "
+ "revokation\n",
+ program, filename);
+ }
+
+ fprintf(stderr,
+ "%s: WARNING: Key %s could collide with "
+ "another key upon revokation. If you plan "
+ "to revoke keys, destroy this key and "
+ "generate a different one.\n",
+ program, filename);
+ }
+
+ ret = dst_key_tofile(key, options, directory);
+ if (ret != ISC_R_SUCCESS) {
+ char keystr[DST_KEY_FORMATSIZE];
+ dst_key_format(key, keystr, sizeof(keystr));
+ fatal("failed to write key %s: %s\n", keystr,
+ isc_result_totext(ret));
+ }
+
+ isc_buffer_clear(&buf);
+ ret = dst_key_buildfilename(key, 0, NULL, &buf);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("dst_key_buildfilename returned: %s\n",
+ isc_result_totext(ret));
+ }
+ printf("%s\n", filename);
+ dst_key_free(&key);
+ if (prevkey != NULL) {
+ dst_key_free(&prevkey);
+ }
+
+ cleanup_logging(&log);
+ dst_lib_destroy();
+ if (verbose > 10) {
+ isc_mem_stats(mctx, stdout);
+ }
+ isc_mem_free(mctx, label);
+ isc_mem_destroy(&mctx);
+
+ if (freeit != NULL) {
+ free(freeit);
+ }
+
+ return (0);
+}
diff --git a/bin/dnssec/dnssec-keyfromlabel.rst b/bin/dnssec/dnssec-keyfromlabel.rst
new file mode 100644
index 0000000..57ab9c8
--- /dev/null
+++ b/bin/dnssec/dnssec-keyfromlabel.rst
@@ -0,0 +1,262 @@
+.. 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.
+
+.. highlight: console
+
+.. _man_dnssec-keyfromlabel:
+
+dnssec-keyfromlabel - DNSSEC key generation tool
+------------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`dnssec-keyfromlabel` {**-l** label} [**-3**] [**-a** algorithm] [**-A** date/offset] [**-c** class] [**-D** date/offset] [**-D** sync date/offset] [**-E** engine] [**-f** flag] [**-G**] [**-I** date/offset] [**-i** interval] [**-k**] [**-K** directory] [**-L** ttl] [**-n** nametype] [**-P** date/offset] [**-P** sync date/offset] [**-p** protocol] [**-R** date/offset] [**-S** key] [**-t** type] [**-v** level] [**-V**] [**-y**] {name}
+
+Description
+~~~~~~~~~~~
+
+``dnssec-keyfromlabel`` generates a pair of key files that reference a
+key object stored in a cryptographic hardware service module (HSM). The
+private key file can be used for DNSSEC signing of zone data as if it
+were a conventional signing key created by ``dnssec-keygen``, but the
+key material is stored within the HSM and the actual signing takes
+place there.
+
+The ``name`` of the key is specified on the command line. This must
+match the name of the zone for which the key is being generated.
+
+Options
+~~~~~~~
+
+``-a algorithm``
+ This option selects the cryptographic algorithm. The value of ``algorithm`` must
+ be one of RSASHA1, NSEC3RSASHA1, RSASHA256, RSASHA512,
+ ECDSAP256SHA256, ECDSAP384SHA384, ED25519, or ED448.
+
+ These values are case-insensitive. In some cases, abbreviations are
+ supported, such as ECDSA256 for ECDSAP256SHA256 and ECDSA384 for
+ ECDSAP384SHA384. If RSASHA1 is specified along with the ``-3``
+ option, then NSEC3RSASHA1 is used instead.
+
+ This option is mandatory except when using the
+ ``-S`` option, which copies the algorithm from the predecessory key.
+
+ .. versionchanged:: 9.12.0
+ The default value RSASHA1 for newly generated keys was removed.
+
+``-3``
+ This option uses an NSEC3-capable algorithm to generate a DNSSEC key. If this
+ option is used with an algorithm that has both NSEC and NSEC3
+ versions, then the NSEC3 version is used; for example,
+ ``dnssec-keygen -3a RSASHA1`` specifies the NSEC3RSASHA1 algorithm.
+
+``-E engine``
+ This option specifies the cryptographic hardware to use.
+
+ When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL
+ engine identifier that drives the cryptographic accelerator or
+ hardware service module (usually ``pkcs11``). When BIND is
+ built with native PKCS#11 cryptography (``--enable-native-pkcs11``), it
+ defaults to the path of the PKCS#11 provider library specified via
+ ``--with-pkcs11``.
+
+``-l label``
+ This option specifies the label for a key pair in the crypto hardware.
+
+ When BIND 9 is built with OpenSSL-based PKCS#11 support, the label is
+ an arbitrary string that identifies a particular key. It may be
+ preceded by an optional OpenSSL engine name, followed by a colon, as
+ in ``pkcs11:keylabel``.
+
+ When BIND 9 is built with native PKCS#11 support, the label is a
+ PKCS#11 URI string in the format
+ ``pkcs11:keyword\ =value[;\ keyword\ =value;...]``. Keywords
+ include ``token``, which identifies the HSM; ``object``, which identifies
+ the key; and ``pin-source``, which identifies a file from which the
+ HSM's PIN code can be obtained. The label is stored in the
+ on-disk ``private`` file.
+
+ If the label contains a ``pin-source`` field, tools using the
+ generated key files are able to use the HSM for signing and other
+ operations without any need for an operator to manually enter a PIN.
+ Note: Making the HSM's PIN accessible in this manner may reduce the
+ security advantage of using an HSM; use caution
+ with this feature.
+
+``-n nametype``
+ This option specifies the owner type of the key. The value of ``nametype`` must
+ either be ZONE (for a DNSSEC zone key (KEY/DNSKEY)), HOST or ENTITY
+ (for a key associated with a host (KEY)), USER (for a key associated
+ with a user (KEY)), or OTHER (DNSKEY). These values are
+ case-insensitive.
+
+``-C``
+ This option enables compatibility mode, which generates an old-style key, without any metadata.
+ By default, ``dnssec-keyfromlabel`` includes the key's creation
+ date in the metadata stored with the private key; other dates may
+ be set there as well, including publication date, activation date, etc. Keys
+ that include this data may be incompatible with older versions of
+ BIND; the ``-C`` option suppresses them.
+
+``-c class``
+ This option indicates that the DNS record containing the key should have the
+ specified class. If not specified, class IN is used.
+
+``-f flag``
+ This option sets the specified flag in the ``flag`` field of the KEY/DNSKEY record.
+ The only recognized flags are KSK (Key-Signing Key) and REVOKE.
+
+``-G``
+ This option generates a key, but does not publish it or sign with it. This option is
+ incompatible with ``-P`` and ``-A``.
+
+``-h``
+ This option prints a short summary of the options and arguments to
+ ``dnssec-keyfromlabel``.
+
+``-K directory``
+ This option sets the directory in which the key files are to be written.
+
+``-k``
+ This option generates KEY records rather than DNSKEY records.
+
+``-L`` ttl
+ This option sets the default TTL to use for this key when it is converted into a
+ DNSKEY RR. This is the TTL used when the key is imported into a zone,
+ unless there was already a DNSKEY RRset in
+ place, in which case the existing TTL would take precedence. Setting
+ the default TTL to ``0`` or ``none`` removes it.
+
+``-p protocol``
+ This option sets the protocol value for the key. The protocol is a number between
+ 0 and 255. The default is 3 (DNSSEC). Other possible values for this
+ argument are listed in :rfc:`2535` and its successors.
+
+``-S key``
+ This option generates a key as an explicit successor to an existing key. The name,
+ algorithm, size, and type of the key are set to match the
+ predecessor. The activation date of the new key is set to the
+ inactivation date of the existing one. The publication date is
+ set to the activation date minus the prepublication interval, which
+ defaults to 30 days.
+
+``-t type``
+ This option indicates the type of the key. ``type`` must be one of AUTHCONF,
+ NOAUTHCONF, NOAUTH, or NOCONF. The default is AUTHCONF. AUTH refers
+ to the ability to authenticate data, and CONF to the ability to encrypt
+ data.
+
+``-v level``
+ This option sets the debugging level.
+
+``-V``
+ This option prints version information.
+
+``-y``
+ This option allows DNSSEC key files to be generated even if the key ID would
+ collide with that of an existing key, in the event of either key
+ being revoked. (This is only safe to enable if
+ :rfc:`5011` trust anchor maintenance is not used with either of the keys
+ involved.)
+
+Timing Options
+~~~~~~~~~~~~~~
+
+Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS. If the
+argument begins with a ``+`` or ``-``, it is interpreted as an offset from
+the present time. For convenience, if such an offset is followed by one
+of the suffixes ``y``, ``mo``, ``w``, ``d``, ``h``, or ``mi``, then the offset is
+computed in years (defined as 365 24-hour days, ignoring leap years),
+months (defined as 30 24-hour days), weeks, days, hours, or minutes,
+respectively. Without a suffix, the offset is computed in seconds. To
+explicitly prevent a date from being set, use ``none`` or ``never``.
+
+``-P date/offset``
+ This option sets the date on which a key is to be published to the zone. After
+ that date, the key is included in the zone but is not used
+ to sign it. If not set, and if the ``-G`` option has not been used, the
+ default is the current date.
+
+``-P sync date/offset``
+ This option sets the date on which CDS and CDNSKEY records that match this key
+ are to be published to the zone.
+
+``-A date/offset``
+ This option sets the date on which the key is to be activated. After that date,
+ the key is included in the zone and used to sign it. If not set,
+ and if the ``-G`` option has not been used, the default is the current date.
+
+``-R date/offset``
+ This option sets the date on which the key is to be revoked. After that date, the
+ key is flagged as revoked. It is included in the zone and
+ is used to sign it.
+
+``-I date/offset``
+ This option sets the date on which the key is to be retired. After that date, the
+ key is still included in the zone, but it is not used to
+ sign it.
+
+``-D date/offset``
+ This option sets the date on which the key is to be deleted. After that date, the
+ key is no longer included in the zone. (However, it may remain in the key
+ repository.)
+
+``-D sync date/offset``
+ This option sets the date on which the CDS and CDNSKEY records that match this
+ key are to be deleted.
+
+``-i interval``
+ This option sets the prepublication interval for a key. If set, then the
+ publication and activation dates must be separated by at least this
+ much time. If the activation date is specified but the publication
+ date is not, the publication date defaults to this much time
+ before the activation date; conversely, if the publication date is
+ specified but not the activation date, activation is set to
+ this much time after publication.
+
+ If the key is being created as an explicit successor to another key,
+ then the default prepublication interval is 30 days; otherwise it is
+ zero.
+
+ As with date offsets, if the argument is followed by one of the
+ suffixes ``y``, ``mo``, ``w``, ``d``, ``h``, or ``mi``, the interval is
+ measured in years, months, weeks, days, hours, or minutes,
+ respectively. Without a suffix, the interval is measured in seconds.
+
+Generated Key Files
+~~~~~~~~~~~~~~~~~~~
+
+When ``dnssec-keyfromlabel`` completes successfully, it prints a string
+of the form ``Knnnn.+aaa+iiiii`` to the standard output. This is an
+identification string for the key files it has generated.
+
+- ``nnnn`` is the key name.
+
+- ``aaa`` is the numeric representation of the algorithm.
+
+- ``iiiii`` is the key identifier (or footprint).
+
+``dnssec-keyfromlabel`` creates two files, with names based on the
+printed string. ``Knnnn.+aaa+iiiii.key`` contains the public key, and
+``Knnnn.+aaa+iiiii.private`` contains the private key.
+
+The ``.key`` file contains a DNS KEY record that can be inserted into a
+zone file (directly or with an $INCLUDE statement).
+
+The ``.private`` file contains algorithm-specific fields. For obvious
+security reasons, this file does not have general read permission.
+
+See Also
+~~~~~~~~
+
+:manpage:`dnssec-keygen(8)`, :manpage:`dnssec-signzone(8)`, BIND 9 Administrator Reference Manual,
+:rfc:`4034`, :rfc:`7512`.
diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c
new file mode 100644
index 0000000..1c663e2
--- /dev/null
+++ b/bin/dnssec/dnssec-keygen.c
@@ -0,0 +1,1315 @@
+/*
+ * Portions 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.
+ *
+ * Portions Copyright (C) Network Associates, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/region.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <pk11/site.h>
+
+#include <dns/dnssec.h>
+#include <dns/fixedname.h>
+#include <dns/kasp.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/result.h>
+#include <dns/secalg.h>
+
+#include <dst/dst.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/grammar.h>
+#include <isccfg/kaspconf.h>
+#include <isccfg/namedconf.h>
+
+#if USE_PKCS11
+#include <pk11/result.h>
+#endif /* if USE_PKCS11 */
+
+#include "dnssectool.h"
+
+#define MAX_RSA 4096 /* should be long enough... */
+
+const char *program = "dnssec-keygen";
+
+isc_log_t *lctx = NULL;
+
+ISC_PLATFORM_NORETURN_PRE static void
+usage(void) ISC_PLATFORM_NORETURN_POST;
+
+static void
+progress(int p);
+
+struct keygen_ctx {
+ const char *predecessor;
+ const char *policy;
+ const char *configfile;
+ const char *directory;
+ char *algname;
+ char *nametype;
+ char *type;
+ int generator;
+ int protocol;
+ int size;
+ int signatory;
+ dns_rdataclass_t rdclass;
+ int options;
+ int dbits;
+ dns_ttl_t ttl;
+ uint16_t kskflag;
+ uint16_t revflag;
+ dns_secalg_t alg;
+ /* timing data */
+ int prepub;
+ isc_stdtime_t now;
+ isc_stdtime_t publish;
+ isc_stdtime_t activate;
+ isc_stdtime_t inactive;
+ isc_stdtime_t revokekey;
+ isc_stdtime_t deltime;
+ isc_stdtime_t syncadd;
+ isc_stdtime_t syncdel;
+ bool setpub;
+ bool setact;
+ bool setinact;
+ bool setrev;
+ bool setdel;
+ bool setsyncadd;
+ bool setsyncdel;
+ bool unsetpub;
+ bool unsetact;
+ bool unsetinact;
+ bool unsetrev;
+ bool unsetdel;
+ /* how to generate the key */
+ bool setttl;
+ bool use_nsec3;
+ bool genonly;
+ bool showprogress;
+ bool quiet;
+ bool oldstyle;
+ /* state */
+ time_t lifetime;
+ bool ksk;
+ bool zsk;
+};
+
+typedef struct keygen_ctx keygen_ctx_t;
+
+static void
+usage(void) {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, " %s [options] name\n\n", program);
+ fprintf(stderr, "Version: %s\n", VERSION);
+ fprintf(stderr, " name: owner of the key\n");
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -K <directory>: write keys into directory\n");
+ fprintf(stderr, " -k <policy>: generate keys for dnssec-policy\n");
+ fprintf(stderr, " -l <file>: configuration file with dnssec-policy "
+ "statement\n");
+ fprintf(stderr, " -a <algorithm>:\n");
+ fprintf(stderr, " RSASHA1 | NSEC3RSASHA1 |\n");
+ fprintf(stderr, " RSASHA256 | RSASHA512 |\n");
+ fprintf(stderr, " ECDSAP256SHA256 | ECDSAP384SHA384 |\n");
+ fprintf(stderr, " ED25519 | ED448 | DH\n");
+ fprintf(stderr, " -3: use NSEC3-capable algorithm\n");
+ fprintf(stderr, " -b <key size in bits>:\n");
+ fprintf(stderr, " RSASHA1:\t[1024..%d]\n", MAX_RSA);
+ fprintf(stderr, " NSEC3RSASHA1:\t[1024..%d]\n", MAX_RSA);
+ fprintf(stderr, " RSASHA256:\t[1024..%d]\n", MAX_RSA);
+ fprintf(stderr, " RSASHA512:\t[1024..%d]\n", MAX_RSA);
+ fprintf(stderr, " DH:\t\t[128..4096]\n");
+ fprintf(stderr, " ECDSAP256SHA256:\tignored\n");
+ fprintf(stderr, " ECDSAP384SHA384:\tignored\n");
+ fprintf(stderr, " ED25519:\tignored\n");
+ fprintf(stderr, " ED448:\tignored\n");
+ fprintf(stderr, " (key size defaults are set according to\n"
+ " algorithm and usage (ZSK or KSK)\n");
+ fprintf(stderr, " -n <nametype>: ZONE | HOST | ENTITY | "
+ "USER | OTHER\n");
+ fprintf(stderr, " (DNSKEY generation defaults to ZONE)\n");
+ fprintf(stderr, " -c <class>: (default: IN)\n");
+ fprintf(stderr, " -d <digest bits> (0 => max, default)\n");
+ fprintf(stderr, " -E <engine>:\n");
+#if USE_PKCS11
+ fprintf(stderr,
+ " path to PKCS#11 provider library "
+ "(default is %s)\n",
+ PK11_LIB_LOCATION);
+#else /* if USE_PKCS11 */
+ fprintf(stderr, " name of an OpenSSL engine to use\n");
+#endif /* if USE_PKCS11 */
+ fprintf(stderr, " -f <keyflag>: KSK | REVOKE\n");
+ fprintf(stderr, " -g <generator>: use specified generator "
+ "(DH only)\n");
+ fprintf(stderr, " -L <ttl>: default key TTL\n");
+ fprintf(stderr, " -p <protocol>: (default: 3 [dnssec])\n");
+ fprintf(stderr, " -s <strength>: strength value this key signs DNS "
+ "records with (default: 0)\n");
+ fprintf(stderr, " -T <rrtype>: DNSKEY | KEY (default: DNSKEY; "
+ "use KEY for SIG(0))\n");
+ fprintf(stderr, " -t <type>: "
+ "AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF "
+ "(default: AUTHCONF)\n");
+ fprintf(stderr, " -h: print usage and exit\n");
+ fprintf(stderr, " -m <memory debugging mode>:\n");
+ fprintf(stderr, " usage | trace | record | size | mctx\n");
+ fprintf(stderr, " -v <level>: set verbosity level (0 - 10)\n");
+ fprintf(stderr, " -V: print version information\n");
+ fprintf(stderr, "Timing options:\n");
+ fprintf(stderr, " -P date/[+-]offset/none: set key publication date "
+ "(default: now)\n");
+ fprintf(stderr, " -P sync date/[+-]offset/none: set CDS and CDNSKEY "
+ "publication date\n");
+ fprintf(stderr, " -A date/[+-]offset/none: set key activation date "
+ "(default: now)\n");
+ fprintf(stderr, " -R date/[+-]offset/none: set key "
+ "revocation date\n");
+ fprintf(stderr, " -I date/[+-]offset/none: set key "
+ "inactivation date\n");
+ fprintf(stderr, " -D date/[+-]offset/none: set key deletion date\n");
+ fprintf(stderr, " -D sync date/[+-]offset/none: set CDS and CDNSKEY "
+ "deletion date\n");
+
+ fprintf(stderr, " -G: generate key only; do not set -P or -A\n");
+ fprintf(stderr, " -C: generate a backward-compatible key, omitting "
+ "all dates\n");
+ fprintf(stderr, " -S <key>: generate a successor to an existing "
+ "key\n");
+ fprintf(stderr, " -i <interval>: prepublication interval for "
+ "successor key "
+ "(default: 30 days)\n");
+ fprintf(stderr, "Output:\n");
+ fprintf(stderr, " K<name>+<alg>+<id>.key, "
+ "K<name>+<alg>+<id>.private\n");
+
+ exit(-1);
+}
+
+static void
+progress(int p) {
+ char c = '*';
+
+ switch (p) {
+ case 0:
+ c = '.';
+ break;
+ case 1:
+ c = '+';
+ break;
+ case 2:
+ c = '*';
+ break;
+ case 3:
+ c = ' ';
+ break;
+ default:
+ break;
+ }
+ (void)putc(c, stderr);
+ (void)fflush(stderr);
+}
+
+static void
+kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name,
+ dns_kasp_t **kaspp) {
+ const cfg_listelt_t *element;
+ const cfg_obj_t *kasps = NULL;
+ dns_kasp_t *kasp = NULL, *kasp_next;
+ isc_result_t result = ISC_R_NOTFOUND;
+ dns_kasplist_t kasplist;
+
+ ISC_LIST_INIT(kasplist);
+
+ (void)cfg_map_get(config, "dnssec-policy", &kasps);
+ for (element = cfg_list_first(kasps); element != NULL;
+ element = cfg_list_next(element))
+ {
+ cfg_obj_t *kconfig = cfg_listelt_value(element);
+ kasp = NULL;
+ if (strcmp(cfg_obj_asstring(cfg_tuple_get(kconfig, "name")),
+ name) != 0)
+ {
+ continue;
+ }
+
+ result = cfg_kasp_fromconfig(kconfig, NULL, mctx, lctx,
+ &kasplist, &kasp);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to configure dnssec-policy '%s': %s",
+ cfg_obj_asstring(cfg_tuple_get(kconfig, "name")),
+ isc_result_totext(result));
+ }
+ INSIST(kasp != NULL);
+ dns_kasp_freeze(kasp);
+ break;
+ }
+
+ *kaspp = kasp;
+
+ /*
+ * Cleanup kasp list.
+ */
+ for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; kasp = kasp_next) {
+ kasp_next = ISC_LIST_NEXT(kasp, link);
+ ISC_LIST_UNLINK(kasplist, kasp, link);
+ dns_kasp_detach(&kasp);
+ }
+}
+
+static void
+keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) {
+ char filename[255];
+ char algstr[DNS_SECALG_FORMATSIZE];
+ uint16_t flags = 0;
+ int param = 0;
+ bool null_key = false;
+ bool conflict = false;
+ bool show_progress = false;
+ isc_buffer_t buf;
+ dns_name_t *name;
+ dns_fixedname_t fname;
+ isc_result_t ret;
+ dst_key_t *key = NULL;
+ dst_key_t *prevkey = NULL;
+
+ UNUSED(argc);
+
+ dns_secalg_format(ctx->alg, algstr, sizeof(algstr));
+
+ if (ctx->predecessor == NULL) {
+ if (ctx->prepub == -1) {
+ ctx->prepub = 0;
+ }
+
+ name = dns_fixedname_initname(&fname);
+ isc_buffer_init(&buf, argv[isc_commandline_index],
+ strlen(argv[isc_commandline_index]));
+ isc_buffer_add(&buf, strlen(argv[isc_commandline_index]));
+ ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("invalid key name %s: %s",
+ argv[isc_commandline_index],
+ isc_result_totext(ret));
+ }
+
+ if (!dst_algorithm_supported(ctx->alg)) {
+ fatal("unsupported algorithm: %s", algstr);
+ }
+
+ if (ctx->alg == DST_ALG_DH) {
+ ctx->options |= DST_TYPE_KEY;
+ }
+
+ if (ctx->use_nsec3) {
+ switch (ctx->alg) {
+ case DST_ALG_RSASHA1:
+ ctx->alg = DST_ALG_NSEC3RSASHA1;
+ break;
+ case DST_ALG_NSEC3RSASHA1:
+ case DST_ALG_RSASHA256:
+ case DST_ALG_RSASHA512:
+ case DST_ALG_ECDSA256:
+ case DST_ALG_ECDSA384:
+ case DST_ALG_ED25519:
+ case DST_ALG_ED448:
+ break;
+ default:
+ fatal("algorithm %s is incompatible with NSEC3"
+ ", do not use the -3 option",
+ algstr);
+ }
+ }
+
+ if (ctx->type != NULL && (ctx->options & DST_TYPE_KEY) != 0) {
+ if (strcasecmp(ctx->type, "NOAUTH") == 0) {
+ flags |= DNS_KEYTYPE_NOAUTH;
+ } else if (strcasecmp(ctx->type, "NOCONF") == 0) {
+ flags |= DNS_KEYTYPE_NOCONF;
+ } else if (strcasecmp(ctx->type, "NOAUTHCONF") == 0) {
+ flags |= (DNS_KEYTYPE_NOAUTH |
+ DNS_KEYTYPE_NOCONF);
+ if (ctx->size < 0) {
+ ctx->size = 0;
+ }
+ } else if (strcasecmp(ctx->type, "AUTHCONF") == 0) {
+ /* nothing */
+ } else {
+ fatal("invalid type %s", ctx->type);
+ }
+ }
+
+ if (ctx->size < 0) {
+ switch (ctx->alg) {
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ case DST_ALG_RSASHA256:
+ case DST_ALG_RSASHA512:
+ ctx->size = 2048;
+ if (verbose > 0) {
+ fprintf(stderr,
+ "key size not "
+ "specified; defaulting"
+ " to %d\n",
+ ctx->size);
+ }
+ break;
+ case DST_ALG_ECDSA256:
+ case DST_ALG_ECDSA384:
+ case DST_ALG_ED25519:
+ case DST_ALG_ED448:
+ break;
+ default:
+ fatal("key size not specified (-b option)");
+ }
+ }
+
+ if (!ctx->oldstyle && ctx->prepub > 0) {
+ if (ctx->setpub && ctx->setact &&
+ (ctx->activate - ctx->prepub) < ctx->publish)
+ {
+ fatal("Activation and publication dates "
+ "are closer together than the\n\t"
+ "prepublication interval.");
+ }
+
+ if (!ctx->setpub && !ctx->setact) {
+ ctx->setpub = ctx->setact = true;
+ ctx->publish = ctx->now;
+ ctx->activate = ctx->now + ctx->prepub;
+ } else if (ctx->setpub && !ctx->setact) {
+ ctx->setact = true;
+ ctx->activate = ctx->publish + ctx->prepub;
+ } else if (ctx->setact && !ctx->setpub) {
+ ctx->setpub = true;
+ ctx->publish = ctx->activate - ctx->prepub;
+ }
+
+ if ((ctx->activate - ctx->prepub) < ctx->now) {
+ fatal("Time until activation is shorter "
+ "than the\n\tprepublication interval.");
+ }
+ }
+ } else {
+ char keystr[DST_KEY_FORMATSIZE];
+ isc_stdtime_t when;
+ int major, minor;
+
+ if (ctx->prepub == -1) {
+ ctx->prepub = (30 * 86400);
+ }
+
+ if (ctx->alg != 0) {
+ fatal("-S and -a cannot be used together");
+ }
+ if (ctx->size >= 0) {
+ fatal("-S and -b cannot be used together");
+ }
+ if (ctx->nametype != NULL) {
+ fatal("-S and -n cannot be used together");
+ }
+ if (ctx->type != NULL) {
+ fatal("-S and -t cannot be used together");
+ }
+ if (ctx->setpub || ctx->unsetpub) {
+ fatal("-S and -P cannot be used together");
+ }
+ if (ctx->setact || ctx->unsetact) {
+ fatal("-S and -A cannot be used together");
+ }
+ if (ctx->use_nsec3) {
+ fatal("-S and -3 cannot be used together");
+ }
+ if (ctx->oldstyle) {
+ fatal("-S and -C cannot be used together");
+ }
+ if (ctx->genonly) {
+ fatal("-S and -G cannot be used together");
+ }
+
+ ret = dst_key_fromnamedfile(
+ ctx->predecessor, ctx->directory,
+ (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE),
+ mctx, &prevkey);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("Invalid keyfile %s: %s", ctx->predecessor,
+ isc_result_totext(ret));
+ }
+ if (!dst_key_isprivate(prevkey)) {
+ fatal("%s is not a private key", ctx->predecessor);
+ }
+
+ name = dst_key_name(prevkey);
+ ctx->alg = dst_key_alg(prevkey);
+ ctx->size = dst_key_size(prevkey);
+ flags = dst_key_flags(prevkey);
+
+ dst_key_format(prevkey, keystr, sizeof(keystr));
+ dst_key_getprivateformat(prevkey, &major, &minor);
+ if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) {
+ fatal("Key %s has incompatible format version %d.%d\n\t"
+ "It is not possible to generate a successor key.",
+ keystr, major, minor);
+ }
+
+ ret = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &when);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("Key %s has no activation date.\n\t"
+ "You must use dnssec-settime -A to set one "
+ "before generating a successor.",
+ keystr);
+ }
+
+ ret = dst_key_gettime(prevkey, DST_TIME_INACTIVE,
+ &ctx->activate);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("Key %s has no inactivation date.\n\t"
+ "You must use dnssec-settime -I to set one "
+ "before generating a successor.",
+ keystr);
+ }
+
+ ctx->publish = ctx->activate - ctx->prepub;
+ if (ctx->publish < ctx->now) {
+ fatal("Key %s becomes inactive\n\t"
+ "sooner than the prepublication period "
+ "for the new key ends.\n\t"
+ "Either change the inactivation date with "
+ "dnssec-settime -I,\n\t"
+ "or use the -i option to set a shorter "
+ "prepublication interval.",
+ keystr);
+ }
+
+ ret = dst_key_gettime(prevkey, DST_TIME_DELETE, &when);
+ if (ret != ISC_R_SUCCESS) {
+ fprintf(stderr,
+ "%s: WARNING: Key %s has no removal "
+ "date;\n\t it will remain in the zone "
+ "indefinitely after rollover.\n\t "
+ "You can use dnssec-settime -D to "
+ "change this.\n",
+ program, keystr);
+ }
+
+ ctx->setpub = ctx->setact = true;
+ }
+
+ switch (ctx->alg) {
+ case DNS_KEYALG_RSASHA1:
+ case DNS_KEYALG_NSEC3RSASHA1:
+ case DNS_KEYALG_RSASHA256:
+ if (ctx->size != 0 && (ctx->size < 1024 || ctx->size > MAX_RSA))
+ {
+ fatal("RSA key size %d out of range", ctx->size);
+ }
+ break;
+ case DNS_KEYALG_RSASHA512:
+ if (ctx->size != 0 && (ctx->size < 1024 || ctx->size > MAX_RSA))
+ {
+ fatal("RSA key size %d out of range", ctx->size);
+ }
+ break;
+ case DNS_KEYALG_DH:
+ if (ctx->size != 0 && (ctx->size < 128 || ctx->size > 4096)) {
+ fatal("DH key size %d out of range", ctx->size);
+ }
+ break;
+ case DST_ALG_ECDSA256:
+ ctx->size = 256;
+ break;
+ case DST_ALG_ECDSA384:
+ ctx->size = 384;
+ break;
+ case DST_ALG_ED25519:
+ ctx->size = 256;
+ break;
+ case DST_ALG_ED448:
+ ctx->size = 456;
+ break;
+ }
+
+ if (ctx->alg != DNS_KEYALG_DH && ctx->generator != 0) {
+ fatal("specified DH generator for a non-DH key");
+ }
+
+ if (ctx->nametype == NULL) {
+ if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */
+ fatal("no nametype specified");
+ }
+ flags |= DNS_KEYOWNER_ZONE; /* DNSKEY */
+ } else if (strcasecmp(ctx->nametype, "zone") == 0) {
+ flags |= DNS_KEYOWNER_ZONE;
+ } else if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */
+ if (strcasecmp(ctx->nametype, "host") == 0 ||
+ strcasecmp(ctx->nametype, "entity") == 0)
+ {
+ flags |= DNS_KEYOWNER_ENTITY;
+ } else if (strcasecmp(ctx->nametype, "user") == 0) {
+ flags |= DNS_KEYOWNER_USER;
+ } else {
+ fatal("invalid KEY nametype %s", ctx->nametype);
+ }
+ } else if (strcasecmp(ctx->nametype, "other") != 0) { /* DNSKEY */
+ fatal("invalid DNSKEY nametype %s", ctx->nametype);
+ }
+
+ if (ctx->directory == NULL) {
+ ctx->directory = ".";
+ }
+
+ if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */
+ flags |= ctx->signatory;
+ } else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */
+ flags |= ctx->kskflag;
+ flags |= ctx->revflag;
+ }
+
+ if (ctx->protocol == -1) {
+ ctx->protocol = DNS_KEYPROTO_DNSSEC;
+ } else if ((ctx->options & DST_TYPE_KEY) == 0 &&
+ ctx->protocol != DNS_KEYPROTO_DNSSEC)
+ {
+ fatal("invalid DNSKEY protocol: %d", ctx->protocol);
+ }
+
+ if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
+ if (ctx->size > 0) {
+ fatal("specified null key with non-zero size");
+ }
+ if ((flags & DNS_KEYFLAG_SIGNATORYMASK) != 0) {
+ fatal("specified null key with signing authority");
+ }
+ }
+
+ if ((flags & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE &&
+ ctx->alg == DNS_KEYALG_DH)
+ {
+ fatal("a key with algorithm %s cannot be a zone key", algstr);
+ }
+
+ switch (ctx->alg) {
+ case DNS_KEYALG_RSASHA1:
+ case DNS_KEYALG_NSEC3RSASHA1:
+ case DNS_KEYALG_RSASHA256:
+ case DNS_KEYALG_RSASHA512:
+ show_progress = true;
+ break;
+
+ case DNS_KEYALG_DH:
+ param = ctx->generator;
+ break;
+
+ case DST_ALG_ECDSA256:
+ case DST_ALG_ECDSA384:
+ case DST_ALG_ED25519:
+ case DST_ALG_ED448:
+ show_progress = true;
+ break;
+ }
+
+ if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
+ null_key = true;
+ }
+
+ isc_buffer_init(&buf, filename, sizeof(filename) - 1);
+
+ do {
+ conflict = false;
+
+ if (!ctx->quiet && show_progress) {
+ fprintf(stderr, "Generating key pair.");
+ ret = dst_key_generate(name, ctx->alg, ctx->size, param,
+ flags, ctx->protocol,
+ ctx->rdclass, mctx, &key,
+ &progress);
+ putc('\n', stderr);
+ fflush(stderr);
+ } else {
+ ret = dst_key_generate(name, ctx->alg, ctx->size, param,
+ flags, ctx->protocol,
+ ctx->rdclass, mctx, &key, NULL);
+ }
+
+ if (ret != ISC_R_SUCCESS) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namestr, sizeof(namestr));
+ fatal("failed to generate key %s/%s: %s\n", namestr,
+ algstr, isc_result_totext(ret));
+ }
+
+ dst_key_setbits(key, ctx->dbits);
+
+ /*
+ * Set key timing metadata (unless using -C)
+ *
+ * Creation date is always set to "now".
+ *
+ * For a new key without an explicit predecessor, publish
+ * and activation dates are set to "now" by default, but
+ * can both be overridden.
+ *
+ * For a successor key, activation is set to match the
+ * predecessor's inactivation date. Publish is set to 30
+ * days earlier than that (XXX: this should be configurable).
+ * If either of the resulting dates are in the past, that's
+ * an error; the inactivation date of the predecessor key
+ * must be updated before a successor key can be created.
+ */
+ if (!ctx->oldstyle) {
+ dst_key_settime(key, DST_TIME_CREATED, ctx->now);
+
+ if (ctx->genonly && (ctx->setpub || ctx->setact)) {
+ fatal("cannot use -G together with "
+ "-P or -A options");
+ }
+
+ if (ctx->setpub) {
+ dst_key_settime(key, DST_TIME_PUBLISH,
+ ctx->publish);
+ } else if (ctx->setact && !ctx->unsetpub) {
+ dst_key_settime(key, DST_TIME_PUBLISH,
+ ctx->activate - ctx->prepub);
+ } else if (!ctx->genonly && !ctx->unsetpub) {
+ dst_key_settime(key, DST_TIME_PUBLISH,
+ ctx->now);
+ }
+
+ if (ctx->setact) {
+ dst_key_settime(key, DST_TIME_ACTIVATE,
+ ctx->activate);
+ } else if (!ctx->genonly && !ctx->unsetact) {
+ dst_key_settime(key, DST_TIME_ACTIVATE,
+ ctx->now);
+ }
+
+ if (ctx->setrev) {
+ if (ctx->kskflag == 0) {
+ fprintf(stderr,
+ "%s: warning: Key is "
+ "not flagged as a KSK, but -R "
+ "was used. Revoking a ZSK is "
+ "legal, but undefined.\n",
+ program);
+ }
+ dst_key_settime(key, DST_TIME_REVOKE,
+ ctx->revokekey);
+ }
+
+ if (ctx->setinact) {
+ dst_key_settime(key, DST_TIME_INACTIVE,
+ ctx->inactive);
+ }
+
+ if (ctx->setdel) {
+ if (ctx->setinact &&
+ ctx->deltime < ctx->inactive)
+ {
+ fprintf(stderr,
+ "%s: warning: Key is "
+ "scheduled to be deleted "
+ "before it is scheduled to be "
+ "made inactive.\n",
+ program);
+ }
+ dst_key_settime(key, DST_TIME_DELETE,
+ ctx->deltime);
+ }
+
+ if (ctx->setsyncadd) {
+ dst_key_settime(key, DST_TIME_SYNCPUBLISH,
+ ctx->syncadd);
+ }
+
+ if (ctx->setsyncdel) {
+ dst_key_settime(key, DST_TIME_SYNCDELETE,
+ ctx->syncdel);
+ }
+ } else {
+ if (ctx->setpub || ctx->setact || ctx->setrev ||
+ ctx->setinact || ctx->setdel || ctx->unsetpub ||
+ ctx->unsetact || ctx->unsetrev || ctx->unsetinact ||
+ ctx->unsetdel || ctx->genonly || ctx->setsyncadd ||
+ ctx->setsyncdel)
+ {
+ fatal("cannot use -C together with "
+ "-P, -A, -R, -I, -D, or -G options");
+ }
+ /*
+ * Compatibility mode: Private-key-format
+ * should be set to 1.2.
+ */
+ dst_key_setprivateformat(key, 1, 2);
+ }
+
+ /* Set the default key TTL */
+ if (ctx->setttl) {
+ dst_key_setttl(key, ctx->ttl);
+ }
+
+ /* Set dnssec-policy related metadata */
+ if (ctx->policy != NULL) {
+ dst_key_setnum(key, DST_NUM_LIFETIME, ctx->lifetime);
+ dst_key_setbool(key, DST_BOOL_KSK, ctx->ksk);
+ dst_key_setbool(key, DST_BOOL_ZSK, ctx->zsk);
+ }
+
+ /*
+ * Do not overwrite an existing key, or create a key
+ * if there is a risk of ID collision due to this key
+ * or another key being revoked.
+ */
+ if (key_collision(key, name, ctx->directory, mctx, NULL)) {
+ conflict = true;
+ if (null_key) {
+ dst_key_free(&key);
+ break;
+ }
+
+ if (verbose > 0) {
+ isc_buffer_clear(&buf);
+ ret = dst_key_buildfilename(
+ key, 0, ctx->directory, &buf);
+ if (ret == ISC_R_SUCCESS) {
+ fprintf(stderr,
+ "%s: %s already exists, or "
+ "might collide with another "
+ "key upon revokation. "
+ "Generating a new key\n",
+ program, filename);
+ }
+ }
+
+ dst_key_free(&key);
+ }
+ } while (conflict);
+
+ if (conflict) {
+ fatal("cannot generate a null key due to possible key ID "
+ "collision");
+ }
+
+ if (ctx->predecessor != NULL && prevkey != NULL) {
+ dst_key_setnum(prevkey, DST_NUM_SUCCESSOR, dst_key_id(key));
+ dst_key_setnum(key, DST_NUM_PREDECESSOR, dst_key_id(prevkey));
+
+ ret = dst_key_tofile(prevkey, ctx->options, ctx->directory);
+ if (ret != ISC_R_SUCCESS) {
+ char keystr[DST_KEY_FORMATSIZE];
+ dst_key_format(prevkey, keystr, sizeof(keystr));
+ fatal("failed to update predecessor %s: %s\n", keystr,
+ isc_result_totext(ret));
+ }
+ }
+
+ ret = dst_key_tofile(key, ctx->options, ctx->directory);
+ if (ret != ISC_R_SUCCESS) {
+ char keystr[DST_KEY_FORMATSIZE];
+ dst_key_format(key, keystr, sizeof(keystr));
+ fatal("failed to write key %s: %s\n", keystr,
+ isc_result_totext(ret));
+ }
+
+ isc_buffer_clear(&buf);
+ ret = dst_key_buildfilename(key, 0, NULL, &buf);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("dst_key_buildfilename returned: %s\n",
+ isc_result_totext(ret));
+ }
+ printf("%s\n", filename);
+
+ dst_key_free(&key);
+ if (prevkey != NULL) {
+ dst_key_free(&prevkey);
+ }
+}
+
+int
+main(int argc, char **argv) {
+ char *algname = NULL, *freeit = NULL;
+ char *classname = NULL;
+ char *endp;
+ isc_mem_t *mctx = NULL;
+ isc_result_t ret;
+ isc_textregion_t r;
+ const char *engine = NULL;
+ unsigned char c;
+ int ch;
+
+ keygen_ctx_t ctx = {
+ .options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC,
+ .prepub = -1,
+ .protocol = -1,
+ .size = -1,
+ };
+
+ if (argc == 1) {
+ usage();
+ }
+
+#if USE_PKCS11
+ pk11_result_register();
+#endif /* if USE_PKCS11 */
+ dns_result_register();
+
+ isc_commandline_errprint = false;
+
+ /*
+ * Process memory debugging argument first.
+ */
+#define CMDLINE_FLAGS \
+ "3A:a:b:Cc:D:d:E:eFf:Gg:hI:i:K:k:L:l:m:n:P:p:qR:r:S:s:" \
+ "T:t:v:V"
+ while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
+ switch (ch) {
+ case 'm':
+ if (strcasecmp(isc_commandline_argument, "record") == 0)
+ {
+ isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
+ }
+ if (strcasecmp(isc_commandline_argument, "trace") == 0)
+ {
+ isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
+ }
+ if (strcasecmp(isc_commandline_argument, "usage") == 0)
+ {
+ isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
+ }
+ if (strcasecmp(isc_commandline_argument, "size") == 0) {
+ isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
+ }
+ if (strcasecmp(isc_commandline_argument, "mctx") == 0) {
+ isc_mem_debugging |= ISC_MEM_DEBUGCTX;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ isc_commandline_reset = true;
+
+ isc_mem_create(&mctx);
+ isc_stdtime_get(&ctx.now);
+
+ while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
+ switch (ch) {
+ case '3':
+ ctx.use_nsec3 = true;
+ break;
+ case 'a':
+ algname = isc_commandline_argument;
+ break;
+ case 'b':
+ ctx.size = strtol(isc_commandline_argument, &endp, 10);
+ if (*endp != '\0' || ctx.size < 0) {
+ fatal("-b requires a non-negative number");
+ }
+ break;
+ case 'C':
+ ctx.oldstyle = true;
+ break;
+ case 'c':
+ classname = isc_commandline_argument;
+ break;
+ case 'd':
+ ctx.dbits = strtol(isc_commandline_argument, &endp, 10);
+ if (*endp != '\0' || ctx.dbits < 0) {
+ fatal("-d requires a non-negative number");
+ }
+ break;
+ case 'E':
+ engine = isc_commandline_argument;
+ break;
+ case 'e':
+ fprintf(stderr, "phased-out option -e "
+ "(was 'use (RSA) large exponent')\n");
+ break;
+ case 'f':
+ c = (unsigned char)(isc_commandline_argument[0]);
+ if (toupper(c) == 'K') {
+ ctx.kskflag = DNS_KEYFLAG_KSK;
+ } else if (toupper(c) == 'R') {
+ ctx.revflag = DNS_KEYFLAG_REVOKE;
+ } else {
+ fatal("unknown flag '%s'",
+ isc_commandline_argument);
+ }
+ break;
+ case 'g':
+ ctx.generator = strtol(isc_commandline_argument, &endp,
+ 10);
+ if (*endp != '\0' || ctx.generator <= 0) {
+ fatal("-g requires a positive number");
+ }
+ break;
+ case 'K':
+ ctx.directory = isc_commandline_argument;
+ ret = try_dir(ctx.directory);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("cannot open directory %s: %s",
+ ctx.directory, isc_result_totext(ret));
+ }
+ break;
+ case 'k':
+ ctx.policy = isc_commandline_argument;
+ break;
+ case 'L':
+ ctx.ttl = strtottl(isc_commandline_argument);
+ ctx.setttl = true;
+ break;
+ case 'l':
+ ctx.configfile = isc_commandline_argument;
+ break;
+ case 'n':
+ ctx.nametype = isc_commandline_argument;
+ break;
+ case 'm':
+ break;
+ case 'p':
+ ctx.protocol = strtol(isc_commandline_argument, &endp,
+ 10);
+ if (*endp != '\0' || ctx.protocol < 0 ||
+ ctx.protocol > 255)
+ {
+ fatal("-p must be followed by a number "
+ "[0..255]");
+ }
+ break;
+ case 'q':
+ ctx.quiet = true;
+ break;
+ case 'r':
+ fatal("The -r option has been deprecated.\n"
+ "System random data is always used.\n");
+ break;
+ case 's':
+ ctx.signatory = strtol(isc_commandline_argument, &endp,
+ 10);
+ if (*endp != '\0' || ctx.signatory < 0 ||
+ ctx.signatory > 15)
+ {
+ fatal("-s must be followed by a number "
+ "[0..15]");
+ }
+ break;
+ case 'T':
+ if (strcasecmp(isc_commandline_argument, "KEY") == 0) {
+ ctx.options |= DST_TYPE_KEY;
+ } else if (strcasecmp(isc_commandline_argument,
+ "DNSKE"
+ "Y") == 0)
+ {
+ /* default behavior */
+ } else {
+ fatal("unknown type '%s'",
+ isc_commandline_argument);
+ }
+ break;
+ case 't':
+ ctx.type = isc_commandline_argument;
+ break;
+ case 'v':
+ endp = NULL;
+ verbose = strtol(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0') {
+ fatal("-v must be followed by a number");
+ }
+ break;
+ case 'G':
+ ctx.genonly = true;
+ break;
+ case 'P':
+ /* -Psync ? */
+ if (isoptarg("sync", argv, usage)) {
+ if (ctx.setsyncadd) {
+ fatal("-P sync specified more than "
+ "once");
+ }
+
+ ctx.syncadd = strtotime(
+ isc_commandline_argument, ctx.now,
+ ctx.now, &ctx.setsyncadd);
+ break;
+ }
+ (void)isoptarg("dnskey", argv, usage);
+ if (ctx.setpub || ctx.unsetpub) {
+ fatal("-P specified more than once");
+ }
+
+ ctx.publish = strtotime(isc_commandline_argument,
+ ctx.now, ctx.now, &ctx.setpub);
+ ctx.unsetpub = !ctx.setpub;
+ break;
+ case 'A':
+ if (ctx.setact || ctx.unsetact) {
+ fatal("-A specified more than once");
+ }
+
+ ctx.activate = strtotime(isc_commandline_argument,
+ ctx.now, ctx.now, &ctx.setact);
+ ctx.unsetact = !ctx.setact;
+ break;
+ case 'R':
+ if (ctx.setrev || ctx.unsetrev) {
+ fatal("-R specified more than once");
+ }
+
+ ctx.revokekey = strtotime(isc_commandline_argument,
+ ctx.now, ctx.now,
+ &ctx.setrev);
+ ctx.unsetrev = !ctx.setrev;
+ break;
+ case 'I':
+ if (ctx.setinact || ctx.unsetinact) {
+ fatal("-I specified more than once");
+ }
+
+ ctx.inactive = strtotime(isc_commandline_argument,
+ ctx.now, ctx.now,
+ &ctx.setinact);
+ ctx.unsetinact = !ctx.setinact;
+ break;
+ case 'D':
+ /* -Dsync ? */
+ if (isoptarg("sync", argv, usage)) {
+ if (ctx.setsyncdel) {
+ fatal("-D sync specified more than "
+ "once");
+ }
+
+ ctx.syncdel = strtotime(
+ isc_commandline_argument, ctx.now,
+ ctx.now, &ctx.setsyncdel);
+ break;
+ }
+ (void)isoptarg("dnskey", argv, usage);
+ if (ctx.setdel || ctx.unsetdel) {
+ fatal("-D specified more than once");
+ }
+
+ ctx.deltime = strtotime(isc_commandline_argument,
+ ctx.now, ctx.now, &ctx.setdel);
+ ctx.unsetdel = !ctx.setdel;
+ break;
+ case 'S':
+ ctx.predecessor = isc_commandline_argument;
+ break;
+ case 'i':
+ ctx.prepub = strtottl(isc_commandline_argument);
+ break;
+ case 'F':
+ /* Reserved for FIPS mode */
+ FALLTHROUGH;
+ 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);
+ }
+ }
+
+ if (!isatty(0)) {
+ ctx.quiet = true;
+ }
+
+ ret = dst_lib_init(mctx, engine);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("could not initialize dst: %s", isc_result_totext(ret));
+ }
+
+ setup_logging(mctx, &lctx);
+
+ ctx.rdclass = strtoclass(classname);
+
+ if (ctx.configfile == NULL || ctx.configfile[0] == '\0') {
+ ctx.configfile = NAMED_CONFFILE;
+ }
+
+ if (ctx.predecessor == NULL) {
+ if (argc < isc_commandline_index + 1) {
+ fatal("the key name was not specified");
+ }
+ if (argc > isc_commandline_index + 1) {
+ fatal("extraneous arguments");
+ }
+ }
+
+ if (ctx.predecessor == NULL && ctx.policy == NULL) {
+ if (algname == NULL) {
+ fatal("no algorithm specified");
+ }
+ r.base = algname;
+ r.length = strlen(algname);
+ ret = dns_secalg_fromtext(&ctx.alg, &r);
+ if (ret != ISC_R_SUCCESS) {
+ fatal("unknown algorithm %s", algname);
+ }
+ if (!dst_algorithm_supported(ctx.alg)) {
+ fatal("unsupported algorithm: %s", algname);
+ }
+ }
+
+ if (ctx.policy != NULL) {
+ if (ctx.nametype != NULL) {
+ fatal("-k and -n cannot be used together");
+ }
+ if (ctx.predecessor != NULL) {
+ fatal("-k and -S cannot be used together");
+ }
+ if (ctx.oldstyle) {
+ fatal("-k and -C cannot be used together");
+ }
+ if (ctx.setttl) {
+ fatal("-k and -L cannot be used together");
+ }
+ if (ctx.prepub > 0) {
+ fatal("-k and -i cannot be used together");
+ }
+ if (ctx.size != -1) {
+ fatal("-k and -b cannot be used together");
+ }
+ if (ctx.kskflag || ctx.revflag) {
+ fatal("-k and -f cannot be used together");
+ }
+ if (ctx.options & DST_TYPE_KEY) {
+ fatal("-k and -T KEY cannot be used together");
+ }
+ if (ctx.use_nsec3) {
+ fatal("-k and -3 cannot be used together");
+ }
+
+ ctx.options |= DST_TYPE_STATE;
+
+ if (strcmp(ctx.policy, "default") == 0) {
+ ctx.use_nsec3 = false;
+ ctx.alg = DST_ALG_ECDSA256;
+ ctx.size = 0;
+ ctx.kskflag = DNS_KEYFLAG_KSK;
+ ctx.ttl = 3600;
+ ctx.setttl = true;
+ ctx.ksk = true;
+ ctx.zsk = true;
+ ctx.lifetime = 0;
+
+ keygen(&ctx, mctx, argc, argv);
+ } else {
+ cfg_parser_t *parser = NULL;
+ cfg_obj_t *config = NULL;
+ dns_kasp_t *kasp = NULL;
+ dns_kasp_key_t *kaspkey = NULL;
+
+ RUNTIME_CHECK(cfg_parser_create(mctx, lctx, &parser) ==
+ ISC_R_SUCCESS);
+ if (cfg_parse_file(parser, ctx.configfile,
+ &cfg_type_namedconf,
+ &config) != ISC_R_SUCCESS)
+ {
+ fatal("unable to load dnssec-policy '%s' from "
+ "'%s'",
+ ctx.policy, ctx.configfile);
+ }
+
+ kasp_from_conf(config, mctx, ctx.policy, &kasp);
+ if (kasp == NULL) {
+ fatal("failed to load dnssec-policy '%s'",
+ ctx.policy);
+ }
+ if (ISC_LIST_EMPTY(dns_kasp_keys(kasp))) {
+ fatal("dnssec-policy '%s' has no keys "
+ "configured",
+ ctx.policy);
+ }
+
+ ctx.ttl = dns_kasp_dnskeyttl(kasp);
+ ctx.setttl = true;
+
+ kaspkey = ISC_LIST_HEAD(dns_kasp_keys(kasp));
+
+ while (kaspkey != NULL) {
+ ctx.use_nsec3 = false;
+ ctx.alg = dns_kasp_key_algorithm(kaspkey);
+ ctx.size = dns_kasp_key_size(kaspkey);
+ ctx.kskflag = dns_kasp_key_ksk(kaspkey)
+ ? DNS_KEYFLAG_KSK
+ : 0;
+ ctx.ksk = dns_kasp_key_ksk(kaspkey);
+ ctx.zsk = dns_kasp_key_zsk(kaspkey);
+ ctx.lifetime = dns_kasp_key_lifetime(kaspkey);
+
+ keygen(&ctx, mctx, argc, argv);
+
+ kaspkey = ISC_LIST_NEXT(kaspkey, link);
+ }
+
+ dns_kasp_detach(&kasp);
+ cfg_obj_destroy(parser, &config);
+ cfg_parser_destroy(&parser);
+ }
+ } else {
+ keygen(&ctx, mctx, argc, argv);
+ }
+
+ cleanup_logging(&lctx);
+ dst_lib_destroy();
+ if (verbose > 10) {
+ isc_mem_stats(mctx, stdout);
+ }
+ isc_mem_destroy(&mctx);
+
+ if (freeit != NULL) {
+ free(freeit);
+ }
+
+ return (0);
+}
diff --git a/bin/dnssec/dnssec-keygen.rst b/bin/dnssec/dnssec-keygen.rst
new file mode 100644
index 0000000..3b6d46d
--- /dev/null
+++ b/bin/dnssec/dnssec-keygen.rst
@@ -0,0 +1,318 @@
+.. 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.
+
+.. highlight: console
+
+.. _man_dnssec-keygen:
+
+dnssec-keygen: DNSSEC key generation tool
+-----------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`dnssec-keygen` [**-3**] [**-A** date/offset] [**-a** algorithm] [**-b** keysize] [**-C**] [**-c** class] [**-D** date/offset] [**-d** bits] [**-D** sync date/offset] [**-E** engine] [**-f** flag] [**-G**] [**-g** generator] [**-h**] [**-I** date/offset] [**-i** interval] [**-K** directory] [**-k** policy] [**-L** ttl] [**-l** file] [**-n** nametype] [**-P** date/offset] [**-P** sync date/offset] [**-p** protocol] [**-q**] [**-R** date/offset] [**-S** key] [**-s** strength] [**-T** rrtype] [**-t** type] [**-V**] [**-v** level] {name}
+
+Description
+~~~~~~~~~~~
+
+``dnssec-keygen`` generates keys for DNSSEC (Secure DNS), as defined in
+:rfc:`2535` and :rfc:`4034`. It can also generate keys for use with TSIG
+(Transaction Signatures) as defined in :rfc:`2845`, or TKEY (Transaction
+Key) as defined in :rfc:`2930`.
+
+The ``name`` of the key is specified on the command line. For DNSSEC
+keys, this must match the name of the zone for which the key is being
+generated.
+
+The ``dnssec-keymgr`` command acts as a wrapper
+around ``dnssec-keygen``, generating and updating keys
+as needed to enforce defined security policies such as key rollover
+scheduling. Using ``dnssec-keymgr`` may be preferable
+to direct use of ``dnssec-keygen``.
+
+Options
+~~~~~~~
+
+``-3``
+ This option uses an NSEC3-capable algorithm to generate a DNSSEC key. If this
+ option is used with an algorithm that has both NSEC and NSEC3
+ versions, then the NSEC3 version is selected; for example,
+ ``dnssec-keygen -3 -a RSASHA1`` specifies the NSEC3RSASHA1 algorithm.
+
+``-a algorithm``
+ This option selects the cryptographic algorithm. For DNSSEC keys, the value of
+ ``algorithm`` must be one of RSASHA1, NSEC3RSASHA1, RSASHA256,
+ RSASHA512, ECDSAP256SHA256, ECDSAP384SHA384, ED25519, or ED448. For
+ TKEY, the value must be DH (Diffie-Hellman); specifying this value
+ automatically sets the ``-T KEY`` option as well.
+
+ These values are case-insensitive. In some cases, abbreviations are
+ supported, such as ECDSA256 for ECDSAP256SHA256 and ECDSA384 for
+ ECDSAP384SHA384. If RSASHA1 is specified along with the ``-3``
+ option, NSEC3RSASHA1 is used instead.
+
+ This parameter *must* be specified except when using the ``-S``
+ option, which copies the algorithm from the predecessor key.
+
+ In prior releases, HMAC algorithms could be generated for use as TSIG
+ keys, but that feature was removed in BIND 9.13.0. Use
+ ``tsig-keygen`` to generate TSIG keys.
+
+``-b keysize``
+ This option specifies the number of bits in the key. The choice of key size
+ depends on the algorithm used: RSA keys must be between 1024 and 4096
+ bits; Diffie-Hellman keys must be between 128 and 4096 bits. Elliptic
+ curve algorithms do not need this parameter.
+
+ If the key size is not specified, some algorithms have pre-defined
+ defaults. For example, RSA keys for use as DNSSEC zone-signing keys
+ have a default size of 1024 bits; RSA keys for use as key-signing
+ keys (KSKs, generated with ``-f KSK``) default to 2048 bits.
+
+``-C``
+ This option enables compatibility mode, which generates an old-style key, without any timing
+ metadata. By default, ``dnssec-keygen`` includes the key's
+ creation date in the metadata stored with the private key; other
+ dates may be set there as well, including publication date, activation date,
+ etc. Keys that include this data may be incompatible with older
+ versions of BIND; the ``-C`` option suppresses them.
+
+``-c class``
+ This option indicates that the DNS record containing the key should have the
+ specified class. If not specified, class IN is used.
+
+``-d bits``
+ This option specifies the key size in bits. For the algorithms RSASHA1, NSEC3RSASA1, RSASHA256, and
+ RSASHA512 the key size must be between 1024 and 4096 bits; DH size is between 128
+ and 4096 bits. This option is ignored for algorithms ECDSAP256SHA256,
+ ECDSAP384SHA384, ED25519, and ED448.
+
+``-E engine``
+ This option specifies the cryptographic hardware to use, when applicable.
+
+ When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL
+ engine identifier that drives the cryptographic accelerator or
+ hardware service module (usually ``pkcs11``). When BIND is
+ built with native PKCS#11 cryptography (``--enable-native-pkcs11``), it
+ defaults to the path of the PKCS#11 provider library specified via
+ ``--with-pkcs11``.
+
+``-f flag``
+ This option sets the specified flag in the flag field of the KEY/DNSKEY record.
+ The only recognized flags are KSK (Key-Signing Key) and REVOKE.
+
+``-G``
+ This option generates a key, but does not publish it or sign with it. This option is
+ incompatible with ``-P`` and ``-A``.
+
+``-g generator``
+ This option indicates the generator to use if generating a Diffie-Hellman key. Allowed
+ values are 2 and 5. If no generator is specified, a known prime from
+ :rfc:`2539` is used if possible; otherwise the default is 2.
+
+``-h``
+ This option prints a short summary of the options and arguments to
+ ``dnssec-keygen``.
+
+``-K directory``
+ This option sets the directory in which the key files are to be written.
+
+``-k policy``
+ This option creates keys for a specific ``dnssec-policy``. If a policy uses multiple keys,
+ ``dnssec-keygen`` generates multiple keys. This also
+ creates a ".state" file to keep track of the key state.
+
+ This option creates keys according to the ``dnssec-policy`` configuration, hence
+ it cannot be used at the same time as many of the other options that
+ ``dnssec-keygen`` provides.
+
+``-L ttl``
+ This option sets the default TTL to use for this key when it is converted into a
+ DNSKEY RR. This is the TTL used when the key is imported into a zone,
+ unless there was already a DNSKEY RRset in
+ place, in which case the existing TTL takes precedence. If this
+ value is not set and there is no existing DNSKEY RRset, the TTL
+ defaults to the SOA TTL. Setting the default TTL to ``0`` or ``none``
+ is the same as leaving it unset.
+
+``-l file``
+ This option provides a configuration file that contains a ``dnssec-policy`` statement
+ (matching the policy set with ``-k``).
+
+``-n nametype``
+ This option specifies the owner type of the key. The value of ``nametype`` must
+ either be ZONE (for a DNSSEC zone key (KEY/DNSKEY)), HOST or ENTITY
+ (for a key associated with a host (KEY)), USER (for a key associated
+ with a user (KEY)), or OTHER (DNSKEY). These values are
+ case-insensitive. The default is ZONE for DNSKEY generation.
+
+``-p protocol``
+ This option sets the protocol value for the generated key, for use with
+ ``-T KEY``. The protocol is a number between 0 and 255. The default
+ is 3 (DNSSEC). Other possible values for this argument are listed in
+ :rfc:`2535` and its successors.
+
+``-q``
+ This option sets quiet mode, which suppresses unnecessary output, including progress
+ indication. Without this option, when ``dnssec-keygen`` is run
+ interactively to generate an RSA or DSA key pair, it prints a
+ string of symbols to ``stderr`` indicating the progress of the key
+ generation. A ``.`` indicates that a random number has been found which
+ passed an initial sieve test; ``+`` means a number has passed a single
+ round of the Miller-Rabin primality test; and a space ( ) means that the
+ number has passed all the tests and is a satisfactory key.
+
+``-S key``
+ This option creates a new key which is an explicit successor to an existing key.
+ The name, algorithm, size, and type of the key are set to match
+ the existing key. The activation date of the new key is set to
+ the inactivation date of the existing one. The publication date is
+ set to the activation date minus the prepublication interval,
+ which defaults to 30 days.
+
+``-s strength``
+ This option specifies the strength value of the key. The strength is a number
+ between 0 and 15, and currently has no defined purpose in DNSSEC.
+
+``-T rrtype``
+ This option specifies the resource record type to use for the key. ``rrtype``
+ must be either DNSKEY or KEY. The default is DNSKEY when using a
+ DNSSEC algorithm, but it can be overridden to KEY for use with
+ SIG(0).
+
+``-t type``
+ This option indicates the type of the key for use with ``-T KEY``. ``type``
+ must be one of AUTHCONF, NOAUTHCONF, NOAUTH, or NOCONF. The default
+ is AUTHCONF. AUTH refers to the ability to authenticate data, and
+ CONF to the ability to encrypt data.
+
+``-V``
+ This option prints version information.
+
+``-v level``
+ This option sets the debugging level.
+
+Timing Options
+~~~~~~~~~~~~~~
+
+Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS. If the
+argument begins with a ``+`` or ``-``, it is interpreted as an offset from
+the present time. For convenience, if such an offset is followed by one
+of the suffixes ``y``, ``mo``, ``w``, ``d``, ``h``, or ``mi``, then the offset is
+computed in years (defined as 365 24-hour days, ignoring leap years),
+months (defined as 30 24-hour days), weeks, days, hours, or minutes,
+respectively. Without a suffix, the offset is computed in seconds. To
+explicitly prevent a date from being set, use ``none`` or ``never``.
+
+``-P date/offset``
+ This option sets the date on which a key is to be published to the zone. After
+ that date, the key is included in the zone but is not used
+ to sign it. If not set, and if the ``-G`` option has not been used, the
+ default is the current date.
+
+``-P sync date/offset``
+ This option sets the date on which CDS and CDNSKEY records that match this key
+ are to be published to the zone.
+
+``-A date/offset``
+ This option sets the date on which the key is to be activated. After that date,
+ the key is included in the zone and used to sign it. If not set,
+ and if the ``-G`` option has not been used, the default is the current date. If set,
+ and ``-P`` is not set, the publication date is set to the
+ activation date minus the prepublication interval.
+
+``-R date/offset``
+ This option sets the date on which the key is to be revoked. After that date, the
+ key is flagged as revoked. It is included in the zone and
+ is used to sign it.
+
+``-I date/offset``
+ This option sets the date on which the key is to be retired. After that date, the
+ key is still included in the zone, but it is not used to
+ sign it.
+
+``-D date/offset``
+ This option sets the date on which the key is to be deleted. After that date, the
+ key is no longer included in the zone. (However, it may remain in the key
+ repository.)
+
+``-D sync date/offset``
+ This option sets the date on which the CDS and CDNSKEY records that match this
+ key are to be deleted.
+
+``-i interval``
+ This option sets the prepublication interval for a key. If set, then the
+ publication and activation dates must be separated by at least this
+ much time. If the activation date is specified but the publication
+ date is not, the publication date defaults to this much time
+ before the activation date; conversely, if the publication date is
+ specified but not the activation date, activation is set to
+ this much time after publication.
+
+ If the key is being created as an explicit successor to another key,
+ then the default prepublication interval is 30 days; otherwise it is
+ zero.
+
+ As with date offsets, if the argument is followed by one of the
+ suffixes ``y``, ``mo``, ``w``, ``d``, ``h``, or ``mi``, the interval is
+ measured in years, months, weeks, days, hours, or minutes,
+ respectively. Without a suffix, the interval is measured in seconds.
+
+Generated Keys
+~~~~~~~~~~~~~~
+
+When ``dnssec-keygen`` completes successfully, it prints a string of the
+form ``Knnnn.+aaa+iiiii`` to the standard output. This is an
+identification string for the key it has generated.
+
+- ``nnnn`` is the key name.
+
+- ``aaa`` is the numeric representation of the algorithm.
+
+- ``iiiii`` is the key identifier (or footprint).
+
+``dnssec-keygen`` creates two files, with names based on the printed
+string. ``Knnnn.+aaa+iiiii.key`` contains the public key, and
+``Knnnn.+aaa+iiiii.private`` contains the private key.
+
+The ``.key`` file contains a DNSKEY or KEY record. When a zone is being
+signed by ``named`` or ``dnssec-signzone -S``, DNSKEY records are
+included automatically. In other cases, the ``.key`` file can be
+inserted into a zone file manually or with an ``$INCLUDE`` statement.
+
+The ``.private`` file contains algorithm-specific fields. For obvious
+security reasons, this file does not have general read permission.
+
+Example
+~~~~~~~
+
+To generate an ECDSAP256SHA256 zone-signing key for the zone
+``example.com``, issue the command:
+
+``dnssec-keygen -a ECDSAP256SHA256 example.com``
+
+The command prints a string of the form:
+
+``Kexample.com.+013+26160``
+
+In this example, ``dnssec-keygen`` creates the files
+``Kexample.com.+013+26160.key`` and ``Kexample.com.+013+26160.private``.
+
+To generate a matching key-signing key, issue the command:
+
+``dnssec-keygen -a ECDSAP256SHA256 -f KSK example.com``
+
+See Also
+~~~~~~~~
+
+:manpage:`dnssec-signzone(8)`, BIND 9 Administrator Reference Manual, :rfc:`2539`,
+:rfc:`2845`, :rfc:`4034`.
diff --git a/bin/dnssec/dnssec-revoke.c b/bin/dnssec/dnssec-revoke.c
new file mode 100644
index 0000000..c532463
--- /dev/null
+++ b/bin/dnssec/dnssec-revoke.c
@@ -0,0 +1,278 @@
+/*
+ * 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 <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.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-revoke";
+
+static isc_mem_t *mctx = NULL;
+
+ISC_PLATFORM_NORETURN_PRE static void
+usage(void) ISC_PLATFORM_NORETURN_POST;
+
+static void
+usage(void) {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, " %s [options] keyfile\n\n", program);
+ fprintf(stderr, "Version: %s\n", VERSION);
+#if USE_PKCS11
+ fprintf(stderr,
+ " -E engine: specify PKCS#11 provider "
+ "(default: %s)\n",
+ PK11_LIB_LOCATION);
+#else /* if USE_PKCS11 */
+ fprintf(stderr, " -E engine: specify OpenSSL engine\n");
+#endif /* if USE_PKCS11 */
+ fprintf(stderr, " -f: force overwrite\n");
+ fprintf(stderr, " -h: help\n");
+ fprintf(stderr, " -K directory: use directory for key files\n");
+ fprintf(stderr, " -r: remove old keyfiles after "
+ "creating revoked version\n");
+ fprintf(stderr, " -v level: set level of verbosity\n");
+ fprintf(stderr, " -V: print version information\n");
+ fprintf(stderr, "Output:\n");
+ fprintf(stderr, " K<name>+<alg>+<new id>.key, "
+ "K<name>+<alg>+<new id>.private\n");
+
+ exit(-1);
+}
+
+int
+main(int argc, char **argv) {
+ isc_result_t result;
+ const char *engine = NULL;
+ char const *filename = NULL;
+ char *dir = NULL;
+ char newname[1024], oldname[1024];
+ char keystr[DST_KEY_FORMATSIZE];
+ char *endp;
+ int ch;
+ dst_key_t *key = NULL;
+ uint32_t flags;
+ isc_buffer_t buf;
+ bool force = false;
+ bool removefile = false;
+ bool id = false;
+
+ 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;
+
+ while ((ch = isc_commandline_parse(argc, argv, "E:fK:rRhv:V")) != -1) {
+ switch (ch) {
+ case 'E':
+ engine = isc_commandline_argument;
+ break;
+ case 'f':
+ force = true;
+ break;
+ case 'K':
+ /*
+ * We don't have to copy it here, but do it to
+ * simplify cleanup later
+ */
+ dir = isc_mem_strdup(mctx, isc_commandline_argument);
+ break;
+ case 'r':
+ removefile = true;
+ break;
+ case 'R':
+ id = true;
+ 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);
+ }
+ }
+
+ if (argc < isc_commandline_index + 1 ||
+ argv[isc_commandline_index] == NULL)
+ {
+ fatal("The key file name was not specified");
+ }
+ if (argc > isc_commandline_index + 1) {
+ fatal("Extraneous arguments");
+ }
+
+ if (dir != NULL) {
+ filename = argv[isc_commandline_index];
+ } else {
+ result = isc_file_splitpath(mctx, argv[isc_commandline_index],
+ &dir, &filename);
+ if (result != ISC_R_SUCCESS) {
+ fatal("cannot process filename %s: %s",
+ argv[isc_commandline_index],
+ isc_result_totext(result));
+ }
+ if (strcmp(dir, ".") == 0) {
+ isc_mem_free(mctx, dir);
+ dir = NULL;
+ }
+ }
+
+ result = dst_lib_init(mctx, engine);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Could not initialize dst: %s",
+ isc_result_totext(result));
+ }
+
+ result = dst_key_fromnamedfile(
+ filename, dir, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, mctx, &key);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Invalid keyfile name %s: %s", filename,
+ isc_result_totext(result));
+ }
+
+ if (id) {
+ fprintf(stdout, "%u\n", dst_key_rid(key));
+ goto cleanup;
+ }
+ dst_key_format(key, keystr, sizeof(keystr));
+
+ if (verbose > 2) {
+ fprintf(stderr, "%s: %s\n", program, keystr);
+ }
+
+ if (force) {
+ set_keyversion(key);
+ } else {
+ check_keyversion(key, keystr);
+ }
+
+ flags = dst_key_flags(key);
+ if ((flags & DNS_KEYFLAG_REVOKE) == 0) {
+ isc_stdtime_t now;
+
+ if ((flags & DNS_KEYFLAG_KSK) == 0) {
+ fprintf(stderr,
+ "%s: warning: Key is not flagged "
+ "as a KSK. Revoking a ZSK is "
+ "legal, but undefined.\n",
+ program);
+ }
+
+ isc_stdtime_get(&now);
+ dst_key_settime(key, DST_TIME_REVOKE, now);
+
+ dst_key_setflags(key, flags | DNS_KEYFLAG_REVOKE);
+
+ isc_buffer_init(&buf, newname, sizeof(newname));
+ dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf);
+
+ if (access(newname, F_OK) == 0 && !force) {
+ fatal("Key file %s already exists; "
+ "use -f to force overwrite",
+ newname);
+ }
+
+ 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));
+ }
+
+ isc_buffer_clear(&buf);
+ dst_key_buildfilename(key, 0, dir, &buf);
+ printf("%s\n", newname);
+
+ /*
+ * Remove old key file, if told to (and if
+ * it isn't the same as the new file)
+ */
+ if (removefile) {
+ isc_buffer_init(&buf, oldname, sizeof(oldname));
+ dst_key_setflags(key, flags & ~DNS_KEYFLAG_REVOKE);
+ dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
+ if (strcmp(oldname, newname) == 0) {
+ goto cleanup;
+ }
+ (void)unlink(oldname);
+ isc_buffer_clear(&buf);
+ dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf);
+ (void)unlink(oldname);
+ }
+ } else {
+ dst_key_format(key, keystr, sizeof(keystr));
+ fatal("Key %s is already revoked", keystr);
+ }
+
+cleanup:
+ dst_key_free(&key);
+ dst_lib_destroy();
+ if (verbose > 10) {
+ isc_mem_stats(mctx, stdout);
+ }
+ if (dir != NULL) {
+ isc_mem_free(mctx, dir);
+ }
+ isc_mem_destroy(&mctx);
+
+ return (0);
+}
diff --git a/bin/dnssec/dnssec-revoke.rst b/bin/dnssec/dnssec-revoke.rst
new file mode 100644
index 0000000..f664646
--- /dev/null
+++ b/bin/dnssec/dnssec-revoke.rst
@@ -0,0 +1,71 @@
+.. 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.
+
+.. highlight: console
+
+.. _man_dnssec-revoke:
+
+dnssec-revoke - set the REVOKED bit on a DNSSEC key
+---------------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`dnssec-revoke` [**-hr**] [**-v** level] [**-V**] [**-K** directory] [**-E** engine] [**-f**] [**-R**] {keyfile}
+
+Description
+~~~~~~~~~~~
+
+``dnssec-revoke`` reads a DNSSEC key file, sets the REVOKED bit on the
+key as defined in :rfc:`5011`, and creates a new pair of key files
+containing the now-revoked key.
+
+Options
+~~~~~~~
+
+``-h``
+ This option emits a usage message and exits.
+
+``-K directory``
+ This option sets the directory in which the key files are to reside.
+
+``-r``
+ This option indicates to remove the original keyset files after writing the new keyset files.
+
+``-v level``
+ This option sets the debugging level.
+
+``-V``
+ This option prints version information.
+
+``-E engine``
+ This option specifies the cryptographic hardware to use, when applicable.
+
+ When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL
+ engine identifier that drives the cryptographic accelerator or
+ hardware service module (usually ``pkcs11``). When BIND is
+ built with native PKCS#11 cryptography (``--enable-native-pkcs11``), it
+ defaults to the path of the PKCS#11 provider library specified via
+ ``--with-pkcs11``.
+
+``-f``
+ This option indicates a forced overwrite and causes ``dnssec-revoke`` to write the new key pair,
+ even if a file already exists matching the algorithm and key ID of
+ the revoked key.
+
+``-R``
+ This option prints the key tag of the key with the REVOKE bit set, but does not
+ revoke the key.
+
+See Also
+~~~~~~~~
+
+:manpage:`dnssec-keygen(8)`, BIND 9 Administrator Reference Manual, :rfc:`5011`.
diff --git a/bin/dnssec/dnssec-settime.c b/bin/dnssec/dnssec-settime.c
new file mode 100644
index 0000000..1df6af1
--- /dev/null
+++ b/bin/dnssec/dnssec-settime.c
@@ -0,0 +1,985 @@
+/*
+ * 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 <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.h>
+#include <dns/log.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-settime";
+
+static isc_mem_t *mctx = NULL;
+
+ISC_PLATFORM_NORETURN_PRE static void
+usage(void) ISC_PLATFORM_NORETURN_POST;
+
+static void
+usage(void) {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, " %s [options] keyfile\n\n", program);
+ fprintf(stderr, "Version: %s\n", VERSION);
+ fprintf(stderr, "General options:\n");
+#if USE_PKCS11
+ fprintf(stderr,
+ " -E engine: specify PKCS#11 provider "
+ "(default: %s)\n",
+ PK11_LIB_LOCATION);
+#elif defined(USE_PKCS11)
+ fprintf(stderr, " -E engine: specify OpenSSL engine "
+ "(default \"pkcs11\")\n");
+#else /* if USE_PKCS11 */
+ fprintf(stderr, " -E engine: specify OpenSSL engine\n");
+#endif /* if USE_PKCS11 */
+ fprintf(stderr, " -f: force update of old-style "
+ "keys\n");
+ fprintf(stderr, " -K directory: set key file location\n");
+ fprintf(stderr, " -L ttl: set default key TTL\n");
+ fprintf(stderr, " -v level: set level of verbosity\n");
+ fprintf(stderr, " -V: print version information\n");
+ fprintf(stderr, " -h: help\n");
+ fprintf(stderr, "Timing options:\n");
+ fprintf(stderr, " -P date/[+-]offset/none: set/unset key "
+ "publication date\n");
+ fprintf(stderr, " -P ds date/[+-]offset/none: set/unset "
+ "DS publication date\n");
+ fprintf(stderr, " -P sync date/[+-]offset/none: set/unset "
+ "CDS and CDNSKEY publication date\n");
+ fprintf(stderr, " -A date/[+-]offset/none: set/unset key "
+ "activation date\n");
+ fprintf(stderr, " -R date/[+-]offset/none: set/unset key "
+ "revocation date\n");
+ fprintf(stderr, " -I date/[+-]offset/none: set/unset key "
+ "inactivation date\n");
+ fprintf(stderr, " -D date/[+-]offset/none: set/unset key "
+ "deletion date\n");
+ fprintf(stderr, " -D ds date/[+-]offset/none: set/unset "
+ "DS deletion date\n");
+ fprintf(stderr, " -D sync date/[+-]offset/none: set/unset "
+ "CDS and CDNSKEY deletion date\n");
+ fprintf(stderr, " -S <key>: generate a successor to an existing "
+ "key\n");
+ fprintf(stderr, " -i <interval>: prepublication interval for "
+ "successor key "
+ "(default: 30 days)\n");
+ fprintf(stderr, "Key state options:\n");
+ fprintf(stderr, " -s: update key state file (default no)\n");
+ fprintf(stderr, " -g state: set the goal state for this key\n");
+ fprintf(stderr, " -d state date/[+-]offset: set the DS state\n");
+ fprintf(stderr, " -k state date/[+-]offset: set the DNSKEY state\n");
+ fprintf(stderr, " -r state date/[+-]offset: set the RRSIG (KSK) "
+ "state\n");
+ fprintf(stderr, " -z state date/[+-]offset: set the RRSIG (ZSK) "
+ "state\n");
+ fprintf(stderr, "Printing options:\n");
+ fprintf(stderr, " -p C/P/Psync/A/R/I/D/Dsync/all: print a "
+ "particular time value or values\n");
+ fprintf(stderr, " -u: print times in unix epoch "
+ "format\n");
+ fprintf(stderr, "Output:\n");
+ fprintf(stderr, " K<name>+<alg>+<new id>.key, "
+ "K<name>+<alg>+<new id>.private\n");
+
+ exit(-1);
+}
+
+static void
+printtime(dst_key_t *key, int type, const char *tag, bool epoch, FILE *stream) {
+ isc_result_t result;
+ isc_stdtime_t when;
+
+ if (tag != NULL) {
+ fprintf(stream, "%s: ", tag);
+ }
+
+ result = dst_key_gettime(key, type, &when);
+ if (result == ISC_R_NOTFOUND) {
+ fprintf(stream, "UNSET\n");
+ } else if (epoch) {
+ fprintf(stream, "%d\n", (int)when);
+ } else {
+ time_t now = when;
+ struct tm t, *tm = localtime_r(&now, &t);
+ unsigned int flen;
+ char timebuf[80];
+
+ if (tm == NULL) {
+ fprintf(stream, "INVALID\n");
+ return;
+ }
+
+ flen = strftime(timebuf, sizeof(timebuf),
+ "%a %b %e %H:%M:%S %Y", tm);
+ INSIST(flen > 0U && flen < sizeof(timebuf));
+ fprintf(stream, "%s\n", timebuf);
+ }
+}
+
+static void
+writekey(dst_key_t *key, const char *directory, bool write_state) {
+ char newname[1024];
+ char keystr[DST_KEY_FORMATSIZE];
+ isc_buffer_t buf;
+ isc_result_t result;
+ int options = DST_TYPE_PUBLIC | DST_TYPE_PRIVATE;
+
+ if (write_state) {
+ options |= DST_TYPE_STATE;
+ }
+
+ isc_buffer_init(&buf, newname, sizeof(newname));
+ result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &buf);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Failed to build public key filename: %s",
+ isc_result_totext(result));
+ }
+
+ result = dst_key_tofile(key, options, directory);
+ 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", newname);
+
+ isc_buffer_clear(&buf);
+ result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &buf);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Failed to build private key filename: %s",
+ isc_result_totext(result));
+ }
+ printf("%s\n", newname);
+
+ if (write_state) {
+ isc_buffer_clear(&buf);
+ result = dst_key_buildfilename(key, DST_TYPE_STATE, directory,
+ &buf);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Failed to build key state filename: %s",
+ isc_result_totext(result));
+ }
+ printf("%s\n", newname);
+ }
+}
+
+int
+main(int argc, char **argv) {
+ isc_result_t result;
+ const char *engine = NULL;
+ const char *filename = NULL;
+ char *directory = NULL;
+ char keystr[DST_KEY_FORMATSIZE];
+ char *endp, *p;
+ int ch;
+ const char *predecessor = NULL;
+ dst_key_t *prevkey = NULL;
+ dst_key_t *key = NULL;
+ dns_name_t *name = NULL;
+ dns_secalg_t alg = 0;
+ unsigned int size = 0;
+ uint16_t flags = 0;
+ int prepub = -1;
+ int options;
+ dns_ttl_t ttl = 0;
+ isc_stdtime_t now;
+ isc_stdtime_t dstime = 0, dnskeytime = 0;
+ isc_stdtime_t krrsigtime = 0, zrrsigtime = 0;
+ isc_stdtime_t pub = 0, act = 0, rev = 0, inact = 0, del = 0;
+ isc_stdtime_t prevact = 0, previnact = 0, prevdel = 0;
+ dst_key_state_t goal = DST_KEY_STATE_NA;
+ dst_key_state_t ds = DST_KEY_STATE_NA;
+ dst_key_state_t dnskey = DST_KEY_STATE_NA;
+ dst_key_state_t krrsig = DST_KEY_STATE_NA;
+ dst_key_state_t zrrsig = DST_KEY_STATE_NA;
+ bool setgoal = false, setds = false, setdnskey = false;
+ bool setkrrsig = false, setzrrsig = false;
+ bool setdstime = false, setdnskeytime = false;
+ bool setkrrsigtime = false, setzrrsigtime = false;
+ bool setpub = false, setact = false;
+ bool setrev = false, setinact = false;
+ bool setdel = false, setttl = false;
+ bool unsetpub = false, unsetact = false;
+ bool unsetrev = false, unsetinact = false;
+ bool unsetdel = false;
+ bool printcreate = false, printpub = false;
+ bool printact = false, printrev = false;
+ bool printinact = false, printdel = false;
+ bool force = false;
+ bool epoch = false;
+ bool changed = false;
+ bool write_state = false;
+ isc_log_t *log = NULL;
+ isc_stdtime_t syncadd = 0, syncdel = 0;
+ bool unsetsyncadd = false, setsyncadd = false;
+ bool unsetsyncdel = false, setsyncdel = false;
+ bool printsyncadd = false, printsyncdel = false;
+ isc_stdtime_t dsadd = 0, dsdel = 0;
+ bool unsetdsadd = false, setdsadd = false;
+ bool unsetdsdel = false, setdsdel = false;
+ bool printdsadd = false, printdsdel = false;
+
+ options = DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE;
+
+ if (argc == 1) {
+ usage();
+ }
+
+ isc_mem_create(&mctx);
+
+ setup_logging(mctx, &log);
+
+#if USE_PKCS11
+ pk11_result_register();
+#endif /* if USE_PKCS11 */
+ dns_result_register();
+
+ isc_commandline_errprint = false;
+
+ isc_stdtime_get(&now);
+
+#define CMDLINE_FLAGS "A:D:d:E:fg:hI:i:K:k:L:P:p:R:r:S:suv:Vz:"
+ while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
+ switch (ch) {
+ case 'A':
+ if (setact || unsetact) {
+ fatal("-A specified more than once");
+ }
+
+ changed = true;
+ act = strtotime(isc_commandline_argument, now, now,
+ &setact);
+ unsetact = !setact;
+ break;
+ case 'D':
+ /* -Dsync ? */
+ if (isoptarg("sync", argv, usage)) {
+ if (unsetsyncdel || setsyncdel) {
+ fatal("-D sync specified more than "
+ "once");
+ }
+
+ changed = true;
+ syncdel = strtotime(isc_commandline_argument,
+ now, now, &setsyncdel);
+ unsetsyncdel = !setsyncdel;
+ break;
+ }
+ /* -Dds ? */
+ if (isoptarg("ds", argv, usage)) {
+ if (unsetdsdel || setdsdel) {
+ fatal("-D ds specified more than once");
+ }
+
+ changed = true;
+ dsdel = strtotime(isc_commandline_argument, now,
+ now, &setdsdel);
+ unsetdsdel = !setdsdel;
+ break;
+ }
+ /* -Ddnskey ? */
+ (void)isoptarg("dnskey", argv, usage);
+ if (setdel || unsetdel) {
+ fatal("-D specified more than once");
+ }
+
+ changed = true;
+ del = strtotime(isc_commandline_argument, now, now,
+ &setdel);
+ unsetdel = !setdel;
+ break;
+ case 'd':
+ if (setds) {
+ fatal("-d specified more than once");
+ }
+
+ ds = strtokeystate(isc_commandline_argument);
+ setds = true;
+ /* time */
+ (void)isoptarg(isc_commandline_argument, argv, usage);
+ dstime = strtotime(isc_commandline_argument, now, now,
+ &setdstime);
+ break;
+ case 'E':
+ engine = isc_commandline_argument;
+ break;
+ case 'f':
+ force = true;
+ break;
+ case 'g':
+ if (setgoal) {
+ fatal("-g specified more than once");
+ }
+
+ goal = strtokeystate(isc_commandline_argument);
+ if (goal != DST_KEY_STATE_NA &&
+ goal != DST_KEY_STATE_HIDDEN &&
+ goal != DST_KEY_STATE_OMNIPRESENT)
+ {
+ fatal("-g must be either none, hidden, or "
+ "omnipresent");
+ }
+ setgoal = true;
+ 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 'I':
+ if (setinact || unsetinact) {
+ fatal("-I specified more than once");
+ }
+
+ changed = true;
+ inact = strtotime(isc_commandline_argument, now, now,
+ &setinact);
+ unsetinact = !setinact;
+ break;
+ case 'i':
+ prepub = strtottl(isc_commandline_argument);
+ break;
+ case 'K':
+ /*
+ * We don't have to copy it here, but do it to
+ * simplify cleanup later
+ */
+ directory = isc_mem_strdup(mctx,
+ isc_commandline_argument);
+ break;
+ case 'k':
+ if (setdnskey) {
+ fatal("-k specified more than once");
+ }
+
+ dnskey = strtokeystate(isc_commandline_argument);
+ setdnskey = true;
+ /* time */
+ (void)isoptarg(isc_commandline_argument, argv, usage);
+ dnskeytime = strtotime(isc_commandline_argument, now,
+ now, &setdnskeytime);
+ break;
+ case 'L':
+ ttl = strtottl(isc_commandline_argument);
+ setttl = true;
+ break;
+ case 'P':
+ /* -Psync ? */
+ if (isoptarg("sync", argv, usage)) {
+ if (unsetsyncadd || setsyncadd) {
+ fatal("-P sync specified more than "
+ "once");
+ }
+
+ changed = true;
+ syncadd = strtotime(isc_commandline_argument,
+ now, now, &setsyncadd);
+ unsetsyncadd = !setsyncadd;
+ break;
+ }
+ /* -Pds ? */
+ if (isoptarg("ds", argv, usage)) {
+ if (unsetdsadd || setdsadd) {
+ fatal("-P ds specified more than once");
+ }
+
+ changed = true;
+ dsadd = strtotime(isc_commandline_argument, now,
+ now, &setdsadd);
+ unsetdsadd = !setdsadd;
+ break;
+ }
+ /* -Pdnskey ? */
+ (void)isoptarg("dnskey", argv, usage);
+ if (setpub || unsetpub) {
+ fatal("-P specified more than once");
+ }
+
+ changed = true;
+ pub = strtotime(isc_commandline_argument, now, now,
+ &setpub);
+ unsetpub = !setpub;
+ break;
+ case 'p':
+ p = isc_commandline_argument;
+ if (!strcasecmp(p, "all")) {
+ printcreate = true;
+ printpub = true;
+ printact = true;
+ printrev = true;
+ printinact = true;
+ printdel = true;
+ printsyncadd = true;
+ printsyncdel = true;
+ printdsadd = true;
+ printdsdel = true;
+ break;
+ }
+
+ do {
+ switch (*p++) {
+ case 'A':
+ printact = true;
+ break;
+ case 'C':
+ printcreate = true;
+ break;
+ case 'D':
+ if (!strncmp(p, "sync", 4)) {
+ p += 4;
+ printsyncdel = true;
+ break;
+ }
+ if (!strncmp(p, "ds", 2)) {
+ p += 2;
+ printdsdel = true;
+ break;
+ }
+ printdel = true;
+ break;
+ case 'I':
+ printinact = true;
+ break;
+ case 'P':
+ if (!strncmp(p, "sync", 4)) {
+ p += 4;
+ printsyncadd = true;
+ break;
+ }
+ if (!strncmp(p, "ds", 2)) {
+ p += 2;
+ printdsadd = true;
+ break;
+ }
+ printpub = true;
+ break;
+ case 'R':
+ printrev = true;
+ break;
+ case ' ':
+ break;
+ default:
+ usage();
+ break;
+ }
+ } while (*p != '\0');
+ break;
+ case 'R':
+ if (setrev || unsetrev) {
+ fatal("-R specified more than once");
+ }
+
+ changed = true;
+ rev = strtotime(isc_commandline_argument, now, now,
+ &setrev);
+ unsetrev = !setrev;
+ break;
+ case 'r':
+ if (setkrrsig) {
+ fatal("-r specified more than once");
+ }
+
+ krrsig = strtokeystate(isc_commandline_argument);
+ setkrrsig = true;
+ /* time */
+ (void)isoptarg(isc_commandline_argument, argv, usage);
+ krrsigtime = strtotime(isc_commandline_argument, now,
+ now, &setkrrsigtime);
+ break;
+ case 'S':
+ predecessor = isc_commandline_argument;
+ break;
+ case 's':
+ write_state = true;
+ break;
+ case 'u':
+ epoch = true;
+ break;
+ case 'V':
+ /* Does not return. */
+ version(program);
+ case 'v':
+ verbose = strtol(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0') {
+ fatal("-v must be followed by a number");
+ }
+ break;
+ case 'z':
+ if (setzrrsig) {
+ fatal("-z specified more than once");
+ }
+
+ zrrsig = strtokeystate(isc_commandline_argument);
+ setzrrsig = true;
+ (void)isoptarg(isc_commandline_argument, argv, usage);
+ zrrsigtime = strtotime(isc_commandline_argument, now,
+ now, &setzrrsigtime);
+ break;
+
+ default:
+ fprintf(stderr, "%s: unhandled option -%c\n", program,
+ isc_commandline_option);
+ exit(1);
+ }
+ }
+
+ if (argc < isc_commandline_index + 1 ||
+ argv[isc_commandline_index] == NULL)
+ {
+ fatal("The key file name was not specified");
+ }
+ if (argc > isc_commandline_index + 1) {
+ fatal("Extraneous arguments");
+ }
+
+ if ((setgoal || setds || setdnskey || setkrrsig || setzrrsig) &&
+ !write_state)
+ {
+ fatal("Options -g, -d, -k, -r and -z require -s to be set");
+ }
+
+ result = dst_lib_init(mctx, engine);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Could not initialize dst: %s",
+ isc_result_totext(result));
+ }
+
+ if (predecessor != NULL) {
+ int major, minor;
+
+ if (prepub == -1) {
+ prepub = (30 * 86400);
+ }
+
+ if (setpub || unsetpub) {
+ fatal("-S and -P cannot be used together");
+ }
+ if (setact || unsetact) {
+ fatal("-S and -A cannot be used together");
+ }
+
+ result = dst_key_fromnamedfile(predecessor, directory, options,
+ mctx, &prevkey);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Invalid keyfile %s: %s", filename,
+ isc_result_totext(result));
+ }
+ if (!dst_key_isprivate(prevkey) && !dst_key_isexternal(prevkey))
+ {
+ fatal("%s is not a private key", filename);
+ }
+
+ name = dst_key_name(prevkey);
+ alg = dst_key_alg(prevkey);
+ size = dst_key_size(prevkey);
+ flags = dst_key_flags(prevkey);
+
+ dst_key_format(prevkey, keystr, sizeof(keystr));
+ dst_key_getprivateformat(prevkey, &major, &minor);
+ if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) {
+ fatal("Predecessor has incompatible format "
+ "version %d.%d\n\t",
+ major, minor);
+ }
+
+ result = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &prevact);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Predecessor has no activation date. "
+ "You must set one before\n\t"
+ "generating a successor.");
+ }
+
+ result = dst_key_gettime(prevkey, DST_TIME_INACTIVE,
+ &previnact);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Predecessor has no inactivation date. "
+ "You must set one before\n\t"
+ "generating a successor.");
+ }
+
+ pub = previnact - prepub;
+ act = previnact;
+
+ if ((previnact - prepub) < now && prepub != 0) {
+ fatal("Time until predecessor inactivation is\n\t"
+ "shorter than the prepublication interval. "
+ "Either change\n\t"
+ "predecessor inactivation date, or use the -i "
+ "option to set\n\t"
+ "a shorter prepublication interval.");
+ }
+
+ result = dst_key_gettime(prevkey, DST_TIME_DELETE, &prevdel);
+ if (result != ISC_R_SUCCESS) {
+ fprintf(stderr,
+ "%s: warning: Predecessor has no "
+ "removal date;\n\t"
+ "it will remain in the zone "
+ "indefinitely after rollover.\n",
+ program);
+ } else if (prevdel < previnact) {
+ fprintf(stderr,
+ "%s: warning: Predecessor is "
+ "scheduled to be deleted\n\t"
+ "before it is scheduled to be "
+ "inactive.\n",
+ program);
+ }
+
+ changed = setpub = setact = true;
+ } else {
+ if (prepub < 0) {
+ prepub = 0;
+ }
+
+ if (prepub > 0) {
+ if (setpub && setact && (act - prepub) < pub) {
+ fatal("Activation and publication dates "
+ "are closer together than the\n\t"
+ "prepublication interval.");
+ }
+
+ if (setpub && !setact) {
+ setact = true;
+ act = pub + prepub;
+ } else if (setact && !setpub) {
+ setpub = true;
+ pub = act - prepub;
+ }
+
+ if ((act - prepub) < now) {
+ fatal("Time until activation is shorter "
+ "than the\n\tprepublication interval.");
+ }
+ }
+ }
+
+ if (directory != NULL) {
+ filename = argv[isc_commandline_index];
+ } else {
+ result = isc_file_splitpath(mctx, argv[isc_commandline_index],
+ &directory, &filename);
+ if (result != ISC_R_SUCCESS) {
+ fatal("cannot process filename %s: %s",
+ argv[isc_commandline_index],
+ isc_result_totext(result));
+ }
+ }
+
+ result = dst_key_fromnamedfile(filename, directory, options, mctx,
+ &key);
+ if (result != ISC_R_SUCCESS) {
+ fatal("Invalid keyfile %s: %s", filename,
+ isc_result_totext(result));
+ }
+
+ if (!dst_key_isprivate(key) && !dst_key_isexternal(key)) {
+ fatal("%s is not a private key", filename);
+ }
+
+ dst_key_format(key, keystr, sizeof(keystr));
+
+ if (predecessor != NULL) {
+ if (!dns_name_equal(name, dst_key_name(key))) {
+ fatal("Key name mismatch");
+ }
+ if (alg != dst_key_alg(key)) {
+ fatal("Key algorithm mismatch");
+ }
+ if (size != dst_key_size(key)) {
+ fatal("Key size mismatch");
+ }
+ if (flags != dst_key_flags(key)) {
+ fatal("Key flags mismatch");
+ }
+ }
+
+ prevdel = previnact = 0;
+ if ((setdel && setinact && del < inact) ||
+ (dst_key_gettime(key, DST_TIME_INACTIVE, &previnact) ==
+ ISC_R_SUCCESS &&
+ setdel && !setinact && !unsetinact && del < previnact) ||
+ (dst_key_gettime(key, DST_TIME_DELETE, &prevdel) == ISC_R_SUCCESS &&
+ setinact && !setdel && !unsetdel && prevdel < inact) ||
+ (!setdel && !unsetdel && !setinact && !unsetinact && prevdel != 0 &&
+ prevdel < previnact))
+ {
+ fprintf(stderr,
+ "%s: warning: Key is scheduled to "
+ "be deleted before it is\n\t"
+ "scheduled to be inactive.\n",
+ program);
+ }
+
+ if (force) {
+ set_keyversion(key);
+ } else {
+ check_keyversion(key, keystr);
+ }
+
+ if (verbose > 2) {
+ fprintf(stderr, "%s: %s\n", program, keystr);
+ }
+
+ /*
+ * Set time values.
+ */
+ if (setpub) {
+ dst_key_settime(key, DST_TIME_PUBLISH, pub);
+ } else if (unsetpub) {
+ dst_key_unsettime(key, DST_TIME_PUBLISH);
+ }
+
+ if (setact) {
+ dst_key_settime(key, DST_TIME_ACTIVATE, act);
+ } else if (unsetact) {
+ dst_key_unsettime(key, DST_TIME_ACTIVATE);
+ }
+
+ if (setrev) {
+ if ((dst_key_flags(key) & DNS_KEYFLAG_REVOKE) != 0) {
+ fprintf(stderr,
+ "%s: warning: Key %s is already "
+ "revoked; changing the revocation date "
+ "will not affect this.\n",
+ program, keystr);
+ }
+ if ((dst_key_flags(key) & DNS_KEYFLAG_KSK) == 0) {
+ fprintf(stderr,
+ "%s: warning: Key %s is not flagged as "
+ "a KSK, but -R was used. Revoking a "
+ "ZSK is legal, but undefined.\n",
+ program, keystr);
+ }
+ dst_key_settime(key, DST_TIME_REVOKE, rev);
+ } else if (unsetrev) {
+ if ((dst_key_flags(key) & DNS_KEYFLAG_REVOKE) != 0) {
+ fprintf(stderr,
+ "%s: warning: Key %s is already "
+ "revoked; removing the revocation date "
+ "will not affect this.\n",
+ program, keystr);
+ }
+ dst_key_unsettime(key, DST_TIME_REVOKE);
+ }
+
+ if (setinact) {
+ dst_key_settime(key, DST_TIME_INACTIVE, inact);
+ } else if (unsetinact) {
+ dst_key_unsettime(key, DST_TIME_INACTIVE);
+ }
+
+ if (setdel) {
+ dst_key_settime(key, DST_TIME_DELETE, del);
+ } else if (unsetdel) {
+ dst_key_unsettime(key, DST_TIME_DELETE);
+ }
+
+ if (setsyncadd) {
+ dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncadd);
+ } else if (unsetsyncadd) {
+ dst_key_unsettime(key, DST_TIME_SYNCPUBLISH);
+ }
+
+ if (setsyncdel) {
+ dst_key_settime(key, DST_TIME_SYNCDELETE, syncdel);
+ } else if (unsetsyncdel) {
+ dst_key_unsettime(key, DST_TIME_SYNCDELETE);
+ }
+
+ if (setdsadd) {
+ dst_key_settime(key, DST_TIME_DSPUBLISH, dsadd);
+ } else if (unsetdsadd) {
+ dst_key_unsettime(key, DST_TIME_DSPUBLISH);
+ }
+
+ if (setdsdel) {
+ dst_key_settime(key, DST_TIME_DSDELETE, dsdel);
+ } else if (unsetdsdel) {
+ dst_key_unsettime(key, DST_TIME_DSDELETE);
+ }
+
+ if (setttl) {
+ dst_key_setttl(key, ttl);
+ }
+
+ if (predecessor != NULL && prevkey != NULL) {
+ dst_key_setnum(prevkey, DST_NUM_SUCCESSOR, dst_key_id(key));
+ dst_key_setnum(key, DST_NUM_PREDECESSOR, dst_key_id(prevkey));
+ }
+
+ /*
+ * No metadata changes were made but we're forcing an upgrade
+ * to the new format anyway: use "-P now -A now" as the default
+ */
+ if (force && !changed) {
+ dst_key_settime(key, DST_TIME_PUBLISH, now);
+ dst_key_settime(key, DST_TIME_ACTIVATE, now);
+ changed = true;
+ }
+
+ /*
+ * Make sure the key state goals are written.
+ */
+ if (write_state) {
+ if (setgoal) {
+ if (goal == DST_KEY_STATE_NA) {
+ dst_key_unsetstate(key, DST_KEY_GOAL);
+ } else {
+ dst_key_setstate(key, DST_KEY_GOAL, goal);
+ }
+ changed = true;
+ }
+ if (setds) {
+ if (ds == DST_KEY_STATE_NA) {
+ dst_key_unsetstate(key, DST_KEY_DS);
+ dst_key_unsettime(key, DST_TIME_DS);
+ } else {
+ dst_key_setstate(key, DST_KEY_DS, ds);
+ dst_key_settime(key, DST_TIME_DS, dstime);
+ }
+ changed = true;
+ }
+ if (setdnskey) {
+ if (dnskey == DST_KEY_STATE_NA) {
+ dst_key_unsetstate(key, DST_KEY_DNSKEY);
+ dst_key_unsettime(key, DST_TIME_DNSKEY);
+ } else {
+ dst_key_setstate(key, DST_KEY_DNSKEY, dnskey);
+ dst_key_settime(key, DST_TIME_DNSKEY,
+ dnskeytime);
+ }
+ changed = true;
+ }
+ if (setkrrsig) {
+ if (krrsig == DST_KEY_STATE_NA) {
+ dst_key_unsetstate(key, DST_KEY_KRRSIG);
+ dst_key_unsettime(key, DST_TIME_KRRSIG);
+ } else {
+ dst_key_setstate(key, DST_KEY_KRRSIG, krrsig);
+ dst_key_settime(key, DST_TIME_KRRSIG,
+ krrsigtime);
+ }
+ changed = true;
+ }
+ if (setzrrsig) {
+ if (zrrsig == DST_KEY_STATE_NA) {
+ dst_key_unsetstate(key, DST_KEY_ZRRSIG);
+ dst_key_unsettime(key, DST_TIME_ZRRSIG);
+ } else {
+ dst_key_setstate(key, DST_KEY_ZRRSIG, zrrsig);
+ dst_key_settime(key, DST_TIME_ZRRSIG,
+ zrrsigtime);
+ }
+ changed = true;
+ }
+ }
+
+ if (!changed && setttl) {
+ changed = true;
+ }
+
+ /*
+ * Print out time values, if -p was used.
+ */
+ if (printcreate) {
+ printtime(key, DST_TIME_CREATED, "Created", epoch, stdout);
+ }
+
+ if (printpub) {
+ printtime(key, DST_TIME_PUBLISH, "Publish", epoch, stdout);
+ }
+
+ if (printact) {
+ printtime(key, DST_TIME_ACTIVATE, "Activate", epoch, stdout);
+ }
+
+ if (printrev) {
+ printtime(key, DST_TIME_REVOKE, "Revoke", epoch, stdout);
+ }
+
+ if (printinact) {
+ printtime(key, DST_TIME_INACTIVE, "Inactive", epoch, stdout);
+ }
+
+ if (printdel) {
+ printtime(key, DST_TIME_DELETE, "Delete", epoch, stdout);
+ }
+
+ if (printsyncadd) {
+ printtime(key, DST_TIME_SYNCPUBLISH, "SYNC Publish", epoch,
+ stdout);
+ }
+
+ if (printsyncdel) {
+ printtime(key, DST_TIME_SYNCDELETE, "SYNC Delete", epoch,
+ stdout);
+ }
+
+ if (printdsadd) {
+ printtime(key, DST_TIME_DSPUBLISH, "DS Publish", epoch, stdout);
+ }
+
+ if (printdsdel) {
+ printtime(key, DST_TIME_DSDELETE, "DS Delete", epoch, stdout);
+ }
+
+ if (changed) {
+ writekey(key, directory, write_state);
+ if (predecessor != NULL && prevkey != NULL) {
+ writekey(prevkey, directory, write_state);
+ }
+ }
+
+ if (prevkey != NULL) {
+ dst_key_free(&prevkey);
+ }
+ dst_key_free(&key);
+ dst_lib_destroy();
+ if (verbose > 10) {
+ isc_mem_stats(mctx, stdout);
+ }
+ cleanup_logging(&log);
+ isc_mem_free(mctx, directory);
+ isc_mem_destroy(&mctx);
+
+ return (0);
+}
diff --git a/bin/dnssec/dnssec-settime.rst b/bin/dnssec/dnssec-settime.rst
new file mode 100644
index 0000000..9f6c428
--- /dev/null
+++ b/bin/dnssec/dnssec-settime.rst
@@ -0,0 +1,231 @@
+.. 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.
+
+.. highlight: console
+
+.. _man_dnssec-settime:
+
+dnssec-settime: set the key timing metadata for a DNSSEC key
+------------------------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`dnssec-settime` [**-f**] [**-K** directory] [**-L** ttl] [**-P** date/offset] [**-P** ds date/offset] [**-P** sync date/offset] [**-A** date/offset] [**-R** date/offset] [**-I** date/offset] [**-D** date/offset] [**-D** ds date/offset] [**-D** sync date/offset] [**-S** key] [**-i** interval] [**-h**] [**-V**] [**-v** level] [**-E** engine] {keyfile} [**-s**] [**-g** state] [**-d** state date/offset] [**-k** state date/offset] [**-r** state date/offset] [**-z** state date/offset]
+
+Description
+~~~~~~~~~~~
+
+``dnssec-settime`` reads a DNSSEC private key file and sets the key
+timing metadata as specified by the ``-P``, ``-A``, ``-R``, ``-I``, and
+``-D`` options. The metadata can then be used by ``dnssec-signzone`` or
+other signing software to determine when a key is to be published,
+whether it should be used for signing a zone, etc.
+
+If none of these options is set on the command line,
+``dnssec-settime`` simply prints the key timing metadata already stored
+in the key.
+
+When key metadata fields are changed, both files of a key pair
+(``Knnnn.+aaa+iiiii.key`` and ``Knnnn.+aaa+iiiii.private``) are
+regenerated.
+
+Metadata fields are stored in the private file. A
+human-readable description of the metadata is also placed in comments in
+the key file. The private file's permissions are always set to be
+inaccessible to anyone other than the owner (mode 0600).
+
+When working with state files, it is possible to update the timing metadata in
+those files as well with ``-s``. With this option, it is also possible to update key
+states with ``-d`` (DS), ``-k`` (DNSKEY), ``-r`` (RRSIG of KSK), or ``-z``
+(RRSIG of ZSK). Allowed states are HIDDEN, RUMOURED, OMNIPRESENT, and
+UNRETENTIVE.
+
+The goal state of the key can also be set with ``-g``. This should be either
+HIDDEN or OMNIPRESENT, representing whether the key should be removed from the
+zone or published.
+
+It is NOT RECOMMENDED to manipulate state files manually, except for testing
+purposes.
+
+Options
+~~~~~~~
+
+``-f``
+ This option forces an update of an old-format key with no metadata fields. Without
+ this option, ``dnssec-settime`` fails when attempting to update a
+ legacy key. With this option, the key is recreated in the new
+ format, but with the original key data retained. The key's creation
+ date is set to the present time. If no other values are
+ specified, then the key's publication and activation dates are also
+ set to the present time.
+
+``-K directory``
+ This option sets the directory in which the key files are to reside.
+
+``-L ttl``
+ This option sets the default TTL to use for this key when it is converted into a
+ DNSKEY RR. This is the TTL used when the key is imported into a zone,
+ unless there was already a DNSKEY RRset in
+ place, in which case the existing TTL takes precedence. If this
+ value is not set and there is no existing DNSKEY RRset, the TTL
+ defaults to the SOA TTL. Setting the default TTL to ``0`` or ``none``
+ removes it from the key.
+
+``-h``
+ This option emits a usage message and exits.
+
+``-V``
+ This option prints version information.
+
+``-v level``
+ This option sets the debugging level.
+
+``-E engine``
+ This option specifies the cryptographic hardware to use, when applicable.
+
+ When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL
+ engine identifier that drives the cryptographic accelerator or
+ hardware service module (usually ``pkcs11``). When BIND is
+ built with native PKCS#11 cryptography (``--enable-native-pkcs11``), it
+ defaults to the path of the PKCS#11 provider library specified via
+ ``--with-pkcs11``.
+
+Timing Options
+~~~~~~~~~~~~~~
+
+Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS. If the
+argument begins with a ``+`` or ``-``, it is interpreted as an offset from
+the present time. For convenience, if such an offset is followed by one
+of the suffixes ``y``, ``mo``, ``w``, ``d``, ``h``, or ``mi``, then the offset is
+computed in years (defined as 365 24-hour days, ignoring leap years),
+months (defined as 30 24-hour days), weeks, days, hours, or minutes,
+respectively. Without a suffix, the offset is computed in seconds. To
+explicitly prevent a date from being set, use ``none`` or ``never``.
+
+``-P date/offset``
+ This option sets the date on which a key is to be published to the zone. After
+ that date, the key is included in the zone but is not used
+ to sign it.
+
+``-P ds date/offset``
+ This option Sets the date on which DS records that match this key have been
+ seen in the parent zone.
+
+``-P sync date/offset``
+ This option sets the date on which CDS and CDNSKEY records that match this key
+ are to be published to the zone.
+
+``-A date/offset``
+ This option sets the date on which the key is to be activated. After that date,
+ the key is included in the zone and used to sign it.
+
+``-R date/offset``
+ This option sets the date on which the key is to be revoked. After that date, the
+ key is flagged as revoked. It is included in the zone and
+ is used to sign it.
+
+``-I date/offset``
+ This option sets the date on which the key is to be retired. After that date, the
+ key is still included in the zone, but it is not used to
+ sign it.
+
+``-D date/offset``
+ This option sets the date on which the key is to be deleted. After that date, the
+ key is no longer included in the zone. (However, it may remain in the key
+ repository.)
+
+``-D ds date/offset``
+ This option sets the date on which the DS records that match this key have
+ been seen removed from the parent zone.
+
+``-D sync date/offset``
+ This option sets the date on which the CDS and CDNSKEY records that match this
+ key are to be deleted.
+
+``-S predecessor key``
+ This option selects a key for which the key being modified is an explicit
+ successor. The name, algorithm, size, and type of the predecessor key
+ must exactly match those of the key being modified. The activation
+ date of the successor key is set to the inactivation date of the
+ predecessor. The publication date is set to the activation date
+ minus the prepublication interval, which defaults to 30 days.
+
+``-i interval``
+ This option sets the prepublication interval for a key. If set, then the
+ publication and activation dates must be separated by at least this
+ much time. If the activation date is specified but the publication
+ date is not, the publication date defaults to this much time
+ before the activation date; conversely, if the publication date is
+ specified but not the activation date, activation is set to
+ this much time after publication.
+
+ If the key is being created as an explicit successor to another key,
+ then the default prepublication interval is 30 days; otherwise it is
+ zero.
+
+ As with date offsets, if the argument is followed by one of the
+ suffixes ``y``, ``mo``, ``w``, ``d``, ``h``, or ``mi``, the interval is
+ measured in years, months, weeks, days, hours, or minutes,
+ respectively. Without a suffix, the interval is measured in seconds.
+
+Key State Options
+~~~~~~~~~~~~~~~~~
+
+To test dnssec-policy it may be necessary to construct keys with artificial
+state information; these options are used by the testing framework for that
+purpose, but should never be used in production.
+
+Known key states are HIDDEN, RUMOURED, OMNIPRESENT, and UNRETENTIVE.
+
+``-s``
+ This option indicates that when setting key timing data, the state file should also be updated.
+
+``-g state``
+ This option sets the goal state for this key. Must be HIDDEN or OMNIPRESENT.
+
+``-d state date/offset``
+ This option sets the DS state for this key as of the specified date, offset from the current date.
+
+``-k state date/offset``
+ This option sets the DNSKEY state for this key as of the specified date, offset from the current date.
+
+``-r state date/offset``
+ This option sets the RRSIG (KSK) state for this key as of the specified date, offset from the current date.
+
+``-z state date/offset``
+ This option sets the RRSIG (ZSK) state for this key as of the specified date, offset from the current date.
+
+Printing Options
+~~~~~~~~~~~~~~~~
+
+``dnssec-settime`` can also be used to print the timing metadata
+associated with a key.
+
+``-u``
+ This option indicates that times should be printed in Unix epoch format.
+
+``-p C/P/Pds/Psync/A/R/I/D/Dds/Dsync/all``
+ This option prints a specific metadata value or set of metadata values.
+ The ``-p`` option may be followed by one or more of the following letters or
+ strings to indicate which value or values to print: ``C`` for the
+ creation date, ``P`` for the publication date, ``Pds` for the DS publication
+ date, ``Psync`` for the CDS and CDNSKEY publication date, ``A`` for the
+ activation date, ``R`` for the revocation date, ``I`` for the inactivation
+ date, ``D`` for the deletion date, ``Dds`` for the DS deletion date,
+ and ``Dsync`` for the CDS and CDNSKEY deletion date. To print all of the
+ metadata, use ``all``.
+
+See Also
+~~~~~~~~
+
+:manpage:`dnssec-keygen(8)`, :manpage:`dnssec-signzone(8)`, BIND 9 Administrator Reference Manual,
+:rfc:`5011`.
diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c
new file mode 100644
index 0000000..2d2c158
--- /dev/null
+++ b/bin/dnssec/dnssec-signzone.c
@@ -0,0 +1,4197 @@
+/*
+ * Portions 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.
+ *
+ * Portions Copyright (C) Network Associates, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/app.h>
+#include <isc/atomic.h>
+#include <isc/base32.h>
+#include <isc/commandline.h>
+#include <isc/event.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/hex.h>
+#include <isc/managers.h>
+#include <isc/md.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/os.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/rwlock.h>
+#include <isc/safe.h>
+#include <isc/serial.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/diff.h>
+#include <dns/dnssec.h>
+#include <dns/ds.h>
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/nsec.h>
+#include <dns/nsec3.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/soa.h>
+#include <dns/time.h>
+#include <dns/update.h>
+#include <dns/zoneverify.h>
+
+#include <dst/dst.h>
+
+#if USE_PKCS11
+#include <pk11/result.h>
+#endif /* if USE_PKCS11 */
+
+#include "dnssectool.h"
+
+const char *program = "dnssec-signzone";
+
+typedef struct hashlist hashlist_t;
+
+static int nsec_datatype = dns_rdatatype_nsec;
+
+#define check_dns_dbiterator_current(result) \
+ check_result((result == DNS_R_NEWORIGIN) ? ISC_R_SUCCESS : result, \
+ "dns_dbiterator_current()")
+
+#define IS_NSEC3 (nsec_datatype == dns_rdatatype_nsec3)
+#define OPTOUT(x) (((x)&DNS_NSEC3FLAG_OPTOUT) != 0)
+
+#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0)
+
+#define BUFSIZE 2048
+#define MAXDSKEYS 8
+
+#define SIGNER_EVENTCLASS ISC_EVENTCLASS(0x4453)
+#define SIGNER_EVENT_WRITE (SIGNER_EVENTCLASS + 0)
+#define SIGNER_EVENT_WORK (SIGNER_EVENTCLASS + 1)
+
+#define SOA_SERIAL_KEEP 0
+#define SOA_SERIAL_INCREMENT 1
+#define SOA_SERIAL_UNIXTIME 2
+#define SOA_SERIAL_DATE 3
+
+typedef struct signer_event sevent_t;
+struct signer_event {
+ ISC_EVENT_COMMON(sevent_t);
+ dns_fixedname_t *fname;
+ dns_dbnode_t *node;
+};
+
+static dns_dnsseckeylist_t keylist;
+static unsigned int keycount = 0;
+static isc_rwlock_t keylist_lock;
+static isc_stdtime_t starttime = 0, endtime = 0, dnskey_endtime = 0, now;
+static int cycle = -1;
+static int jitter = 0;
+static bool tryverify = false;
+static bool printstats = false;
+static isc_mem_t *mctx = NULL;
+static dns_ttl_t zone_soa_min_ttl;
+static dns_ttl_t soa_ttl;
+static FILE *outfp = NULL;
+static char *tempfile = NULL;
+static const dns_master_style_t *masterstyle;
+static dns_masterformat_t inputformat = dns_masterformat_text;
+static dns_masterformat_t outputformat = dns_masterformat_text;
+static uint32_t rawversion = 1, serialnum = 0;
+static bool snset = false;
+static unsigned int nsigned = 0, nretained = 0, ndropped = 0;
+static unsigned int nverified = 0, nverifyfailed = 0;
+static const char *directory = NULL, *dsdir = NULL;
+static isc_mutex_t namelock, statslock;
+static isc_nm_t *netmgr = NULL;
+static isc_taskmgr_t *taskmgr = NULL;
+static dns_db_t *gdb; /* The database */
+static dns_dbversion_t *gversion; /* The database version */
+static dns_dbiterator_t *gdbiter; /* The database iterator */
+static dns_rdataclass_t gclass; /* The class */
+static dns_name_t *gorigin; /* The database origin */
+static int nsec3flags = 0;
+static dns_iterations_t nsec3iter = 10U;
+static unsigned char saltbuf[255];
+static unsigned char *gsalt = saltbuf;
+static size_t salt_length = 0;
+static isc_task_t *master = NULL;
+static unsigned int ntasks = 0;
+static atomic_bool shuttingdown;
+static atomic_bool finished;
+static bool nokeys = false;
+static bool removefile = false;
+static bool generateds = false;
+static bool ignore_kskflag = false;
+static bool keyset_kskonly = false;
+static dns_master_style_t *dsstyle = NULL;
+static unsigned int serialformat = SOA_SERIAL_KEEP;
+static unsigned int hash_length = 0;
+static bool unknownalg = false;
+static bool disable_zone_check = false;
+static bool update_chain = false;
+static bool set_keyttl = false;
+static dns_ttl_t keyttl;
+static bool smartsign = false;
+static bool remove_orphansigs = false;
+static bool remove_inactkeysigs = false;
+static bool output_dnssec_only = false;
+static bool output_stdout = false;
+static bool set_maxttl = false;
+static dns_ttl_t maxttl = 0;
+static bool no_max_check = false;
+
+#define INCSTAT(counter) \
+ if (printstats) { \
+ LOCK(&statslock); \
+ counter++; \
+ UNLOCK(&statslock); \
+ }
+
+static void
+sign(isc_task_t *task, isc_event_t *event);
+
+/*%
+ * Store a copy of 'name' in 'fzonecut' and return a pointer to that copy.
+ */
+static dns_name_t *
+savezonecut(dns_fixedname_t *fzonecut, dns_name_t *name) {
+ dns_name_t *result;
+
+ result = dns_fixedname_initname(fzonecut);
+ dns_name_copynf(name, result);
+
+ return (result);
+}
+
+static void
+dumpnode(dns_name_t *name, dns_dbnode_t *node) {
+ dns_rdataset_t rds;
+ dns_rdatasetiter_t *iter = NULL;
+ isc_buffer_t *buffer = NULL;
+ isc_region_t r;
+ isc_result_t result;
+ unsigned bufsize = 4096;
+
+ if (outputformat != dns_masterformat_text) {
+ return;
+ }
+
+ if (!output_dnssec_only) {
+ result = dns_master_dumpnodetostream(mctx, gdb, gversion, node,
+ name, masterstyle, outfp);
+ check_result(result, "dns_master_dumpnodetostream");
+ return;
+ }
+
+ result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &iter);
+ check_result(result, "dns_db_allrdatasets");
+
+ dns_rdataset_init(&rds);
+
+ isc_buffer_allocate(mctx, &buffer, bufsize);
+
+ for (result = dns_rdatasetiter_first(iter); result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iter))
+ {
+ dns_rdatasetiter_current(iter, &rds);
+
+ if (rds.type != dns_rdatatype_rrsig &&
+ rds.type != dns_rdatatype_nsec &&
+ rds.type != dns_rdatatype_nsec3 &&
+ rds.type != dns_rdatatype_nsec3param &&
+ (!smartsign || rds.type != dns_rdatatype_dnskey))
+ {
+ dns_rdataset_disassociate(&rds);
+ continue;
+ }
+
+ for (;;) {
+ result = dns_master_rdatasettotext(
+ name, &rds, masterstyle, NULL, buffer);
+ if (result != ISC_R_NOSPACE) {
+ break;
+ }
+
+ bufsize <<= 1;
+ isc_buffer_free(&buffer);
+ isc_buffer_allocate(mctx, &buffer, bufsize);
+ }
+ check_result(result, "dns_master_rdatasettotext");
+
+ isc_buffer_usedregion(buffer, &r);
+ result = isc_stdio_write(r.base, 1, r.length, outfp, NULL);
+ check_result(result, "isc_stdio_write");
+ isc_buffer_clear(buffer);
+
+ dns_rdataset_disassociate(&rds);
+ }
+
+ isc_buffer_free(&buffer);
+ dns_rdatasetiter_destroy(&iter);
+}
+
+/*%
+ * Sign the given RRset with given key, and add the signature record to the
+ * given tuple.
+ */
+static void
+signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dst_key_t *key,
+ dns_ttl_t ttl, dns_diff_t *add, const char *logmsg) {
+ isc_result_t result;
+ isc_stdtime_t jendtime, expiry;
+ char keystr[DST_KEY_FORMATSIZE];
+ dns_rdata_t trdata = DNS_RDATA_INIT;
+ unsigned char array[BUFSIZE];
+ isc_buffer_t b;
+ dns_difftuple_t *tuple;
+
+ dst_key_format(key, keystr, sizeof(keystr));
+ vbprintf(1, "\t%s %s\n", logmsg, keystr);
+
+ if (rdataset->type == dns_rdatatype_dnskey) {
+ expiry = dnskey_endtime;
+ } else {
+ expiry = endtime;
+ }
+
+ jendtime = (jitter != 0) ? expiry - isc_random_uniform(jitter) : expiry;
+ isc_buffer_init(&b, array, sizeof(array));
+ result = dns_dnssec_sign(name, rdataset, key, &starttime, &jendtime,
+ mctx, &b, &trdata);
+ if (result != ISC_R_SUCCESS) {
+ fatal("dnskey '%s' failed to sign data: %s", keystr,
+ isc_result_totext(result));
+ }
+ INCSTAT(nsigned);
+
+ if (tryverify) {
+ result = dns_dnssec_verify(name, rdataset, key, true, 0, mctx,
+ &trdata, NULL);
+ if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) {
+ vbprintf(3, "\tsignature verified\n");
+ INCSTAT(nverified);
+ } else {
+ vbprintf(3, "\tsignature failed to verify\n");
+ INCSTAT(nverifyfailed);
+ }
+ }
+
+ tuple = NULL;
+ result = dns_difftuple_create(mctx, DNS_DIFFOP_ADDRESIGN, name, ttl,
+ &trdata, &tuple);
+ check_result(result, "dns_difftuple_create");
+ dns_diff_append(add, &tuple);
+}
+
+static bool
+issigningkey(dns_dnsseckey_t *key) {
+ return (key->force_sign || key->hint_sign);
+}
+
+static bool
+ispublishedkey(dns_dnsseckey_t *key) {
+ return ((key->force_publish || key->hint_publish) && !key->hint_remove);
+}
+
+static bool
+iszonekey(dns_dnsseckey_t *key) {
+ return (dns_name_equal(dst_key_name(key->key), gorigin) &&
+ dst_key_iszonekey(key->key));
+}
+
+static bool
+isksk(dns_dnsseckey_t *key) {
+ return (key->ksk);
+}
+
+static bool
+iszsk(dns_dnsseckey_t *key) {
+ return (ignore_kskflag || !key->ksk);
+}
+
+/*%
+ * Find the key that generated an RRSIG, if it is in the key list. If
+ * so, return a pointer to it, otherwise return NULL.
+ *
+ * No locking is performed here, this must be done by the caller.
+ */
+static dns_dnsseckey_t *
+keythatsigned_unlocked(dns_rdata_rrsig_t *rrsig) {
+ dns_dnsseckey_t *key;
+
+ for (key = ISC_LIST_HEAD(keylist); key != NULL;
+ key = ISC_LIST_NEXT(key, link))
+ {
+ if (rrsig->keyid == dst_key_id(key->key) &&
+ rrsig->algorithm == dst_key_alg(key->key) &&
+ dns_name_equal(&rrsig->signer, dst_key_name(key->key)))
+ {
+ return (key);
+ }
+ }
+ return (NULL);
+}
+
+/*%
+ * Finds the key that generated a RRSIG, if possible. First look at the keys
+ * that we've loaded already, and then see if there's a key on disk.
+ */
+static dns_dnsseckey_t *
+keythatsigned(dns_rdata_rrsig_t *rrsig) {
+ isc_result_t result;
+ dst_key_t *pubkey = NULL, *privkey = NULL;
+ dns_dnsseckey_t *key = NULL;
+
+ RWLOCK(&keylist_lock, isc_rwlocktype_read);
+ key = keythatsigned_unlocked(rrsig);
+ RWUNLOCK(&keylist_lock, isc_rwlocktype_read);
+ if (key != NULL) {
+ return (key);
+ }
+
+ /*
+ * We did not find the key in our list. Get a write lock now, since
+ * we may be modifying the bits. We could do the tryupgrade() dance,
+ * but instead just get a write lock and check once again to see if
+ * it is on our list. It's possible someone else may have added it
+ * after all.
+ */
+ isc_rwlock_lock(&keylist_lock, isc_rwlocktype_write);
+ key = keythatsigned_unlocked(rrsig);
+ if (key != NULL) {
+ isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
+ return (key);
+ }
+
+ result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
+ rrsig->algorithm, DST_TYPE_PUBLIC, directory,
+ mctx, &pubkey);
+ if (result != ISC_R_SUCCESS) {
+ isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
+ return (NULL);
+ }
+
+ result = dst_key_fromfile(
+ &rrsig->signer, rrsig->keyid, rrsig->algorithm,
+ DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, directory, mctx, &privkey);
+ if (result == ISC_R_SUCCESS) {
+ dst_key_free(&pubkey);
+ result = dns_dnsseckey_create(mctx, &privkey, &key);
+ } else {
+ result = dns_dnsseckey_create(mctx, &pubkey, &key);
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ key->force_publish = false;
+ key->force_sign = false;
+ key->index = keycount++;
+ ISC_LIST_APPEND(keylist, key, link);
+ }
+
+ isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
+ return (key);
+}
+
+/*%
+ * Check to see if we expect to find a key at this name. If we see a RRSIG
+ * and can't find the signing key that we expect to find, we drop the rrsig.
+ * I'm not sure if this is completely correct, but it seems to work.
+ */
+static bool
+expecttofindkey(dns_name_t *name) {
+ unsigned int options = DNS_DBFIND_NOWILD;
+ dns_fixedname_t fname;
+ isc_result_t result;
+ char namestr[DNS_NAME_FORMATSIZE];
+
+ dns_fixedname_init(&fname);
+ result = dns_db_find(gdb, name, gversion, dns_rdatatype_dnskey, options,
+ 0, NULL, dns_fixedname_name(&fname), NULL, NULL);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NXRRSET:
+ return (true);
+ case DNS_R_DELEGATION:
+ case DNS_R_CNAME:
+ case DNS_R_DNAME:
+ return (false);
+ }
+ dns_name_format(name, namestr, sizeof(namestr));
+ fatal("failure looking for '%s DNSKEY' in database: %s", namestr,
+ isc_result_totext(result));
+ UNREACHABLE();
+ return (false); /* removes a warning */
+}
+
+static bool
+setverifies(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ dns_rdata_t *rrsig) {
+ isc_result_t result;
+ result = dns_dnssec_verify(name, set, key, false, 0, mctx, rrsig, NULL);
+ if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) {
+ INCSTAT(nverified);
+ return (true);
+ } else {
+ INCSTAT(nverifyfailed);
+ return (false);
+ }
+}
+
+/*%
+ * Signs a set. Goes through contortions to decide if each RRSIG should
+ * be dropped or retained, and then determines if any new SIGs need to
+ * be generated.
+ */
+static void
+signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name,
+ dns_rdataset_t *set) {
+ dns_rdataset_t sigset;
+ dns_rdata_t sigrdata = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t rrsig;
+ dns_dnsseckey_t *key;
+ isc_result_t result;
+ bool nosigs = false;
+ bool *wassignedby, *nowsignedby;
+ int arraysize;
+ dns_difftuple_t *tuple;
+ dns_ttl_t ttl;
+ int i;
+ char namestr[DNS_NAME_FORMATSIZE];
+ char typestr[DNS_RDATATYPE_FORMATSIZE];
+ char sigstr[SIG_FORMATSIZE];
+
+ dns_name_format(name, namestr, sizeof(namestr));
+ dns_rdatatype_format(set->type, typestr, sizeof(typestr));
+
+ ttl = ISC_MIN(set->ttl, endtime - starttime);
+
+ dns_rdataset_init(&sigset);
+ result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_rrsig,
+ set->type, 0, &sigset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ vbprintf(2, "no existing signatures for %s/%s\n", namestr,
+ typestr);
+ result = ISC_R_SUCCESS;
+ nosigs = true;
+ }
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed while looking for '%s RRSIG %s': %s", namestr,
+ typestr, isc_result_totext(result));
+ }
+
+ vbprintf(1, "%s/%s:\n", namestr, typestr);
+
+ arraysize = keycount;
+ if (!nosigs) {
+ arraysize += dns_rdataset_count(&sigset);
+ }
+ wassignedby = isc_mem_get(mctx, arraysize * sizeof(bool));
+ nowsignedby = isc_mem_get(mctx, arraysize * sizeof(bool));
+
+ for (i = 0; i < arraysize; i++) {
+ wassignedby[i] = nowsignedby[i] = false;
+ }
+
+ if (nosigs) {
+ result = ISC_R_NOMORE;
+ } else {
+ result = dns_rdataset_first(&sigset);
+ }
+
+ while (result == ISC_R_SUCCESS) {
+ bool expired, future;
+ bool keep = false, resign = false;
+
+ dns_rdataset_current(&sigset, &sigrdata);
+
+ result = dns_rdata_tostruct(&sigrdata, &rrsig, NULL);
+ check_result(result, "dns_rdata_tostruct");
+
+ future = isc_serial_lt(now, rrsig.timesigned);
+
+ key = keythatsigned(&rrsig);
+ sig_format(&rrsig, sigstr, sizeof(sigstr));
+ expired = isc_serial_gt(now + cycle, rrsig.timeexpire);
+
+ if (isc_serial_gt(rrsig.timesigned, rrsig.timeexpire)) {
+ /* rrsig is dropped and not replaced */
+ vbprintf(2,
+ "\trrsig by %s dropped - "
+ "invalid validity period\n",
+ sigstr);
+ } else if (key == NULL && !future &&
+ expecttofindkey(&rrsig.signer))
+ {
+ /* rrsig is dropped and not replaced */
+ vbprintf(2,
+ "\trrsig by %s dropped - "
+ "private dnskey not found\n",
+ sigstr);
+ } else if (key == NULL || future) {
+ keep = (!expired && !remove_orphansigs);
+ vbprintf(2, "\trrsig by %s %s - dnskey not found\n",
+ keep ? "retained" : "dropped", sigstr);
+ } else if (!dns_dnssec_keyactive(key->key, now) &&
+ remove_inactkeysigs)
+ {
+ keep = false;
+ vbprintf(2, "\trrsig by %s dropped - key inactive\n",
+ sigstr);
+ } else if (issigningkey(key)) {
+ wassignedby[key->index] = true;
+
+ if (!expired && rrsig.originalttl == set->ttl &&
+ setverifies(name, set, key->key, &sigrdata))
+ {
+ vbprintf(2, "\trrsig by %s retained\n", sigstr);
+ keep = true;
+ } else {
+ vbprintf(2, "\trrsig by %s dropped - %s\n",
+ sigstr,
+ expired ? "expired"
+ : rrsig.originalttl != set->ttl
+ ? "ttl change"
+ : "failed to "
+ "verify");
+ resign = true;
+ }
+ } else if (!ispublishedkey(key) && remove_orphansigs) {
+ vbprintf(2, "\trrsig by %s dropped - dnskey removed\n",
+ sigstr);
+ } else if (iszonekey(key)) {
+ wassignedby[key->index] = true;
+
+ if (!expired && rrsig.originalttl == set->ttl &&
+ setverifies(name, set, key->key, &sigrdata))
+ {
+ vbprintf(2, "\trrsig by %s retained\n", sigstr);
+ keep = true;
+ } else {
+ vbprintf(2, "\trrsig by %s dropped - %s\n",
+ sigstr,
+ expired ? "expired"
+ : rrsig.originalttl != set->ttl
+ ? "ttl change"
+ : "failed to "
+ "verify");
+ }
+ } else if (!expired) {
+ vbprintf(2, "\trrsig by %s retained\n", sigstr);
+ keep = true;
+ } else {
+ vbprintf(2, "\trrsig by %s expired\n", sigstr);
+ }
+
+ if (keep) {
+ if (key != NULL) {
+ nowsignedby[key->index] = true;
+ }
+ INCSTAT(nretained);
+ if (sigset.ttl != ttl) {
+ vbprintf(2, "\tfixing ttl %s\n", sigstr);
+ tuple = NULL;
+ result = dns_difftuple_create(
+ mctx, DNS_DIFFOP_DELRESIGN, name,
+ sigset.ttl, &sigrdata, &tuple);
+ check_result(result, "dns_difftuple_create");
+ dns_diff_append(del, &tuple);
+ result = dns_difftuple_create(
+ mctx, DNS_DIFFOP_ADDRESIGN, name, ttl,
+ &sigrdata, &tuple);
+ check_result(result, "dns_difftuple_create");
+ dns_diff_append(add, &tuple);
+ }
+ } else {
+ tuple = NULL;
+ vbprintf(2, "\tremoving signature by %s\n", sigstr);
+ result = dns_difftuple_create(
+ mctx, DNS_DIFFOP_DELRESIGN, name, sigset.ttl,
+ &sigrdata, &tuple);
+ check_result(result, "dns_difftuple_create");
+ dns_diff_append(del, &tuple);
+ INCSTAT(ndropped);
+ }
+
+ if (resign) {
+ INSIST(!keep);
+
+ signwithkey(name, set, key->key, ttl, add,
+ "resigning with dnskey");
+ nowsignedby[key->index] = true;
+ }
+
+ dns_rdata_reset(&sigrdata);
+ dns_rdata_freestruct(&rrsig);
+ result = dns_rdataset_next(&sigset);
+ }
+ if (result == ISC_R_NOMORE) {
+ result = ISC_R_SUCCESS;
+ }
+
+ check_result(result, "dns_rdataset_first/next");
+ if (dns_rdataset_isassociated(&sigset)) {
+ dns_rdataset_disassociate(&sigset);
+ }
+
+ for (key = ISC_LIST_HEAD(keylist); key != NULL;
+ key = ISC_LIST_NEXT(key, link))
+ {
+ if (nowsignedby[key->index]) {
+ continue;
+ }
+
+ if (!issigningkey(key)) {
+ continue;
+ }
+
+ if ((set->type == dns_rdatatype_cds ||
+ set->type == dns_rdatatype_cdnskey ||
+ set->type == dns_rdatatype_dnskey) &&
+ dns_name_equal(name, gorigin))
+ {
+ bool have_ksk;
+ dns_dnsseckey_t *curr;
+
+ have_ksk = isksk(key);
+ for (curr = ISC_LIST_HEAD(keylist); curr != NULL;
+ curr = ISC_LIST_NEXT(curr, link))
+ {
+ if (dst_key_alg(key->key) !=
+ dst_key_alg(curr->key))
+ {
+ continue;
+ }
+ if (REVOKE(curr->key)) {
+ continue;
+ }
+ if (isksk(curr)) {
+ have_ksk = true;
+ }
+ }
+ if (isksk(key) || !have_ksk ||
+ (iszsk(key) && !keyset_kskonly))
+ {
+ signwithkey(name, set, key->key, ttl, add,
+ "signing with dnskey");
+ }
+ } else if (iszsk(key)) {
+ /*
+ * Sign with the ZSK unless there is a predecessor
+ * key that already signs this RRset.
+ */
+ bool have_pre_sig = false;
+ dns_dnsseckey_t *curr;
+ uint32_t pre;
+ isc_result_t ret = dst_key_getnum(
+ key->key, DST_NUM_PREDECESSOR, &pre);
+ if (ret == ISC_R_SUCCESS) {
+ /*
+ * This key has a predecessor, look for the
+ * corresponding key in the keylist. The
+ * key we are looking for must be:
+ * - From the same cryptographic algorithm.
+ * - Have the ZSK type (iszsk).
+ * - Have key ID equal to the predecessor id.
+ * - Have a successor that matches 'key' id.
+ */
+ for (curr = ISC_LIST_HEAD(keylist);
+ curr != NULL;
+ curr = ISC_LIST_NEXT(curr, link))
+ {
+ uint32_t suc;
+
+ if (dst_key_alg(key->key) !=
+ dst_key_alg(curr->key) ||
+ !iszsk(curr) ||
+ dst_key_id(curr->key) != pre)
+ {
+ continue;
+ }
+ ret = dst_key_getnum(curr->key,
+ DST_NUM_SUCCESSOR,
+ &suc);
+ if (ret != ISC_R_SUCCESS ||
+ dst_key_id(key->key) != suc)
+ {
+ continue;
+ }
+
+ /*
+ * curr is the predecessor we were
+ * looking for. Check if this key
+ * signs this RRset.
+ */
+ if (nowsignedby[curr->index]) {
+ have_pre_sig = true;
+ }
+ }
+ }
+
+ /*
+ * If we have a signature of a predecessor key,
+ * skip signing with this key.
+ */
+ if (!have_pre_sig) {
+ signwithkey(name, set, key->key, ttl, add,
+ "signing with dnskey");
+ }
+ }
+ }
+
+ isc_mem_put(mctx, wassignedby, arraysize * sizeof(bool));
+ isc_mem_put(mctx, nowsignedby, arraysize * sizeof(bool));
+}
+
+struct hashlist {
+ unsigned char *hashbuf;
+ size_t entries;
+ size_t size;
+ size_t length;
+};
+
+static void
+hashlist_init(hashlist_t *l, unsigned int nodes, unsigned int length) {
+ l->entries = 0;
+ l->length = length + 1;
+
+ if (nodes != 0) {
+ l->size = nodes;
+ l->hashbuf = malloc(l->size * l->length);
+ if (l->hashbuf == NULL) {
+ l->size = 0;
+ }
+ } else {
+ l->size = 0;
+ l->hashbuf = NULL;
+ }
+}
+
+static void
+hashlist_free(hashlist_t *l) {
+ if (l->hashbuf) {
+ free(l->hashbuf);
+ l->hashbuf = NULL;
+ l->entries = 0;
+ l->length = 0;
+ l->size = 0;
+ }
+}
+
+static void
+hashlist_add(hashlist_t *l, const unsigned char *hash, size_t len) {
+ REQUIRE(len <= l->length);
+
+ if (l->entries == l->size) {
+ l->size = l->size * 2 + 100;
+ l->hashbuf = realloc(l->hashbuf, l->size * l->length);
+ if (l->hashbuf == NULL) {
+ fatal("unable to grow hashlist: out of memory");
+ }
+ }
+ memset(l->hashbuf + l->entries * l->length, 0, l->length);
+ memmove(l->hashbuf + l->entries * l->length, hash, len);
+ l->entries++;
+}
+
+static void
+hashlist_add_dns_name(hashlist_t *l,
+ /*const*/ dns_name_t *name, unsigned int hashalg,
+ unsigned int iterations, const unsigned char *salt,
+ size_t salt_len, bool speculative) {
+ char nametext[DNS_NAME_FORMATSIZE];
+ unsigned char hash[NSEC3_MAX_HASH_LENGTH + 1];
+ unsigned int len;
+ size_t i;
+
+ len = isc_iterated_hash(hash, hashalg, iterations, salt, (int)salt_len,
+ name->ndata, name->length);
+ if (verbose) {
+ dns_name_format(name, nametext, sizeof nametext);
+ for (i = 0; i < len; i++) {
+ fprintf(stderr, "%02x", hash[i]);
+ }
+ fprintf(stderr, " %s\n", nametext);
+ }
+ hash[len++] = speculative ? 1 : 0;
+ hashlist_add(l, hash, len);
+}
+
+static int
+hashlist_comp(const void *a, const void *b) {
+ return (memcmp(a, b, hash_length + 1));
+}
+
+static void
+hashlist_sort(hashlist_t *l) {
+ INSIST(l->hashbuf != NULL || l->length == 0);
+ if (l->length > 0) {
+ qsort(l->hashbuf, l->entries, l->length, hashlist_comp);
+ }
+}
+
+static bool
+hashlist_hasdup(hashlist_t *l) {
+ unsigned char *current;
+ unsigned char *next = l->hashbuf;
+ size_t entries = l->entries;
+
+ /*
+ * Skip initial speculative wild card hashes.
+ */
+ while (entries > 0U && next[l->length - 1] != 0U) {
+ next += l->length;
+ entries--;
+ }
+
+ current = next;
+ while (entries-- > 1U) {
+ next += l->length;
+ if (next[l->length - 1] != 0) {
+ continue;
+ }
+ if (isc_safe_memequal(current, next, l->length - 1)) {
+ return (true);
+ }
+ current = next;
+ }
+ return (false);
+}
+
+static const unsigned char *
+hashlist_findnext(const hashlist_t *l,
+ const unsigned char hash[NSEC3_MAX_HASH_LENGTH]) {
+ size_t entries = l->entries;
+ const unsigned char *next = bsearch(hash, l->hashbuf, l->entries,
+ l->length, hashlist_comp);
+ INSIST(next != NULL);
+
+ do {
+ if (next < l->hashbuf + (l->entries - 1) * l->length) {
+ next += l->length;
+ } else {
+ next = l->hashbuf;
+ }
+ if (next[l->length - 1] == 0) {
+ break;
+ }
+ } while (entries-- > 1U);
+ INSIST(entries != 0U);
+ return (next);
+}
+
+static bool
+hashlist_exists(const hashlist_t *l,
+ const unsigned char hash[NSEC3_MAX_HASH_LENGTH]) {
+ if (bsearch(hash, l->hashbuf, l->entries, l->length, hashlist_comp)) {
+ return (true);
+ } else {
+ return (false);
+ }
+}
+
+static void
+addnowildcardhash(hashlist_t *l,
+ /*const*/ dns_name_t *name, unsigned int hashalg,
+ unsigned int iterations, const unsigned char *salt,
+ size_t salt_len) {
+ dns_fixedname_t fixed;
+ dns_name_t *wild;
+ dns_dbnode_t *node = NULL;
+ isc_result_t result;
+ char namestr[DNS_NAME_FORMATSIZE];
+
+ wild = dns_fixedname_initname(&fixed);
+
+ result = dns_name_concatenate(dns_wildcardname, name, wild, NULL);
+ if (result == ISC_R_NOSPACE) {
+ return;
+ }
+ check_result(result, "addnowildcardhash: dns_name_concatenate()");
+
+ result = dns_db_findnode(gdb, wild, false, &node);
+ if (result == ISC_R_SUCCESS) {
+ dns_db_detachnode(gdb, &node);
+ return;
+ }
+
+ if (verbose) {
+ dns_name_format(wild, namestr, sizeof(namestr));
+ fprintf(stderr, "adding no-wildcardhash for %s\n", namestr);
+ }
+
+ hashlist_add_dns_name(l, wild, hashalg, iterations, salt, salt_len,
+ true);
+}
+
+static void
+opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass,
+ dns_db_t **dbp) {
+ char filename[PATH_MAX];
+ isc_buffer_t b;
+ isc_result_t result;
+
+ isc_buffer_init(&b, filename, sizeof(filename));
+ if (dsdir != NULL) {
+ /* allow room for a trailing slash */
+ if (strlen(dsdir) >= isc_buffer_availablelength(&b)) {
+ fatal("path '%s' is too long", dsdir);
+ }
+ isc_buffer_putstr(&b, dsdir);
+ if (dsdir[strlen(dsdir) - 1] != '/') {
+ isc_buffer_putstr(&b, "/");
+ }
+ }
+ if (strlen(prefix) > isc_buffer_availablelength(&b)) {
+ fatal("path '%s' is too long", dsdir);
+ }
+ isc_buffer_putstr(&b, prefix);
+ result = dns_name_tofilenametext(name, false, &b);
+ check_result(result, "dns_name_tofilenametext()");
+ if (isc_buffer_availablelength(&b) == 0) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namestr, sizeof(namestr));
+ fatal("name '%s' is too long", namestr);
+ }
+ isc_buffer_putuint8(&b, 0);
+
+ result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
+ rdclass, 0, NULL, dbp);
+ check_result(result, "dns_db_create()");
+
+ result = dns_db_load(*dbp, filename, inputformat, DNS_MASTER_HINT);
+ if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) {
+ dns_db_detach(dbp);
+ }
+}
+
+/*%
+ * Load the DS set for a child zone, if a dsset-* file can be found.
+ * If not, try to find a keyset-* file from an earlier version of
+ * dnssec-signzone, and build DS records from that.
+ */
+static isc_result_t
+loadds(dns_name_t *name, uint32_t ttl, dns_rdataset_t *dsset) {
+ dns_db_t *db = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_dbnode_t *node = NULL;
+ isc_result_t result;
+ dns_rdataset_t keyset;
+ dns_rdata_t key, ds;
+ unsigned char dsbuf[DNS_DS_BUFFERSIZE];
+ dns_diff_t diff;
+ dns_difftuple_t *tuple = NULL;
+
+ opendb("dsset-", name, gclass, &db);
+ if (db != NULL) {
+ result = dns_db_findnode(db, name, false, &node);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdataset_init(dsset);
+ result = dns_db_findrdataset(db, node, NULL,
+ dns_rdatatype_ds, 0, 0,
+ dsset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result == ISC_R_SUCCESS) {
+ vbprintf(2, "found DS records\n");
+ dsset->ttl = ttl;
+ dns_db_detach(&db);
+ return (result);
+ }
+ }
+ dns_db_detach(&db);
+ }
+
+ /* No DS records found; try again, looking for DNSKEY records */
+ opendb("keyset-", name, gclass, &db);
+ if (db == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ result = dns_db_findnode(db, name, false, &node);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_detach(&db);
+ return (result);
+ }
+
+ dns_rdataset_init(&keyset);
+ result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, 0,
+ &keyset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+ return (result);
+ }
+ vbprintf(2, "found DNSKEY records\n");
+
+ result = dns_db_newversion(db, &ver);
+ check_result(result, "dns_db_newversion");
+ dns_diff_init(mctx, &diff);
+
+ for (result = dns_rdataset_first(&keyset); result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&keyset))
+ {
+ dns_rdata_init(&key);
+ dns_rdata_init(&ds);
+ dns_rdataset_current(&keyset, &key);
+ result = dns_ds_buildrdata(name, &key, DNS_DSDIGEST_SHA256,
+ dsbuf, &ds);
+ check_result(result, "dns_ds_buildrdata");
+
+ result = dns_difftuple_create(mctx, DNS_DIFFOP_ADDRESIGN, name,
+ ttl, &ds, &tuple);
+ check_result(result, "dns_difftuple_create");
+ dns_diff_append(&diff, &tuple);
+ }
+
+ result = dns_diff_apply(&diff, db, ver);
+ check_result(result, "dns_diff_apply");
+ dns_diff_clear(&diff);
+
+ dns_db_closeversion(db, &ver, true);
+
+ result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ds, 0, 0,
+ dsset, NULL);
+ check_result(result, "dns_db_findrdataset");
+
+ dns_rdataset_disassociate(&keyset);
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+ return (result);
+}
+
+static bool
+secure(dns_name_t *name, dns_dbnode_t *node) {
+ dns_rdataset_t dsset;
+ isc_result_t result;
+
+ if (dns_name_equal(name, gorigin)) {
+ return (false);
+ }
+
+ dns_rdataset_init(&dsset);
+ result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_ds, 0,
+ 0, &dsset, NULL);
+ if (dns_rdataset_isassociated(&dsset)) {
+ dns_rdataset_disassociate(&dsset);
+ }
+
+ return (result == ISC_R_SUCCESS);
+}
+
+static bool
+is_delegation(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
+ dns_name_t *name, dns_dbnode_t *node, uint32_t *ttlp) {
+ dns_rdataset_t nsset;
+ isc_result_t result;
+
+ if (dns_name_equal(name, origin)) {
+ return (false);
+ }
+
+ dns_rdataset_init(&nsset);
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_ns, 0, 0,
+ &nsset, NULL);
+ if (dns_rdataset_isassociated(&nsset)) {
+ if (ttlp != NULL) {
+ *ttlp = nsset.ttl;
+ }
+ dns_rdataset_disassociate(&nsset);
+ }
+
+ return ((result == ISC_R_SUCCESS));
+}
+
+/*%
+ * Return true if version 'ver' of database 'db' contains a DNAME RRset at
+ * 'node'; return false otherwise.
+ */
+static bool
+has_dname(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) {
+ dns_rdataset_t dnameset;
+ isc_result_t result;
+
+ dns_rdataset_init(&dnameset);
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dname, 0, 0,
+ &dnameset, NULL);
+ if (dns_rdataset_isassociated(&dnameset)) {
+ dns_rdataset_disassociate(&dnameset);
+ }
+
+ return ((result == ISC_R_SUCCESS));
+}
+
+/*%
+ * Signs all records at a name.
+ */
+static void
+signname(dns_dbnode_t *node, dns_name_t *name) {
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_rdatasetiter_t *rdsiter;
+ bool isdelegation = false;
+ dns_diff_t del, add;
+ char namestr[DNS_NAME_FORMATSIZE];
+
+ dns_rdataset_init(&rdataset);
+ dns_name_format(name, namestr, sizeof(namestr));
+
+ /*
+ * Determine if this is a delegation point.
+ */
+ if (is_delegation(gdb, gversion, gorigin, name, node, NULL)) {
+ isdelegation = true;
+ }
+
+ /*
+ * Now iterate through the rdatasets.
+ */
+ dns_diff_init(mctx, &del);
+ dns_diff_init(mctx, &add);
+ rdsiter = NULL;
+ result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter);
+ check_result(result, "dns_db_allrdatasets()");
+ result = dns_rdatasetiter_first(rdsiter);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+
+ /* If this is a RRSIG set, skip it. */
+ if (rdataset.type == dns_rdatatype_rrsig) {
+ goto skip;
+ }
+
+ /*
+ * If this name is a delegation point, skip all records
+ * except NSEC and DS sets. Otherwise check that there
+ * isn't a DS record.
+ */
+ if (isdelegation) {
+ if (rdataset.type != nsec_datatype &&
+ rdataset.type != dns_rdatatype_ds)
+ {
+ goto skip;
+ }
+ } else if (rdataset.type == dns_rdatatype_ds) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ fatal("'%s': found DS RRset without NS RRset\n",
+ namebuf);
+ }
+
+ signset(&del, &add, node, name, &rdataset);
+
+ skip:
+ dns_rdataset_disassociate(&rdataset);
+ result = dns_rdatasetiter_next(rdsiter);
+ }
+ if (result != ISC_R_NOMORE) {
+ fatal("rdataset iteration for name '%s' failed: %s", namestr,
+ isc_result_totext(result));
+ }
+
+ dns_rdatasetiter_destroy(&rdsiter);
+
+ result = dns_diff_applysilently(&del, gdb, gversion);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to delete SIGs at node '%s': %s", namestr,
+ isc_result_totext(result));
+ }
+
+ result = dns_diff_applysilently(&add, gdb, gversion);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to add SIGs at node '%s': %s", namestr,
+ isc_result_totext(result));
+ }
+
+ dns_diff_clear(&del);
+ dns_diff_clear(&add);
+}
+
+/*
+ * See if the node contains any non RRSIG/NSEC records and report to
+ * caller. Clean out extraneous RRSIG records for node.
+ */
+static bool
+active_node(dns_dbnode_t *node) {
+ dns_rdatasetiter_t *rdsiter = NULL;
+ dns_rdatasetiter_t *rdsiter2 = NULL;
+ bool active = false;
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_rdatatype_t type;
+ dns_rdatatype_t covers;
+ bool found;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter);
+ check_result(result, "dns_db_allrdatasets()");
+ result = dns_rdatasetiter_first(rdsiter);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ if (rdataset.type != dns_rdatatype_nsec &&
+ rdataset.type != dns_rdatatype_nsec3 &&
+ rdataset.type != dns_rdatatype_rrsig)
+ {
+ active = true;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (!active) {
+ result = dns_rdatasetiter_next(rdsiter);
+ } else {
+ result = ISC_R_NOMORE;
+ }
+ }
+ if (result != ISC_R_NOMORE) {
+ fatal("rdataset iteration failed: %s",
+ isc_result_totext(result));
+ }
+
+ if (!active && nsec_datatype == dns_rdatatype_nsec) {
+ /*%
+ * The node is empty of everything but NSEC / RRSIG records.
+ */
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter))
+ {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ result = dns_db_deleterdataset(gdb, node, gversion,
+ rdataset.type,
+ rdataset.covers);
+ check_result(result, "dns_db_deleterdataset()");
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if (result != ISC_R_NOMORE) {
+ fatal("rdataset iteration failed: %s",
+ isc_result_totext(result));
+ }
+ } else {
+ /*
+ * Delete RRSIGs for types that no longer exist.
+ */
+ result = dns_db_allrdatasets(gdb, node, gversion, 0, 0,
+ &rdsiter2);
+ check_result(result, "dns_db_allrdatasets()");
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter))
+ {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ type = rdataset.type;
+ covers = rdataset.covers;
+ dns_rdataset_disassociate(&rdataset);
+ /*
+ * Delete the NSEC chain if we are signing with
+ * NSEC3.
+ */
+ if (nsec_datatype == dns_rdatatype_nsec3 &&
+ (type == dns_rdatatype_nsec ||
+ covers == dns_rdatatype_nsec))
+ {
+ result = dns_db_deleterdataset(
+ gdb, node, gversion, type, covers);
+ check_result(result, "dns_db_deleterdataset("
+ "nsec/rrsig)");
+ continue;
+ }
+ if (type != dns_rdatatype_rrsig) {
+ continue;
+ }
+ found = false;
+ for (result = dns_rdatasetiter_first(rdsiter2);
+ !found && result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter2))
+ {
+ dns_rdatasetiter_current(rdsiter2, &rdataset);
+ if (rdataset.type == covers) {
+ found = true;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if (!found) {
+ if (result != ISC_R_NOMORE) {
+ fatal("rdataset iteration failed: %s",
+ isc_result_totext(result));
+ }
+ result = dns_db_deleterdataset(
+ gdb, node, gversion, type, covers);
+ check_result(result, "dns_db_deleterdataset("
+ "rrsig)");
+ } else if (result != ISC_R_NOMORE &&
+ result != ISC_R_SUCCESS)
+ {
+ fatal("rdataset iteration failed: %s",
+ isc_result_totext(result));
+ }
+ }
+ if (result != ISC_R_NOMORE) {
+ fatal("rdataset iteration failed: %s",
+ isc_result_totext(result));
+ }
+ dns_rdatasetiter_destroy(&rdsiter2);
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+
+ return (active);
+}
+
+/*%
+ * Extracts the minimum TTL from the SOA record, and the SOA record's TTL.
+ */
+static void
+get_soa_ttls(void) {
+ dns_rdataset_t soaset;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ name = dns_fixedname_initname(&fname);
+ dns_rdataset_init(&soaset);
+ result = dns_db_find(gdb, gorigin, gversion, dns_rdatatype_soa, 0, 0,
+ NULL, name, &soaset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to find an SOA at the zone apex: %s",
+ isc_result_totext(result));
+ }
+
+ result = dns_rdataset_first(&soaset);
+ check_result(result, "dns_rdataset_first");
+ dns_rdataset_current(&soaset, &rdata);
+ soa_ttl = soaset.ttl;
+ zone_soa_min_ttl = ISC_MIN(dns_soa_getminimum(&rdata), soa_ttl);
+ if (set_maxttl) {
+ zone_soa_min_ttl = ISC_MIN(zone_soa_min_ttl, maxttl);
+ soa_ttl = ISC_MIN(soa_ttl, maxttl);
+ }
+ dns_rdataset_disassociate(&soaset);
+}
+
+/*%
+ * Increment (or set if nonzero) the SOA serial
+ */
+static isc_result_t
+setsoaserial(uint32_t serial, dns_updatemethod_t method) {
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ uint32_t old_serial, new_serial = 0;
+ dns_updatemethod_t used = dns_updatemethod_none;
+
+ result = dns_db_getoriginnode(gdb, &node);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_soa, 0,
+ 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ result = dns_rdataset_first(&rdataset);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ dns_rdataset_current(&rdataset, &rdata);
+
+ old_serial = dns_soa_getserial(&rdata);
+
+ if (method == dns_updatemethod_date ||
+ method == dns_updatemethod_unixtime)
+ {
+ new_serial = dns_update_soaserial(old_serial, method, &used);
+ } else if (serial != 0 || method == dns_updatemethod_none) {
+ /* Set SOA serial to the value provided. */
+ new_serial = serial;
+ used = method;
+ } else {
+ new_serial = dns_update_soaserial(old_serial, method, &used);
+ }
+
+ if (method != used) {
+ fprintf(stderr,
+ "%s: warning: Serial number would not advance, "
+ "using increment method instead\n",
+ program);
+ }
+
+ /* If the new serial is not likely to cause a zone transfer
+ * (a/ixfr) from servers having the old serial, warn the user.
+ *
+ * RFC1982 section 7 defines the maximum increment to be
+ * (2^(32-1))-1. Using u_int32_t arithmetic, we can do a single
+ * comparison. (5 - 6 == (2^32)-1, not negative-one)
+ */
+ if (new_serial == old_serial || (new_serial - old_serial) > 0x7fffffffU)
+ {
+ fprintf(stderr,
+ "%s: warning: Serial number not advanced, "
+ "zone may not transfer\n",
+ program);
+ }
+
+ dns_soa_setserial(new_serial, &rdata);
+
+ result = dns_db_deleterdataset(gdb, node, gversion, dns_rdatatype_soa,
+ 0);
+ check_result(result, "dns_db_deleterdataset");
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ result = dns_db_addrdataset(gdb, node, gversion, 0, &rdataset, 0, NULL);
+ check_result(result, "dns_db_addrdataset");
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+cleanup:
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL) {
+ dns_db_detachnode(gdb, &node);
+ }
+ dns_rdata_reset(&rdata);
+
+ return (result);
+}
+
+/*%
+ * Delete any RRSIG records at a node.
+ */
+static void
+cleannode(dns_db_t *db, dns_dbversion_t *dbversion, dns_dbnode_t *node) {
+ dns_rdatasetiter_t *rdsiter = NULL;
+ dns_rdataset_t set;
+ isc_result_t result, dresult;
+
+ if (outputformat != dns_masterformat_text || !disable_zone_check) {
+ return;
+ }
+
+ dns_rdataset_init(&set);
+ result = dns_db_allrdatasets(db, node, dbversion, 0, 0, &rdsiter);
+ check_result(result, "dns_db_allrdatasets");
+ result = dns_rdatasetiter_first(rdsiter);
+ while (result == ISC_R_SUCCESS) {
+ bool destroy = false;
+ dns_rdatatype_t covers = 0;
+ dns_rdatasetiter_current(rdsiter, &set);
+ if (set.type == dns_rdatatype_rrsig) {
+ covers = set.covers;
+ destroy = true;
+ }
+ dns_rdataset_disassociate(&set);
+ result = dns_rdatasetiter_next(rdsiter);
+ if (destroy) {
+ dresult = dns_db_deleterdataset(db, node, dbversion,
+ dns_rdatatype_rrsig,
+ covers);
+ check_result(dresult, "dns_db_deleterdataset");
+ }
+ }
+ if (result != ISC_R_NOMORE) {
+ fatal("rdataset iteration failed: %s",
+ isc_result_totext(result));
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+}
+
+/*%
+ * Set up the iterator and global state before starting the tasks.
+ */
+static void
+presign(void) {
+ isc_result_t result;
+
+ gdbiter = NULL;
+ result = dns_db_createiterator(gdb, 0, &gdbiter);
+ check_result(result, "dns_db_createiterator()");
+}
+
+/*%
+ * Clean up the iterator and global state after the tasks complete.
+ */
+static void
+postsign(void) {
+ dns_dbiterator_destroy(&gdbiter);
+}
+
+/*%
+ * Sign the apex of the zone.
+ * Note the origin may not be the first node if there are out of zone
+ * records.
+ */
+static void
+signapex(void) {
+ dns_dbnode_t *node = NULL;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_result_t result;
+
+ name = dns_fixedname_initname(&fixed);
+ result = dns_dbiterator_seek(gdbiter, gorigin);
+ check_result(result, "dns_dbiterator_seek()");
+ result = dns_dbiterator_current(gdbiter, &node, name);
+ check_dns_dbiterator_current(result);
+ signname(node, name);
+ dumpnode(name, node);
+ cleannode(gdb, gversion, node);
+ dns_db_detachnode(gdb, &node);
+ result = dns_dbiterator_first(gdbiter);
+ if (result == ISC_R_NOMORE) {
+ atomic_store(&finished, true);
+ } else if (result != ISC_R_SUCCESS) {
+ fatal("failure iterating database: %s",
+ isc_result_totext(result));
+ }
+}
+
+/*%
+ * Assigns a node to a worker thread. This is protected by the master task's
+ * lock.
+ */
+static void
+assignwork(isc_task_t *task, isc_task_t *worker) {
+ dns_fixedname_t *fname;
+ dns_name_t *name;
+ dns_dbnode_t *node;
+ sevent_t *sevent;
+ dns_rdataset_t nsec;
+ bool found;
+ isc_result_t result;
+ static dns_name_t *zonecut = NULL; /* Protected by namelock. */
+ static dns_fixedname_t fzonecut; /* Protected by namelock. */
+ static unsigned int ended = 0; /* Protected by namelock. */
+
+ if (atomic_load(&shuttingdown)) {
+ return;
+ }
+
+ LOCK(&namelock);
+ if (atomic_load(&finished)) {
+ ended++;
+ if (ended == ntasks) {
+ isc_task_detach(&task);
+ isc_app_shutdown();
+ }
+ goto unlock;
+ }
+
+ fname = isc_mem_get(mctx, sizeof(dns_fixedname_t));
+ name = dns_fixedname_initname(fname);
+ node = NULL;
+ found = false;
+ while (!found) {
+ result = dns_dbiterator_current(gdbiter, &node, name);
+ check_dns_dbiterator_current(result);
+ /*
+ * The origin was handled by signapex().
+ */
+ if (dns_name_equal(name, gorigin)) {
+ dns_db_detachnode(gdb, &node);
+ goto next;
+ }
+ /*
+ * Sort the zone data from the glue and out-of-zone data.
+ * For NSEC zones nodes with zone data have NSEC records.
+ * For NSEC3 zones the NSEC3 nodes are zone data but
+ * outside of the zone name space. For the rest we need
+ * to track the bottom of zone cuts.
+ * Nodes which don't need to be signed are dumped here.
+ */
+ dns_rdataset_init(&nsec);
+ result = dns_db_findrdataset(gdb, node, gversion, nsec_datatype,
+ 0, 0, &nsec, NULL);
+ if (dns_rdataset_isassociated(&nsec)) {
+ dns_rdataset_disassociate(&nsec);
+ }
+ if (result == ISC_R_SUCCESS) {
+ found = true;
+ } else if (nsec_datatype == dns_rdatatype_nsec3) {
+ if (dns_name_issubdomain(name, gorigin) &&
+ (zonecut == NULL ||
+ !dns_name_issubdomain(name, zonecut)))
+ {
+ if (is_delegation(gdb, gversion, gorigin, name,
+ node, NULL))
+ {
+ zonecut = savezonecut(&fzonecut, name);
+ if (!OPTOUT(nsec3flags) ||
+ secure(name, node))
+ {
+ found = true;
+ }
+ } else if (has_dname(gdb, gversion, node)) {
+ zonecut = savezonecut(&fzonecut, name);
+ found = true;
+ } else {
+ found = true;
+ }
+ }
+ }
+
+ if (!found) {
+ dumpnode(name, node);
+ dns_db_detachnode(gdb, &node);
+ }
+
+ next:
+ result = dns_dbiterator_next(gdbiter);
+ if (result == ISC_R_NOMORE) {
+ atomic_store(&finished, true);
+ break;
+ } else if (result != ISC_R_SUCCESS) {
+ fatal("failure iterating database: %s",
+ isc_result_totext(result));
+ }
+ }
+ if (!found) {
+ ended++;
+ if (ended == ntasks) {
+ isc_task_detach(&task);
+ isc_app_shutdown();
+ }
+ isc_mem_put(mctx, fname, sizeof(dns_fixedname_t));
+ goto unlock;
+ }
+ sevent = (sevent_t *)isc_event_allocate(mctx, task, SIGNER_EVENT_WORK,
+ sign, NULL, sizeof(sevent_t));
+
+ sevent->node = node;
+ sevent->fname = fname;
+ isc_task_send(worker, ISC_EVENT_PTR(&sevent));
+unlock:
+ UNLOCK(&namelock);
+}
+
+/*%
+ * Start a worker task
+ */
+static void
+startworker(isc_task_t *task, isc_event_t *event) {
+ isc_task_t *worker;
+
+ worker = (isc_task_t *)event->ev_arg;
+ assignwork(task, worker);
+ isc_event_free(&event);
+}
+
+/*%
+ * Write a node to the output file, and restart the worker task.
+ */
+static void
+writenode(isc_task_t *task, isc_event_t *event) {
+ isc_task_t *worker;
+ sevent_t *sevent = (sevent_t *)event;
+
+ worker = (isc_task_t *)event->ev_sender;
+ dumpnode(dns_fixedname_name(sevent->fname), sevent->node);
+ cleannode(gdb, gversion, sevent->node);
+ dns_db_detachnode(gdb, &sevent->node);
+ isc_mem_put(mctx, sevent->fname, sizeof(dns_fixedname_t));
+ assignwork(task, worker);
+ isc_event_free(&event);
+}
+
+/*%
+ * Sign a database node.
+ */
+static void
+sign(isc_task_t *task, isc_event_t *event) {
+ dns_fixedname_t *fname;
+ dns_dbnode_t *node;
+ sevent_t *sevent, *wevent;
+
+ sevent = (sevent_t *)event;
+ node = sevent->node;
+ fname = sevent->fname;
+ isc_event_free(&event);
+
+ signname(node, dns_fixedname_name(fname));
+ wevent = (sevent_t *)isc_event_allocate(mctx, task, SIGNER_EVENT_WRITE,
+ writenode, NULL,
+ sizeof(sevent_t));
+ wevent->node = node;
+ wevent->fname = fname;
+ isc_task_send(master, ISC_EVENT_PTR(&wevent));
+}
+
+/*%
+ * Update / remove the DS RRset. Preserve RRSIG(DS) if possible.
+ */
+static void
+add_ds(dns_name_t *name, dns_dbnode_t *node, uint32_t nsttl) {
+ dns_rdataset_t dsset;
+ dns_rdataset_t sigdsset;
+ isc_result_t result;
+
+ dns_rdataset_init(&dsset);
+ dns_rdataset_init(&sigdsset);
+ result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_ds, 0,
+ 0, &dsset, &sigdsset);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&dsset);
+ result = dns_db_deleterdataset(gdb, node, gversion,
+ dns_rdatatype_ds, 0);
+ check_result(result, "dns_db_deleterdataset");
+ }
+
+ result = loadds(name, nsttl, &dsset);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_db_addrdataset(gdb, node, gversion, 0, &dsset, 0,
+ NULL);
+ check_result(result, "dns_db_addrdataset");
+ dns_rdataset_disassociate(&dsset);
+ if (dns_rdataset_isassociated(&sigdsset)) {
+ dns_rdataset_disassociate(&sigdsset);
+ }
+ } else if (dns_rdataset_isassociated(&sigdsset)) {
+ result = dns_db_deleterdataset(gdb, node, gversion,
+ dns_rdatatype_rrsig,
+ dns_rdatatype_ds);
+ check_result(result, "dns_db_deleterdataset");
+ dns_rdataset_disassociate(&sigdsset);
+ }
+}
+
+/*
+ * Remove records of the given type and their signatures.
+ */
+static void
+remove_records(dns_dbnode_t *node, dns_rdatatype_t which, bool checknsec) {
+ isc_result_t result;
+ dns_rdatatype_t type, covers;
+ dns_rdatasetiter_t *rdsiter = NULL;
+ dns_rdataset_t rdataset;
+
+ dns_rdataset_init(&rdataset);
+
+ /*
+ * Delete any records of the given type at the apex.
+ */
+ result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter);
+ check_result(result, "dns_db_allrdatasets()");
+ for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter))
+ {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ type = rdataset.type;
+ covers = rdataset.covers;
+ dns_rdataset_disassociate(&rdataset);
+ if (type == which || covers == which) {
+ if (which == dns_rdatatype_nsec && checknsec &&
+ !update_chain)
+ {
+ fatal("Zone contains NSEC records. Use -u "
+ "to update to NSEC3.");
+ }
+ if (which == dns_rdatatype_nsec3param && checknsec &&
+ !update_chain)
+ {
+ fatal("Zone contains NSEC3 chains. Use -u "
+ "to update to NSEC.");
+ }
+ result = dns_db_deleterdataset(gdb, node, gversion,
+ type, covers);
+ check_result(result, "dns_db_deleterdataset()");
+ }
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+}
+
+/*
+ * Remove signatures covering the given type. If type == 0,
+ * then remove all signatures, unless this is a delegation, in
+ * which case remove all signatures except for DS or nsec_datatype
+ */
+static void
+remove_sigs(dns_dbnode_t *node, bool delegation, dns_rdatatype_t which) {
+ isc_result_t result;
+ dns_rdatatype_t type, covers;
+ dns_rdatasetiter_t *rdsiter = NULL;
+ dns_rdataset_t rdataset;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter);
+ check_result(result, "dns_db_allrdatasets()");
+ for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter))
+ {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ type = rdataset.type;
+ covers = rdataset.covers;
+ dns_rdataset_disassociate(&rdataset);
+
+ if (type != dns_rdatatype_rrsig) {
+ continue;
+ }
+
+ if (which == 0 && delegation &&
+ (dns_rdatatype_atparent(covers) ||
+ (nsec_datatype == dns_rdatatype_nsec &&
+ covers == nsec_datatype)))
+ {
+ continue;
+ }
+
+ if (which != 0 && covers != which) {
+ continue;
+ }
+
+ result = dns_db_deleterdataset(gdb, node, gversion, type,
+ covers);
+ check_result(result, "dns_db_deleterdataset()");
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+}
+
+/*%
+ * Generate NSEC records for the zone and remove NSEC3/NSEC3PARAM records.
+ */
+static void
+nsecify(void) {
+ dns_dbiterator_t *dbiter = NULL;
+ dns_dbnode_t *node = NULL, *nextnode = NULL;
+ dns_fixedname_t fname, fnextname, fzonecut;
+ dns_name_t *name, *nextname, *zonecut;
+ dns_rdataset_t rdataset;
+ dns_rdatasetiter_t *rdsiter = NULL;
+ dns_rdatatype_t type, covers;
+ bool done = false;
+ isc_result_t result;
+ uint32_t nsttl = 0;
+
+ dns_rdataset_init(&rdataset);
+ name = dns_fixedname_initname(&fname);
+ nextname = dns_fixedname_initname(&fnextname);
+ zonecut = NULL;
+
+ /*
+ * Remove any NSEC3 chains.
+ */
+ result = dns_db_createiterator(gdb, DNS_DB_NSEC3ONLY, &dbiter);
+ check_result(result, "dns_db_createiterator()");
+ for (result = dns_dbiterator_first(dbiter); result == ISC_R_SUCCESS;
+ result = dns_dbiterator_next(dbiter))
+ {
+ result = dns_dbiterator_current(dbiter, &node, name);
+ check_dns_dbiterator_current(result);
+ result = dns_db_allrdatasets(gdb, node, gversion, 0, 0,
+ &rdsiter);
+ check_result(result, "dns_db_allrdatasets()");
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter))
+ {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ type = rdataset.type;
+ covers = rdataset.covers;
+ dns_rdataset_disassociate(&rdataset);
+ result = dns_db_deleterdataset(gdb, node, gversion,
+ type, covers);
+ check_result(result, "dns_db_deleterdataset(nsec3param/"
+ "rrsig)");
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+ dns_db_detachnode(gdb, &node);
+ }
+ dns_dbiterator_destroy(&dbiter);
+
+ result = dns_db_createiterator(gdb, DNS_DB_NONSEC3, &dbiter);
+ check_result(result, "dns_db_createiterator()");
+
+ result = dns_dbiterator_first(dbiter);
+ check_result(result, "dns_dbiterator_first()");
+
+ while (!done) {
+ result = dns_dbiterator_current(dbiter, &node, name);
+ check_dns_dbiterator_current(result);
+ /*
+ * Skip out-of-zone records.
+ */
+ if (!dns_name_issubdomain(name, gorigin)) {
+ result = dns_dbiterator_next(dbiter);
+ if (result == ISC_R_NOMORE) {
+ done = true;
+ } else {
+ check_result(result, "dns_dbiterator_next()");
+ }
+ dns_db_detachnode(gdb, &node);
+ continue;
+ }
+
+ if (dns_name_equal(name, gorigin)) {
+ remove_records(node, dns_rdatatype_nsec3param, true);
+ /* Clean old rrsigs at apex. */
+ (void)active_node(node);
+ }
+
+ if (is_delegation(gdb, gversion, gorigin, name, node, &nsttl)) {
+ zonecut = savezonecut(&fzonecut, name);
+ remove_sigs(node, true, 0);
+ if (generateds) {
+ add_ds(name, node, nsttl);
+ }
+ } else if (has_dname(gdb, gversion, node)) {
+ zonecut = savezonecut(&fzonecut, name);
+ }
+
+ result = dns_dbiterator_next(dbiter);
+ nextnode = NULL;
+ while (result == ISC_R_SUCCESS) {
+ bool active = false;
+ result = dns_dbiterator_current(dbiter, &nextnode,
+ nextname);
+ check_dns_dbiterator_current(result);
+ active = active_node(nextnode);
+ if (!active) {
+ dns_db_detachnode(gdb, &nextnode);
+ result = dns_dbiterator_next(dbiter);
+ continue;
+ }
+ if (!dns_name_issubdomain(nextname, gorigin) ||
+ (zonecut != NULL &&
+ dns_name_issubdomain(nextname, zonecut)))
+ {
+ remove_sigs(nextnode, false, 0);
+ remove_records(nextnode, dns_rdatatype_nsec,
+ false);
+ dns_db_detachnode(gdb, &nextnode);
+ result = dns_dbiterator_next(dbiter);
+ continue;
+ }
+ dns_db_detachnode(gdb, &nextnode);
+ break;
+ }
+ if (result == ISC_R_NOMORE) {
+ dns_name_clone(gorigin, nextname);
+ done = true;
+ } else if (result != ISC_R_SUCCESS) {
+ fatal("iterating through the database failed: %s",
+ isc_result_totext(result));
+ }
+ dns_dbiterator_pause(dbiter);
+ result = dns_nsec_build(gdb, gversion, node, nextname,
+ zone_soa_min_ttl);
+ check_result(result, "dns_nsec_build()");
+ dns_db_detachnode(gdb, &node);
+ }
+
+ dns_dbiterator_destroy(&dbiter);
+}
+
+static void
+addnsec3param(const unsigned char *salt, size_t salt_len,
+ dns_iterations_t iterations) {
+ dns_dbnode_t *node = NULL;
+ dns_rdata_nsec3param_t nsec3param;
+ unsigned char nsec3parambuf[5 + 255];
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_buffer_t b;
+ isc_result_t result;
+
+ dns_rdataset_init(&rdataset);
+
+ nsec3param.common.rdclass = gclass;
+ nsec3param.common.rdtype = dns_rdatatype_nsec3param;
+ ISC_LINK_INIT(&nsec3param.common, link);
+ nsec3param.mctx = NULL;
+ nsec3param.flags = 0;
+ nsec3param.hash = unknownalg ? DNS_NSEC3_UNKNOWNALG : dns_hash_sha1;
+ nsec3param.iterations = iterations;
+ nsec3param.salt_length = (unsigned char)salt_len;
+ DE_CONST(salt, nsec3param.salt);
+
+ isc_buffer_init(&b, nsec3parambuf, sizeof(nsec3parambuf));
+ result = dns_rdata_fromstruct(&rdata, gclass, dns_rdatatype_nsec3param,
+ &nsec3param, &b);
+ check_result(result, "dns_rdata_fromstruct()");
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.rdclass = rdata.rdclass;
+ rdatalist.type = rdata.type;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+ result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
+ check_result(result, "dns_rdatalist_tordataset()");
+
+ result = dns_db_findnode(gdb, gorigin, true, &node);
+ check_result(result, "dns_db_findnode(gorigin)");
+
+ /*
+ * Delete any current NSEC3PARAM records.
+ */
+ result = dns_db_deleterdataset(gdb, node, gversion,
+ dns_rdatatype_nsec3param, 0);
+ if (result == DNS_R_UNCHANGED) {
+ result = ISC_R_SUCCESS;
+ }
+ check_result(result, "dddnsec3param: dns_db_deleterdataset()");
+
+ result = dns_db_addrdataset(gdb, node, gversion, 0, &rdataset,
+ DNS_DBADD_MERGE, NULL);
+ if (result == DNS_R_UNCHANGED) {
+ result = ISC_R_SUCCESS;
+ }
+ check_result(result, "addnsec3param: dns_db_addrdataset()");
+ dns_db_detachnode(gdb, &node);
+}
+
+static void
+addnsec3(dns_name_t *name, dns_dbnode_t *node, const unsigned char *salt,
+ size_t salt_len, unsigned int iterations, hashlist_t *hashlist,
+ dns_ttl_t ttl) {
+ unsigned char hash[NSEC3_MAX_HASH_LENGTH];
+ const unsigned char *nexthash;
+ unsigned char nsec3buffer[DNS_NSEC3_BUFFERSIZE];
+ dns_fixedname_t hashname;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+ dns_dbnode_t *nsec3node = NULL;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ size_t hash_len;
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+
+ dns_fixedname_init(&hashname);
+ dns_rdataset_init(&rdataset);
+
+ dns_name_downcase(name, name, NULL);
+ result = dns_nsec3_hashname(&hashname, hash, &hash_len, name, gorigin,
+ dns_hash_sha1, iterations, salt, salt_len);
+ check_result(result, "addnsec3: dns_nsec3_hashname()");
+ nexthash = hashlist_findnext(hashlist, hash);
+ result = dns_nsec3_buildrdata(
+ gdb, gversion, node,
+ unknownalg ? DNS_NSEC3_UNKNOWNALG : dns_hash_sha1, nsec3flags,
+ iterations, salt, salt_len, nexthash, ISC_SHA1_DIGESTLENGTH,
+ nsec3buffer, &rdata);
+ check_result(result, "addnsec3: dns_nsec3_buildrdata()");
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.rdclass = rdata.rdclass;
+ rdatalist.type = rdata.type;
+ rdatalist.ttl = ttl;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+ result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
+ check_result(result, "dns_rdatalist_tordataset()");
+ result = dns_db_findnsec3node(gdb, dns_fixedname_name(&hashname), true,
+ &nsec3node);
+ check_result(result, "addnsec3: dns_db_findnode()");
+ result = dns_db_addrdataset(gdb, nsec3node, gversion, 0, &rdataset, 0,
+ NULL);
+ if (result == DNS_R_UNCHANGED) {
+ result = ISC_R_SUCCESS;
+ }
+ check_result(result, "addnsec3: dns_db_addrdataset()");
+ dns_db_detachnode(gdb, &nsec3node);
+}
+
+/*%
+ * Clean out NSEC3 record and RRSIG(NSEC3) that are not in the hash list.
+ *
+ * Extract the hash from the first label of 'name' then see if it
+ * is in hashlist. If 'name' is not in the hashlist then delete the
+ * any NSEC3 records which have the same parameters as the chain we
+ * are building.
+ *
+ * XXXMPA Should we also check that it of the form &lt;hash&gt;.&lt;origin&gt;?
+ */
+static void
+nsec3clean(dns_name_t *name, dns_dbnode_t *node, unsigned int hashalg,
+ unsigned int iterations, const unsigned char *salt, size_t salt_len,
+ hashlist_t *hashlist) {
+ dns_label_t label;
+ dns_rdata_nsec3_t nsec3;
+ dns_rdata_t rdata, delrdata;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset, delrdataset;
+ bool delete_rrsigs = false;
+ isc_buffer_t target;
+ isc_result_t result;
+ unsigned char hash[NSEC3_MAX_HASH_LENGTH + 1];
+ bool exists;
+
+ /*
+ * Get the first label.
+ */
+ dns_name_getlabel(name, 0, &label);
+
+ /*
+ * We want just the label contents.
+ */
+ isc_region_consume(&label, 1);
+
+ /*
+ * Decode base32hex string.
+ */
+ isc_buffer_init(&target, hash, sizeof(hash) - 1);
+ result = isc_base32hex_decoderegion(&label, &target);
+ if (result != ISC_R_SUCCESS) {
+ return;
+ }
+
+ hash[isc_buffer_usedlength(&target)] = 0;
+
+ exists = hashlist_exists(hashlist, hash);
+
+ /*
+ * Verify that the NSEC3 parameters match the current ones
+ * otherwise we are dealing with a different NSEC3 chain.
+ */
+ dns_rdataset_init(&rdataset);
+ dns_rdataset_init(&delrdataset);
+
+ result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_nsec3,
+ 0, 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return;
+ }
+
+ /*
+ * Delete any NSEC3 records which are not part of the current
+ * NSEC3 chain.
+ */
+ for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
+ check_result(result, "dns_rdata_tostruct");
+ if (exists && nsec3.hash == hashalg &&
+ nsec3.iterations == iterations &&
+ nsec3.salt_length == salt_len &&
+ isc_safe_memequal(nsec3.salt, salt, salt_len))
+ {
+ continue;
+ }
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.rdclass = rdata.rdclass;
+ rdatalist.type = rdata.type;
+ if (set_maxttl) {
+ rdatalist.ttl = ISC_MIN(rdataset.ttl, maxttl);
+ }
+ dns_rdata_init(&delrdata);
+ dns_rdata_clone(&rdata, &delrdata);
+ ISC_LIST_APPEND(rdatalist.rdata, &delrdata, link);
+ result = dns_rdatalist_tordataset(&rdatalist, &delrdataset);
+ check_result(result, "dns_rdatalist_tordataset()");
+ result = dns_db_subtractrdataset(gdb, node, gversion,
+ &delrdataset, 0, NULL);
+ dns_rdataset_disassociate(&delrdataset);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NXRRSET) {
+ check_result(result, "dns_db_subtractrdataset(NSEC3)");
+ }
+ delete_rrsigs = true;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_NOMORE) {
+ check_result(result, "dns_rdataset_first/next");
+ }
+
+ if (!delete_rrsigs) {
+ return;
+ }
+ /*
+ * Delete the NSEC3 RRSIGs
+ */
+ result = dns_db_deleterdataset(gdb, node, gversion, dns_rdatatype_rrsig,
+ dns_rdatatype_nsec3);
+ if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) {
+ check_result(result, "dns_db_deleterdataset(RRSIG(NSEC3))");
+ }
+}
+
+static void
+rrset_cleanup(dns_name_t *name, dns_rdataset_t *rdataset, dns_diff_t *add,
+ dns_diff_t *del) {
+ isc_result_t result;
+ unsigned int count1 = 0;
+ dns_rdataset_t tmprdataset;
+ char namestr[DNS_NAME_FORMATSIZE];
+ char typestr[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_name_format(name, namestr, sizeof(namestr));
+ dns_rdatatype_format(rdataset->type, typestr, sizeof(typestr));
+
+ dns_rdataset_init(&tmprdataset);
+ for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset))
+ {
+ dns_rdata_t rdata1 = DNS_RDATA_INIT;
+ unsigned int count2 = 0;
+
+ count1++;
+ dns_rdataset_current(rdataset, &rdata1);
+ dns_rdataset_clone(rdataset, &tmprdataset);
+ for (result = dns_rdataset_first(&tmprdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&tmprdataset))
+ {
+ dns_rdata_t rdata2 = DNS_RDATA_INIT;
+ dns_difftuple_t *tuple = NULL;
+ count2++;
+ dns_rdataset_current(&tmprdataset, &rdata2);
+ if (count1 < count2 &&
+ dns_rdata_casecompare(&rdata1, &rdata2) == 0)
+ {
+ vbprintf(2, "removing duplicate at %s/%s\n",
+ namestr, typestr);
+ result = dns_difftuple_create(
+ mctx, DNS_DIFFOP_DELRESIGN, name,
+ rdataset->ttl, &rdata2, &tuple);
+ check_result(result, "dns_difftuple_create");
+ dns_diff_append(del, &tuple);
+ } else if (set_maxttl && rdataset->ttl > maxttl) {
+ vbprintf(2,
+ "reducing ttl of %s/%s "
+ "from %d to %d\n",
+ namestr, typestr, rdataset->ttl,
+ maxttl);
+ result = dns_difftuple_create(
+ mctx, DNS_DIFFOP_DELRESIGN, name,
+ rdataset->ttl, &rdata2, &tuple);
+ check_result(result, "dns_difftuple_create");
+ dns_diff_append(del, &tuple);
+ tuple = NULL;
+ result = dns_difftuple_create(
+ mctx, DNS_DIFFOP_ADDRESIGN, name,
+ maxttl, &rdata2, &tuple);
+ check_result(result, "dns_difftuple_create");
+ dns_diff_append(add, &tuple);
+ }
+ }
+ dns_rdataset_disassociate(&tmprdataset);
+ }
+}
+
+static void
+cleanup_zone(void) {
+ isc_result_t result;
+ dns_dbiterator_t *dbiter = NULL;
+ dns_rdatasetiter_t *rdsiter = NULL;
+ dns_diff_t add, del;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ dns_diff_init(mctx, &add);
+ dns_diff_init(mctx, &del);
+ name = dns_fixedname_initname(&fname);
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_createiterator(gdb, 0, &dbiter);
+ check_result(result, "dns_db_createiterator()");
+
+ for (result = dns_dbiterator_first(dbiter); result == ISC_R_SUCCESS;
+ result = dns_dbiterator_next(dbiter))
+ {
+ result = dns_dbiterator_current(dbiter, &node, name);
+ check_dns_dbiterator_current(result);
+ result = dns_db_allrdatasets(gdb, node, gversion, 0, 0,
+ &rdsiter);
+ check_result(result, "dns_db_allrdatasets()");
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter))
+ {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ rrset_cleanup(name, &rdataset, &add, &del);
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if (result != ISC_R_NOMORE) {
+ fatal("rdatasets iteration failed.");
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+ dns_db_detachnode(gdb, &node);
+ }
+ if (result != ISC_R_NOMORE) {
+ fatal("zone iteration failed.");
+ }
+
+ result = dns_diff_applysilently(&del, gdb, gversion);
+ check_result(result, "dns_diff_applysilently");
+
+ result = dns_diff_applysilently(&add, gdb, gversion);
+ check_result(result, "dns_diff_applysilently");
+
+ dns_diff_clear(&del);
+ dns_diff_clear(&add);
+ dns_dbiterator_destroy(&dbiter);
+}
+
+/*
+ * Generate NSEC3 records for the zone.
+ */
+static void
+nsec3ify(unsigned int hashalg, dns_iterations_t iterations,
+ const unsigned char *salt, size_t salt_len, hashlist_t *hashlist) {
+ dns_dbiterator_t *dbiter = NULL;
+ dns_dbnode_t *node = NULL, *nextnode = NULL;
+ dns_fixedname_t fname, fnextname, fzonecut;
+ dns_name_t *name, *nextname, *zonecut;
+ dns_rdataset_t rdataset;
+ int order;
+ bool active;
+ bool done = false;
+ isc_result_t result;
+ uint32_t nsttl = 0;
+ unsigned int count, nlabels;
+
+ dns_rdataset_init(&rdataset);
+ name = dns_fixedname_initname(&fname);
+ nextname = dns_fixedname_initname(&fnextname);
+ zonecut = NULL;
+
+ /*
+ * Walk the zone generating the hash names.
+ */
+ result = dns_db_createiterator(gdb, DNS_DB_NONSEC3, &dbiter);
+ check_result(result, "dns_db_createiterator()");
+
+ result = dns_dbiterator_first(dbiter);
+ check_result(result, "dns_dbiterator_first()");
+
+ while (!done) {
+ result = dns_dbiterator_current(dbiter, &node, name);
+ check_dns_dbiterator_current(result);
+ /*
+ * Skip out-of-zone records.
+ */
+ if (!dns_name_issubdomain(name, gorigin)) {
+ result = dns_dbiterator_next(dbiter);
+ if (result == ISC_R_NOMORE) {
+ done = true;
+ } else {
+ check_result(result, "dns_dbiterator_next()");
+ }
+ dns_db_detachnode(gdb, &node);
+ continue;
+ }
+
+ if (dns_name_equal(name, gorigin)) {
+ remove_records(node, dns_rdatatype_nsec, true);
+ /* Clean old rrsigs at apex. */
+ (void)active_node(node);
+ }
+
+ if (has_dname(gdb, gversion, node)) {
+ zonecut = savezonecut(&fzonecut, name);
+ }
+
+ result = dns_dbiterator_next(dbiter);
+ nextnode = NULL;
+ while (result == ISC_R_SUCCESS) {
+ result = dns_dbiterator_current(dbiter, &nextnode,
+ nextname);
+ check_dns_dbiterator_current(result);
+ active = active_node(nextnode);
+ if (!active) {
+ dns_db_detachnode(gdb, &nextnode);
+ result = dns_dbiterator_next(dbiter);
+ continue;
+ }
+ if (!dns_name_issubdomain(nextname, gorigin) ||
+ (zonecut != NULL &&
+ dns_name_issubdomain(nextname, zonecut)))
+ {
+ remove_sigs(nextnode, false, 0);
+ dns_db_detachnode(gdb, &nextnode);
+ result = dns_dbiterator_next(dbiter);
+ continue;
+ }
+ if (is_delegation(gdb, gversion, gorigin, nextname,
+ nextnode, &nsttl))
+ {
+ zonecut = savezonecut(&fzonecut, nextname);
+ remove_sigs(nextnode, true, 0);
+ if (generateds) {
+ add_ds(nextname, nextnode, nsttl);
+ }
+ if (OPTOUT(nsec3flags) &&
+ !secure(nextname, nextnode))
+ {
+ dns_db_detachnode(gdb, &nextnode);
+ result = dns_dbiterator_next(dbiter);
+ continue;
+ }
+ } else if (has_dname(gdb, gversion, nextnode)) {
+ zonecut = savezonecut(&fzonecut, nextname);
+ }
+ dns_db_detachnode(gdb, &nextnode);
+ break;
+ }
+ if (result == ISC_R_NOMORE) {
+ dns_name_copynf(gorigin, nextname);
+ done = true;
+ } else if (result != ISC_R_SUCCESS) {
+ fatal("iterating through the database failed: %s",
+ isc_result_totext(result));
+ }
+ dns_name_downcase(name, name, NULL);
+ hashlist_add_dns_name(hashlist, name, hashalg, iterations, salt,
+ salt_len, false);
+ dns_db_detachnode(gdb, &node);
+ /*
+ * Add hashes for empty nodes. Use closest encloser logic.
+ * The closest encloser either has data or is a empty
+ * node for another <name,nextname> span so we don't add
+ * it here. Empty labels on nextname are within the span.
+ */
+ dns_name_downcase(nextname, nextname, NULL);
+ dns_name_fullcompare(name, nextname, &order, &nlabels);
+ addnowildcardhash(hashlist, name, hashalg, iterations, salt,
+ salt_len);
+ count = dns_name_countlabels(nextname);
+ while (count > nlabels + 1) {
+ count--;
+ dns_name_split(nextname, count, NULL, nextname);
+ hashlist_add_dns_name(hashlist, nextname, hashalg,
+ iterations, salt, salt_len,
+ false);
+ addnowildcardhash(hashlist, nextname, hashalg,
+ iterations, salt, salt_len);
+ }
+ }
+ dns_dbiterator_destroy(&dbiter);
+
+ /*
+ * We have all the hashes now so we can sort them.
+ */
+ hashlist_sort(hashlist);
+
+ /*
+ * Check for duplicate hashes. If found the salt needs to
+ * be changed.
+ */
+ if (hashlist_hasdup(hashlist)) {
+ fatal("Duplicate hash detected. Pick a different salt.");
+ }
+
+ /*
+ * Generate the nsec3 records.
+ */
+ zonecut = NULL;
+ done = false;
+
+ addnsec3param(salt, salt_len, iterations);
+
+ /*
+ * Clean out NSEC3 records which don't match this chain.
+ */
+ result = dns_db_createiterator(gdb, DNS_DB_NSEC3ONLY, &dbiter);
+ check_result(result, "dns_db_createiterator()");
+
+ for (result = dns_dbiterator_first(dbiter); result == ISC_R_SUCCESS;
+ result = dns_dbiterator_next(dbiter))
+ {
+ result = dns_dbiterator_current(dbiter, &node, name);
+ check_dns_dbiterator_current(result);
+ nsec3clean(name, node, hashalg, iterations, salt, salt_len,
+ hashlist);
+ dns_db_detachnode(gdb, &node);
+ }
+ dns_dbiterator_destroy(&dbiter);
+
+ /*
+ * Generate / complete the new chain.
+ */
+ result = dns_db_createiterator(gdb, DNS_DB_NONSEC3, &dbiter);
+ check_result(result, "dns_db_createiterator()");
+
+ result = dns_dbiterator_first(dbiter);
+ check_result(result, "dns_dbiterator_first()");
+
+ while (!done) {
+ result = dns_dbiterator_current(dbiter, &node, name);
+ check_dns_dbiterator_current(result);
+ /*
+ * Skip out-of-zone records.
+ */
+ if (!dns_name_issubdomain(name, gorigin)) {
+ result = dns_dbiterator_next(dbiter);
+ if (result == ISC_R_NOMORE) {
+ done = true;
+ } else {
+ check_result(result, "dns_dbiterator_next()");
+ }
+ dns_db_detachnode(gdb, &node);
+ continue;
+ }
+
+ if (has_dname(gdb, gversion, node)) {
+ zonecut = savezonecut(&fzonecut, name);
+ }
+
+ result = dns_dbiterator_next(dbiter);
+ nextnode = NULL;
+ while (result == ISC_R_SUCCESS) {
+ result = dns_dbiterator_current(dbiter, &nextnode,
+ nextname);
+ check_dns_dbiterator_current(result);
+ active = active_node(nextnode);
+ if (!active) {
+ dns_db_detachnode(gdb, &nextnode);
+ result = dns_dbiterator_next(dbiter);
+ continue;
+ }
+ if (!dns_name_issubdomain(nextname, gorigin) ||
+ (zonecut != NULL &&
+ dns_name_issubdomain(nextname, zonecut)))
+ {
+ dns_db_detachnode(gdb, &nextnode);
+ result = dns_dbiterator_next(dbiter);
+ continue;
+ }
+ if (is_delegation(gdb, gversion, gorigin, nextname,
+ nextnode, NULL))
+ {
+ zonecut = savezonecut(&fzonecut, nextname);
+ if (OPTOUT(nsec3flags) &&
+ !secure(nextname, nextnode))
+ {
+ dns_db_detachnode(gdb, &nextnode);
+ result = dns_dbiterator_next(dbiter);
+ continue;
+ }
+ } else if (has_dname(gdb, gversion, nextnode)) {
+ zonecut = savezonecut(&fzonecut, nextname);
+ }
+ dns_db_detachnode(gdb, &nextnode);
+ break;
+ }
+ if (result == ISC_R_NOMORE) {
+ dns_name_copynf(gorigin, nextname);
+ done = true;
+ } else if (result != ISC_R_SUCCESS) {
+ fatal("iterating through the database failed: %s",
+ isc_result_totext(result));
+ }
+ /*
+ * We need to pause here to release the lock on the database.
+ */
+ dns_dbiterator_pause(dbiter);
+ addnsec3(name, node, salt, salt_len, iterations, hashlist,
+ zone_soa_min_ttl);
+ dns_db_detachnode(gdb, &node);
+ /*
+ * Add NSEC3's for empty nodes. Use closest encloser logic.
+ */
+ dns_name_fullcompare(name, nextname, &order, &nlabels);
+ count = dns_name_countlabels(nextname);
+ while (count > nlabels + 1) {
+ count--;
+ dns_name_split(nextname, count, NULL, nextname);
+ addnsec3(nextname, NULL, salt, salt_len, iterations,
+ hashlist, zone_soa_min_ttl);
+ }
+ }
+ dns_dbiterator_destroy(&dbiter);
+}
+
+/*%
+ * Load the zone file from disk
+ */
+static void
+loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) {
+ isc_buffer_t b;
+ int len;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result;
+
+ len = strlen(origin);
+ isc_buffer_init(&b, origin, len);
+ isc_buffer_add(&b, len);
+
+ name = dns_fixedname_initname(&fname);
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed converting name '%s' to dns format: %s", origin,
+ isc_result_totext(result));
+ }
+
+ result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0,
+ NULL, db);
+ check_result(result, "dns_db_create()");
+
+ result = dns_db_load(*db, file, inputformat, 0);
+ if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) {
+ fatal("failed loading zone from '%s': %s", file,
+ isc_result_totext(result));
+ }
+}
+
+/*%
+ * Finds all public zone keys in the zone, and attempts to load the
+ * private keys from disk.
+ */
+static void
+loadzonekeys(bool preserve_keys, bool load_public) {
+ dns_dbnode_t *node;
+ dns_dbversion_t *currentversion = NULL;
+ isc_result_t result;
+ dns_rdataset_t rdataset, keysigs, soasigs;
+
+ node = NULL;
+ result = dns_db_findnode(gdb, gorigin, false, &node);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to find the zone's origin: %s",
+ isc_result_totext(result));
+ }
+
+ dns_db_currentversion(gdb, &currentversion);
+
+ dns_rdataset_init(&rdataset);
+ dns_rdataset_init(&soasigs);
+ dns_rdataset_init(&keysigs);
+
+ /* Make note of the keys which signed the SOA, if any */
+ result = dns_db_findrdataset(gdb, node, currentversion,
+ dns_rdatatype_soa, 0, 0, &rdataset,
+ &soasigs);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ /* Preserve the TTL of the DNSKEY RRset, if any */
+ dns_rdataset_disassociate(&rdataset);
+ result = dns_db_findrdataset(gdb, node, currentversion,
+ dns_rdatatype_dnskey, 0, 0, &rdataset,
+ &keysigs);
+
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (set_keyttl && keyttl != rdataset.ttl) {
+ fprintf(stderr,
+ "User-specified TTL %u conflicts "
+ "with existing DNSKEY RRset TTL.\n",
+ keyttl);
+ fprintf(stderr,
+ "Imported keys will use the RRSet "
+ "TTL %u instead.\n",
+ rdataset.ttl);
+ }
+ keyttl = rdataset.ttl;
+
+ /* Load keys corresponding to the existing DNSKEY RRset. */
+ result = dns_dnssec_keylistfromrdataset(
+ gorigin, directory, mctx, &rdataset, &keysigs, &soasigs,
+ preserve_keys, load_public, &keylist);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to load the zone keys: %s",
+ isc_result_totext(result));
+ }
+
+cleanup:
+ if (dns_rdataset_isassociated(&rdataset)) {
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if (dns_rdataset_isassociated(&keysigs)) {
+ dns_rdataset_disassociate(&keysigs);
+ }
+ if (dns_rdataset_isassociated(&soasigs)) {
+ dns_rdataset_disassociate(&soasigs);
+ }
+ dns_db_detachnode(gdb, &node);
+ dns_db_closeversion(gdb, &currentversion, false);
+}
+
+static void
+loadexplicitkeys(char *keyfiles[], int n, bool setksk) {
+ isc_result_t result;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ dns_dnsseckey_t *key = NULL;
+ dst_key_t *newkey = NULL;
+
+ result = dst_key_fromnamedfile(
+ keyfiles[i], directory,
+ DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, mctx, &newkey);
+ if (result != ISC_R_SUCCESS) {
+ fatal("cannot load dnskey %s: %s", keyfiles[i],
+ isc_result_totext(result));
+ }
+
+ if (!dns_name_equal(gorigin, dst_key_name(newkey))) {
+ fatal("key %s not at origin\n", keyfiles[i]);
+ }
+
+ if (!dst_key_isprivate(newkey)) {
+ fatal("cannot sign zone with non-private dnskey %s",
+ keyfiles[i]);
+ }
+
+ /* Skip any duplicates */
+ for (key = ISC_LIST_HEAD(keylist); key != NULL;
+ key = ISC_LIST_NEXT(key, link))
+ {
+ if (dst_key_id(key->key) == dst_key_id(newkey) &&
+ dst_key_alg(key->key) == dst_key_alg(newkey))
+ {
+ break;
+ }
+ }
+
+ if (key == NULL) {
+ /* We haven't seen this key before */
+ dns_dnsseckey_create(mctx, &newkey, &key);
+ ISC_LIST_APPEND(keylist, key, link);
+ key->source = dns_keysource_user;
+ } else {
+ dst_key_free(&key->key);
+ key->key = newkey;
+ }
+
+ key->force_publish = true;
+ key->force_sign = true;
+
+ if (setksk) {
+ key->ksk = true;
+ }
+ }
+}
+
+static void
+report(const char *format, ...) {
+ if (!quiet) {
+ FILE *out = output_stdout ? stderr : stdout;
+ char buf[4096];
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+ fprintf(out, "%s\n", buf);
+ }
+}
+
+static void
+clear_keylist(dns_dnsseckeylist_t *list) {
+ dns_dnsseckey_t *key;
+ while (!ISC_LIST_EMPTY(*list)) {
+ key = ISC_LIST_HEAD(*list);
+ ISC_LIST_UNLINK(*list, key, link);
+ dns_dnsseckey_destroy(mctx, &key);
+ }
+}
+
+static void
+build_final_keylist(void) {
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff;
+ dns_dnsseckeylist_t rmkeys, matchkeys;
+ char name[DNS_NAME_FORMATSIZE];
+ dns_rdataset_t cdsset, cdnskeyset, soaset;
+
+ ISC_LIST_INIT(rmkeys);
+ ISC_LIST_INIT(matchkeys);
+
+ dns_rdataset_init(&soaset);
+ dns_rdataset_init(&cdsset);
+ dns_rdataset_init(&cdnskeyset);
+
+ /*
+ * Find keys that match this zone in the key repository.
+ */
+ result = dns_dnssec_findmatchingkeys(gorigin, directory, now, mctx,
+ &matchkeys);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ }
+ check_result(result, "dns_dnssec_findmatchingkeys");
+
+ result = dns_db_newversion(gdb, &ver);
+ check_result(result, "dns_db_newversion");
+
+ result = dns_db_getoriginnode(gdb, &node);
+ check_result(result, "dns_db_getoriginnode");
+
+ /* Get the CDS rdataset */
+ result = dns_db_findrdataset(gdb, node, ver, dns_rdatatype_cds,
+ dns_rdatatype_none, 0, &cdsset, NULL);
+ if (result != ISC_R_SUCCESS && dns_rdataset_isassociated(&cdsset)) {
+ dns_rdataset_disassociate(&cdsset);
+ }
+
+ /* Get the CDNSKEY rdataset */
+ result = dns_db_findrdataset(gdb, node, ver, dns_rdatatype_cdnskey,
+ dns_rdatatype_none, 0, &cdnskeyset, NULL);
+ if (result != ISC_R_SUCCESS && dns_rdataset_isassociated(&cdnskeyset)) {
+ dns_rdataset_disassociate(&cdnskeyset);
+ }
+
+ dns_diff_init(mctx, &diff);
+
+ /*
+ * Update keylist with information from from the key repository.
+ */
+ dns_dnssec_updatekeys(&keylist, &matchkeys, NULL, gorigin, keyttl,
+ &diff, mctx, report);
+
+ /*
+ * Update keylist with sync records.
+ */
+ dns_dnssec_syncupdate(&keylist, &rmkeys, &cdsset, &cdnskeyset, now,
+ keyttl, &diff, mctx);
+
+ dns_name_format(gorigin, name, sizeof(name));
+
+ result = dns_diff_applysilently(&diff, gdb, ver);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to update DNSKEY RRset at node '%s': %s", name,
+ isc_result_totext(result));
+ }
+
+ dns_db_detachnode(gdb, &node);
+ dns_db_closeversion(gdb, &ver, true);
+
+ dns_diff_clear(&diff);
+
+ if (dns_rdataset_isassociated(&cdsset)) {
+ dns_rdataset_disassociate(&cdsset);
+ }
+ if (dns_rdataset_isassociated(&cdnskeyset)) {
+ dns_rdataset_disassociate(&cdnskeyset);
+ }
+
+ clear_keylist(&rmkeys);
+ clear_keylist(&matchkeys);
+}
+
+static void
+warnifallksk(dns_db_t *db) {
+ dns_dbversion_t *currentversion = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+ dns_rdata_dnskey_t dnskey;
+ bool have_non_ksk = false;
+
+ dns_db_currentversion(db, &currentversion);
+
+ result = dns_db_findnode(db, gorigin, false, &node);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to find the zone's origin: %s",
+ isc_result_totext(result));
+ }
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, currentversion,
+ dns_rdatatype_dnskey, 0, 0, &rdataset,
+ NULL);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to find keys at the zone apex: %s",
+ isc_result_totext(result));
+ }
+ result = dns_rdataset_first(&rdataset);
+ check_result(result, "dns_rdataset_first");
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &dnskey, NULL);
+ check_result(result, "dns_rdata_tostruct");
+ if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0) {
+ have_non_ksk = true;
+ result = ISC_R_NOMORE;
+ } else {
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdata_freestruct(&dnskey);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ dns_db_closeversion(db, &currentversion, false);
+ if (!have_non_ksk && !ignore_kskflag) {
+ if (disable_zone_check) {
+ fprintf(stderr,
+ "%s: warning: No non-KSK DNSKEY found; "
+ "supply a ZSK or use '-z'.\n",
+ program);
+ } else {
+ fatal("No non-KSK DNSKEY found; "
+ "supply a ZSK or use '-z'.");
+ }
+ }
+}
+
+static void
+set_nsec3params(bool update, bool set_salt, bool set_optout, bool set_iter) {
+ isc_result_t result;
+ dns_dbversion_t *ver = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_nsec3_t nsec3;
+ dns_fixedname_t fname;
+ dns_name_t *hashname;
+ unsigned char orig_salt[255];
+ size_t orig_saltlen;
+ dns_hash_t orig_hash;
+ uint16_t orig_iter;
+
+ dns_db_currentversion(gdb, &ver);
+ dns_rdataset_init(&rdataset);
+
+ orig_saltlen = sizeof(orig_salt);
+ result = dns_db_getnsec3parameters(gdb, ver, &orig_hash, NULL,
+ &orig_iter, orig_salt,
+ &orig_saltlen);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ nsec_datatype = dns_rdatatype_nsec3;
+
+ if (!update && set_salt) {
+ if (salt_length != orig_saltlen ||
+ !isc_safe_memequal(saltbuf, orig_salt, salt_length))
+ {
+ fatal("An NSEC3 chain exists with a different salt. "
+ "Use -u to update it.");
+ }
+ } else if (!set_salt) {
+ salt_length = orig_saltlen;
+ memmove(saltbuf, orig_salt, orig_saltlen);
+ gsalt = saltbuf;
+ }
+
+ if (!update && set_iter) {
+ if (nsec3iter != orig_iter) {
+ fatal("An NSEC3 chain exists with different "
+ "iterations. Use -u to update it.");
+ }
+ } else if (!set_iter) {
+ nsec3iter = orig_iter;
+ }
+
+ /*
+ * Find an NSEC3 record to get the current OPTOUT value.
+ * (This assumes all NSEC3 records agree.)
+ */
+
+ hashname = dns_fixedname_initname(&fname);
+ result = dns_nsec3_hashname(&fname, NULL, NULL, gorigin, gorigin,
+ dns_hash_sha1, orig_iter, orig_salt,
+ orig_saltlen);
+ check_result(result, "dns_nsec3_hashname");
+
+ result = dns_db_findnsec3node(gdb, hashname, false, &node);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ result = dns_db_findrdataset(gdb, node, ver, dns_rdatatype_nsec3, 0, 0,
+ &rdataset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ result = dns_rdataset_first(&rdataset);
+ check_result(result, "dns_rdataset_first");
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
+ check_result(result, "dns_rdata_tostruct");
+
+ if (!update && set_optout) {
+ if (nsec3flags != nsec3.flags) {
+ fatal("An NSEC3 chain exists with%s OPTOUT. "
+ "Use -u -%s to %s it.",
+ OPTOUT(nsec3.flags) ? "" : "out",
+ OPTOUT(nsec3.flags) ? "AA" : "A",
+ OPTOUT(nsec3.flags) ? "clear" : "set");
+ }
+ } else if (!set_optout) {
+ nsec3flags = nsec3.flags;
+ }
+
+ dns_rdata_freestruct(&nsec3);
+
+cleanup:
+ if (dns_rdataset_isassociated(&rdataset)) {
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if (node != NULL) {
+ dns_db_detachnode(gdb, &node);
+ }
+ dns_db_closeversion(gdb, &ver, false);
+}
+
+static void
+writeset(const char *prefix, dns_rdatatype_t type) {
+ char *filename;
+ char namestr[DNS_NAME_FORMATSIZE];
+ dns_db_t *db = NULL;
+ dns_dbversion_t *dbversion = NULL;
+ dns_diff_t diff;
+ dns_difftuple_t *tuple = NULL;
+ dns_name_t *name;
+ dns_rdata_t rdata, ds;
+ bool have_ksk = false;
+ bool have_non_ksk = false;
+ isc_buffer_t b;
+ isc_buffer_t namebuf;
+ isc_region_t r;
+ isc_result_t result;
+ dns_dnsseckey_t *key, *curr;
+ unsigned char dsbuf[DNS_DS_BUFFERSIZE];
+ unsigned char keybuf[DST_KEY_MAXSIZE];
+ unsigned int filenamelen;
+ const dns_master_style_t *style = (type == dns_rdatatype_dnskey)
+ ? masterstyle
+ : dsstyle;
+
+ isc_buffer_init(&namebuf, namestr, sizeof(namestr));
+ result = dns_name_tofilenametext(gorigin, false, &namebuf);
+ check_result(result, "dns_name_tofilenametext");
+ isc_buffer_putuint8(&namebuf, 0);
+ filenamelen = strlen(prefix) + strlen(namestr) + 1;
+ if (dsdir != NULL) {
+ filenamelen += strlen(dsdir) + 1;
+ }
+ filename = isc_mem_get(mctx, filenamelen);
+ if (dsdir != NULL) {
+ snprintf(filename, filenamelen, "%s/", dsdir);
+ } else {
+ filename[0] = 0;
+ }
+ strlcat(filename, prefix, filenamelen);
+ strlcat(filename, namestr, filenamelen);
+
+ dns_diff_init(mctx, &diff);
+
+ name = gorigin;
+
+ for (key = ISC_LIST_HEAD(keylist); key != NULL;
+ key = ISC_LIST_NEXT(key, link))
+ {
+ if (REVOKE(key->key)) {
+ continue;
+ }
+ if (isksk(key)) {
+ have_ksk = true;
+ have_non_ksk = false;
+ } else {
+ have_ksk = false;
+ have_non_ksk = true;
+ }
+ for (curr = ISC_LIST_HEAD(keylist); curr != NULL;
+ curr = ISC_LIST_NEXT(curr, link))
+ {
+ if (dst_key_alg(key->key) != dst_key_alg(curr->key)) {
+ continue;
+ }
+ if (REVOKE(curr->key)) {
+ continue;
+ }
+ if (isksk(curr)) {
+ have_ksk = true;
+ } else {
+ have_non_ksk = true;
+ }
+ }
+ if (have_ksk && have_non_ksk && !isksk(key)) {
+ continue;
+ }
+ dns_rdata_init(&rdata);
+ dns_rdata_init(&ds);
+ isc_buffer_init(&b, keybuf, sizeof(keybuf));
+ result = dst_key_todns(key->key, &b);
+ check_result(result, "dst_key_todns");
+ isc_buffer_usedregion(&b, &r);
+ dns_rdata_fromregion(&rdata, gclass, dns_rdatatype_dnskey, &r);
+ if (type != dns_rdatatype_dnskey) {
+ result = dns_ds_buildrdata(gorigin, &rdata,
+ DNS_DSDIGEST_SHA256, dsbuf,
+ &ds);
+ check_result(result, "dns_ds_buildrdata");
+ result = dns_difftuple_create(mctx,
+ DNS_DIFFOP_ADDRESIGN,
+ name, 0, &ds, &tuple);
+ } else {
+ result = dns_difftuple_create(
+ mctx, DNS_DIFFOP_ADDRESIGN, gorigin,
+ zone_soa_min_ttl, &rdata, &tuple);
+ }
+ check_result(result, "dns_difftuple_create");
+ dns_diff_append(&diff, &tuple);
+ }
+
+ result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
+ gclass, 0, NULL, &db);
+ check_result(result, "dns_db_create");
+
+ result = dns_db_newversion(db, &dbversion);
+ check_result(result, "dns_db_newversion");
+
+ result = dns_diff_apply(&diff, db, dbversion);
+ check_result(result, "dns_diff_apply");
+ dns_diff_clear(&diff);
+
+ result = dns_master_dump(mctx, db, dbversion, style, filename,
+ dns_masterformat_text, NULL);
+ check_result(result, "dns_master_dump");
+
+ isc_mem_put(mctx, filename, filenamelen);
+
+ dns_db_closeversion(db, &dbversion, false);
+ dns_db_detach(&db);
+}
+
+static void
+print_time(FILE *fp) {
+ time_t currenttime = time(NULL);
+ struct tm t, *tm = localtime_r(&currenttime, &t);
+ unsigned int flen;
+ char timebuf[80];
+
+ if (tm == NULL || outputformat != dns_masterformat_text) {
+ return;
+ }
+
+ flen = strftime(timebuf, sizeof(timebuf), "%a %b %e %H:%M:%S %Y", tm);
+ INSIST(flen > 0U && flen < sizeof(timebuf));
+ fprintf(fp, "; File written on %s\n", timebuf);
+}
+
+static void
+print_version(FILE *fp) {
+ if (outputformat != dns_masterformat_text) {
+ return;
+ }
+
+ fprintf(fp, "; dnssec_signzone version " VERSION "\n");
+}
+
+ISC_PLATFORM_NORETURN_PRE static void
+usage(void) ISC_PLATFORM_NORETURN_POST;
+
+static void
+usage(void) {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, "\t%s [options] zonefile [keys]\n", program);
+
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "Version: %s\n", VERSION);
+
+ fprintf(stderr, "Options: (default value in parenthesis) \n");
+ fprintf(stderr, "\t-S:\tsmart signing: automatically finds key files\n"
+ "\t\tfor the zone and determines how they are to "
+ "be used\n");
+ fprintf(stderr, "\t-K directory:\n");
+ fprintf(stderr, "\t\tdirectory to find key files (.)\n");
+ fprintf(stderr, "\t-d directory:\n");
+ fprintf(stderr, "\t\tdirectory to find dsset-* files (.)\n");
+ fprintf(stderr, "\t-g:\t");
+ fprintf(stderr, "update DS records based on child zones' "
+ "dsset-* files\n");
+ fprintf(stderr, "\t-s [YYYYMMDDHHMMSS|+offset]:\n");
+ fprintf(stderr, "\t\tRRSIG start time "
+ "- absolute|offset (now - 1 hour)\n");
+ fprintf(stderr, "\t-e [YYYYMMDDHHMMSS|+offset|\"now\"+offset]:\n");
+ fprintf(stderr, "\t\tRRSIG end time "
+ "- absolute|from start|from now "
+ "(now + 30 days)\n");
+ fprintf(stderr, "\t-X [YYYYMMDDHHMMSS|+offset|\"now\"+offset]:\n");
+ fprintf(stderr, "\t\tDNSKEY RRSIG end "
+ "- absolute|from start|from now "
+ "(matches -e)\n");
+ fprintf(stderr, "\t-i interval:\n");
+ fprintf(stderr, "\t\tcycle interval - resign "
+ "if < interval from end ( (end-start)/4 )\n");
+ fprintf(stderr, "\t-j jitter:\n");
+ fprintf(stderr, "\t\trandomize signature end time up to jitter "
+ "seconds\n");
+ fprintf(stderr, "\t-v debuglevel (0)\n");
+ fprintf(stderr, "\t-q quiet\n");
+ fprintf(stderr, "\t-V:\tprint version information\n");
+ fprintf(stderr, "\t-o origin:\n");
+ fprintf(stderr, "\t\tzone origin (name of zonefile)\n");
+ fprintf(stderr, "\t-f outfile:\n");
+ fprintf(stderr, "\t\tfile the signed zone is written in "
+ "(zonefile + .signed)\n");
+ fprintf(stderr, "\t-I format:\n");
+ fprintf(stderr, "\t\tfile format of input zonefile (text)\n");
+ fprintf(stderr, "\t-O format:\n");
+ fprintf(stderr, "\t\tfile format of signed zone file (text)\n");
+ fprintf(stderr, "\t-N format:\n");
+ fprintf(stderr, "\t\tsoa serial format of signed zone file (keep)\n");
+ fprintf(stderr, "\t-D:\n");
+ fprintf(stderr, "\t\toutput only DNSSEC-related records\n");
+ fprintf(stderr, "\t-a:\t");
+ fprintf(stderr, "verify generated signatures\n");
+ fprintf(stderr, "\t-c class (IN)\n");
+ fprintf(stderr, "\t-E engine:\n");
+#if USE_PKCS11
+ fprintf(stderr,
+ "\t\tpath to PKCS#11 provider library "
+ "(default is %s)\n",
+ PK11_LIB_LOCATION);
+#else /* if USE_PKCS11 */
+ fprintf(stderr, "\t\tname of an OpenSSL engine to use\n");
+#endif /* if USE_PKCS11 */
+ fprintf(stderr, "\t-P:\t");
+ fprintf(stderr, "disable post-sign verification\n");
+ fprintf(stderr, "\t-Q:\t");
+ fprintf(stderr, "remove signatures from keys that are no "
+ "longer active\n");
+ fprintf(stderr, "\t-R:\t");
+ fprintf(stderr, "remove signatures from keys that no longer exist\n");
+ fprintf(stderr, "\t-T TTL:\tTTL for newly added DNSKEYs\n");
+ fprintf(stderr, "\t-t:\t");
+ fprintf(stderr, "print statistics\n");
+ fprintf(stderr, "\t-u:\t");
+ fprintf(stderr, "update or replace an existing NSEC/NSEC3 chain\n");
+ fprintf(stderr, "\t-x:\tsign DNSKEY record with KSKs only, not ZSKs\n");
+ fprintf(stderr, "\t-z:\tsign all records with KSKs\n");
+ fprintf(stderr, "\t-C:\tgenerate a keyset file, for compatibility\n"
+ "\t\twith older versions of dnssec-signzone -g\n");
+ fprintf(stderr, "\t-n ncpus (number of cpus present)\n");
+ fprintf(stderr, "\t-k key_signing_key\n");
+ fprintf(stderr, "\t-3 NSEC3 salt\n");
+ fprintf(stderr, "\t-H NSEC3 iterations (10)\n");
+ fprintf(stderr, "\t-A NSEC3 optout\n");
+
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "Signing Keys: ");
+ fprintf(stderr, "(default: all zone keys that have private keys)\n");
+ fprintf(stderr, "\tkeyfile (Kname+alg+tag)\n");
+
+ exit(0);
+}
+
+static void
+removetempfile(void) {
+ if (removefile) {
+ isc_file_remove(tempfile);
+ }
+}
+
+static void
+print_stats(isc_time_t *timer_start, isc_time_t *timer_finish,
+ isc_time_t *sign_start, isc_time_t *sign_finish) {
+ uint64_t time_us; /* Time in microseconds */
+ uint64_t time_ms; /* Time in milliseconds */
+ uint64_t sig_ms; /* Signatures per millisecond */
+ FILE *out = output_stdout ? stderr : stdout;
+
+ fprintf(out, "Signatures generated: %10u\n", nsigned);
+ fprintf(out, "Signatures retained: %10u\n", nretained);
+ fprintf(out, "Signatures dropped: %10u\n", ndropped);
+ fprintf(out, "Signatures successfully verified: %10u\n", nverified);
+ fprintf(out,
+ "Signatures unsuccessfully "
+ "verified: %10u\n",
+ nverifyfailed);
+
+ time_us = isc_time_microdiff(sign_finish, sign_start);
+ time_ms = time_us / 1000;
+ fprintf(out, "Signing time in seconds: %7u.%03u\n",
+ (unsigned int)(time_ms / 1000), (unsigned int)(time_ms % 1000));
+ if (time_us > 0) {
+ sig_ms = ((uint64_t)nsigned * 1000000000) / time_us;
+ fprintf(out, "Signatures per second: %7u.%03u\n",
+ (unsigned int)sig_ms / 1000,
+ (unsigned int)sig_ms % 1000);
+ }
+
+ time_us = isc_time_microdiff(timer_finish, timer_start);
+ time_ms = time_us / 1000;
+ fprintf(out, "Runtime in seconds: %7u.%03u\n",
+ (unsigned int)(time_ms / 1000), (unsigned int)(time_ms % 1000));
+}
+
+int
+main(int argc, char *argv[]) {
+ int i, ch;
+ char *startstr = NULL, *endstr = NULL, *classname = NULL;
+ char *dnskey_endstr = NULL;
+ char *origin = NULL, *file = NULL, *output = NULL;
+ char *inputformatstr = NULL, *outputformatstr = NULL;
+ char *serialformatstr = NULL;
+ char *dskeyfile[MAXDSKEYS];
+ int ndskeys = 0;
+ char *endp;
+ isc_time_t timer_start, timer_finish;
+ isc_time_t sign_start, sign_finish;
+ dns_dnsseckey_t *key;
+ isc_result_t result, vresult;
+ isc_log_t *log = NULL;
+ const char *engine = NULL;
+ bool free_output = false;
+ int tempfilelen = 0;
+ dns_rdataclass_t rdclass;
+ isc_task_t **tasks = NULL;
+ hashlist_t hashlist;
+ bool make_keyset = false;
+ bool set_salt = false;
+ bool set_optout = false;
+ bool set_iter = false;
+ bool nonsecify = false;
+
+ atomic_init(&shuttingdown, false);
+ atomic_init(&finished, false);
+
+ /* Unused letters: Bb G J q Yy (and F is reserved). */
+#define CMDLINE_FLAGS \
+ "3:AaCc:Dd:E:e:f:FghH:i:I:j:K:k:L:l:m:M:n:N:o:O:PpQqRr:s:ST:tuUv:VX:" \
+ "xzZ:"
+
+ /*
+ * Process memory debugging argument first.
+ */
+ while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
+ switch (ch) {
+ case 'm':
+ if (strcasecmp(isc_commandline_argument, "record") == 0)
+ {
+ isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
+ }
+ if (strcasecmp(isc_commandline_argument, "trace") == 0)
+ {
+ isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
+ }
+ if (strcasecmp(isc_commandline_argument, "usage") == 0)
+ {
+ isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
+ }
+ if (strcasecmp(isc_commandline_argument, "size") == 0) {
+ isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
+ }
+ if (strcasecmp(isc_commandline_argument, "mctx") == 0) {
+ isc_mem_debugging |= ISC_MEM_DEBUGCTX;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ isc_commandline_reset = true;
+
+#ifdef _WIN32
+ InitSockets();
+#endif /* ifdef _WIN32 */
+
+ masterstyle = &dns_master_style_explicitttl;
+
+ check_result(isc_app_start(), "isc_app_start");
+
+ isc_mem_create(&mctx);
+
+#if USE_PKCS11
+ pk11_result_register();
+#endif /* if USE_PKCS11 */
+ dns_result_register();
+
+ isc_commandline_errprint = false;
+
+ while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
+ switch (ch) {
+ case '3':
+ set_salt = true;
+ nsec_datatype = dns_rdatatype_nsec3;
+ if (strcmp(isc_commandline_argument, "-") != 0) {
+ isc_buffer_t target;
+ char *sarg;
+
+ sarg = isc_commandline_argument;
+ isc_buffer_init(&target, saltbuf,
+ sizeof(saltbuf));
+ result = isc_hex_decodestring(sarg, &target);
+ check_result(result, "isc_hex_decodestring("
+ "salt)");
+ salt_length = isc_buffer_usedlength(&target);
+ }
+ break;
+
+ case 'A':
+ set_optout = true;
+ if (OPTOUT(nsec3flags)) {
+ nsec3flags &= ~DNS_NSEC3FLAG_OPTOUT;
+ } else {
+ nsec3flags |= DNS_NSEC3FLAG_OPTOUT;
+ }
+ break;
+
+ case 'a':
+ tryverify = true;
+ break;
+
+ case 'C':
+ make_keyset = true;
+ break;
+
+ case 'c':
+ classname = isc_commandline_argument;
+ break;
+
+ case 'd':
+ dsdir = isc_commandline_argument;
+ if (strlen(dsdir) == 0U) {
+ fatal("DS directory must be non-empty string");
+ }
+ result = try_dir(dsdir);
+ if (result != ISC_R_SUCCESS) {
+ fatal("cannot open directory %s: %s", dsdir,
+ isc_result_totext(result));
+ }
+ break;
+
+ case 'D':
+ output_dnssec_only = true;
+ break;
+
+ case 'E':
+ engine = isc_commandline_argument;
+ break;
+
+ case 'e':
+ endstr = isc_commandline_argument;
+ break;
+
+ case 'f':
+ output = isc_commandline_argument;
+ if (strcmp(output, "-") == 0) {
+ output_stdout = true;
+ }
+ break;
+
+ case 'g':
+ generateds = true;
+ break;
+
+ case 'H':
+ set_iter = true;
+ /* too-many is NOT DOCUMENTED */
+ if (strcmp(isc_commandline_argument, "too-many") == 0) {
+ nsec3iter = 151;
+ no_max_check = true;
+ break;
+ }
+ nsec3iter = strtoul(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0') {
+ fatal("iterations must be numeric");
+ }
+ if (nsec3iter > 0xffffU) {
+ fatal("iterations too big");
+ }
+ break;
+
+ case 'I':
+ inputformatstr = isc_commandline_argument;
+ break;
+
+ case 'i':
+ endp = NULL;
+ cycle = strtol(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0' || cycle < 0) {
+ fatal("cycle period must be numeric and "
+ "positive");
+ }
+ break;
+
+ case 'j':
+ endp = NULL;
+ jitter = strtol(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0' || jitter < 0) {
+ fatal("jitter must be numeric and positive");
+ }
+ break;
+
+ case 'K':
+ directory = isc_commandline_argument;
+ break;
+
+ case 'k':
+ if (ndskeys == MAXDSKEYS) {
+ fatal("too many key-signing keys specified");
+ }
+ dskeyfile[ndskeys++] = isc_commandline_argument;
+ break;
+
+ case 'L':
+ snset = true;
+ endp = NULL;
+ serialnum = strtol(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0') {
+ fprintf(stderr, "source serial number "
+ "must be numeric");
+ exit(1);
+ }
+ break;
+
+ case 'l':
+ fatal("-l option (DLV lookaside) is obsolete");
+ break;
+
+ case 'M':
+ endp = NULL;
+ set_maxttl = true;
+ maxttl = strtol(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0') {
+ fprintf(stderr, "maximum TTL "
+ "must be numeric");
+ exit(1);
+ }
+ break;
+
+ case 'm':
+ break;
+
+ case 'N':
+ serialformatstr = isc_commandline_argument;
+ break;
+
+ case 'n':
+ endp = NULL;
+ ntasks = strtol(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0' || ntasks > INT32_MAX) {
+ fatal("number of cpus must be numeric");
+ }
+ break;
+
+ case 'O':
+ outputformatstr = isc_commandline_argument;
+ break;
+
+ case 'o':
+ origin = isc_commandline_argument;
+ break;
+
+ case 'P':
+ disable_zone_check = true;
+ break;
+
+ case 'p':
+ fatal("The -p option has been deprecated.\n");
+ break;
+
+ case 'Q':
+ remove_inactkeysigs = true;
+ break;
+
+ case 'R':
+ remove_orphansigs = true;
+ break;
+
+ case 'r':
+ fatal("The -r options has been deprecated.\n");
+ break;
+
+ case 'S':
+ smartsign = true;
+ break;
+
+ case 's':
+ startstr = isc_commandline_argument;
+ break;
+
+ case 'T':
+ endp = NULL;
+ set_keyttl = true;
+ keyttl = strtottl(isc_commandline_argument);
+ break;
+
+ case 't':
+ printstats = true;
+ break;
+
+ case 'U': /* Undocumented for testing only. */
+ unknownalg = true;
+ break;
+
+ case 'u':
+ update_chain = true;
+ break;
+
+ case 'v':
+ endp = NULL;
+ verbose = strtol(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0') {
+ fatal("verbose level must be numeric");
+ }
+ break;
+
+ case 'q':
+ quiet = true;
+ break;
+
+ case 'X':
+ dnskey_endstr = isc_commandline_argument;
+ break;
+
+ case 'x':
+ keyset_kskonly = true;
+ break;
+
+ case 'z':
+ ignore_kskflag = true;
+ break;
+
+ case 'F':
+ /* Reserved for FIPS mode */
+ FALLTHROUGH;
+ 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);
+
+ case 'Z': /* Undocumented test options */
+ if (!strcmp(isc_commandline_argument, "nonsecify")) {
+ nonsecify = true;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "%s: unhandled option -%c\n", program,
+ isc_commandline_option);
+ exit(1);
+ }
+ }
+
+ result = dst_lib_init(mctx, engine);
+ if (result != ISC_R_SUCCESS) {
+ fatal("could not initialize dst: %s",
+ isc_result_totext(result));
+ }
+
+ isc_stdtime_get(&now);
+
+ if (startstr != NULL) {
+ starttime = strtotime(startstr, now, now, NULL);
+ } else {
+ starttime = now - 3600; /* Allow for some clock skew. */
+ }
+
+ if (endstr != NULL) {
+ endtime = strtotime(endstr, now, starttime, NULL);
+ } else {
+ endtime = starttime + (30 * 24 * 60 * 60);
+ }
+
+ if (dnskey_endstr != NULL) {
+ dnskey_endtime = strtotime(dnskey_endstr, now, starttime, NULL);
+ if (endstr != NULL && dnskey_endtime == endtime) {
+ fprintf(stderr, "WARNING: -e and -X were both set, "
+ "but have identical values.\n");
+ }
+ } else {
+ dnskey_endtime = endtime;
+ }
+
+ if (cycle == -1) {
+ cycle = (endtime - starttime) / 4;
+ }
+
+ if (ntasks == 0) {
+ ntasks = isc_os_ncpus() * 2;
+ }
+ vbprintf(4, "using %d cpus\n", ntasks);
+
+ rdclass = strtoclass(classname);
+
+ if (directory == NULL) {
+ directory = ".";
+ }
+
+ setup_logging(mctx, &log);
+
+ argc -= isc_commandline_index;
+ argv += isc_commandline_index;
+
+ if (argc < 1) {
+ usage();
+ }
+
+ file = argv[0];
+
+ argc -= 1;
+ argv += 1;
+
+ if (origin == NULL) {
+ origin = file;
+ }
+
+ if (output == NULL) {
+ size_t size;
+ free_output = true;
+ size = strlen(file) + strlen(".signed") + 1;
+ output = isc_mem_allocate(mctx, size);
+ snprintf(output, size, "%s.signed", file);
+ }
+
+ if (inputformatstr != NULL) {
+ if (strcasecmp(inputformatstr, "text") == 0) {
+ inputformat = dns_masterformat_text;
+ } else if (strcasecmp(inputformatstr, "map") == 0) {
+ inputformat = dns_masterformat_map;
+ } else if (strcasecmp(inputformatstr, "raw") == 0) {
+ inputformat = dns_masterformat_raw;
+ } else if (strncasecmp(inputformatstr, "raw=", 4) == 0) {
+ inputformat = dns_masterformat_raw;
+ fprintf(stderr, "WARNING: input format version "
+ "ignored\n");
+ } else {
+ fatal("unknown file format: %s", inputformatstr);
+ }
+ }
+
+ if (outputformatstr != NULL) {
+ if (strcasecmp(outputformatstr, "text") == 0) {
+ outputformat = dns_masterformat_text;
+ } else if (strcasecmp(outputformatstr, "full") == 0) {
+ outputformat = dns_masterformat_text;
+ masterstyle = &dns_master_style_full;
+ } else if (strcasecmp(outputformatstr, "map") == 0) {
+ outputformat = dns_masterformat_map;
+ } else if (strcasecmp(outputformatstr, "raw") == 0) {
+ outputformat = dns_masterformat_raw;
+ } else if (strncasecmp(outputformatstr, "raw=", 4) == 0) {
+ char *end;
+
+ outputformat = dns_masterformat_raw;
+ rawversion = strtol(outputformatstr + 4, &end, 10);
+ if (end == outputformatstr + 4 || *end != '\0' ||
+ rawversion > 1U)
+ {
+ fprintf(stderr, "unknown raw format version\n");
+ exit(1);
+ }
+ } else {
+ fatal("unknown file format: %s", outputformatstr);
+ }
+ }
+
+ if (serialformatstr != NULL) {
+ if (strcasecmp(serialformatstr, "keep") == 0) {
+ serialformat = SOA_SERIAL_KEEP;
+ } else if (strcasecmp(serialformatstr, "increment") == 0 ||
+ strcasecmp(serialformatstr, "incr") == 0)
+ {
+ serialformat = SOA_SERIAL_INCREMENT;
+ } else if (strcasecmp(serialformatstr, "unixtime") == 0) {
+ serialformat = SOA_SERIAL_UNIXTIME;
+ } else if (strcasecmp(serialformatstr, "date") == 0) {
+ serialformat = SOA_SERIAL_DATE;
+ } else {
+ fatal("unknown soa serial format: %s", serialformatstr);
+ }
+ }
+
+ if (output_dnssec_only && outputformat != dns_masterformat_text) {
+ fatal("option -D can only be used with \"-O text\"");
+ }
+
+ if (output_dnssec_only && serialformat != SOA_SERIAL_KEEP) {
+ fatal("option -D can only be used with \"-N keep\"");
+ }
+
+ if (output_dnssec_only && set_maxttl) {
+ fatal("option -D cannot be used with -M");
+ }
+
+ result = dns_master_stylecreate(&dsstyle, DNS_STYLEFLAG_NO_TTL, 0, 24,
+ 0, 0, 0, 8, 0xffffffff, mctx);
+ check_result(result, "dns_master_stylecreate");
+
+ gdb = NULL;
+ TIME_NOW(&timer_start);
+ loadzone(file, origin, rdclass, &gdb);
+ gorigin = dns_db_origin(gdb);
+ gclass = dns_db_class(gdb);
+ get_soa_ttls();
+
+ if (set_maxttl && set_keyttl && keyttl > maxttl) {
+ fprintf(stderr,
+ "%s: warning: Specified key TTL %u "
+ "exceeds maximum zone TTL; reducing to %u\n",
+ program, keyttl, maxttl);
+ keyttl = maxttl;
+ }
+
+ if (!set_keyttl) {
+ keyttl = soa_ttl;
+ }
+
+ /*
+ * Check for any existing NSEC3 parameters in the zone,
+ * and use them as defaults if -u was not specified.
+ */
+ if (update_chain && !set_optout && !set_iter && !set_salt) {
+ nsec_datatype = dns_rdatatype_nsec;
+ } else {
+ set_nsec3params(update_chain, set_salt, set_optout, set_iter);
+ }
+
+ /*
+ * We need to do this early on, as we start messing with the list
+ * of keys rather early.
+ */
+ ISC_LIST_INIT(keylist);
+ isc_rwlock_init(&keylist_lock, 0, 0);
+
+ /*
+ * Fill keylist with:
+ * 1) Keys listed in the DNSKEY set that have
+ * private keys associated, *if* no keys were
+ * set on the command line.
+ * 2) ZSKs set on the command line
+ * 3) KSKs set on the command line
+ * 4) Any keys remaining in the DNSKEY set which
+ * do not have private keys associated and were
+ * not specified on the command line.
+ */
+ if (argc == 0 || smartsign) {
+ loadzonekeys(!smartsign, false);
+ }
+ loadexplicitkeys(argv, argc, false);
+ loadexplicitkeys(dskeyfile, ndskeys, true);
+ loadzonekeys(!smartsign, true);
+
+ /*
+ * If we're doing smart signing, look in the key repository for
+ * key files with metadata, and merge them with the keylist
+ * we have now.
+ */
+ if (smartsign) {
+ build_final_keylist();
+ }
+
+ /* Now enumerate the key list */
+ for (key = ISC_LIST_HEAD(keylist); key != NULL;
+ key = ISC_LIST_NEXT(key, link))
+ {
+ key->index = keycount++;
+ }
+
+ if (keycount == 0) {
+ if (disable_zone_check) {
+ fprintf(stderr,
+ "%s: warning: No keys specified "
+ "or found\n",
+ program);
+ } else {
+ fatal("No signing keys specified or found.");
+ }
+ nokeys = true;
+ }
+
+ warnifallksk(gdb);
+
+ if (IS_NSEC3) {
+ bool answer;
+
+ hash_length = dns_nsec3_hashlength(dns_hash_sha1);
+ hashlist_init(&hashlist, dns_db_nodecount(gdb) * 2,
+ hash_length);
+ result = dns_nsec_nseconly(gdb, gversion, &answer);
+ if (result == ISC_R_NOTFOUND) {
+ fprintf(stderr,
+ "%s: warning: NSEC3 generation "
+ "requested with no DNSKEY; ignoring\n",
+ program);
+ } else if (result != ISC_R_SUCCESS) {
+ check_result(result, "dns_nsec_nseconly");
+ } else if (answer) {
+ fatal("NSEC3 generation requested with "
+ "NSEC-only DNSKEY");
+ }
+
+ if (nsec3iter > dns_nsec3_maxiterations()) {
+ if (no_max_check) {
+ fprintf(stderr,
+ "Ignoring max iterations check.\n");
+ } else {
+ fatal("NSEC3 iterations too big. Maximum "
+ "iterations allowed %u.",
+ dns_nsec3_maxiterations());
+ }
+ }
+ } else {
+ hashlist_init(&hashlist, 0, 0); /* silence clang */
+ }
+
+ gversion = NULL;
+ result = dns_db_newversion(gdb, &gversion);
+ check_result(result, "dns_db_newversion()");
+
+ switch (serialformat) {
+ case SOA_SERIAL_INCREMENT:
+ setsoaserial(0, dns_updatemethod_increment);
+ break;
+ case SOA_SERIAL_UNIXTIME:
+ setsoaserial(now, dns_updatemethod_unixtime);
+ break;
+ case SOA_SERIAL_DATE:
+ setsoaserial(now, dns_updatemethod_date);
+ break;
+ case SOA_SERIAL_KEEP:
+ default:
+ /* do nothing */
+ break;
+ }
+
+ /* Remove duplicates and cap TTLs at maxttl */
+ cleanup_zone();
+
+ if (!nonsecify) {
+ if (IS_NSEC3) {
+ nsec3ify(dns_hash_sha1, nsec3iter, gsalt, salt_length,
+ &hashlist);
+ } else {
+ nsecify();
+ }
+ }
+
+ if (!nokeys) {
+ writeset("dsset-", dns_rdatatype_ds);
+ if (make_keyset) {
+ writeset("keyset-", dns_rdatatype_dnskey);
+ }
+ }
+
+ if (output_stdout) {
+ outfp = stdout;
+ if (outputformatstr == NULL) {
+ masterstyle = &dns_master_style_full;
+ }
+ } else {
+ tempfilelen = strlen(output) + 20;
+ tempfile = isc_mem_get(mctx, tempfilelen);
+
+ result = isc_file_mktemplate(output, tempfile, tempfilelen);
+ check_result(result, "isc_file_mktemplate");
+
+ if (outputformat == dns_masterformat_text) {
+ result = isc_file_openunique(tempfile, &outfp);
+ } else {
+ result = isc_file_bopenunique(tempfile, &outfp);
+ }
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to open temporary output file: %s",
+ isc_result_totext(result));
+ }
+ removefile = true;
+ setfatalcallback(&removetempfile);
+ }
+
+ print_time(outfp);
+ print_version(outfp);
+
+ result = isc_managers_create(mctx, ntasks, 0, &netmgr, &taskmgr);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to create task manager: %s",
+ isc_result_totext(result));
+ }
+
+ master = NULL;
+ result = isc_task_create(taskmgr, 0, &master);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to create task: %s", isc_result_totext(result));
+ }
+
+ tasks = isc_mem_get(mctx, ntasks * sizeof(isc_task_t *));
+ for (i = 0; i < (int)ntasks; i++) {
+ tasks[i] = NULL;
+ result = isc_task_create(taskmgr, 0, &tasks[i]);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to create task: %s",
+ isc_result_totext(result));
+ }
+ }
+
+ isc_mutex_init(&namelock);
+
+ if (printstats) {
+ isc_mutex_init(&statslock);
+ }
+
+ presign();
+ TIME_NOW(&sign_start);
+ signapex();
+ if (!atomic_load(&finished)) {
+ /*
+ * There is more work to do. Spread it out over multiple
+ * processors if possible.
+ */
+ for (i = 0; i < (int)ntasks; i++) {
+ result = isc_app_onrun(mctx, master, startworker,
+ tasks[i]);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to start task: %s",
+ isc_result_totext(result));
+ }
+ }
+ (void)isc_app_run();
+ if (!atomic_load(&finished)) {
+ fatal("process aborted by user");
+ }
+ } else {
+ isc_task_detach(&master);
+ }
+ atomic_store(&shuttingdown, true);
+ for (i = 0; i < (int)ntasks; i++) {
+ isc_task_detach(&tasks[i]);
+ }
+ isc_managers_destroy(&netmgr, &taskmgr);
+ isc_mem_put(mctx, tasks, ntasks * sizeof(isc_task_t *));
+ postsign();
+ TIME_NOW(&sign_finish);
+
+ if (disable_zone_check) {
+ vresult = ISC_R_SUCCESS;
+ } else {
+ vresult = dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin,
+ NULL, mctx, ignore_kskflag,
+ keyset_kskonly, report);
+ if (vresult != ISC_R_SUCCESS) {
+ fprintf(output_stdout ? stderr : stdout,
+ "Zone verification failed (%s)\n",
+ isc_result_totext(vresult));
+ }
+ }
+
+ if (outputformat != dns_masterformat_text) {
+ dns_masterrawheader_t header;
+ dns_master_initrawheader(&header);
+ if (rawversion == 0U) {
+ header.flags = DNS_MASTERRAW_COMPAT;
+ } else if (snset) {
+ header.flags = DNS_MASTERRAW_SOURCESERIALSET;
+ header.sourceserial = serialnum;
+ }
+ result = dns_master_dumptostream(mctx, gdb, gversion,
+ masterstyle, outputformat,
+ &header, outfp);
+ check_result(result, "dns_master_dumptostream3");
+ }
+
+ isc_mutex_destroy(&namelock);
+ if (printstats) {
+ isc_mutex_destroy(&statslock);
+ }
+
+ if (!output_stdout) {
+ result = isc_stdio_close(outfp);
+ check_result(result, "isc_stdio_close");
+ removefile = false;
+
+ if (vresult == ISC_R_SUCCESS) {
+ result = isc_file_rename(tempfile, output);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed to rename temp file to %s: %s",
+ output, isc_result_totext(result));
+ }
+ printf("%s\n", output);
+ } else {
+ isc_file_remove(tempfile);
+ }
+ }
+
+ dns_db_closeversion(gdb, &gversion, false);
+ dns_db_detach(&gdb);
+
+ hashlist_free(&hashlist);
+
+ while (!ISC_LIST_EMPTY(keylist)) {
+ key = ISC_LIST_HEAD(keylist);
+ ISC_LIST_UNLINK(keylist, key, link);
+ dns_dnsseckey_destroy(mctx, &key);
+ }
+
+ if (tempfilelen != 0) {
+ isc_mem_put(mctx, tempfile, tempfilelen);
+ }
+
+ if (free_output) {
+ isc_mem_free(mctx, output);
+ }
+
+ dns_master_styledestroy(&dsstyle, mctx);
+
+ cleanup_logging(&log);
+ dst_lib_destroy();
+ if (verbose > 10) {
+ isc_mem_stats(mctx, stdout);
+ }
+ isc_mem_destroy(&mctx);
+
+ (void)isc_app_finish();
+
+ if (printstats) {
+ TIME_NOW(&timer_finish);
+ print_stats(&timer_start, &timer_finish, &sign_start,
+ &sign_finish);
+ }
+
+#ifdef _WIN32
+ DestroySockets();
+#endif /* ifdef _WIN32 */
+ return (vresult == ISC_R_SUCCESS ? 0 : 1);
+}
diff --git a/bin/dnssec/dnssec-signzone.rst b/bin/dnssec/dnssec-signzone.rst
new file mode 100644
index 0000000..f5056bb
--- /dev/null
+++ b/bin/dnssec/dnssec-signzone.rst
@@ -0,0 +1,396 @@
+.. 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.
+
+.. highlight: console
+
+.. _man_dnssec-signzone:
+
+dnssec-signzone - DNSSEC zone signing tool
+------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`dnssec-signzone` [**-a**] [**-c** class] [**-d** directory] [**-D**] [**-E** engine] [**-e** end-time] [**-f** output-file] [**-g**] [**-h**] [**-i** interval] [**-I** input-format] [**-j** jitter] [**-K** directory] [**-k** key] [**-L** serial] [**-M** maxttl] [**-N** soa-serial-format] [**-o** origin] [**-O** output-format] [**-P**] [**-Q**] [**-q**] [**-R**] [**-S**] [**-s** start-time] [**-T** ttl] [**-t**] [**-u**] [**-v** level] [**-V**] [**-X** extended end-time] [**-x**] [**-z**] [**-3** salt] [**-H** iterations] [**-A**] {zonefile} [key...]
+
+Description
+~~~~~~~~~~~
+
+``dnssec-signzone`` signs a zone; it generates NSEC and RRSIG records
+and produces a signed version of the zone. The security status of
+delegations from the signed zone (that is, whether the child zones are
+secure) is determined by the presence or absence of a ``keyset``
+file for each child zone.
+
+Options
+~~~~~~~
+
+``-a``
+ This option verifies all generated signatures.
+
+``-c class``
+ This option specifies the DNS class of the zone.
+
+``-C``
+ This option sets compatibility mode, in which a ``keyset-zonename`` file is generated in addition
+ to ``dsset-zonename`` when signing a zone, for use by older versions
+ of ``dnssec-signzone``.
+
+``-d directory``
+ This option indicates the directory where BIND 9 should look for ``dsset-`` or ``keyset-`` files.
+
+``-D``
+ This option indicates that only those record types automatically managed by
+ ``dnssec-signzone``, i.e., RRSIG, NSEC, NSEC3 and NSEC3PARAM records, should be included in the output.
+ If smart signing (``-S``) is used, DNSKEY records are also included.
+ The resulting file can be included in the original zone file with
+ ``$INCLUDE``. This option cannot be combined with ``-O raw``,
+ ``-O map``, or serial-number updating.
+
+``-E engine``
+ This option specifies the hardware to use for cryptographic
+ operations, such as a secure key store used for signing, when applicable.
+
+ When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL
+ engine identifier that drives the cryptographic accelerator or
+ hardware service module (usually ``pkcs11``). When BIND is
+ built with native PKCS#11 cryptography (``--enable-native-pkcs11``), it
+ defaults to the path of the PKCS#11 provider library specified via
+ ``--with-pkcs11``.
+
+``-g``
+ This option indicates that DS records for child zones should be generated from a ``dsset-`` or ``keyset-``
+ file. Existing DS records are removed.
+
+``-K directory``
+ This option specifies the directory to search for DNSSEC keys. If not
+ specified, it defaults to the current directory.
+
+``-k key``
+ This option tells BIND 9 to treat the specified key as a key-signing key, ignoring any key flags. This
+ option may be specified multiple times.
+
+``-M maxttl``
+ This option sets the maximum TTL for the signed zone. Any TTL higher than ``maxttl``
+ in the input zone is reduced to ``maxttl`` in the output. This
+ provides certainty as to the largest possible TTL in the signed zone,
+ which is useful to know when rolling keys. The maxttl is the longest
+ possible time before signatures that have been retrieved by resolvers
+ expire from resolver caches. Zones that are signed with this
+ option should be configured to use a matching ``max-zone-ttl`` in
+ ``named.conf``. (Note: This option is incompatible with ``-D``,
+ because it modifies non-DNSSEC data in the output zone.)
+
+``-s start-time``
+ This option specifies the date and time when the generated RRSIG records become
+ valid. This can be either an absolute or relative time. An absolute
+ start time is indicated by a number in YYYYMMDDHHMMSS notation;
+ 20000530144500 denotes 14:45:00 UTC on May 30th, 2000. A relative
+ start time is indicated by ``+N``, which is N seconds from the current
+ time. If no ``start-time`` is specified, the current time minus 1
+ hour (to allow for clock skew) is used.
+
+``-e end-time``
+ This option specifies the date and time when the generated RRSIG records expire. As
+ with ``start-time``, an absolute time is indicated in YYYYMMDDHHMMSS
+ notation. A time relative to the start time is indicated with ``+N``,
+ which is N seconds from the start time. A time relative to the
+ current time is indicated with ``now+N``. If no ``end-time`` is
+ specified, 30 days from the start time is the default.
+ ``end-time`` must be later than ``start-time``.
+
+``-X extended end-time``
+ This option specifies the date and time when the generated RRSIG records for the
+ DNSKEY RRset expire. This is to be used in cases when the DNSKEY
+ signatures need to persist longer than signatures on other records;
+ e.g., when the private component of the KSK is kept offline and the
+ KSK signature is to be refreshed manually.
+
+ As with ``end-time``, an absolute time is indicated in
+ YYYYMMDDHHMMSS notation. A time relative to the start time is
+ indicated with ``+N``, which is N seconds from the start time. A time
+ relative to the current time is indicated with ``now+N``. If no
+ ``extended end-time`` is specified, the value of ``end-time`` is used
+ as the default. (``end-time``, in turn, defaults to 30 days from the
+ start time.) ``extended end-time`` must be later than ``start-time``.
+
+``-f output-file``
+ This option indicates the name of the output file containing the signed zone. The default
+ is to append ``.signed`` to the input filename. If ``output-file`` is
+ set to ``-``, then the signed zone is written to the standard
+ output, with a default output format of ``full``.
+
+``-h``
+ This option prints a short summary of the options and arguments to
+ ``dnssec-signzone``.
+
+``-V``
+ This option prints version information.
+
+``-i interval``
+ This option indicates that, when a previously signed zone is passed as input, records may be
+ re-signed. The ``interval`` option specifies the cycle interval as an
+ offset from the current time, in seconds. If a RRSIG record expires
+ after the cycle interval, it is retained; otherwise, it is considered
+ to be expiring soon and it is replaced.
+
+ The default cycle interval is one quarter of the difference between
+ the signature end and start times. So if neither ``end-time`` nor
+ ``start-time`` is specified, ``dnssec-signzone`` generates
+ signatures that are valid for 30 days, with a cycle interval of 7.5
+ days. Therefore, if any existing RRSIG records are due to expire in
+ less than 7.5 days, they are replaced.
+
+``-I input-format``
+ This option sets the format of the input zone file. Possible formats are ``text``
+ (the default), ``raw``, and ``map``. This option is primarily
+ intended to be used for dynamic signed zones, so that the dumped zone
+ file in a non-text format containing updates can be signed directly.
+ This option is not useful for non-dynamic zones.
+
+``-j jitter``
+ When signing a zone with a fixed signature lifetime, all RRSIG
+ records issued at the time of signing expire simultaneously. If the
+ zone is incrementally signed, i.e., a previously signed zone is passed
+ as input to the signer, all expired signatures must be regenerated
+ at approximately the same time. The ``jitter`` option specifies a jitter
+ window that is used to randomize the signature expire time, thus
+ spreading incremental signature regeneration over time.
+
+ Signature lifetime jitter also, to some extent, benefits validators and
+ servers by spreading out cache expiration, i.e., if large numbers of
+ RRSIGs do not expire at the same time from all caches, there is
+ less congestion than if all validators need to refetch at around the
+ same time.
+
+``-L serial``
+ When writing a signed zone to "raw" or "map" format, this option sets the "source
+ serial" value in the header to the specified ``serial`` number. (This is
+ expected to be used primarily for testing purposes.)
+
+``-n ncpus``
+ This option specifies the number of threads to use. By default, one thread is
+ started for each detected CPU.
+
+``-N soa-serial-format``
+ This option sets the SOA serial number format of the signed zone. Possible formats are
+ ``keep`` (the default), ``increment``, ``unixtime``, and
+ ``date``.
+
+ **keep**
+ This format indicates that the SOA serial number should not be modified.
+
+ **increment**
+ This format increments the SOA serial number using :rfc:`1982` arithmetic.
+
+ **unixtime**
+ This format sets the SOA serial number to the number of seconds
+ since the beginning of the Unix epoch, unless the serial
+ number is already greater than or equal to that value, in
+ which case it is simply incremented by one.
+
+ **date**
+ This format sets the SOA serial number to today's date, in
+ YYYYMMDDNN format, unless the serial number is already greater
+ than or equal to that value, in which case it is simply
+ incremented by one.
+
+``-o origin``
+ This option sets the zone origin. If not specified, the name of the zone file is
+ assumed to be the origin.
+
+``-O output-format``
+ This option sets the format of the output file containing the signed zone. Possible
+ formats are ``text`` (the default), which is the standard textual
+ representation of the zone; ``full``, which is text output in a
+ format suitable for processing by external scripts; and ``map``,
+ ``raw``, and ``raw=N``, which store the zone in binary formats
+ for rapid loading by ``named``. ``raw=N`` specifies the format
+ version of the raw zone file: if N is 0, the raw file can be read by
+ any version of ``named``; if N is 1, the file can be read by release
+ 9.9.0 or higher. The default is 1.
+
+``-P``
+ This option disables post-sign verification tests.
+
+ The post-sign verification tests ensure that for each algorithm in
+ use there is at least one non-revoked self-signed KSK key, that all
+ revoked KSK keys are self-signed, and that all records in the zone
+ are signed by the algorithm. This option skips these tests.
+
+``-Q``
+ This option removes signatures from keys that are no longer active.
+
+ Normally, when a previously signed zone is passed as input to the
+ signer, and a DNSKEY record has been removed and replaced with a new
+ one, signatures from the old key that are still within their validity
+ period are retained. This allows the zone to continue to validate
+ with cached copies of the old DNSKEY RRset. The ``-Q`` option forces
+ ``dnssec-signzone`` to remove signatures from keys that are no longer
+ active. This enables ZSK rollover using the procedure described in
+ :rfc:`4641#4.2.1.1` ("Pre-Publish Key Rollover").
+
+``-q``
+ This option enables quiet mode, which suppresses unnecessary output. Without this option, when
+ ``dnssec-signzone`` is run it prints three pieces of information to standard output: the number of
+ keys in use; the algorithms used to verify the zone was signed correctly and
+ other status information; and the filename containing the signed
+ zone. With the option that output is suppressed, leaving only the filename.
+
+``-R``
+ This option removes signatures from keys that are no longer published.
+
+ This option is similar to ``-Q``, except it forces
+ ``dnssec-signzone`` to remove signatures from keys that are no longer
+ published. This enables ZSK rollover using the procedure described in
+ :rfc:`4641#4.2.1.2` ("Double Signature Zone Signing Key
+ Rollover").
+
+``-S``
+ This option enables smart signing, which instructs ``dnssec-signzone`` to search the key
+ repository for keys that match the zone being signed, and to include
+ them in the zone if appropriate.
+
+ When a key is found, its timing metadata is examined to determine how
+ it should be used, according to the following rules. Each successive
+ rule takes priority over the prior ones:
+
+ If no timing metadata has been set for the key, the key is
+ published in the zone and used to sign the zone.
+
+ If the key's publication date is set and is in the past, the key
+ is published in the zone.
+
+ If the key's activation date is set and is in the past, the key is
+ published (regardless of publication date) and used to sign the
+ zone.
+
+ If the key's revocation date is set and is in the past, and the key
+ is published, then the key is revoked, and the revoked key is used
+ to sign the zone.
+
+ If either the key's unpublication or deletion date is set and
+ in the past, the key is NOT published or used to sign the zone,
+ regardless of any other metadata.
+
+ If the key's sync publication date is set and is in the past,
+ synchronization records (type CDS and/or CDNSKEY) are created.
+
+ If the key's sync deletion date is set and is in the past,
+ synchronization records (type CDS and/or CDNSKEY) are removed.
+
+``-T ttl``
+ This option specifies a TTL to be used for new DNSKEY records imported into the
+ zone from the key repository. If not specified, the default is the
+ TTL value from the zone's SOA record. This option is ignored when
+ signing without ``-S``, since DNSKEY records are not imported from
+ the key repository in that case. It is also ignored if there are any
+ pre-existing DNSKEY records at the zone apex, in which case new
+ records' TTL values are set to match them, or if any of the
+ imported DNSKEY records had a default TTL value. In the event of a
+ conflict between TTL values in imported keys, the shortest one is
+ used.
+
+``-t``
+ This option prints statistics at completion.
+
+``-u``
+ This option updates the NSEC/NSEC3 chain when re-signing a previously signed zone.
+ With this option, a zone signed with NSEC can be switched to NSEC3,
+ or a zone signed with NSEC3 can be switched to NSEC or to NSEC3 with
+ different parameters. Without this option, ``dnssec-signzone``
+ retains the existing chain when re-signing.
+
+``-v level``
+ This option sets the debugging level.
+
+``-x``
+ This option indicates that BIND 9 should only sign the DNSKEY, CDNSKEY, and CDS RRsets with key-signing keys,
+ and should omit signatures from zone-signing keys. (This is similar to the
+ ``dnssec-dnskey-kskonly yes;`` zone option in ``named``.)
+
+``-z``
+ This option indicates that BIND 9 should ignore the KSK flag on keys when determining what to sign. This causes
+ KSK-flagged keys to sign all records, not just the DNSKEY RRset.
+ (This is similar to the ``update-check-ksk no;`` zone option in
+ ``named``.)
+
+``-3 salt``
+ This option generates an NSEC3 chain with the given hex-encoded salt. A dash
+ (-) can be used to indicate that no salt is to be used when
+ generating the NSEC3 chain.
+
+ .. note::
+ ``-3 -`` is the recommended configuration. Adding salt provides no practical benefits.
+
+``-H iterations``
+ This option indicates that, when generating an NSEC3 chain, BIND 9 should use this many iterations. The default
+ is 10.
+
+ .. warning::
+ Values greater than 0 cause interoperability issues and also increase the risk of CPU-exhausting DoS attacks. The default value has not been changed because the best practices has changed only after BIND 9.16 reached Extended Support Version status.
+
+``-A``
+ This option indicates that, when generating an NSEC3 chain, BIND 9 should set the OPTOUT flag on all NSEC3
+ records and should not generate NSEC3 records for insecure delegations.
+
+ .. warning::
+ Do not use this option unless all its implications are fully understood. This option is intended only for extremely large zones (comparable to ``com.``) with sparse secure delegations.
+
+ Using this option twice (i.e., ``-AA``) turns the OPTOUT flag off for
+ all records. This is useful when using the ``-u`` option to modify an
+ NSEC3 chain which previously had OPTOUT set.
+
+``zonefile``
+ This option sets the file containing the zone to be signed.
+
+``key``
+ This option specifies which keys should be used to sign the zone. If no keys are
+ specified, the zone is examined for DNSKEY records at the
+ zone apex. If these records are found and there are matching private keys in
+ the current directory, they are used for signing.
+
+Example
+~~~~~~~
+
+The following command signs the ``example.com`` zone with the
+ECDSAP256SHA256 key generated by ``dnssec-keygen``
+(Kexample.com.+013+17247). Because the ``-S`` option is not being used,
+the zone's keys must be in the master file (``db.example.com``). This
+invocation looks for ``dsset`` files in the current directory, so that
+DS records can be imported from them (``-g``).
+
+::
+
+ % dnssec-signzone -g -o example.com db.example.com \
+ Kexample.com.+013+17247
+ db.example.com.signed
+ %
+
+In the above example, ``dnssec-signzone`` creates the file
+``db.example.com.signed``. This file should be referenced in a zone
+statement in the ``named.conf`` file.
+
+This example re-signs a previously signed zone with default parameters.
+The private keys are assumed to be in the current directory.
+
+::
+
+ % cp db.example.com.signed db.example.com
+ % dnssec-signzone -o example.com db.example.com
+ db.example.com.signed
+ %
+
+See Also
+~~~~~~~~
+
+:manpage:`dnssec-keygen(8)`, BIND 9 Administrator Reference Manual, :rfc:`4033`,
+:rfc:`4641`.
diff --git a/bin/dnssec/dnssec-verify.c b/bin/dnssec/dnssec-verify.c
new file mode 100644
index 0000000..85cc54b
--- /dev/null
+++ b/bin/dnssec/dnssec-verify.c
@@ -0,0 +1,366 @@
+/*
+ * 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 <time.h>
+
+#include <isc/app.h>
+#include <isc/base32.h>
+#include <isc/commandline.h>
+#include <isc/event.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/hex.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/os.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/rwlock.h>
+#include <isc/serial.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/diff.h>
+#include <dns/dnssec.h>
+#include <dns/ds.h>
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/nsec.h>
+#include <dns/nsec3.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/soa.h>
+#include <dns/time.h>
+#include <dns/zoneverify.h>
+
+#include <dst/dst.h>
+
+#if USE_PKCS11
+#include <pk11/result.h>
+#endif /* if USE_PKCS11 */
+
+#include "dnssectool.h"
+
+const char *program = "dnssec-verify";
+
+static isc_stdtime_t now;
+static isc_mem_t *mctx = NULL;
+static dns_masterformat_t inputformat = dns_masterformat_text;
+static dns_db_t *gdb; /* The database */
+static dns_dbversion_t *gversion; /* The database version */
+static dns_rdataclass_t gclass; /* The class */
+static dns_name_t *gorigin; /* The database origin */
+static bool ignore_kskflag = false;
+static bool keyset_kskonly = false;
+
+static void
+report(const char *format, ...) {
+ if (!quiet) {
+ char buf[4096];
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+ fprintf(stdout, "%s\n", buf);
+ }
+}
+
+/*%
+ * Load the zone file from disk
+ */
+static void
+loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) {
+ isc_buffer_t b;
+ int len;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result;
+
+ len = strlen(origin);
+ isc_buffer_init(&b, origin, len);
+ isc_buffer_add(&b, len);
+
+ name = dns_fixedname_initname(&fname);
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ fatal("failed converting name '%s' to dns format: %s", origin,
+ isc_result_totext(result));
+ }
+
+ result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0,
+ NULL, db);
+ check_result(result, "dns_db_create()");
+
+ result = dns_db_load(*db, file, inputformat, 0);
+ switch (result) {
+ case DNS_R_SEENINCLUDE:
+ case ISC_R_SUCCESS:
+ break;
+ case DNS_R_NOTZONETOP:
+ /*
+ * Comparing pointers (vs. using strcmp()) is intentional: we
+ * want to check whether -o was supplied on the command line,
+ * not whether origin and file contain the same string.
+ */
+ if (origin == file) {
+ fatal("failed loading zone '%s' from file '%s': "
+ "use -o to specify a different zone origin",
+ origin, file);
+ }
+ FALLTHROUGH;
+ default:
+ fatal("failed loading zone from '%s': %s", file,
+ isc_result_totext(result));
+ }
+}
+
+ISC_PLATFORM_NORETURN_PRE static void
+usage(void) ISC_PLATFORM_NORETURN_POST;
+
+static void
+usage(void) {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, "\t%s [options] zonefile [keys]\n", program);
+
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "Version: %s\n", VERSION);
+
+ fprintf(stderr, "Options: (default value in parenthesis) \n");
+ fprintf(stderr, "\t-v debuglevel (0)\n");
+ fprintf(stderr, "\t-q quiet\n");
+ fprintf(stderr, "\t-V:\tprint version information\n");
+ fprintf(stderr, "\t-o origin:\n");
+ fprintf(stderr, "\t\tzone origin (name of zonefile)\n");
+ fprintf(stderr, "\t-I format:\n");
+ fprintf(stderr, "\t\tfile format of input zonefile (text)\n");
+ fprintf(stderr, "\t-c class (IN)\n");
+ fprintf(stderr, "\t-E engine:\n");
+#if USE_PKCS11
+ fprintf(stderr,
+ "\t\tpath to PKCS#11 provider library "
+ "(default is %s)\n",
+ PK11_LIB_LOCATION);
+#else /* if USE_PKCS11 */
+ fprintf(stderr, "\t\tname of an OpenSSL engine to use\n");
+#endif /* if USE_PKCS11 */
+ fprintf(stderr, "\t-x:\tDNSKEY record signed with KSKs only, "
+ "not ZSKs\n");
+ fprintf(stderr, "\t-z:\tAll records signed with KSKs\n");
+ exit(0);
+}
+
+int
+main(int argc, char *argv[]) {
+ char *origin = NULL, *file = NULL;
+ char *inputformatstr = NULL;
+ isc_result_t result;
+ isc_log_t *log = NULL;
+ const char *engine = NULL;
+ char *classname = NULL;
+ dns_rdataclass_t rdclass;
+ char *endp;
+ int ch;
+
+#define CMDLINE_FLAGS "c:E:hm:o:I:qv:Vxz"
+
+ /*
+ * Process memory debugging argument first.
+ */
+ while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
+ switch (ch) {
+ case 'm':
+ if (strcasecmp(isc_commandline_argument, "record") == 0)
+ {
+ isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
+ }
+ if (strcasecmp(isc_commandline_argument, "trace") == 0)
+ {
+ isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
+ }
+ if (strcasecmp(isc_commandline_argument, "usage") == 0)
+ {
+ isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
+ }
+ if (strcasecmp(isc_commandline_argument, "size") == 0) {
+ isc_mem_debugging |= ISC_MEM_DEBUGSIZE;
+ }
+ if (strcasecmp(isc_commandline_argument, "mctx") == 0) {
+ isc_mem_debugging |= ISC_MEM_DEBUGCTX;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ isc_commandline_reset = true;
+ check_result(isc_app_start(), "isc_app_start");
+
+ isc_mem_create(&mctx);
+
+#if USE_PKCS11
+ pk11_result_register();
+#endif /* if USE_PKCS11 */
+ dns_result_register();
+
+ isc_commandline_errprint = false;
+
+ while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
+ switch (ch) {
+ case 'c':
+ classname = isc_commandline_argument;
+ break;
+
+ case 'E':
+ engine = isc_commandline_argument;
+ break;
+
+ case 'I':
+ inputformatstr = isc_commandline_argument;
+ break;
+
+ case 'm':
+ break;
+
+ case 'o':
+ origin = isc_commandline_argument;
+ break;
+
+ case 'v':
+ endp = NULL;
+ verbose = strtol(isc_commandline_argument, &endp, 0);
+ if (*endp != '\0') {
+ fatal("verbose level must be numeric");
+ }
+ break;
+
+ case 'q':
+ quiet = true;
+ break;
+
+ case 'x':
+ keyset_kskonly = true;
+ break;
+
+ case 'z':
+ ignore_kskflag = true;
+ 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);
+ }
+ }
+
+ result = dst_lib_init(mctx, engine);
+ if (result != ISC_R_SUCCESS) {
+ fatal("could not initialize dst: %s",
+ isc_result_totext(result));
+ }
+
+ isc_stdtime_get(&now);
+
+ rdclass = strtoclass(classname);
+
+ setup_logging(mctx, &log);
+
+ argc -= isc_commandline_index;
+ argv += isc_commandline_index;
+
+ if (argc < 1) {
+ usage();
+ }
+
+ file = argv[0];
+
+ argc -= 1;
+ argv += 1;
+
+ POST(argc);
+ POST(argv);
+
+ if (origin == NULL) {
+ origin = file;
+ }
+
+ if (inputformatstr != NULL) {
+ if (strcasecmp(inputformatstr, "text") == 0) {
+ inputformat = dns_masterformat_text;
+ } else if (strcasecmp(inputformatstr, "raw") == 0) {
+ inputformat = dns_masterformat_raw;
+ } else {
+ fatal("unknown file format: %s\n", inputformatstr);
+ }
+ }
+
+ gdb = NULL;
+ report("Loading zone '%s' from file '%s'\n", origin, file);
+ loadzone(file, origin, rdclass, &gdb);
+ gorigin = dns_db_origin(gdb);
+ gclass = dns_db_class(gdb);
+
+ gversion = NULL;
+ result = dns_db_newversion(gdb, &gversion);
+ check_result(result, "dns_db_newversion()");
+
+ result = dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, NULL, mctx,
+ ignore_kskflag, keyset_kskonly, report);
+
+ dns_db_closeversion(gdb, &gversion, false);
+ dns_db_detach(&gdb);
+
+ cleanup_logging(&log);
+ dst_lib_destroy();
+ if (verbose > 10) {
+ isc_mem_stats(mctx, stdout);
+ }
+ isc_mem_destroy(&mctx);
+
+ (void)isc_app_finish();
+
+ return (result == ISC_R_SUCCESS ? 0 : 1);
+}
diff --git a/bin/dnssec/dnssec-verify.rst b/bin/dnssec/dnssec-verify.rst
new file mode 100644
index 0000000..4a0c8e7
--- /dev/null
+++ b/bin/dnssec/dnssec-verify.rst
@@ -0,0 +1,98 @@
+.. 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.
+
+.. highlight: console
+
+.. _man_dnssec-verify:
+
+dnssec-verify - DNSSEC zone verification tool
+---------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`dnssec-verify` [**-c** class] [**-E** engine] [**-I** input-format] [**-o** origin] [**-q**] [**-v** level] [**-V**] [**-x**] [**-z**] {zonefile}
+
+Description
+~~~~~~~~~~~
+
+``dnssec-verify`` verifies that a zone is fully signed for each
+algorithm found in the DNSKEY RRset for the zone, and that the
+NSEC/NSEC3 chains are complete.
+
+Options
+~~~~~~~
+
+``-c class``
+ This option specifies the DNS class of the zone.
+
+``-E engine``
+ This option specifies the cryptographic hardware to use, when applicable.
+
+ When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL
+ engine identifier that drives the cryptographic accelerator or
+ hardware service module (usually ``pkcs11``). When BIND is
+ built with native PKCS#11 cryptography (``--enable-native-pkcs11``), it
+ defaults to the path of the PKCS#11 provider library specified via
+ ``--with-pkcs11``.
+
+``-I input-format``
+ This option sets the format of the input zone file. Possible formats are ``text``
+ (the default) and ``raw``. This option is primarily intended to be used
+ for dynamic signed zones, so that the dumped zone file in a non-text
+ format containing updates can be verified independently.
+ This option is not useful for non-dynamic zones.
+
+``-o origin``
+ This option indicates the zone origin. If not specified, the name of the zone file is
+ assumed to be the origin.
+
+``-v level``
+ This option sets the debugging level.
+
+``-V``
+ This option prints version information.
+
+``-q``
+ This option sets quiet mode, which suppresses output. Without this option, when ``dnssec-verify``
+ is run it prints to standard output the number of keys in use, the
+ algorithms used to verify the zone was signed correctly, and other status
+ information. With this option, all non-error output is suppressed, and only the exit
+ code indicates success.
+
+``-x``
+ This option verifies only that the DNSKEY RRset is signed with key-signing keys.
+ Without this flag, it is assumed that the DNSKEY RRset is signed
+ by all active keys. When this flag is set, it is not an error if
+ the DNSKEY RRset is not signed by zone-signing keys. This corresponds
+ to the ``-x`` option in ``dnssec-signzone``.
+
+``-z``
+ This option indicates that the KSK flag on the keys should be ignored when determining whether the zone is
+ correctly signed. Without this flag, it is assumed that there is
+ a non-revoked, self-signed DNSKEY with the KSK flag set for each
+ algorithm, and that RRsets other than DNSKEY RRset are signed with
+ a different DNSKEY without the KSK flag set.
+
+ With this flag set, BIND 9 only requires that for each algorithm, there
+ be at least one non-revoked, self-signed DNSKEY, regardless of
+ the KSK flag state, and that other RRsets be signed by a
+ non-revoked key for the same algorithm that includes the self-signed
+ key; the same key may be used for both purposes. This corresponds to
+ the ``-z`` option in ``dnssec-signzone``.
+
+``zonefile``
+ This option indicates the file containing the zone to be signed.
+
+See Also
+~~~~~~~~
+
+:manpage:`dnssec-signzone(8)`, BIND 9 Administrator Reference Manual, :rfc:`4033`.
diff --git a/bin/dnssec/dnssectool.c b/bin/dnssec/dnssectool.c
new file mode 100644
index 0000000..ce903ca
--- /dev/null
+++ b/bin/dnssec/dnssectool.c
@@ -0,0 +1,594 @@
+/*
+ * 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 */
+
+/*%
+ * DNSSEC Support Routines.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+#include <Winsock2.h>
+#endif /* ifdef _WIN32 */
+
+#include <isc/base32.h>
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/dir.h>
+#include <isc/file.h>
+#include <isc/heap.h>
+#include <isc/list.h>
+#include <isc/mem.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/dnssec.h>
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/nsec.h>
+#include <dns/nsec3.h>
+#include <dns/rdataclass.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/secalg.h>
+#include <dns/time.h>
+
+#include "dnssectool.h"
+
+#define KEYSTATES_NVALUES 4
+static const char *keystates[KEYSTATES_NVALUES] = {
+ "hidden",
+ "rumoured",
+ "omnipresent",
+ "unretentive",
+};
+
+int verbose = 0;
+bool quiet = false;
+uint8_t dtype[8];
+
+static fatalcallback_t *fatalcallback = NULL;
+
+void
+fatal(const char *format, ...) {
+ va_list args;
+
+ fprintf(stderr, "%s: fatal: ", program);
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ if (fatalcallback != NULL) {
+ (*fatalcallback)();
+ }
+ exit(1);
+}
+
+void
+setfatalcallback(fatalcallback_t *callback) {
+ fatalcallback = callback;
+}
+
+void
+check_result(isc_result_t result, const char *message) {
+ if (result != ISC_R_SUCCESS) {
+ fatal("%s: %s", message, isc_result_totext(result));
+ }
+}
+
+void
+vbprintf(int level, const char *fmt, ...) {
+ va_list ap;
+ if (level > verbose) {
+ return;
+ }
+ va_start(ap, fmt);
+ fprintf(stderr, "%s: ", program);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+void
+version(const char *name) {
+ fprintf(stderr, "%s %s\n", name, VERSION);
+ exit(0);
+}
+
+void
+sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ char algstr[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(&sig->signer, namestr, sizeof(namestr));
+ dns_secalg_format(sig->algorithm, algstr, sizeof(algstr));
+ snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid);
+}
+
+void
+setup_logging(isc_mem_t *mctx, isc_log_t **logp) {
+ isc_logdestination_t destination;
+ isc_logconfig_t *logconfig = NULL;
+ isc_log_t *log = NULL;
+ int level;
+
+ if (verbose < 0) {
+ verbose = 0;
+ }
+ switch (verbose) {
+ case 0:
+ /*
+ * We want to see warnings about things like out-of-zone
+ * data in the master file even when not verbose.
+ */
+ level = ISC_LOG_WARNING;
+ break;
+ case 1:
+ level = ISC_LOG_INFO;
+ break;
+ default:
+ level = ISC_LOG_DEBUG(verbose - 2 + 1);
+ break;
+ }
+
+ isc_log_create(mctx, &log, &logconfig);
+ isc_log_setcontext(log);
+ dns_log_init(log);
+ dns_log_setcontext(log);
+ isc_log_settag(logconfig, program);
+
+ /*
+ * Set up a channel similar to default_stderr except:
+ * - the logging level is passed in
+ * - the program name and logging level are printed
+ * - no time stamp is printed
+ */
+ destination.file.stream = stderr;
+ destination.file.name = NULL;
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.maximum_size = 0;
+ isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC, level,
+ &destination,
+ ISC_LOG_PRINTTAG | ISC_LOG_PRINTLEVEL);
+
+ RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr", NULL, NULL) ==
+ ISC_R_SUCCESS);
+
+ *logp = log;
+}
+
+void
+cleanup_logging(isc_log_t **logp) {
+ isc_log_t *log;
+
+ REQUIRE(logp != NULL);
+
+ log = *logp;
+ *logp = NULL;
+
+ if (log == NULL) {
+ return;
+ }
+
+ isc_log_destroy(&log);
+ isc_log_setcontext(NULL);
+ dns_log_setcontext(NULL);
+}
+
+static isc_stdtime_t
+time_units(isc_stdtime_t offset, char *suffix, const char *str) {
+ switch (suffix[0]) {
+ case 'Y':
+ case 'y':
+ return (offset * (365 * 24 * 3600));
+ case 'M':
+ case 'm':
+ switch (suffix[1]) {
+ case 'O':
+ case 'o':
+ return (offset * (30 * 24 * 3600));
+ case 'I':
+ case 'i':
+ return (offset * 60);
+ case '\0':
+ fatal("'%s' ambiguous: use 'mi' for minutes "
+ "or 'mo' for months",
+ str);
+ default:
+ fatal("time value %s is invalid", str);
+ }
+ UNREACHABLE();
+ break;
+ case 'W':
+ case 'w':
+ return (offset * (7 * 24 * 3600));
+ case 'D':
+ case 'd':
+ return (offset * (24 * 3600));
+ case 'H':
+ case 'h':
+ return (offset * 3600);
+ case 'S':
+ case 's':
+ case '\0':
+ return (offset);
+ default:
+ fatal("time value %s is invalid", str);
+ }
+ UNREACHABLE();
+ return (0); /* silence compiler warning */
+}
+
+static bool
+isnone(const char *str) {
+ return ((strcasecmp(str, "none") == 0) ||
+ (strcasecmp(str, "never") == 0));
+}
+
+dns_ttl_t
+strtottl(const char *str) {
+ const char *orig = str;
+ dns_ttl_t ttl;
+ char *endp;
+
+ if (isnone(str)) {
+ return ((dns_ttl_t)0);
+ }
+
+ ttl = strtol(str, &endp, 0);
+ if (ttl == 0 && endp == str) {
+ fatal("TTL must be numeric");
+ }
+ ttl = time_units(ttl, endp, orig);
+ return (ttl);
+}
+
+dst_key_state_t
+strtokeystate(const char *str) {
+ if (isnone(str)) {
+ return (DST_KEY_STATE_NA);
+ }
+
+ for (int i = 0; i < KEYSTATES_NVALUES; i++) {
+ if (keystates[i] != NULL && strcasecmp(str, keystates[i]) == 0)
+ {
+ return ((dst_key_state_t)i);
+ }
+ }
+ fatal("unknown key state %s", str);
+}
+
+isc_stdtime_t
+strtotime(const char *str, int64_t now, int64_t base, bool *setp) {
+ int64_t val, offset;
+ isc_result_t result;
+ const char *orig = str;
+ char *endp;
+ size_t n;
+
+ if (isnone(str)) {
+ if (setp != NULL) {
+ *setp = false;
+ }
+ return ((isc_stdtime_t)0);
+ }
+
+ if (setp != NULL) {
+ *setp = true;
+ }
+
+ if ((str[0] == '0' || str[0] == '-') && str[1] == '\0') {
+ return ((isc_stdtime_t)0);
+ }
+
+ /*
+ * We accept times in the following formats:
+ * now([+-]offset)
+ * YYYYMMDD([+-]offset)
+ * YYYYMMDDhhmmss([+-]offset)
+ * [+-]offset
+ */
+ n = strspn(str, "0123456789");
+ if ((n == 8u || n == 14u) &&
+ (str[n] == '\0' || str[n] == '-' || str[n] == '+'))
+ {
+ char timestr[15];
+
+ strlcpy(timestr, str, sizeof(timestr));
+ timestr[n] = 0;
+ if (n == 8u) {
+ strlcat(timestr, "000000", sizeof(timestr));
+ }
+ result = dns_time64_fromtext(timestr, &val);
+ if (result != ISC_R_SUCCESS) {
+ fatal("time value %s is invalid: %s", orig,
+ isc_result_totext(result));
+ }
+ base = val;
+ str += n;
+ } else if (strncmp(str, "now", 3) == 0) {
+ base = now;
+ str += 3;
+ }
+
+ if (str[0] == '\0') {
+ return ((isc_stdtime_t)base);
+ } else if (str[0] == '+') {
+ offset = strtol(str + 1, &endp, 0);
+ offset = time_units((isc_stdtime_t)offset, endp, orig);
+ val = base + offset;
+ } else if (str[0] == '-') {
+ offset = strtol(str + 1, &endp, 0);
+ offset = time_units((isc_stdtime_t)offset, endp, orig);
+ val = base - offset;
+ } else {
+ fatal("time value %s is invalid", orig);
+ }
+
+ return ((isc_stdtime_t)val);
+}
+
+dns_rdataclass_t
+strtoclass(const char *str) {
+ isc_textregion_t r;
+ dns_rdataclass_t rdclass;
+ isc_result_t result;
+
+ if (str == NULL) {
+ return (dns_rdataclass_in);
+ }
+ DE_CONST(str, r.base);
+ r.length = strlen(str);
+ result = dns_rdataclass_fromtext(&rdclass, &r);
+ if (result != ISC_R_SUCCESS) {
+ fatal("unknown class %s", str);
+ }
+ return (rdclass);
+}
+
+unsigned int
+strtodsdigest(const char *str) {
+ isc_textregion_t r;
+ dns_dsdigest_t alg;
+ isc_result_t result;
+
+ DE_CONST(str, r.base);
+ r.length = strlen(str);
+ result = dns_dsdigest_fromtext(&alg, &r);
+ if (result != ISC_R_SUCCESS) {
+ fatal("unknown DS algorithm %s", str);
+ }
+ return (alg);
+}
+
+static int
+cmp_dtype(const void *ap, const void *bp) {
+ int a = *(const uint8_t *)ap;
+ int b = *(const uint8_t *)bp;
+ return (a - b);
+}
+
+void
+add_dtype(unsigned int dt) {
+ unsigned i, n;
+
+ /* ensure there is space for a zero terminator */
+ n = sizeof(dtype) / sizeof(dtype[0]) - 1;
+ for (i = 0; i < n; i++) {
+ if (dtype[i] == dt) {
+ return;
+ }
+ if (dtype[i] == 0) {
+ dtype[i] = dt;
+ qsort(dtype, i + 1, 1, cmp_dtype);
+ return;
+ }
+ }
+ fatal("too many -a digest type arguments");
+}
+
+isc_result_t
+try_dir(const char *dirname) {
+ isc_result_t result;
+ isc_dir_t d;
+
+ isc_dir_init(&d);
+ result = isc_dir_open(&d, dirname);
+ if (result == ISC_R_SUCCESS) {
+ isc_dir_close(&d);
+ }
+ return (result);
+}
+
+/*
+ * Check private key version compatibility.
+ */
+void
+check_keyversion(dst_key_t *key, char *keystr) {
+ int major, minor;
+ dst_key_getprivateformat(key, &major, &minor);
+ INSIST(major <= DST_MAJOR_VERSION); /* invalid private key */
+
+ if (major < DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) {
+ fatal("Key %s has incompatible format version %d.%d, "
+ "use -f to force upgrade to new version.",
+ keystr, major, minor);
+ }
+ if (minor > DST_MINOR_VERSION) {
+ fatal("Key %s has incompatible format version %d.%d, "
+ "use -f to force downgrade to current version.",
+ keystr, major, minor);
+ }
+}
+
+void
+set_keyversion(dst_key_t *key) {
+ int major, minor;
+ dst_key_getprivateformat(key, &major, &minor);
+ INSIST(major <= DST_MAJOR_VERSION);
+
+ if (major != DST_MAJOR_VERSION || minor != DST_MINOR_VERSION) {
+ dst_key_setprivateformat(key, DST_MAJOR_VERSION,
+ DST_MINOR_VERSION);
+ }
+
+ /*
+ * If the key is from a version older than 1.3, set
+ * set the creation date
+ */
+ if (major < 1 || (major == 1 && minor <= 2)) {
+ isc_stdtime_t now;
+ isc_stdtime_get(&now);
+ dst_key_settime(key, DST_TIME_CREATED, now);
+ }
+}
+
+bool
+key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir,
+ isc_mem_t *mctx, bool *exact) {
+ isc_result_t result;
+ bool conflict = false;
+ dns_dnsseckeylist_t matchkeys;
+ dns_dnsseckey_t *key = NULL;
+ uint16_t id, oldid;
+ uint32_t rid, roldid;
+ dns_secalg_t alg;
+ char filename[NAME_MAX];
+ isc_buffer_t fileb;
+ isc_stdtime_t now;
+
+ if (exact != NULL) {
+ *exact = false;
+ }
+
+ id = dst_key_id(dstkey);
+ rid = dst_key_rid(dstkey);
+ alg = dst_key_alg(dstkey);
+
+ /*
+ * For Diffie Hellman just check if there is a direct collision as
+ * they can't be revoked. Additionally dns_dnssec_findmatchingkeys
+ * only handles DNSKEY which is not used for HMAC.
+ */
+ if (alg == DST_ALG_DH) {
+ isc_buffer_init(&fileb, filename, sizeof(filename));
+ result = dst_key_buildfilename(dstkey, DST_TYPE_PRIVATE, dir,
+ &fileb);
+ if (result != ISC_R_SUCCESS) {
+ return (true);
+ }
+ return (isc_file_exists(filename));
+ }
+
+ ISC_LIST_INIT(matchkeys);
+ isc_stdtime_get(&now);
+ result = dns_dnssec_findmatchingkeys(name, dir, now, mctx, &matchkeys);
+ if (result == ISC_R_NOTFOUND) {
+ return (false);
+ }
+
+ while (!ISC_LIST_EMPTY(matchkeys) && !conflict) {
+ key = ISC_LIST_HEAD(matchkeys);
+ if (dst_key_alg(key->key) != alg) {
+ goto next;
+ }
+
+ oldid = dst_key_id(key->key);
+ roldid = dst_key_rid(key->key);
+
+ if (oldid == rid || roldid == id || id == oldid) {
+ conflict = true;
+ if (id != oldid) {
+ if (verbose > 1) {
+ fprintf(stderr,
+ "Key ID %d could "
+ "collide with %d\n",
+ id, oldid);
+ }
+ } else {
+ if (exact != NULL) {
+ *exact = true;
+ }
+ if (verbose > 1) {
+ fprintf(stderr, "Key ID %d exists\n",
+ id);
+ }
+ }
+ }
+
+ next:
+ ISC_LIST_UNLINK(matchkeys, key, link);
+ dns_dnsseckey_destroy(mctx, &key);
+ }
+
+ /* Finish freeing the list */
+ while (!ISC_LIST_EMPTY(matchkeys)) {
+ key = ISC_LIST_HEAD(matchkeys);
+ ISC_LIST_UNLINK(matchkeys, key, link);
+ dns_dnsseckey_destroy(mctx, &key);
+ }
+
+ return (conflict);
+}
+
+bool
+isoptarg(const char *arg, char **argv, void (*usage)(void)) {
+ if (!strcasecmp(isc_commandline_argument, arg)) {
+ if (argv[isc_commandline_index] == NULL) {
+ fprintf(stderr, "%s: missing argument -%c %s\n",
+ program, isc_commandline_option,
+ isc_commandline_argument);
+ usage();
+ }
+ isc_commandline_argument = argv[isc_commandline_index];
+ /* skip to next argument */
+ isc_commandline_index++;
+ return (true);
+ }
+ return (false);
+}
+
+#ifdef _WIN32
+void
+InitSockets(void) {
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ wVersionRequested = MAKEWORD(2, 0);
+
+ err = WSAStartup(wVersionRequested, &wsaData);
+ if (err != 0) {
+ fprintf(stderr, "WSAStartup() failed: %d\n", err);
+ exit(1);
+ }
+}
+
+void
+DestroySockets(void) {
+ WSACleanup();
+}
+#endif /* ifdef _WIN32 */
diff --git a/bin/dnssec/dnssectool.h b/bin/dnssec/dnssectool.h
new file mode 100644
index 0000000..4ce8490
--- /dev/null
+++ b/bin/dnssec/dnssectool.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef DNSSECTOOL_H
+#define DNSSECTOOL_H 1
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/log.h>
+#include <isc/platform.h>
+#include <isc/stdtime.h>
+
+#include <dns/rdatastruct.h>
+
+#include <dst/dst.h>
+
+/*! verbosity: set by -v and -q option in each program, defined in dnssectool.c
+ */
+extern int verbose;
+extern bool quiet;
+
+/*! program name, statically initialized in each program */
+extern const char *program;
+
+/*!
+ * List of DS digest types used by dnssec-cds and dnssec-dsfromkey,
+ * defined in dnssectool.c. Filled in by add_dtype() from -a
+ * arguments, sorted (so that DS records are in a canonical order) and
+ * terminated by a zero. The size of the array is an arbitrary limit
+ * which should be greater than the number of known digest types.
+ */
+extern uint8_t dtype[8];
+
+typedef void(fatalcallback_t)(void);
+
+#ifndef CPPCHECK
+ISC_PLATFORM_NORETURN_PRE void
+fatal(const char *format, ...)
+ ISC_FORMAT_PRINTF(1, 2) ISC_PLATFORM_NORETURN_POST;
+#else /* CPPCHECK */
+#define fatal(...) exit(1)
+#endif
+
+void
+setfatalcallback(fatalcallback_t *callback);
+
+void
+check_result(isc_result_t result, const char *message);
+
+void
+vbprintf(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);
+
+ISC_PLATFORM_NORETURN_PRE void
+version(const char *program) ISC_PLATFORM_NORETURN_POST;
+
+void
+sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size);
+#define SIG_FORMATSIZE \
+ (DNS_NAME_FORMATSIZE + DNS_SECALG_FORMATSIZE + sizeof("65535"))
+
+void
+setup_logging(isc_mem_t *mctx, isc_log_t **logp);
+
+void
+cleanup_logging(isc_log_t **logp);
+
+dns_ttl_t
+strtottl(const char *str);
+
+dst_key_state_t
+strtokeystate(const char *str);
+
+isc_stdtime_t
+strtotime(const char *str, int64_t now, int64_t base, bool *setp);
+
+dns_rdataclass_t
+strtoclass(const char *str);
+
+unsigned int
+strtodsdigest(const char *str);
+
+void
+add_dtype(unsigned int dt);
+
+isc_result_t
+try_dir(const char *dirname);
+
+void
+check_keyversion(dst_key_t *key, char *keystr);
+
+void
+set_keyversion(dst_key_t *key);
+
+bool
+key_collision(dst_key_t *key, dns_name_t *name, const char *dir,
+ isc_mem_t *mctx, bool *exact);
+
+bool
+isoptarg(const char *arg, char **argv, void (*usage)(void));
+
+#ifdef _WIN32
+void
+InitSockets(void);
+void
+DestroySockets(void);
+#endif /* ifdef _WIN32 */
+
+#endif /* DNSSEC_DNSSECTOOL_H */
diff --git a/bin/dnssec/win32/cds.vcxproj.filters.in b/bin/dnssec/win32/cds.vcxproj.filters.in
new file mode 100644
index 0000000..b6893db
--- /dev/null
+++ b/bin/dnssec/win32/cds.vcxproj.filters.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-cds.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
diff --git a/bin/dnssec/win32/cds.vcxproj.in b/bin/dnssec/win32/cds.vcxproj.in
new file mode 100644
index 0000000..0a543eb
--- /dev/null
+++ b/bin/dnssec/win32/cds.vcxproj.in
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{0EB1727E-2BBD-47A6-AD12-418F9DEB0531}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>cds</RootNamespace>
+ @WINDOWS_TARGET_PLATFORM_VERSION@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>false</TreatWarningAsError>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level1</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-cds.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/dnssec/win32/cds.vcxproj.user b/bin/dnssec/win32/cds.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/bin/dnssec/win32/cds.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/dnssectool.vcxproj.filters.in b/bin/dnssec/win32/dnssectool.vcxproj.filters.in
new file mode 100644
index 0000000..1743f84
--- /dev/null
+++ b/bin/dnssec/win32/dnssectool.vcxproj.filters.in
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\dnssectool.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssectool.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/dnssectool.vcxproj.in b/bin/dnssec/win32/dnssectool.vcxproj.in
new file mode 100644
index 0000000..be87bca
--- /dev/null
+++ b/bin/dnssec/win32/dnssectool.vcxproj.in
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\dnssectool.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssectool.c" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{2CB7DC75-023B-4AA3-AF3A-AE5046A4EE70}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>dnssectool</RootNamespace>
+ @WINDOWS_TARGET_PLATFORM_VERSION@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <OutDir>.\$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <OutDir>.\$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>false</TreatWarningAsError>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\include;..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level1</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\include;..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <LinkTimeCodeGeneration>false</LinkTimeCodeGeneration>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/dnssec/win32/dnssectool.vcxproj.user b/bin/dnssec/win32/dnssectool.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/bin/dnssec/win32/dnssectool.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/dsfromkey.vcxproj.filters.in b/bin/dnssec/win32/dsfromkey.vcxproj.filters.in
new file mode 100644
index 0000000..7d0a58e
--- /dev/null
+++ b/bin/dnssec/win32/dsfromkey.vcxproj.filters.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-dsfromkey.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/dsfromkey.vcxproj.in b/bin/dnssec/win32/dsfromkey.vcxproj.in
new file mode 100644
index 0000000..5b2b51d
--- /dev/null
+++ b/bin/dnssec/win32/dsfromkey.vcxproj.in
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{6E6297F4-69D7-4533-85E1-BD17C30017C8}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>dsfromkey</RootNamespace>
+ @WINDOWS_TARGET_PLATFORM_VERSION@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>false</TreatWarningAsError>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+@IF PYTHON
+ <PostBuildEvent>
+ <Command>cd ..\..\python
+copy /Y dnssec-checkds.py ..\..\Build\$(Configuration)\dnssec-checkds.py
+copy /Y dnssec-coverage.py ..\..\Build\$(Configuration)\dnssec-coverage.py
+copy /Y dnssec-keymgr.py ..\..\Build\$(Configuration)\dnssec-keymgr.py
+cd isc
+@PYTHON@ policy.py parse \dev\nul > nul
+set PYTHONPATH=.
+@PYTHON@ -m parsetab
+</Command>
+ </PostBuildEvent>
+@END PYTHON
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level1</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+@IF PYTHON
+ <PostBuildEvent>
+ <Command>cd ..\..\python
+copy /Y dnssec-checkds.py ..\..\Build\$(Configuration)\dnssec-checkds.py
+copy /Y dnssec-coverage.py ..\..\Build\$(Configuration)\dnssec-coverage.py
+copy /Y dnssec-keymgr.py ..\..\Build\$(Configuration)\dnssec-keymgr.py
+cd isc
+@PYTHON@ policy.py parse \dev\nul > nul
+set PYTHONPATH=.
+@PYTHON@ -m parsetab
+</Command>
+ </PostBuildEvent>
+@END PYTHON
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-dsfromkey.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/dnssec/win32/dsfromkey.vcxproj.user b/bin/dnssec/win32/dsfromkey.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/bin/dnssec/win32/dsfromkey.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/importkey.vcxproj.filters.in b/bin/dnssec/win32/importkey.vcxproj.filters.in
new file mode 100644
index 0000000..0bced36
--- /dev/null
+++ b/bin/dnssec/win32/importkey.vcxproj.filters.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-importkey.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
diff --git a/bin/dnssec/win32/importkey.vcxproj.in b/bin/dnssec/win32/importkey.vcxproj.in
new file mode 100644
index 0000000..f1d10d0
--- /dev/null
+++ b/bin/dnssec/win32/importkey.vcxproj.in
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{AB6690A0-055E-458f-BAC5-BF38BCC5834F}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>importkey</RootNamespace>
+ @WINDOWS_TARGET_PLATFORM_VERSION@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>false</TreatWarningAsError>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level1</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-importkey.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/dnssec/win32/importkey.vcxproj.user b/bin/dnssec/win32/importkey.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/bin/dnssec/win32/importkey.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/keyfromlabel.vcxproj.filters.in b/bin/dnssec/win32/keyfromlabel.vcxproj.filters.in
new file mode 100644
index 0000000..bb54f81
--- /dev/null
+++ b/bin/dnssec/win32/keyfromlabel.vcxproj.filters.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-keyfromlabel.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/keyfromlabel.vcxproj.in b/bin/dnssec/win32/keyfromlabel.vcxproj.in
new file mode 100644
index 0000000..496e2b9
--- /dev/null
+++ b/bin/dnssec/win32/keyfromlabel.vcxproj.in
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{17455DC6-5FBB-47C3-8F44-7DB574A188D3}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>keyfromlabel</RootNamespace>
+ @WINDOWS_TARGET_PLATFORM_VERSION@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>false</TreatWarningAsError>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level1</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-keyfromlabel.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/dnssec/win32/keyfromlabel.vcxproj.user b/bin/dnssec/win32/keyfromlabel.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/bin/dnssec/win32/keyfromlabel.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/keygen.vcxproj.filters.in b/bin/dnssec/win32/keygen.vcxproj.filters.in
new file mode 100644
index 0000000..5d1fa4c
--- /dev/null
+++ b/bin/dnssec/win32/keygen.vcxproj.filters.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-keygen.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/keygen.vcxproj.in b/bin/dnssec/win32/keygen.vcxproj.in
new file mode 100644
index 0000000..6051dbf
--- /dev/null
+++ b/bin/dnssec/win32/keygen.vcxproj.in
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{0BF11E21-168C-4CAA-B784-429D126BBAE5}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>keygen</RootNamespace>
+ @WINDOWS_TARGET_PLATFORM_VERSION@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>false</TreatWarningAsError>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\isccfg\win32;..\..\..\lib\isccfg\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libisccfg.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level1</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\isccfg\win32;..\..\..\lib\isccfg\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libisccfg.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-keygen.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/dnssec/win32/keygen.vcxproj.user b/bin/dnssec/win32/keygen.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/bin/dnssec/win32/keygen.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/revoke.vcxproj.filters.in b/bin/dnssec/win32/revoke.vcxproj.filters.in
new file mode 100644
index 0000000..46e7310
--- /dev/null
+++ b/bin/dnssec/win32/revoke.vcxproj.filters.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-revoke.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/revoke.vcxproj.in b/bin/dnssec/win32/revoke.vcxproj.in
new file mode 100644
index 0000000..28e6868
--- /dev/null
+++ b/bin/dnssec/win32/revoke.vcxproj.in
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{D171F185-D3C2-4463-9CF3-ED1D0B1D6832}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>revoke</RootNamespace>
+ @WINDOWS_TARGET_PLATFORM_VERSION@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>false</TreatWarningAsError>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level1</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-revoke.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/dnssec/win32/revoke.vcxproj.user b/bin/dnssec/win32/revoke.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/bin/dnssec/win32/revoke.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/settime.vcxproj.filters.in b/bin/dnssec/win32/settime.vcxproj.filters.in
new file mode 100644
index 0000000..62b0e82
--- /dev/null
+++ b/bin/dnssec/win32/settime.vcxproj.filters.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-settime.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/settime.vcxproj.in b/bin/dnssec/win32/settime.vcxproj.in
new file mode 100644
index 0000000..f5a6152
--- /dev/null
+++ b/bin/dnssec/win32/settime.vcxproj.in
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{03FB7588-C5A7-4572-968F-14F1206BC69C}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>settime</RootNamespace>
+ @WINDOWS_TARGET_PLATFORM_VERSION@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>false</TreatWarningAsError>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level1</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-settime.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/dnssec/win32/settime.vcxproj.user b/bin/dnssec/win32/settime.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/bin/dnssec/win32/settime.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/signzone.vcxproj.filters.in b/bin/dnssec/win32/signzone.vcxproj.filters.in
new file mode 100644
index 0000000..682ae55
--- /dev/null
+++ b/bin/dnssec/win32/signzone.vcxproj.filters.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-signzone.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/signzone.vcxproj.in b/bin/dnssec/win32/signzone.vcxproj.in
new file mode 100644
index 0000000..2afa4dd
--- /dev/null
+++ b/bin/dnssec/win32/signzone.vcxproj.in
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{205ED8A9-2E4C-41CC-9385-F3613402AA90}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>signzone</RootNamespace>
+ @WINDOWS_TARGET_PLATFORM_VERSION@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>false</TreatWarningAsError>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level1</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-signzone.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/dnssec/win32/signzone.vcxproj.user b/bin/dnssec/win32/signzone.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/bin/dnssec/win32/signzone.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/verify.vcxproj.filters.in b/bin/dnssec/win32/verify.vcxproj.filters.in
new file mode 100644
index 0000000..3b194bd
--- /dev/null
+++ b/bin/dnssec/win32/verify.vcxproj.filters.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-verify.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/bin/dnssec/win32/verify.vcxproj.in b/bin/dnssec/win32/verify.vcxproj.in
new file mode 100644
index 0000000..971eccd
--- /dev/null
+++ b/bin/dnssec/win32/verify.vcxproj.in
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{FD653434-F1A8-44A9-85B2-A7468491DA6D}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>verify</RootNamespace>
+ @WINDOWS_TARGET_PLATFORM_VERSION@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ @PLATFORM_TOOLSET@
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ <IntDirSharingDetected>None</IntDirSharingDetected>
+ <TargetName>dnssec-$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>false</TreatWarningAsError>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level1</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dnssectool.lib;libisc.lib;libdns.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\dnssec-verify.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/dnssec/win32/verify.vcxproj.user b/bin/dnssec/win32/verify.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/bin/dnssec/win32/verify.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file