From 0c91911b4d1e872b87eaf6431ed47fe24d18dd43 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 4 Sep 2013 13:53:02 +1000 Subject: [PATCH] 3642. [func] Allow externally generated DNSKEY to be imported into the DNSKEY management framework. A new tool dnssec-importkey is used to this. [RT #34698] --- CHANGES | 4 + bin/dnssec/.gitignore | 1 + bin/dnssec/Makefile.in | 8 +- bin/dnssec/dnssec-importkey.8 | 112 +++++++++ bin/dnssec/dnssec-importkey.c | 399 +++++++++++++++++++++++++++++++++ bin/dnssec/dnssec-importkey.docbook | 185 +++++++++++++++ bin/dnssec/dnssec-importkey.html | 112 +++++++++ bin/dnssec/dnssec-settime.c | 4 +- bin/tests/system/conf.sh.in | 1 + bin/tests/system/inline/clean.sh | 3 + bin/tests/system/inline/ns1/root.db.in | 3 + bin/tests/system/inline/ns3/named.conf | 8 + bin/tests/system/inline/ns3/sign.sh | 29 +++ bin/tests/system/inline/setup.sh | 1 + bin/tests/system/inline/tests.sh | 17 +- lib/dns/dnssec.c | 96 ++++---- lib/dns/dst_api.c | 10 + lib/dns/dst_internal.h | 1 + lib/dns/dst_parse.c | 63 ++++-- lib/dns/dst_result.c | 2 +- lib/dns/include/dns/master.h | 1 + lib/dns/include/dst/dst.h | 6 + lib/dns/master.c | 4 +- lib/dns/openssldsa_link.c | 19 ++ lib/dns/opensslecdsa_link.c | 53 +++-- lib/dns/opensslgost_link.c | 27 ++- lib/dns/opensslrsa_link.c | 18 +- lib/dns/zone.c | 4 + 28 files changed, 1108 insertions(+), 83 deletions(-) create mode 100644 bin/dnssec/dnssec-importkey.8 create mode 100644 bin/dnssec/dnssec-importkey.c create mode 100644 bin/dnssec/dnssec-importkey.docbook create mode 100644 bin/dnssec/dnssec-importkey.html diff --git a/bin/dnssec/Makefile.in b/bin/dnssec/Makefile.in index 4f8bceb..ecb0fae 100644 --- a/bin/dnssec/Makefile.in +++ b/bin/dnssec/Makefile.in @@ -45,13 +45,13 @@ NOSYMLIBS = ${DNSLIBS} ${ISCNOSYMLIBS} @LIBS@ TARGETS = dnssec-keygen@EXEEXT@ dnssec-signzone@EXEEXT@ \ dnssec-keyfromlabel@EXEEXT@ dnssec-dsfromkey@EXEEXT@ \ dnssec-revoke@EXEEXT@ dnssec-settime@EXEEXT@ \ - dnssec-verify@EXEEXT@ + dnssec-verify@EXEEXT@ dnssec-importkey@EXEEXT@ OBJS = dnssectool.@O@ SRCS = dnssec-dsfromkey.c dnssec-keyfromlabel.c dnssec-keygen.c \ dnssec-revoke.c dnssec-settime.c dnssec-signzone.c \ - dnssec-verify.c dnssectool.c + dnssec-verify.c dnssec-importkey.c dnssectool.c MANPAGES = dnssec-dsfromkey.8 dnssec-keyfromlabel.8 dnssec-keygen.8 \ dnssec-revoke.8 dnssec-settime.8 dnssec-signzone.8 \ @@ -102,6 +102,10 @@ 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} + doc man:: ${MANOBJS} docclean manclean maintainer-clean:: diff --git a/bin/dnssec/dnssec-importkey.8 b/bin/dnssec/dnssec-importkey.8 new file mode 100644 index 0000000..33a3ef4 --- /dev/null +++ b/bin/dnssec/dnssec-importkey.8 @@ -0,0 +1,112 @@ +.\" Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC") +.\" +.\" 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 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. +.\" +.\" $Id$ +.\" +.hy 0 +.ad l +'\" t +.\" Title: dnssec-importkey +.\" Author: [see the "AUTHOR" section] +.\" Generator: DocBook XSL Stylesheets v1.78.1 +.\" Date: August 30, 2013 +.\" Manual: BIND9 +.\" Source: BIND9 +.\" Language: English +.\" +.TH "DNSSEC\-IMPORTKEY" "8" "August 30, 2013" "BIND9" "BIND9" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +dnssec-importkey \- Import DNSKEY records from external systems so they can be managed\&. +.SH "SYNOPSIS" +.HP 17 +\fBdnssec\-importkey\fR [\fB\-f\ \fR\fB\fIfilename\fR\fR] [\fB\-K\ \fR\fB\fIdirectory\fR\fR] [\fB\-P\ \fR\fB\fIdate/offset\fR\fR] [\fB\-D\ \fR\fB\fIdate/offset\fR\fR] [\fB\-h\fR] [\fB\-v\ \fR\fB\fIlevel\fR\fR] [\fBkeyname\fR] +.SH "DESCRIPTION" +.PP +\fBdnssec\-importkey\fR +read a DNSKEY record and generated a \&.key/\&.private key pair\&. Publication (\fB\-P\fR) and deletions (\fB\-D\fR) times can be set for the key\&. +.SH "OPTIONS" +.PP +\-f \fIfilename\fR +.RS 4 +Filename to read the key from\&. +.RE +.PP +\-K \fIdirectory\fR +.RS 4 +Sets the directory in which the key files are to reside\&. +.RE +.PP +\-L \fIttl\fR +.RS 4 +Sets the default TTL to use for this key when it is converted into a DNSKEY RR\&. If the key is imported into a zone, this is the TTL that will be used for it, unless there was already a DNSKEY RRset in place, in which case the existing TTL would take precedence\&. importkey the default TTL to +0 +or +none +removes it\&. +.RE +.PP +\-h +.RS 4 +Emit usage message and exit\&. +.RE +.PP +\-v \fIlevel\fR +.RS 4 +Sets the debugging level\&. +.RE +.SH "TIMING OPTIONS" +.PP +Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS\&. If the argument begins with a \*(Aq+\*(Aq or \*(Aq\-\*(Aq, it is interpreted as an offset from the present time\&. For convenience, if such an offset is followed by one of the suffixes \*(Aqy\*(Aq, \*(Aqmo\*(Aq, \*(Aqw\*(Aq, \*(Aqd\*(Aq, \*(Aqh\*(Aq, or \*(Aqmi\*(Aq, 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 unset a date, use \*(Aqnone\*(Aq\&. +.PP +\-P \fIdate/offset\fR +.RS 4 +Sets the date on which a key is to be published to the zone\&. After that date, the key will be included in the zone but will not be used to sign it\&. +.RE +.PP +\-D \fIdate/offset\fR +.RS 4 +Sets the date on which the key is to be deleted\&. After that date, the key will no longer be included in the zone\&. (It may remain in the key repository, however\&.) +.RE +.SH "SEE ALSO" +.PP +\fBdnssec-keygen\fR(8), +\fBdnssec-signzone\fR(8), +BIND 9 Administrator Reference Manual, +RFC 5011\&. +.SH "AUTHOR" +.PP +Internet Systems Consortium +.SH "COPYRIGHT" +.br +Copyright \(co 2013 Internet Systems Consortium, Inc. ("ISC") +.br diff --git a/bin/dnssec/dnssec-importkey.c b/bin/dnssec/dnssec-importkey.c new file mode 100644 index 0000000..3491828 --- /dev/null +++ b/bin/dnssec/dnssec-importkey.c @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2008-2012 Internet Systems Consortium, Inc. ("ISC") + * + * 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 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dnssectool.h" + +#ifndef PATH_MAX +#define PATH_MAX 1024 /* AIX, WIN32, and others don't define this. */ +#endif + +const char *program = "dnssec-importkey"; +int verbose; + +static dns_rdataclass_t rdclass; +static dns_fixedname_t fixed; +static dns_name_t *name = NULL; +static isc_mem_t *mctx = NULL; +static isc_boolean_t setpub = ISC_FALSE, setdel = ISC_FALSE; +static isc_stdtime_t pub = 0, del = 0; + +static isc_result_t +initname(char *setname) { + isc_result_t result; + isc_buffer_t buf; + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&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.add, &callbacks.add_private); + 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.add_private); + 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_load3(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, ISC_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); + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + result = dns_name_copy(dst_key_name(key), name, NULL); + if (result != ISC_R_SUCCESS) + fatal("can't copy 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 newname[1024]; + isc_buffer_t buf; + dst_key_t *key = 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)); + } + + dst_key_setexternal(key, ISC_TRUE); + if (setpub) + dst_key_settime(key, DST_TIME_PUBLISH, pub); + if (setdel) + dst_key_settime(key, DST_TIME_DELETE, del); + + isc_buffer_init(&buf, newname, sizeof(newname)); + 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)); + } + + 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", newname); + + 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", newname); + 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] file\n\n", program); + fprintf(stderr, "Version: %s\n", VERSION); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -v \n"); + fprintf(stderr, " -K : directory in which to store " + "the keyset files\n"); + fprintf(stderr, " -f file: read keyset from zone file\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; + isc_entropy_t *ectx = 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(); + + result = isc_mem_create(0, 0, &mctx); + if (result != ISC_R_SUCCESS) + fatal("out of memory"); + + dns_result_register(); + + isc_commandline_errprint = ISC_FALSE; + + while ((ch = isc_commandline_parse(argc, argv, "D:f:hK:P:v:")) != -1) { + switch (ch) { + case 'D': + if (setdel) + fatal("-D specified more than once"); + + setdel = ISC_TRUE; + del = strtotime(isc_commandline_argument, now, now); + break; + case 'K': + dir = isc_commandline_argument; + if (strlen(dir) == 0U) + fatal("directory must be non-empty string"); + break; + case 'P': + if (setpub) + fatal("-P specified more than once"); + + setpub = ISC_TRUE; + pub = strtotime(isc_commandline_argument, now, now); + 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': + usage(); + + 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"); + + if (ectx == NULL) + setup_entropy(mctx, NULL, &ectx); + result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE); + if (result != ISC_R_SUCCESS) + fatal("could not initialize hash"); + result = dst_lib_init(mctx, ectx, + ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY); + if (result != ISC_R_SUCCESS) + fatal("could not initialize dst: %s", + isc_result_totext(result)); + isc_entropy_stopcallbacksources(ectx); + + setup_logging(verbose, mctx, &log); + + dns_rdataset_init(&rdataset); + + if (filename != NULL) { + if (argc < isc_commandline_index + 1 && filename != NULL) { + /* using zone name as the zone file 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(); + isc_hash_destroy(); + cleanup_entropy(&ectx); + dns_name_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.docbook b/bin/dnssec/dnssec-importkey.docbook new file mode 100644 index 0000000..b8d160c --- /dev/null +++ b/bin/dnssec/dnssec-importkey.docbook @@ -0,0 +1,185 @@ +]> + + + + + + August 30, 2013 + + + + dnssec-importkey + 8 + BIND9 + + + + dnssec-importkey + Import DNSKEY records from external systems so they can be managed. + + + + + 2013 + Internet Systems Consortium, Inc. ("ISC") + + + + + + dnssec-importkey + + + + + + + + + + + + DESCRIPTION + dnssec-importkey + read a DNSKEY record and generated a .key/.private key pair. + Publication () and deletions () + times can be set for the key. + + + + + OPTIONS + + + + -f filename + + + Filename to read the key from. + + + + + + -K directory + + + Sets the directory in which the key files are to reside. + + + + + + -L ttl + + + Sets the default TTL to use for this key when it is converted + into a DNSKEY RR. If the key is imported into a zone, + this is the TTL that will be used for it, unless there was + already a DNSKEY RRset in place, in which case the existing TTL + would take precedence. importkey the default TTL to + 0 or none removes it. + + + + + + -h + + + Emit usage message and exit. + + + + + + -v level + + + 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 unset a date, use 'none'. + + + + + -P date/offset + + + Sets the date on which a key is to be published to the zone. + After that date, the key will be included in the zone but will + not be used to sign it. + + + + + + -D date/offset + + + Sets the date on which the key is to be deleted. After that + date, the key will no longer be included in the zone. (It + may remain in the key repository, however.) + + + + + + + + + SEE ALSO + + dnssec-keygen8 + , + + dnssec-signzone8 + , + BIND 9 Administrator Reference Manual, + RFC 5011. + + + + + AUTHOR + Internet Systems Consortium + + + + diff --git a/bin/dnssec/dnssec-importkey.html b/bin/dnssec/dnssec-importkey.html new file mode 100644 index 0000000..f74be50 --- /dev/null +++ b/bin/dnssec/dnssec-importkey.html @@ -0,0 +1,112 @@ + + + + + +dnssec-importkey + + +
+
+
+

Name

+

dnssec-importkey — Import DNSKEY records from external systems so they can be managed.

+
+
+

Synopsis

+

dnssec-importkey [-f filename] [-K directory] [-P date/offset] [-D date/offset] [-h] [-v level] [keyname]

+
+
+

DESCRIPTION

+

dnssec-importkey + read a DNSKEY record and generated a .key/.private key pair. + Publication (-P) and deletions (-D) + times can be set for the key. +

+
+
+

OPTIONS

+
+
-f filename
+

+ Filename to read the key from. +

+
-K directory
+

+ Sets the directory in which the key files are to reside. +

+
-L ttl
+

+ Sets the default TTL to use for this key when it is converted + into a DNSKEY RR. If the key is imported into a zone, + this is the TTL that will be used for it, unless there was + already a DNSKEY RRset in place, in which case the existing TTL + would take precedence. importkey the default TTL to + 0 or none removes it. +

+
-h
+

+ Emit usage message and exit. +

+
-v level
+

+ 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 unset a date, use 'none'. +

+
+
-P date/offset
+

+ Sets the date on which a key is to be published to the zone. + After that date, the key will be included in the zone but will + not be used to sign it. +

+
-D date/offset
+

+ Sets the date on which the key is to be deleted. After that + date, the key will no longer be included in the zone. (It + may remain in the key repository, however.) +

+
+
+
+

SEE ALSO

+

dnssec-keygen(8), + dnssec-signzone(8), + BIND 9 Administrator Reference Manual, + RFC 5011. +

+
+
+

AUTHOR

+

Internet Systems Consortium +

+
+
+ diff --git a/bin/dnssec/dnssec-settime.c b/bin/dnssec/dnssec-settime.c index 4c88a07..108d803 100644 --- a/bin/dnssec/dnssec-settime.c +++ b/bin/dnssec/dnssec-settime.c @@ -370,7 +370,7 @@ main(int argc, char **argv) { if (result != ISC_R_SUCCESS) fatal("Invalid keyfile %s: %s", filename, isc_result_totext(result)); - if (!dst_key_isprivate(prevkey)) + if (!dst_key_isprivate(prevkey) && !dst_key_isexternal(prevkey)) fatal("%s is not a private key", filename); name = dst_key_name(prevkey); @@ -462,7 +462,7 @@ main(int argc, char **argv) { fatal("Invalid keyfile %s: %s", filename, isc_result_totext(result)); - if (!dst_key_isprivate(key)) + if (!dst_key_isprivate(key) && !dst_key_isexternal(key)) fatal("%s is not a private key", filename); dst_key_format(key, keystr, sizeof(keystr)); diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index 60f22f8..b15853d 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -43,6 +43,7 @@ SIGNER=$TOP/bin/dnssec/dnssec-signzone REVOKE=$TOP/bin/dnssec/dnssec-revoke SETTIME=$TOP/bin/dnssec/dnssec-settime DSFROMKEY=$TOP/bin/dnssec/dnssec-dsfromkey +IMPORTKEY=$TOP/bin/dnssec/dnssec-importkey CHECKDS=$TOP/bin/python/dnssec-checkds COVERAGE=$TOP/bin/python/dnssec-coverage CHECKZONE=$TOP/bin/check/named-checkzone diff --git a/bin/tests/system/inline/clean.sh b/bin/tests/system/inline/clean.sh index 8ef3e2c..412031a 100644 --- a/bin/tests/system/inline/clean.sh +++ b/bin/tests/system/inline/clean.sh @@ -60,6 +60,9 @@ rm -f ns3/retransfer.bk rm -f ns3/retransfer.bk.jnl rm -f ns3/retransfer.bk.signed rm -f ns3/retransfer.bk.signed.jnl +rm -f ns3/externalkey.db +rm -f ns3/externalkey.db.signed +rm -f ns3/externalkey.db.signed.jnl rm -f ns4/K* rm -f ns4/noixfr.db rm -f ns4/noixfr.db.jnl diff --git a/bin/tests/system/inline/ns1/root.db.in b/bin/tests/system/inline/ns1/root.db.in index 8d3af51..a08db51 100644 --- a/bin/tests/system/inline/ns1/root.db.in +++ b/bin/tests/system/inline/ns1/root.db.in @@ -50,3 +50,6 @@ ns3.retransfer. A 10.53.0.3 nsec3. NS ns3.nsec3. ns3.nsec3. A 10.53.0.3 + +externalkey. NS ns3.externalkey. +ns3.externalkey. A 10.53.0.3 diff --git a/bin/tests/system/inline/ns3/named.conf b/bin/tests/system/inline/ns3/named.conf index a17384c..7c23edd 100644 --- a/bin/tests/system/inline/ns3/named.conf +++ b/bin/tests/system/inline/ns3/named.conf @@ -103,3 +103,11 @@ zone "nsec3" { allow-update { any; }; file "nsec3.db"; }; + +zone "externalkey" { + type master; + inline-signing yes; + auto-dnssec maintain; + allow-update { any; }; + file "externalkey.db"; +}; diff --git a/bin/tests/system/inline/ns3/sign.sh b/bin/tests/system/inline/ns3/sign.sh index bbd11af..f695973 100644 --- a/bin/tests/system/inline/ns3/sign.sh +++ b/bin/tests/system/inline/ns3/sign.sh @@ -92,3 +92,32 @@ do keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 768 -n zone $zone` keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -f KSK $zone` done + +zone=externalkey +rm -f K${zone}.+*+*.key +rm -f K${zone}.+*+*.private + +for alg in ECDSAP256SHA256 NSEC3RSASHA1 DSA ECCGOST +do + +if test $alg = ECCGOST +then + sh ../../gost/prereq.sh 2> /dev/null || continue +fi + +k1=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone -f KSK $zone` +k2=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone $zone` +k3=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone $zone` +k4=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone $zone` +keyname=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone $zone` +keyname=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone -f KSK $zone` +$DSFROMKEY -T 1200 $keyname >> ../ns1/root.db +rm -f ${k3}.* ${k4}.* + +# +# Convert k1 and k2 in to External Keys. +rm -f $k1.private +$IMPORTKEY -P now -D now+3600 -f $k1.key $zone +rm -f $k2.private +$IMPORTKEY -f $k2.key $zone +done diff --git a/bin/tests/system/inline/setup.sh b/bin/tests/system/inline/setup.sh index 9fa42ab..adee4ff 100644 --- a/bin/tests/system/inline/setup.sh +++ b/bin/tests/system/inline/setup.sh @@ -29,6 +29,7 @@ cp ns3/master.db.in ns3/dynamic.db cp ns3/master.db.in ns3/updated.db cp ns3/master.db.in ns3/expired.db cp ns3/master.db.in ns3/nsec3.db +cp ns3/master.db.in ns3/externalkey.db touch ns4/trusted.conf cp ns4/noixfr.db.in ns4/noixfr.db diff --git a/bin/tests/system/inline/tests.sh b/bin/tests/system/inline/tests.sh index 7e2e3d2..8acdee2 100644 --- a/bin/tests/system/inline/tests.sh +++ b/bin/tests/system/inline/tests.sh @@ -809,7 +809,22 @@ $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 addzone test-$zone \ $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 delzone test-$zone done -if [ $ret != 0 ]; then echo "I:failed"; fi +n=`expr $n + 1` +echo "I:testing adding external keys to a inline zone ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.3 -p 5300 dnskey externalkey > dig.out.ns3.test$n +for alg in 3 7 12 13 +do +if test $alg = 12 +then + sh ../gost/prereq.sh 2>/dev/null || continue; +fi + +dnskeys=`grep "IN.DNSKEY.25[67] [0-9]* $alg " dig.out.ns3.test$n | wc -l` +rrsigs=`grep "RRSIG.DNSKEY $alg " dig.out.ns3.test$n | wc -l` +test ${dnskeys:-0} -eq 3 || { echo "I: failed $alg (dnskeys ${dnskeys:-0})"; ret=1; } +test ${rrsigs:-0} -eq 2 || { echo "I: failed $alg (rrsigs ${rrsigs:-0})"; ret=1; } +done status=`expr $status + $ret` exit $status diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index a1c5c69..cf97404 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -684,6 +684,7 @@ dns_dnssec_findzonekeys2(dns_db_t *db, dns_dbversion_t *ver, isc_stdtime_get(&now); *nkeys = 0; + memset(keys, 0, sizeof(*keys) * maxkeys); dns_rdataset_init(&rdataset); RETERR(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0, &rdataset, NULL)); @@ -1312,9 +1313,9 @@ dns_dnssec_findmatchingkeys(dns_name_t *origin, const char *directory, isc_dir_t dir; dns_dnsseckey_t *key = NULL; dst_key_t *dstkey = NULL; - char namebuf[DNS_NAME_FORMATSIZE], *p; + char namebuf[DNS_NAME_FORMATSIZE]; isc_buffer_t b; - unsigned int len; + unsigned int len, i; isc_stdtime_t now; REQUIRE(keylist != NULL); @@ -1334,49 +1335,62 @@ dns_dnssec_findmatchingkeys(dns_name_t *origin, const char *directory, isc_stdtime_get(&now); while (isc_dir_read(&dir) == ISC_R_SUCCESS) { - if (dir.entry.name[0] == 'K' && - dir.entry.length > len + 1 && - dir.entry.name[len + 1] == '+' && - strncasecmp(dir.entry.name + 1, namebuf, len) == 0) { - p = strrchr(dir.entry.name, '.'); - if (p != NULL && strcmp(p, ".private") != 0) - continue; + if (dir.entry.name[0] != 'K' || + dir.entry.length < len + 1 || + dir.entry.name[len + 1] != '+' || + strncasecmp(dir.entry.name + 1, namebuf, len) != 0) + continue; + + for (i = len + 1 + 1; i < dir.entry.length ; i++) + if (dir.entry.name[i] < '0' || dir.entry.name[i] > '9') + break; + + if (i == len + 1 + 1 || i >= dir.entry.length || + dir.entry.name[i] != '+') + continue; + + for (i++ ; i < dir.entry.length ; i++) + if (dir.entry.name[i] < '0' || dir.entry.name[i] > '9') + break; - dstkey = NULL; - result = dst_key_fromnamedfile(dir.entry.name, - directory, - DST_TYPE_PUBLIC | - DST_TYPE_PRIVATE, - mctx, &dstkey); - - if (result != ISC_R_SUCCESS) { - isc_log_write(dns_lctx, - DNS_LOGCATEGORY_GENERAL, - DNS_LOGMODULE_DNSSEC, - ISC_LOG_WARNING, - "dns_dnssec_findmatchingkeys: " - "error reading key file %s: %s", - dir.entry.name, - isc_result_totext(result)); + if (strcmp(dir.entry.name + i, ".private") != 0) continue; - } - RETERR(dns_dnsseckey_create(mctx, &dstkey, &key)); - key->source = dns_keysource_repository; - get_hints(key, now); + dstkey = NULL; + result = dst_key_fromnamedfile(dir.entry.name, + directory, + DST_TYPE_PUBLIC | + DST_TYPE_PRIVATE, + mctx, &dstkey); - if (key->legacy) { - dns_dnsseckey_destroy(mctx, &key); - } else { - ISC_LIST_APPEND(list, key, link); - key = NULL; - } + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_DNSSEC, + ISC_LOG_WARNING, + "dns_dnssec_findmatchingkeys: " + "error reading key file %s: %s", + dir.entry.name, + isc_result_totext(result)); + continue; + } + + RETERR(dns_dnsseckey_create(mctx, &dstkey, &key)); + key->source = dns_keysource_repository; + get_hints(key, now); + + if (key->legacy) { + dns_dnsseckey_destroy(mctx, &key); + } else { + ISC_LIST_APPEND(list, key, link); + key = NULL; } } - if (!ISC_LIST_EMPTY(list)) + if (!ISC_LIST_EMPTY(list)) { + result = ISC_R_SUCCESS; ISC_LIST_APPENDLIST(*keylist, list, link); - else + } else result = ISC_R_NOTFOUND; failure: @@ -1794,7 +1808,13 @@ dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys, for (key2 = ISC_LIST_HEAD(*keys); key2 != NULL; key2 = ISC_LIST_NEXT(key2, link)) { - if (dst_key_pubcompare(key1->key, key2->key, + int f1 = dst_key_flags(key1->key); + int f2 = dst_key_flags(key2->key); + int nr1 = f1 & ~DNS_KEYFLAG_REVOKE; + int nr2 = f2 & ~DNS_KEYFLAG_REVOKE; + if (nr1 == nr2 && + dst_key_alg(key1->key) == dst_key_alg(key2->key) && + dst_key_pubcompare(key1->key, key2->key, ISC_TRUE)) { int r1, r2; r1 = dst_key_flags(key1->key) & diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index c310be5..4c174f3 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -448,6 +448,16 @@ dst_key_tofile(const dst_key_t *key, int type, const char *directory) { return (ISC_R_SUCCESS); } +void +dst_key_setexternal(dst_key_t *key, isc_boolean_t value) { + key->external = value; +} + +isc_boolean_t +dst_key_isexternal(dst_key_t *key) { + return (key->external); +} + isc_result_t dst_key_fromfile(dns_name_t *name, dns_keytag_t id, unsigned int alg, int type, const char *directory, diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h index 48ce9b8..49ca424 100644 --- a/lib/dns/dst_internal.h +++ b/lib/dns/dst_internal.h @@ -128,6 +128,7 @@ struct dst_key { isc_boolean_t numset[DST_MAX_NUMERIC + 1]; /*%< data set? */ isc_boolean_t inactive; /*%< private key not present as it is inactive */ + isc_boolean_t external; /*%< external key */ int fmt_major; /*%< private key format, major version */ int fmt_minor; /*%< private key format, minor version */ diff --git a/lib/dns/dst_parse.c b/lib/dns/dst_parse.c index ca43cb3..6348cc1 100644 --- a/lib/dns/dst_parse.c +++ b/lib/dns/dst_parse.c @@ -178,14 +178,18 @@ find_numericdata(const char *s) { } static int -check_rsa(const dst_private_t *priv) { +check_rsa(const dst_private_t *priv, isc_boolean_t external) { int i, j; isc_boolean_t have[RSA_NTAGS]; isc_boolean_t ok; unsigned int mask; + if (external) + return ((priv->nelements == 0) ? 0 : -1); + for (i = 0; i < RSA_NTAGS; i++) have[i] = ISC_FALSE; + for (j = 0; j < priv->nelements; j++) { for (i = 0; i < RSA_NTAGS; i++) if (priv->elements[j].tag == TAG(DST_ALG_RSAMD5, i)) @@ -231,10 +235,15 @@ check_dh(const dst_private_t *priv) { } static int -check_dsa(const dst_private_t *priv) { +check_dsa(const dst_private_t *priv, isc_boolean_t external) { int i, j; + + if (external) + return ((priv->nelements == 0)? 0 : -1); + if (priv->nelements != DSA_NTAGS) return (-1); + for (i = 0; i < DSA_NTAGS; i++) { for (j = 0; j < priv->nelements; j++) if (priv->elements[j].tag == TAG(DST_ALG_DSA, i)) @@ -246,7 +255,11 @@ check_dsa(const dst_private_t *priv) { } static int -check_gost(const dst_private_t *priv) { +check_gost(const dst_private_t *priv, isc_boolean_t external) { + + if (external) + return ((priv->nelements == 0)? 0 : -1); + if (priv->nelements != GOST_NTAGS) return (-1); if (priv->elements[0].tag != TAG(DST_ALG_ECCGOST, 0)) @@ -255,7 +268,11 @@ check_gost(const dst_private_t *priv) { } static int -check_ecdsa(const dst_private_t *priv) { +check_ecdsa(const dst_private_t *priv, isc_boolean_t external) { + + if (external) + return ((priv->nelements == 0) ? 0 : -1); + if (priv->nelements != ECDSA_NTAGS) return (-1); if (priv->elements[0].tag != TAG(DST_ALG_ECDSA256, 0)) @@ -309,7 +326,7 @@ check_hmac_sha(const dst_private_t *priv, unsigned int ntags, static int check_data(const dst_private_t *priv, const unsigned int alg, - isc_boolean_t old) + isc_boolean_t old, isc_boolean_t external) { /* XXXVIX this switch statement is too sparse to gen a jump table. */ switch (alg) { @@ -318,17 +335,17 @@ check_data(const dst_private_t *priv, const unsigned int alg, case DST_ALG_NSEC3RSASHA1: case DST_ALG_RSASHA256: case DST_ALG_RSASHA512: - return (check_rsa(priv)); + return (check_rsa(priv, external)); case DST_ALG_DH: return (check_dh(priv)); case DST_ALG_DSA: case DST_ALG_NSEC3DSA: - return (check_dsa(priv)); + return (check_dsa(priv, external)); case DST_ALG_ECCGOST: - return (check_gost(priv)); + return (check_gost(priv, external)); case DST_ALG_ECDSA256: case DST_ALG_ECDSA384: - return (check_ecdsa(priv)); + return (check_ecdsa(priv, external)); case DST_ALG_HMACMD5: return (check_hmac_md5(priv, old)); case DST_ALG_HMACSHA1: @@ -372,6 +389,7 @@ dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex, unsigned int opt = ISC_LEXOPT_EOL; isc_stdtime_t when; isc_result_t ret; + isc_boolean_t external = ISC_FALSE; REQUIRE(priv != NULL); @@ -467,9 +485,15 @@ dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex, if (token.type != isc_tokentype_string) { ret = DST_R_INVALIDPRIVATEKEY; + NEXTTOKEN(lex, opt, &token); goto fail; } + if (strcmp(DST_AS_STR(token), "External:") == 0) { + external = ISC_TRUE; + goto next; + } + /* Numeric metadata */ tag = find_numericdata(DST_AS_STR(token)); if (tag >= 0) { @@ -534,8 +558,14 @@ dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex, READLINE(lex, opt, &token); data = NULL; } + done: - check = check_data(priv, alg, ISC_TRUE); + if (external && priv->nelements != 0) { + ret = DST_R_INVALIDPRIVATEKEY; + goto fail; + } + + check = check_data(priv, alg, ISC_TRUE, external); if (check < 0) { ret = DST_R_INVALIDPRIVATEKEY; goto fail; @@ -544,6 +574,8 @@ dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex, goto fail; } + key->external = external; + return (ISC_R_SUCCESS); fail: @@ -573,7 +605,7 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv, REQUIRE(priv != NULL); - ret = check_data(priv, dst_key_alg(key), ISC_FALSE); + ret = check_data(priv, dst_key_alg(key), ISC_FALSE, key->external); if (ret < 0) return (DST_R_INVALIDPRIVATEKEY); else if (ret != ISC_R_SUCCESS) @@ -691,6 +723,9 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv, fprintf(fp, "%s %.*s\n", s, (int)r.length, r.base); } + if (key->external) + fprintf(fp, "External:\n"); + /* Add the metadata tags */ if (major > 1 || (major == 1 && minor >= 3)) { for (i = 0; i < NUMERIC_NTAGS; i++) { @@ -706,14 +741,14 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv, isc_buffer_init(&b, buffer, sizeof(buffer)); result = dns_time32_totext(when, &b); - if (result != ISC_R_SUCCESS) { + if (result != ISC_R_SUCCESS) { fclose(fp); return (DST_R_INVALIDPRIVATEKEY); - } + } isc_buffer_usedregion(&b, &r); - fprintf(fp, "%s %.*s\n", timetags[i], (int)r.length, + fprintf(fp, "%s %.*s\n", timetags[i], (int)r.length, r.base); } } diff --git a/lib/dns/dst_result.c b/lib/dns/dst_result.c index 297e809..30aa1fa 100644 --- a/lib/dns/dst_result.c +++ b/lib/dns/dst_result.c @@ -35,7 +35,7 @@ static const char *text[DST_R_NRESULTS] = { "illegal operation for a null key", /*%< 3 */ "public key is invalid", /*%< 4 */ "private key is invalid", /*%< 5 */ - "UNUSED6", /*%< 6 */ + "external key", /*%< 6 */ "error occurred writing key to disk", /*%< 7 */ "invalid algorithm specific parameter", /*%< 8 */ "UNUSED9", /*%< 9 */ diff --git a/lib/dns/include/dns/master.h b/lib/dns/include/dns/master.h index 6abcb7e..931e930 100644 --- a/lib/dns/include/dns/master.h +++ b/lib/dns/include/dns/master.h @@ -57,6 +57,7 @@ #define DNS_MASTER_RESIGN 0x00002000 #define DNS_MASTER_KEY 0x00004000 /*%< Loading a key zone master file. */ +#define DNS_MASTER_NOTTL 0x00008000 /*%< Don't require ttl. */ ISC_LANG_BEGINDECLS diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h index 6ce3a0c..8676d1a 100644 --- a/lib/dns/include/dst/dst.h +++ b/lib/dns/include/dst/dst.h @@ -953,6 +953,12 @@ dst_key_setinactive(dst_key_t *key, isc_boolean_t inactive); * 'key' to be valid. */ +void +dst_key_setexternal(dst_key_t *key, isc_boolean_t value); + +isc_boolean_t +dst_key_isexternal(dst_key_t *key); + ISC_LANG_ENDDECLS #endif /* DST_DST_H */ diff --git a/lib/dns/master.c b/lib/dns/master.c index aa8f1ac..1a2c84a 100644 --- a/lib/dns/master.c +++ b/lib/dns/master.c @@ -592,9 +592,9 @@ loadctx_create(dns_masterformat_t format, isc_mem_t *mctx, isc_lex_setcomments(lctx->lex, ISC_LEXCOMMENT_DNSMASTERFILE); } - lctx->ttl_known = ISC_FALSE; + lctx->ttl_known = ISC_TF((options & DNS_MASTER_NOTTL) != 0); lctx->ttl = 0; - lctx->default_ttl_known = ISC_FALSE; + lctx->default_ttl_known = lctx->ttl_known; lctx->default_ttl = 0; lctx->warn_1035 = ISC_TRUE; /* XXX Argument? */ lctx->warn_tcr = ISC_TRUE; /* XXX Argument? */ diff --git a/lib/dns/openssldsa_link.c b/lib/dns/openssldsa_link.c index 8bea1c0..a24baae 100644 --- a/lib/dns/openssldsa_link.c +++ b/lib/dns/openssldsa_link.c @@ -522,6 +522,11 @@ openssldsa_tofile(const dst_key_t *key, const char *directory) { if (key->keydata.dsa == NULL) return (DST_R_NULLKEY); + + if (key->external) { + priv.nelements = 0; + return (dst__privstruct_writefile(key, &priv, directory)); + } dsa = key->keydata.dsa; @@ -569,6 +574,7 @@ openssldsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { #define DST_RET(a) {ret = a; goto err;} UNUSED(pub); + /* read private key file */ ret = dst__privstruct_parse(key, DST_ALG_DSA, lexer, mctx, &priv); if (ret != ISC_R_SUCCESS) @@ -607,6 +613,19 @@ openssldsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { } dst__privstruct_free(&priv, mctx); + if (key->external) { + if (pub == NULL) + DST_RET(DST_R_INVALIDPRIVATEKEY); + dsa->q = pub->keydata.dsa->q; + pub->keydata.dsa->q = NULL; + dsa->p = pub->keydata.dsa->p; + pub->keydata.dsa->p = NULL; + dsa->g = pub->keydata.dsa->g; + pub->keydata.dsa->g = NULL; + dsa->pub_key = pub->keydata.dsa->pub_key; + pub->keydata.dsa->pub_key = NULL; + } + key->key_size = BN_num_bits(dsa->p); return (ISC_R_SUCCESS); diff --git a/lib/dns/opensslecdsa_link.c b/lib/dns/opensslecdsa_link.c index c3f5061..7eff9a0 100644 --- a/lib/dns/opensslecdsa_link.c +++ b/lib/dns/opensslecdsa_link.c @@ -453,6 +453,11 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) { if (key->keydata.pkey == NULL) return (DST_R_NULLKEY); + if (key->external) { + priv.nelements = 0; + return (dst__privstruct_writefile(key, &priv, directory)); + } + pkey = key->keydata.pkey; eckey = EVP_PKEY_get1_EC_KEY(pkey); if (eckey == NULL) @@ -514,8 +519,9 @@ static isc_result_t opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { dst_private_t priv; isc_result_t ret; - EVP_PKEY *pkey; - EC_KEY *eckey = NULL; + EVP_PKEY *pkey, *pubpkey; + EC_KEY *eckey = NULL, *pubeckey = NULL; + const EC_POINT *pubkey; BIGNUM *privkey; int group_nid; isc_mem_t *mctx = key->mctx; @@ -537,17 +543,36 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { if (ret != ISC_R_SUCCESS) goto err; - privkey = BN_bin2bn(priv.elements[0].data, - priv.elements[0].length, NULL); - if (privkey == NULL) - DST_RET(ISC_R_NOMEMORY); - if (!EC_KEY_set_private_key(eckey, privkey)) - DST_RET(ISC_R_NOMEMORY); - if (ecdsa_check(eckey, pub) != ISC_R_SUCCESS) - DST_RET(DST_R_INVALIDPRIVATEKEY); - dst__privstruct_free(&priv, mctx); - memset(&priv, 0, sizeof(priv)); - + if (key->external) { + /* + * Copy the public key to this new key. + */ + if (pub == NULL) + DST_RET(DST_R_INVALIDPRIVATEKEY); + pubpkey = pub->keydata.pkey; + pubeckey = EVP_PKEY_get1_EC_KEY(pubpkey); + if (pubeckey == NULL) + DST_RET(DST_R_INVALIDPRIVATEKEY); + pubkey = EC_KEY_get0_public_key(pubeckey); + if (pubkey == NULL) + DST_RET(DST_R_INVALIDPRIVATEKEY); + if (EC_KEY_set_public_key(eckey, pubkey) != 1) + DST_RET(DST_R_INVALIDPRIVATEKEY); + if (EC_KEY_check_key(eckey) != 1) + DST_RET(DST_R_INVALIDPRIVATEKEY); + } else { + privkey = BN_bin2bn(priv.elements[0].data, + priv.elements[0].length, NULL); + if (privkey == NULL) + DST_RET(ISC_R_NOMEMORY); + if (!EC_KEY_set_private_key(eckey, privkey)) + DST_RET(ISC_R_NOMEMORY); + if (ecdsa_check(eckey, pub) != ISC_R_SUCCESS) + DST_RET(DST_R_INVALIDPRIVATEKEY); + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + } + pkey = EVP_PKEY_new(); if (pkey == NULL) DST_RET (ISC_R_NOMEMORY); @@ -561,6 +586,8 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { err: if (eckey != NULL) EC_KEY_free(eckey); + if (pubeckey != NULL) + EC_KEY_free(pubeckey); dst__privstruct_free(&priv, mctx); memset(&priv, 0, sizeof(priv)); return (ret); diff --git a/lib/dns/opensslgost_link.c b/lib/dns/opensslgost_link.c index 1ce4405..325a7c0 100644 --- a/lib/dns/opensslgost_link.c +++ b/lib/dns/opensslgost_link.c @@ -296,6 +296,11 @@ opensslgost_tofile(const dst_key_t *key, const char *directory) { if (key->keydata.pkey == NULL) return (DST_R_NULLKEY); + if (key->external) { + priv.nelements = 0; + return (dst__privstruct_writefile(key, &priv, directory)); + } + pkey = key->keydata.pkey; len = i2d_PrivateKey(pkey, NULL); @@ -337,13 +342,21 @@ opensslgost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { if (ret != ISC_R_SUCCESS) return (ret); - INSIST(priv.elements[0].tag == TAG_GOST_PRIVASN1); - p = priv.elements[0].data; - if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p, - (long) priv.elements[0].length) == NULL) - DST_RET(dst__openssl_toresult2("d2i_PrivateKey", - DST_R_INVALIDPRIVATEKEY)); - key->keydata.pkey = pkey; + if (key->external) { + INSIST(priv.nelements == 0); + if (pub == NULL) + DST_RET(DST_R_INVALIDPRIVATEKEY); + key->keydata.pkey = pub->keydata.pkey; + pub->keydata.pkey = NULL; + } else { + INSIST(priv.elements[0].tag == TAG_GOST_PRIVASN1); + p = priv.elements[0].data; + if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p, + (long) priv.elements[0].length) == NULL) + DST_RET(dst__openssl_toresult2("d2i_PrivateKey", + DST_R_INVALIDPRIVATEKEY)); + key->keydata.pkey = pkey; + } key->key_size = EVP_PKEY_bits(pkey); dst__privstruct_free(&priv, mctx); memset(&priv, 0, sizeof(priv)); diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c index fa7412c..894c7ae 100644 --- a/lib/dns/opensslrsa_link.c +++ b/lib/dns/opensslrsa_link.c @@ -1048,8 +1048,14 @@ opensslrsa_tofile(const dst_key_t *key, const char *directory) { return (DST_R_NULLKEY); rsa = key->keydata.rsa; #endif - memset(bufs, 0, sizeof(bufs)); + + if (key->external) { + priv.nelements = 0; + result = dst__privstruct_writefile(key, &priv, directory); + goto fail; + } + for (i = 0; i < 8; i++) { bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(rsa->n)); if (bufs[i] == NULL) { @@ -1205,6 +1211,9 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { if (ret != ISC_R_SUCCESS) goto err; + if (key->external && priv.nelements != 0) + DST_RET(DST_R_INVALIDPRIVATEKEY); + for (i = 0; i < priv.nelements; i++) { switch (priv.elements[i].tag) { case TAG_RSA_ENGINE: @@ -1217,6 +1226,7 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { break; } } + /* * Is this key is stored in a HSM? * See if we can fetch it. @@ -1328,8 +1338,10 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { if (rsa_check(rsa, pubrsa) != ISC_R_SUCCESS) DST_RET(DST_R_INVALIDPRIVATEKEY); - if (BN_num_bits(rsa->e) > RSA_MAX_PUBEXP_BITS) - DST_RET(ISC_R_RANGE); + if (!key->external) { + if (BN_num_bits(rsa->e) > RSA_MAX_PUBEXP_BITS) + DST_RET(ISC_R_RANGE); + } key->key_size = BN_num_bits(rsa->n); if (pubrsa != NULL) RSA_free(pubrsa); diff --git a/lib/dns/zone.c b/lib/dns/zone.c index daf495b..b82ad58 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -5545,6 +5545,7 @@ find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, const char *directory = dns_zone_getkeydirectory(zone); CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); + memset(keys, 0, sizeof(*keys) * maxkeys); result = dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db), directory, mctx, maxkeys, keys, nkeys); @@ -13132,6 +13133,7 @@ sync_secure_db(dns_zone_t *seczone, dns_db_t *secdb, static void receive_secure_serial(isc_task_t *task, isc_event_t *event) { + static char me[] = "receive_secure_serial"; isc_result_t result; dns_journal_t *rjournal = NULL; isc_uint32_t start, end; @@ -13147,6 +13149,8 @@ receive_secure_serial(isc_task_t *task, isc_event_t *event) { end = ((struct secure_event *)event)->serial; isc_event_free(&event); + ENTER; + LOCK_ZONE(zone); dns_diff_init(zone->mctx, &diff); -- 1.9.3