+
+
+
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 4c88a07ac4..108d8033c1 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 60f22f8501..b15853d768 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 8ef3e2cf38..412031ace7 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 8d3af5159d..a08db511bc 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 a17384ca1a..7c23edd1d7 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 bbd11af2cb..f69597336b 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 9fa42abf36..adee4ffb02 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 7e2e3d2e0c..8acdee26ea 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 a1c5c69177..cf9740406a 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)
+ 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;
+
+ if (strcmp(dir.entry.name + i, ".private") != 0)
continue;
- dstkey = NULL;
- result = dst_key_fromnamedfile(dir.entry.name,
- directory,
- DST_TYPE_PUBLIC |
- DST_TYPE_PRIVATE,
- mctx, &dstkey);
+ 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));
- continue;
- }
+ 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);
+ 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 (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 c310be5a11..4c174f3e0b 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 48ce9b85be..49ca424e5b 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 ca43cb3d12..6348cc17a7 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 297e809cc9..30aa1fad92 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 6abcb7ef45..931e930844 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 6ce3a0c3a8..8676d1a1b7 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 aa8f1ac4a4..1a2c84a406 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 8bea1c09e0..a24baae683 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 c3f5061b75..7eff9a07ff 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 1ce4405eb2..325a7c0042 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 fa7412cbdd..894c7ae459 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 daf495b6a3..b82ad580f3 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);