mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-09 14:29:59 -04:00
new: usr: Support restricted key tag range when generating new keys
It is useful when multiple signers are being used to sign a zone to able to specify a restricted range of range of key tags that will be used by an operator to sign the zone. This adds controls to named (dnssec-policy), dnssec-signzone, dnssec-keyfromlabel and dnssec-ksr (dnssec-policy) to specify such ranges. Closes #4830 Merge branch '4830-support-restricted-key-tag-range-when-generating-new-keys' into 'main' Closes #4830 See merge request isc-projects/bind9!9258
This commit is contained in:
commit
d40b722d46
26 changed files with 483 additions and 21 deletions
|
|
@ -43,6 +43,8 @@
|
|||
|
||||
const char *program = "dnssec-keyfromlabel";
|
||||
|
||||
static uint16_t tag_min = 0, tag_max = 0xffff;
|
||||
|
||||
ISC_NORETURN static void
|
||||
usage(void);
|
||||
|
||||
|
|
@ -68,6 +70,7 @@ usage(void) {
|
|||
"key files\n");
|
||||
fprintf(stderr, " -k: generate a TYPE=KEY key\n");
|
||||
fprintf(stderr, " -L ttl: default key TTL\n");
|
||||
fprintf(stderr, " -M <min>:<max>: allowed Key ID range\n");
|
||||
fprintf(stderr, " -n nametype: ZONE | HOST | ENTITY | USER | "
|
||||
"OTHER\n");
|
||||
fprintf(stderr, " (DNSKEY generation defaults to ZONE\n");
|
||||
|
|
@ -156,7 +159,7 @@ main(int argc, char **argv) {
|
|||
|
||||
isc_commandline_errprint = false;
|
||||
|
||||
#define CMDLINE_FLAGS "3A:a:Cc:D:E:Ff:GhI:i:kK:L:l:n:P:p:R:S:t:v:Vy"
|
||||
#define CMDLINE_FLAGS "3A:a:Cc:D:E:Ff:GhI:i:kK:L:l:M:n:P:p:R:S:t:v:Vy"
|
||||
while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
|
||||
switch (ch) {
|
||||
case '3':
|
||||
|
|
@ -203,6 +206,20 @@ main(int argc, char **argv) {
|
|||
case 'l':
|
||||
label = isc_mem_strdup(mctx, isc_commandline_argument);
|
||||
break;
|
||||
case 'M': {
|
||||
unsigned long ul;
|
||||
tag_min = ul = strtoul(isc_commandline_argument, &endp,
|
||||
10);
|
||||
if (*endp != ':' || ul > 0xffff) {
|
||||
fatal("-M range invalid");
|
||||
}
|
||||
tag_max = ul = strtoul(endp + 1, &endp, 10);
|
||||
if (*endp != '\0' || ul > 0xffff || tag_max <= tag_min)
|
||||
{
|
||||
fatal("-M range invalid");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
nametype = isc_commandline_argument;
|
||||
break;
|
||||
|
|
@ -677,7 +694,8 @@ main(int argc, char **argv) {
|
|||
* is a risk of ID collision due to this key or another key
|
||||
* being revoked.
|
||||
*/
|
||||
if (key_collision(key, name, directory, mctx, &exact)) {
|
||||
if (key_collision(key, name, directory, mctx, tag_min, tag_max, &exact))
|
||||
{
|
||||
isc_buffer_clear(&buf);
|
||||
ret = dst_key_buildfilename(key, 0, directory, &buf);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ 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] [**-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}
|
||||
:program:`dnssec-keyfromlabel` {**-l** label} [**-3**] [**-a** algorithm] [**-A** date/offset] [**-c** class] [**-D** date/offset] [**-D** sync date/offset] [**-f** flag] [**-G**] [**-I** date/offset] [**-i** interval] [**-k**] [**-K** directory] [**-L** ttl] [**-M** tag_min:tag_max] [**-n** nametype] [**-P** date/offset] [**-P** sync date/offset] [**-p** protocol] [**-R** date/offset] [**-S** key] [**-t** type] [**-v** level] [**-V**] [**-y**] {name}
|
||||
|
||||
Description
|
||||
~~~~~~~~~~~
|
||||
|
|
@ -123,6 +123,18 @@ Options
|
|||
place, in which case the existing TTL would take precedence. Setting
|
||||
the default TTL to ``0`` or ``none`` removes it.
|
||||
|
||||
.. option:: -M tag_min:tag_max
|
||||
|
||||
This option sets the range of key tag values
|
||||
that ``dnssec-keyfromlabel`` will accept. If the key tag of the new
|
||||
key or the key tag of the revoked version of the new key is
|
||||
outside this range, the new key will be rejected. This is
|
||||
designed to be used when generating keys in a multi-signer
|
||||
scenario, where each operator is given a range of key tags to
|
||||
prevent collisions among different operators. The valid
|
||||
values for ``tag_min`` and ``tag_max`` are [0..65535]. The
|
||||
default allows all key tag values to be accepted.
|
||||
|
||||
.. option:: -p protocol
|
||||
|
||||
This option sets the protocol value for the key. The protocol is a number between
|
||||
|
|
|
|||
|
|
@ -89,6 +89,8 @@ struct keygen_ctx {
|
|||
char *type;
|
||||
int protocol;
|
||||
int size;
|
||||
uint16_t tag_min;
|
||||
uint16_t tag_max;
|
||||
int signatory;
|
||||
dns_rdataclass_t rdclass;
|
||||
int options;
|
||||
|
|
@ -177,6 +179,7 @@ usage(void) {
|
|||
fprintf(stderr, " -f <keyflag>: ZSK | KSK | REVOKE\n");
|
||||
fprintf(stderr, " -F: FIPS mode\n");
|
||||
fprintf(stderr, " -L <ttl>: default key TTL\n");
|
||||
fprintf(stderr, " -M <min>:<max>: allowed Key ID range\n");
|
||||
fprintf(stderr, " -p <protocol>: (default: 3 [dnssec])\n");
|
||||
fprintf(stderr, " -s <strength>: strength value this key signs DNS "
|
||||
"records with (default: 0)\n");
|
||||
|
|
@ -753,7 +756,9 @@ keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) {
|
|||
* 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)) {
|
||||
if (key_collision(key, name, ctx->directory, mctx, ctx->tag_min,
|
||||
ctx->tag_max, NULL))
|
||||
{
|
||||
conflict = true;
|
||||
if (null_key) {
|
||||
dst_key_free(&key);
|
||||
|
|
@ -862,8 +867,8 @@ main(int argc, char **argv) {
|
|||
/*
|
||||
* Process memory debugging argument first.
|
||||
*/
|
||||
#define CMDLINE_FLAGS \
|
||||
"3A:a:b:Cc:D:d:E:Ff:GhI:i:K:k:L:l:m:n:P:p:qR:r:S:s:" \
|
||||
#define CMDLINE_FLAGS \
|
||||
"3A:a:b:Cc:D:d:E:Ff:GhI:i:K:k:L:l:M:m:n:P:p:qR:r:S:s:" \
|
||||
"T:t:v:V"
|
||||
while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
|
||||
switch (ch) {
|
||||
|
|
@ -952,6 +957,21 @@ main(int argc, char **argv) {
|
|||
case 'n':
|
||||
ctx.nametype = isc_commandline_argument;
|
||||
break;
|
||||
case 'M': {
|
||||
unsigned long ul;
|
||||
ctx.tag_min = ul = strtoul(isc_commandline_argument,
|
||||
&endp, 10);
|
||||
if (*endp != ':' || ul > 0xffff) {
|
||||
fatal("-M range invalid");
|
||||
}
|
||||
ctx.tag_max = ul = strtoul(endp + 1, &endp, 10);
|
||||
if (*endp != '\0' || ul > 0xffff ||
|
||||
ctx.tag_max <= ctx.tag_min)
|
||||
{
|
||||
fatal("-M range invalid");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'm':
|
||||
break;
|
||||
case 'p':
|
||||
|
|
@ -1215,6 +1235,8 @@ main(int argc, char **argv) {
|
|||
ctx.ksk = true;
|
||||
ctx.zsk = true;
|
||||
ctx.lifetime = 0;
|
||||
ctx.tag_min = 0;
|
||||
ctx.tag_max = 0xffff;
|
||||
|
||||
keygen(&ctx, mctx, argc, argv);
|
||||
} else {
|
||||
|
|
@ -1263,6 +1285,8 @@ main(int argc, char **argv) {
|
|||
if (ctx.keystore != NULL) {
|
||||
check_keystore_options(&ctx);
|
||||
}
|
||||
ctx.tag_min = dns_kasp_key_tagmin(kaspkey);
|
||||
ctx.tag_max = dns_kasp_key_tagmax(kaspkey);
|
||||
if ((ctx.ksk && !ctx.wantksk && ctx.wantzsk) ||
|
||||
(ctx.zsk && !ctx.wantzsk && ctx.wantksk))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ 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] [**-f** flag] [**-F**] [**-G**] [**-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}
|
||||
:program:`dnssec-keygen` [**-3**] [**-A** date/offset] [**-a** algorithm] [**-b** keysize] [**-C**] [**-c** class] [**-D** date/offset] [**-d** bits] [**-D** sync date/offset] [**-f** flag] [**-F**] [**-G**] [**-h**] [**-I** date/offset] [**-i** interval] [**-K** directory] [**-k** policy] [**-L** ttl] [**-l** file] [**-M** tag_min:tag_max] [**-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
|
||||
~~~~~~~~~~~
|
||||
|
|
@ -150,6 +150,19 @@ Options
|
|||
This option provides a configuration file that contains a ``dnssec-policy`` statement
|
||||
(matching the policy set with :option:`-k`).
|
||||
|
||||
.. option:: -M tag_min:tag_max
|
||||
|
||||
This option sets the range of acceptable key tag values that ``dnssec-keygen``
|
||||
will produce. If the key tag of the new key or the key tag of
|
||||
the revoked version of the new key is outside this range,
|
||||
the new key will be rejected and another new key will be generated.
|
||||
This is designed to be used when generating keys in a multi-signer
|
||||
scenario, where each operator is given a range of key tags to
|
||||
prevent collisions among different operators. The valid values
|
||||
for ``tag_min`` and ``tag_max`` are [0..65535]. The default allows all
|
||||
key tag values to be produced. This option is ignored when ``-k policy``
|
||||
is specified.
|
||||
|
||||
.. option:: -n nametype
|
||||
|
||||
This option specifies the owner type of the key. The value of ``nametype`` must
|
||||
|
|
|
|||
|
|
@ -436,7 +436,10 @@ create_zsk(ksr_ctx_t *ksr, dns_kasp_key_t *kaspkey, dns_dnsseckeylist_t *keys,
|
|||
}
|
||||
|
||||
/* Do not overwrite an existing key. */
|
||||
if (key_collision(key, name, ksr->keydir, mctx, NULL)) {
|
||||
if (key_collision(key, name, ksr->keydir, mctx,
|
||||
dns_kasp_key_tagmin(kaspkey),
|
||||
dns_kasp_key_tagmax(kaspkey), NULL))
|
||||
{
|
||||
conflict = true;
|
||||
if (verbose > 0) {
|
||||
isc_buffer_clear(&buf);
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ set_keyversion(dst_key_t *key) {
|
|||
|
||||
bool
|
||||
key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir,
|
||||
isc_mem_t *mctx, bool *exact) {
|
||||
isc_mem_t *mctx, uint16_t min, uint16_t max, bool *exact) {
|
||||
isc_result_t result;
|
||||
bool conflict = false;
|
||||
dns_dnsseckeylist_t matchkeys;
|
||||
|
|
@ -468,6 +468,21 @@ key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir,
|
|||
rid = dst_key_rid(dstkey);
|
||||
alg = dst_key_alg(dstkey);
|
||||
|
||||
if (min != max) {
|
||||
if (id < min || id > max) {
|
||||
fprintf(stderr, "Key ID %d outside of [%u..%u]\n", id,
|
||||
min, max);
|
||||
return (true);
|
||||
}
|
||||
if (rid < min || rid > max) {
|
||||
fprintf(stderr,
|
||||
"Revoked Key ID %d (for tag %d) outside of "
|
||||
"[%u..%u]\n",
|
||||
rid, id, min, max);
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
ISC_LIST_INIT(matchkeys);
|
||||
result = dns_dnssec_findmatchingkeys(name, NULL, dir, NULL, now, mctx,
|
||||
&matchkeys);
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ 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);
|
||||
isc_mem_t *mctx, uint16_t min, uint16_t max, bool *exact);
|
||||
|
||||
bool
|
||||
isoptarg(const char *arg, char **argv, void (*usage)(void));
|
||||
|
|
|
|||
18
bin/tests/system/checkconf/bad-dnssec-policy-range1.conf
Normal file
18
bin/tests/system/checkconf/bad-dnssec-policy-range1.conf
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
dnssec-policy reverse-order {
|
||||
keys {
|
||||
csk lifetime unlimited algorithm rsasha256 tag-range 32767 0 2048;
|
||||
};
|
||||
};
|
||||
18
bin/tests/system/checkconf/bad-dnssec-policy-range2.conf
Normal file
18
bin/tests/system/checkconf/bad-dnssec-policy-range2.conf
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
dnssec-policy too-big-start {
|
||||
keys {
|
||||
csk lifetime unlimited algorithm rsasha256 tag-range 65536 0 2048;
|
||||
};
|
||||
};
|
||||
18
bin/tests/system/checkconf/bad-dnssec-policy-range3.conf
Normal file
18
bin/tests/system/checkconf/bad-dnssec-policy-range3.conf
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
dnssec-policy too-big-end {
|
||||
keys {
|
||||
csk lifetime unlimited algorithm rsasha256 tag-range 0 65536 2048;
|
||||
};
|
||||
};
|
||||
18
bin/tests/system/checkconf/bad-dnssec-policy-range4.conf
Normal file
18
bin/tests/system/checkconf/bad-dnssec-policy-range4.conf
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
dnssec-policy start-equals-end {
|
||||
keys {
|
||||
csk lifetime unlimited algorithm rsasha256 tag-range 0 0 2048;
|
||||
};
|
||||
};
|
||||
26
bin/tests/system/checkconf/good-dnssec-policy-range.conf
Normal file
26
bin/tests/system/checkconf/good-dnssec-policy-range.conf
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
dnssec-policy restricted-range {
|
||||
keys {
|
||||
ksk lifetime unlimited algorithm rsasha256 tag-range 0 32767 2048;
|
||||
zsk lifetime unlimited algorithm rsasha256 tag-range 0 32767;
|
||||
};
|
||||
};
|
||||
|
||||
dnssec-policy unrestricted-range {
|
||||
keys {
|
||||
ksk lifetime unlimited algorithm rsasha256 2048;
|
||||
zsk lifetime unlimited algorithm rsasha256;
|
||||
};
|
||||
};
|
||||
|
|
@ -23,7 +23,7 @@ dnssec-policy "test" {
|
|||
};
|
||||
dnskey-ttl 3600;
|
||||
keys {
|
||||
ksk key-directory lifetime P1Y algorithm 13;
|
||||
ksk key-directory lifetime P1Y algorithm 13 tag-range 0 32767;
|
||||
zsk lifetime P30D algorithm 13;
|
||||
csk key-store "hsm" lifetime P30D algorithm 8 2048;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4468,5 +4468,24 @@ n=$((n + 1))
|
|||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "check that dnssec-keygen honours key tag ranges ($n)"
|
||||
ret=0
|
||||
zone=settagrange
|
||||
ksk=$("$KEYGEN" -f KSK -q -a $DEFAULT_ALGORITHM -n zone -M 0:32767 "$zone")
|
||||
zsk=$("$KEYGEN" -q -a $DEFAULT_ALGORITHM -n zone -M 32768:65535 "$zone")
|
||||
kid=$(keyfile_to_key_id "$ksk")
|
||||
zid=$(keyfile_to_key_id "$zsk")
|
||||
[ $kid -ge 0 -a $kid -le 32767 ] || ret=1
|
||||
[ $zid -ge 32768 -a $zid -le 65535 ] || ret=1
|
||||
rksk=$($REVOKE -R $ksk)
|
||||
rzsk=$($REVOKE -R $zsk)
|
||||
krid=$(keyfile_to_key_id "$rksk")
|
||||
zrid=$(keyfile_to_key_id "$rzsk")
|
||||
[ $krid -ge 0 -a $krid -le 32767 ] || ret=1
|
||||
[ $zrid -ge 32768 -a $zrid -le 65535 ] || ret=1
|
||||
n=$((n + 1))
|
||||
if [ "$ret" -ne 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ key_stat() {
|
|||
key_save() {
|
||||
# Save key id.
|
||||
key_set "$1" ID "$KEY_ID"
|
||||
key_set "$1" RID "$KEY_RID"
|
||||
# Save base filename.
|
||||
key_set "$1" BASEFILE "$BASE_FILE"
|
||||
# Save creation date.
|
||||
|
|
@ -107,6 +108,7 @@ key_save() {
|
|||
# This will update either the KEY1, KEY2, or KEY3 array.
|
||||
key_clear() {
|
||||
key_set "$1" "ID" 'no'
|
||||
key_set "$1" "RID" 'no'
|
||||
key_set "$1" "IDPAD" 'no'
|
||||
key_set "$1" "EXPECT" 'no'
|
||||
key_set "$1" "ROLE" 'none'
|
||||
|
|
@ -407,6 +409,9 @@ check_key() {
|
|||
[ "$ret" -eq 0 ] || _log_error "${BASE_FILE} files missing"
|
||||
[ "$ret" -eq 0 ] || return 0
|
||||
|
||||
# Retrieve revoked key id
|
||||
KEY_RID=$($REVOKE -R ${BASE_FILE})
|
||||
|
||||
# Retrieve creation date.
|
||||
grep "; Created:" "$KEY_FILE" >"${ZONE}.${KEY_ID}.${_alg_num}.created" || _log_error "mismatch created comment in $KEY_FILE"
|
||||
KEY_CREATED=$(awk '{print $3}' <"${ZONE}.${KEY_ID}.${_alg_num}.created")
|
||||
|
|
|
|||
|
|
@ -219,6 +219,17 @@ zone "multisigner-model2.kasp" {
|
|||
allow-update { any; };
|
||||
};
|
||||
|
||||
/*
|
||||
* A zone that starts with keys that have tags that are
|
||||
* outside of the desired multi-signer key tag range.
|
||||
*/
|
||||
zone "single-to-multisigner.kasp" {
|
||||
type primary;
|
||||
file "single-to-multisigner.kasp.db";
|
||||
dnssec-policy "multisigner-model2";
|
||||
allow-update { any; };
|
||||
};
|
||||
|
||||
/*
|
||||
* Different algorithms.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ dnssec-policy "multisigner-model2" {
|
|||
inline-signing no;
|
||||
|
||||
keys {
|
||||
ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
|
||||
zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@;
|
||||
ksk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@ tag-range 32768 65535;
|
||||
zsk key-directory lifetime unlimited algorithm @DEFAULT_ALGORITHM@ tag-range 32768 65535;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -130,8 +130,8 @@ $KEYGEN -G -k rsasha256 -l policies/kasp.conf $zone >keygen.out.$zone.2 2>&1
|
|||
zone="multisigner-model2.kasp"
|
||||
echo_i "setting up zone: $zone"
|
||||
# Import the ZSK sets of the other providers into their DNSKEY RRset.
|
||||
ZSK1=$($KEYGEN -K ../ -a $DEFAULT_ALGORITHM -L 3600 $zone 2>keygen.out.$zone.1)
|
||||
ZSK2=$($KEYGEN -K ../ -a $DEFAULT_ALGORITHM -L 3600 $zone 2>keygen.out.$zone.2)
|
||||
ZSK1=$($KEYGEN -K ../ -a $DEFAULT_ALGORITHM -L 3600 -M 0:32767 $zone 2>keygen.out.$zone.1)
|
||||
ZSK2=$($KEYGEN -K ../ -a $DEFAULT_ALGORITHM -L 3600 -M 0:32767 $zone 2>keygen.out.$zone.2)
|
||||
# ZSK1 will be added to the unsigned zonefile.
|
||||
cat "../${ZSK1}.key" | grep -v ";.*" >>"${zone}.db"
|
||||
cat "../${ZSK1}.key" | grep -v ";.*" >"${zone}.zsk1"
|
||||
|
|
@ -184,6 +184,17 @@ cat template.db.in "${CSK}.key" >"$infile"
|
|||
cp $infile $zonefile
|
||||
$SIGNER -PS -z -x -s now-2w -e now-1mi -o $zone -f "${zonefile}.signed" $infile >signer.out.$zone.1 2>&1
|
||||
|
||||
# We are changing an existing single-signed zone to multi-signed
|
||||
# zone where the key tags do not match the dnssec-policy key tag range
|
||||
setup single-to-multisigner.kasp
|
||||
T="now-1d"
|
||||
KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -M 0:32767 -L 3600 -f KSK $ksktimes $zone 2>keygen.out.$zone.1)
|
||||
ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -M 0:32767 -L 3600 $zsktimes $zone 2>keygen.out.$zone.2)
|
||||
$SETTIME -s -g $O -d $O $T -k $O $T -r $O $T "$KSK" >settime.out.$zone.1 2>&1
|
||||
$SETTIME -s -g $O -k $O $T -z $O $T "$ZSK" >settime.out.$zone.2 2>&1
|
||||
cat template.db.in "${KSK}.key" "${ZSK}.key" >"$infile"
|
||||
$SIGNER -PS -z -x -s now-2w -e now-1mi -o $zone -f "${zonefile}" $infile >signer.out.$zone.1 2>&1
|
||||
|
||||
# These signatures are set to expire long in the past, update immediately.
|
||||
setup expired-sigs.autosign
|
||||
T="now-6mo"
|
||||
|
|
|
|||
|
|
@ -2154,6 +2154,86 @@ retry_quiet 10 zsks_are_published || ret=1
|
|||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
#
|
||||
# A zone transitioning from single-signed to multi-signed.
|
||||
# We should have the old omnipresent keys outside of the
|
||||
# desired key range and the new keys in the desired key range
|
||||
# KEY1 and KEY2 are the new keys. KEY3 and KEY4 are the old keys.
|
||||
#
|
||||
set_zone "single-to-multisigner.kasp"
|
||||
set_policy "multisigner-model2" "4" "3600"
|
||||
set_server "ns3" "10.53.0.3"
|
||||
key_clear "KEY1"
|
||||
key_clear "KEY2"
|
||||
key_clear "KEY3"
|
||||
key_clear "KEY4"
|
||||
|
||||
# Key properties.
|
||||
set_keyrole "KEY1" "ksk"
|
||||
set_keylifetime "KEY1" "0"
|
||||
set_keyalgorithm "KEY1" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
|
||||
set_keysigning "KEY1" "yes"
|
||||
set_zonesigning "KEY1" "no"
|
||||
|
||||
set_keyrole "KEY2" "zsk"
|
||||
set_keylifetime "KEY2" "0"
|
||||
set_keyalgorithm "KEY2" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
|
||||
set_keysigning "KEY2" "no"
|
||||
set_zonesigning "KEY2" "no" # waiting for DNSKEY to be omnipresent
|
||||
|
||||
set_keyrole "KEY3" "ksk"
|
||||
set_keyalgorithm "KEY3" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
|
||||
set_keysigning "KEY3" "yes"
|
||||
set_zonesigning "KEY3" "no"
|
||||
|
||||
set_keyrole "KEY4" "zsk"
|
||||
set_keyalgorithm "KEY4" "$DEFAULT_ALGORITHM_NUMBER" "$DEFAULT_ALGORITHM" "$DEFAULT_BITS"
|
||||
set_keysigning "KEY4" "no"
|
||||
set_zonesigning "KEY4" "yes"
|
||||
|
||||
set_keystate "KEY1" "GOAL" "omnipresent"
|
||||
set_keystate "KEY1" "STATE_DNSKEY" "rumoured"
|
||||
set_keystate "KEY1" "STATE_KRRSIG" "rumoured"
|
||||
set_keystate "KEY1" "STATE_DS" "hidden"
|
||||
|
||||
set_keystate "KEY2" "GOAL" "omnipresent"
|
||||
set_keystate "KEY2" "STATE_DNSKEY" "rumoured"
|
||||
set_keystate "KEY2" "STATE_ZRRSIG" "hidden" # waiting for DNSKEY to be omnipresent
|
||||
|
||||
set_keystate "KEY3" "GOAL" "hidden"
|
||||
set_keystate "KEY3" "STATE_DNSKEY" "omnipresent"
|
||||
set_keystate "KEY3" "STATE_KRRSIG" "omnipresent"
|
||||
set_keystate "KEY3" "STATE_DS" "omnipresent"
|
||||
|
||||
set_keystate "KEY4" "GOAL" "hidden"
|
||||
set_keystate "KEY4" "STATE_DNSKEY" "omnipresent"
|
||||
set_keystate "KEY4" "STATE_ZRRSIG" "omnipresent"
|
||||
|
||||
check_keys
|
||||
check_dnssecstatus "$SERVER" "$POLICY" "$ZONE"
|
||||
check_apex
|
||||
check_subdomain
|
||||
dnssec_verify
|
||||
|
||||
# KEY1 tag range 32768 65535
|
||||
# KEY2 tag range 32768 65535
|
||||
# KEY3 tag range 0 32767
|
||||
# KEY4 tag range 0 32767
|
||||
n=$((n + 1))
|
||||
echo_i "check that the key IDs are in the expected ranges ($n)"
|
||||
ret=0
|
||||
test $(key_get KEY1 ID) -ge 32768 -a $(key_get KEY1 ID) -le 65535 || ret=1
|
||||
test $(key_get KEY2 ID) -ge 32768 -a $(key_get KEY2 ID) -le 65535 || ret=1
|
||||
test $(key_get KEY3 ID) -ge 0 -a $(key_get KEY3 ID) -le 32767 || ret=1
|
||||
test $(key_get KEY4 ID) -ge 0 -a $(key_get KEY4 ID) -le 32767 || ret=1
|
||||
|
||||
test $(key_get KEY1 RID) -ge 32768 -a $(key_get KEY1 RID) -le 65535 || ret=1
|
||||
test $(key_get KEY2 RID) -ge 32768 -a $(key_get KEY2 RID) -le 65535 || ret=1
|
||||
test $(key_get KEY3 RID) -ge 0 -a $(key_get KEY3 RID) -le 32767 || ret=1
|
||||
test $(key_get KEY4 RID) -ge 0 -a $(key_get KEY4 RID) -le 32767 || ret=1
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
#
|
||||
# Testing manual rollover.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -6474,7 +6474,7 @@ The following options can be specified in a :any:`dnssec-policy` statement:
|
|||
|
||||
keys {
|
||||
ksk key-directory lifetime unlimited algorithm rsasha256 2048;
|
||||
zsk lifetime 30d algorithm 8;
|
||||
zsk lifetime 30d algorithm 8 tag-range 0 32767;
|
||||
csk key-store "hsm" lifetime P6MT12H3M15S algorithm ecdsa256;
|
||||
};
|
||||
|
||||
|
|
@ -6498,6 +6498,11 @@ The following options can be specified in a :any:`dnssec-policy` statement:
|
|||
When using ``key-directory``, the key is stored in the zone's
|
||||
configured :any:`key-directory`. This is also the default.
|
||||
|
||||
When using ``tag-range``, valid key tags for managed keys are
|
||||
restricted to this range [``tag-min`` ``tag-max``]. The optional
|
||||
``tag-range`` is intended to be used in multi-signer scenarios.
|
||||
The default is unlimited ([0..65535]).
|
||||
|
||||
The ``lifetime`` parameter specifies how long a key may be used
|
||||
before rolling over. For convenience, TTL-style time-unit suffixes
|
||||
can be used to specify the key lifetime. It also accepts ISO 8601
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ dnssec-policy <string> {
|
|||
cds-digest-types { <string>; ... };
|
||||
dnskey-ttl <duration>;
|
||||
inline-signing <boolean>;
|
||||
keys { ( csk | ksk | zsk ) [ key-directory | key-store <string> ] lifetime <duration_or_unlimited> algorithm <string> [ <integer> ]; ... };
|
||||
keys { ( csk | ksk | zsk ) [ key-directory | key-store <string> ] lifetime <duration_or_unlimited> algorithm <string> [ tag-range <integer> <integer> ] [ <integer> ]; ... };
|
||||
max-zone-ttl <duration>;
|
||||
nsec3param [ iterations <integer> ] [ optout <boolean> ] [ salt-length <integer> ];
|
||||
offline-ksk <boolean>;
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ struct dns_kasp_key {
|
|||
uint8_t algorithm;
|
||||
int length;
|
||||
uint8_t role;
|
||||
uint16_t tag_min;
|
||||
uint16_t tag_max;
|
||||
};
|
||||
|
||||
struct dns_kasp_nsec3param {
|
||||
|
|
@ -721,6 +723,26 @@ dns_kasp_key_zsk(dns_kasp_key_t *key);
|
|||
*
|
||||
*/
|
||||
|
||||
uint16_t
|
||||
dns_kasp_key_tagmin(dns_kasp_key_t *key);
|
||||
/*%<
|
||||
* Returns the minimum permitted key tag value.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
*\li key != NULL
|
||||
*/
|
||||
|
||||
uint16_t
|
||||
dns_kasp_key_tagmax(dns_kasp_key_t *key);
|
||||
/*%<
|
||||
* Returns the maximum permitted key tag value.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
*\li key != NULL
|
||||
*/
|
||||
|
||||
bool
|
||||
dns_kasp_key_match(dns_kasp_key_t *key, dns_dnsseckey_t *dkey);
|
||||
/*%<
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ dns_kasp_addkey(dns_kasp_t *kasp, dns_kasp_key_t *key) {
|
|||
isc_result_t
|
||||
dns_kasp_key_create(dns_kasp_t *kasp, dns_kasp_key_t **keyp) {
|
||||
dns_kasp_key_t *key = NULL;
|
||||
dns_kasp_key_t k = { .length = -1 };
|
||||
dns_kasp_key_t k = { .tag_max = 0xffff, .length = -1 };
|
||||
|
||||
REQUIRE(DNS_KASP_VALID(kasp));
|
||||
REQUIRE(keyp != NULL && *keyp == NULL);
|
||||
|
|
@ -507,6 +507,18 @@ dns_kasp_key_zsk(dns_kasp_key_t *key) {
|
|||
return (key->role & DNS_KASP_KEY_ROLE_ZSK);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
dns_kasp_key_tagmin(dns_kasp_key_t *key) {
|
||||
REQUIRE(key != NULL);
|
||||
return (key->tag_min);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
dns_kasp_key_tagmax(dns_kasp_key_t *key) {
|
||||
REQUIRE(key != NULL);
|
||||
return (key->tag_min);
|
||||
}
|
||||
|
||||
bool
|
||||
dns_kasp_key_match(dns_kasp_key_t *key, dns_dnsseckey_t *dkey) {
|
||||
isc_result_t ret;
|
||||
|
|
@ -532,6 +544,16 @@ dns_kasp_key_match(dns_kasp_key_t *key, dns_dnsseckey_t *dkey) {
|
|||
if (ret != ISC_R_SUCCESS || role != dns_kasp_key_zsk(key)) {
|
||||
return (false);
|
||||
}
|
||||
/* Valid key tag range? */
|
||||
uint16_t id = dst_key_id(dkey->key);
|
||||
uint16_t rid = dst_key_rid(dkey->key);
|
||||
if (id < key->tag_min || id > key->tag_max) {
|
||||
return (false);
|
||||
}
|
||||
if (rid < key->tag_min || rid > key->tag_max) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* Found a match. */
|
||||
return (true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -426,11 +426,19 @@ keymgr_key_update_lifetime(dns_dnsseckey_t *key, dns_kasp_t *kasp,
|
|||
}
|
||||
|
||||
static bool
|
||||
keymgr_keyid_conflict(dst_key_t *newkey, dns_dnsseckeylist_t *keys) {
|
||||
keymgr_keyid_conflict(dst_key_t *newkey, uint16_t min, uint16_t max,
|
||||
dns_dnsseckeylist_t *keys) {
|
||||
uint16_t id = dst_key_id(newkey);
|
||||
uint32_t rid = dst_key_rid(newkey);
|
||||
uint32_t alg = dst_key_alg(newkey);
|
||||
|
||||
if (id < min || id > max) {
|
||||
return (true);
|
||||
}
|
||||
if (rid < min || rid > max) {
|
||||
return (true);
|
||||
}
|
||||
|
||||
for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keys); dkey != NULL;
|
||||
dkey = ISC_LIST_NEXT(dkey, link))
|
||||
{
|
||||
|
|
@ -484,9 +492,11 @@ keymgr_createkey(dns_kasp_key_t *kkey, const dns_name_t *origin,
|
|||
}
|
||||
|
||||
/* Key collision? */
|
||||
conflict = keymgr_keyid_conflict(newkey, keylist);
|
||||
conflict = keymgr_keyid_conflict(newkey, kkey->tag_min,
|
||||
kkey->tag_max, keylist);
|
||||
if (!conflict) {
|
||||
conflict = keymgr_keyid_conflict(newkey, newkeys);
|
||||
conflict = keymgr_keyid_conflict(
|
||||
newkey, kkey->tag_min, kkey->tag_max, newkeys);
|
||||
}
|
||||
if (conflict) {
|
||||
/* Try again. */
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
|
|||
uint32_t ksk_min_lifetime, uint32_t zsk_min_lifetime) {
|
||||
isc_result_t result;
|
||||
dns_kasp_key_t *key = NULL;
|
||||
const cfg_obj_t *tagrange = NULL;
|
||||
|
||||
/* Create a new key reference. */
|
||||
result = dns_kasp_key_create(kasp, &key);
|
||||
|
|
@ -291,6 +292,38 @@ cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
|
|||
|
||||
key->length = size;
|
||||
}
|
||||
|
||||
tagrange = cfg_tuple_get(config, "tag-range");
|
||||
if (cfg_obj_istuple(tagrange)) {
|
||||
uint32_t tag_min = 0, tag_max = 0xffff;
|
||||
obj = cfg_tuple_get(tagrange, "tag-min");
|
||||
tag_min = cfg_obj_asuint32(obj);
|
||||
if (tag_min > 0xffff) {
|
||||
cfg_obj_log(obj, ISC_LOG_ERROR,
|
||||
"dnssec-policy: tag-min "
|
||||
"too big");
|
||||
result = ISC_R_RANGE;
|
||||
goto cleanup;
|
||||
}
|
||||
obj = cfg_tuple_get(tagrange, "tag-max");
|
||||
tag_max = cfg_obj_asuint32(obj);
|
||||
if (tag_max > 0xffff) {
|
||||
cfg_obj_log(obj, ISC_LOG_ERROR,
|
||||
"dnssec-policy: tag-max "
|
||||
"too big");
|
||||
result = ISC_R_RANGE;
|
||||
goto cleanup;
|
||||
}
|
||||
if (tag_min >= tag_max) {
|
||||
cfg_obj_log(
|
||||
obj, ISC_LOG_ERROR,
|
||||
"dnssec-policy: tag-min >= tag_max");
|
||||
result = ISC_R_RANGE;
|
||||
goto cleanup;
|
||||
}
|
||||
key->tag_min = tag_min;
|
||||
key->tag_max = tag_max;
|
||||
}
|
||||
}
|
||||
|
||||
dns_kasp_addkey(kasp, key);
|
||||
|
|
|
|||
|
|
@ -649,12 +649,73 @@ static keyword_type_t lifetime_kw = { "lifetime",
|
|||
static cfg_type_t cfg_type_lifetime = { "lifetime", parse_keyvalue,
|
||||
print_keyvalue, doc_keyvalue,
|
||||
&cfg_rep_duration, &lifetime_kw };
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static void
|
||||
print_tagrange(cfg_printer_t *pctx, const cfg_obj_t *obj) {
|
||||
REQUIRE(pctx != NULL);
|
||||
REQUIRE(obj != NULL);
|
||||
REQUIRE(obj->type->rep == &cfg_rep_tuple);
|
||||
|
||||
if (cfg_obj_istuple(obj)) {
|
||||
cfg_print_cstr(pctx, "tag-range ");
|
||||
cfg_print_tuple(pctx, obj);
|
||||
}
|
||||
}
|
||||
|
||||
static cfg_tuplefielddef_t tagrange_fields[] = {
|
||||
{ "tag-min", &cfg_type_uint32, 0 },
|
||||
{ "tag-max", &cfg_type_uint32, 0 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static cfg_type_t cfg_type_tagrange = { "tagrange", cfg_parse_tuple,
|
||||
print_tagrange, cfg_doc_tuple,
|
||||
&cfg_rep_tuple, tagrange_fields };
|
||||
|
||||
static keyword_type_t tagrange_kw = { "tag-range", &cfg_type_tagrange };
|
||||
static void
|
||||
doc_optionaltagrange(cfg_printer_t *pctx, const cfg_type_t *type) {
|
||||
UNUSED(type);
|
||||
|
||||
cfg_print_cstr(pctx, "[ tag-range <integer> <integer> ]");
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
parse_optionaltagrange(cfg_parser_t *pctx, const cfg_type_t *type,
|
||||
cfg_obj_t **ret) {
|
||||
isc_result_t result;
|
||||
cfg_obj_t *obj = NULL;
|
||||
|
||||
UNUSED(type);
|
||||
|
||||
CHECK(cfg_peektoken(pctx, 0));
|
||||
if (pctx->token.type == isc_tokentype_string &&
|
||||
strcasecmp(TOKEN_STRING(pctx), "tag-range") == 0)
|
||||
{
|
||||
CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
|
||||
CHECK(cfg_parse_obj(pctx, &cfg_type_tagrange, &obj));
|
||||
} else {
|
||||
CHECK(cfg_parse_void(pctx, NULL, &obj));
|
||||
}
|
||||
|
||||
*ret = obj;
|
||||
cleanup:
|
||||
return (result);
|
||||
}
|
||||
|
||||
static cfg_type_t cfg_type_optional_tagrange = {
|
||||
"optionaltagrange", parse_optionaltagrange, NULL,
|
||||
doc_optionaltagrange, &cfg_rep_tuple, &tagrange_kw
|
||||
};
|
||||
|
||||
static cfg_tuplefielddef_t kaspkey_fields[] = {
|
||||
{ "role", &cfg_type_dnsseckeyrole, 0 },
|
||||
{ "keystorage", &cfg_type_optional_keystore, 0 },
|
||||
{ "lifetime", &cfg_type_lifetime, 0 },
|
||||
{ "algorithm", &cfg_type_algorithm, 0 },
|
||||
{ "tag-range", &cfg_type_optional_tagrange, 0 },
|
||||
{ "length", &cfg_type_optional_uint32, 0 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue