mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-21 17:41:35 -04:00
Change checkconf to include built-in dnssec-policy
The configuration should also take into account the built-in DNSSEC policies when verifying the keys in the key-directory match the given policy. Update the code accordingly and add some good and failure test cases.
This commit is contained in:
parent
3918a8ca4c
commit
dcd49f2ead
8 changed files with 282 additions and 30 deletions
|
|
@ -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.
|
||||
*/
|
||||
|
||||
zone "bad-default-algorithm.example" {
|
||||
type primary;
|
||||
file "bad-default-algorithm.example.db";
|
||||
dnssec-policy "default";
|
||||
};
|
||||
18
bin/tests/system/checkconf-keys/bad-default-kz.conf.j2
Normal file
18
bin/tests/system/checkconf-keys/bad-default-kz.conf.j2
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.
|
||||
*/
|
||||
|
||||
zone "bad-default-kz.example" {
|
||||
type primary;
|
||||
file "bad-default-kz.example.db";
|
||||
dnssec-policy "default";
|
||||
};
|
||||
|
|
@ -53,6 +53,12 @@ dnssec-policy "keystores-kz" {
|
|||
};
|
||||
};
|
||||
|
||||
zone "default.example" {
|
||||
type primary;
|
||||
file "default.example.db";
|
||||
dnssec-policy "default";
|
||||
};
|
||||
|
||||
zone "alternative.kz.example" {
|
||||
type primary;
|
||||
file "alternative.kz.example.db";
|
||||
|
|
|
|||
|
|
@ -19,6 +19,19 @@ set -e
|
|||
mkdir ksk
|
||||
mkdir zsk
|
||||
|
||||
zone="default.example"
|
||||
cp template.db.in "${zone}.db"
|
||||
$KEYGEN -a 13 -fK $zone 2>keygen.out.$zone.1
|
||||
|
||||
zone="bad-default-kz.example"
|
||||
cp template.db.in "${zone}.db"
|
||||
$KEYGEN -a 13 -fK $zone 2>keygen.out.$zone.1
|
||||
$KEYGEN -a 13 $zone 2>keygen.out.$zone.2
|
||||
|
||||
zone="bad-default-algorithm.example"
|
||||
cp template.db.in "${zone}.db"
|
||||
$KEYGEN -a 8 -fK $zone 2>keygen.out.$zone.1
|
||||
|
||||
zone="alternative.kz.example"
|
||||
cp template.db.in "${zone}.db"
|
||||
$KEYGEN -a RSASHA256 -b 2048 $zone 2>keygen.out.$zone.1
|
||||
|
|
|
|||
|
|
@ -121,3 +121,42 @@ def test_dnssecpolicy_keystore():
|
|||
f"zone '{zone}': no key file found matching dnssec-policy default-kz key:'zsk algorithm:ECDSAP256SHA256 length:256 tag-range:0-65535'"
|
||||
in err
|
||||
)
|
||||
|
||||
# Mismatch algorithm (default policy)
|
||||
zone = "bad-default-algorithm.example"
|
||||
out = isctest.run.cmd(
|
||||
[CHECKCONF, "-k", "bad-default-algorithm.conf"], raise_on_exception=False
|
||||
)
|
||||
err = out.stdout.decode("utf-8")
|
||||
keys = isctest.kasp.keydir_to_keylist(zone)
|
||||
assert len(keys) == 1
|
||||
assert (
|
||||
f"zone '{zone}': key file '{zone}/RSASHA256/{keys[0].tag}' does not match dnssec-policy default"
|
||||
in err
|
||||
)
|
||||
assert (
|
||||
f"zone '{zone}': no key file found matching dnssec-policy default key:'csk algorithm:ECDSAP256SHA256 length:256 tag-range:0-65535'"
|
||||
in err
|
||||
)
|
||||
|
||||
# Mismatch role (default policy)
|
||||
zone = "bad-default-kz.example"
|
||||
out = isctest.run.cmd(
|
||||
[CHECKCONF, "-k", "bad-default-kz.conf"], raise_on_exception=False
|
||||
)
|
||||
err = out.stdout.decode("utf-8")
|
||||
keys = isctest.kasp.keydir_to_keylist(zone)
|
||||
assert len(keys) == 2
|
||||
assert (
|
||||
f"zone '{zone}': key file '{zone}/ECDSAP256SHA256/{keys[0].tag}' does not match dnssec-policy default"
|
||||
in err
|
||||
)
|
||||
assert (
|
||||
f"zone '{zone}': key file '{zone}/ECDSAP256SHA256/{keys[1].tag}' does not match dnssec-policy default"
|
||||
in err
|
||||
)
|
||||
assert (
|
||||
f"zone '{zone}': no key file found matching dnssec-policy default key:'csk algorithm:ECDSAP256SHA256 length:256 tag-range:0-65535'"
|
||||
in err
|
||||
)
|
||||
assert f"zone '{zone}': wrong number of key files (2, expected 1)" in err
|
||||
|
|
|
|||
|
|
@ -2826,10 +2826,11 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig,
|
|||
bool check_keys) {
|
||||
const char *dir = keydir;
|
||||
isc_result_t ret, result = ISC_R_SUCCESS;
|
||||
bool done = false;
|
||||
bool keystore = false;
|
||||
const cfg_obj_t *kasps = NULL;
|
||||
const cfg_obj_t *kaspobj = NULL;
|
||||
const cfg_obj_t *obj = NULL;
|
||||
dns_kasp_t *default_kasp = NULL;
|
||||
dns_kasp_t *kasp = NULL;
|
||||
dns_kasplist_t kasplist;
|
||||
const cfg_obj_t *keystores = NULL;
|
||||
|
|
@ -2855,37 +2856,86 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig,
|
|||
(void)cfg_keystore_fromconfig(NULL, mctx, &kslist, NULL);
|
||||
|
||||
/*
|
||||
* Look for the dnssec-policy by name, which is the dnssec-policy
|
||||
* for the zone in question.
|
||||
* dnssec-policy "default".
|
||||
*/
|
||||
ret = cfg_kasp_builtinconfig(mctx, "default", &kslist, &kasplist,
|
||||
&default_kasp);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
cfg_obj_log(config, ISC_LOG_ERROR,
|
||||
"failed to load the 'default' dnssec-policy: %s",
|
||||
isc_result_totext(ret));
|
||||
result = ret;
|
||||
goto check;
|
||||
}
|
||||
dns_kasp_freeze(default_kasp);
|
||||
|
||||
/*
|
||||
* dnssec-policy "insecure".
|
||||
*/
|
||||
ret = cfg_kasp_builtinconfig(mctx, "insecure", &kslist, &kasplist,
|
||||
&kasp);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
cfg_obj_log(config, ISC_LOG_ERROR,
|
||||
"failed to load the 'insecure' dnssec-policy: %s",
|
||||
isc_result_totext(ret));
|
||||
result = ret;
|
||||
goto check;
|
||||
}
|
||||
dns_kasp_freeze(kasp);
|
||||
dns_kasp_detach(&kasp);
|
||||
|
||||
/*
|
||||
* Configured dnssec-policy clauses.
|
||||
*/
|
||||
CFG_LIST_FOREACH(kasps, element) {
|
||||
cfg_obj_t *kconfig = cfg_listelt_value(element);
|
||||
kaspobj = NULL;
|
||||
obj = NULL;
|
||||
|
||||
if (!cfg_obj_istuple(kconfig)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
kaspobj = cfg_tuple_get(kconfig, "name");
|
||||
if (strcmp(name, cfg_obj_asstring(kaspobj)) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = cfg_kasp_fromconfig(kconfig, NULL, 0, mctx, &kslist,
|
||||
&kasplist, &kasp);
|
||||
obj = cfg_tuple_get(kconfig, "name");
|
||||
ret = cfg_kasp_fromconfig(kconfig, default_kasp, 0, mctx,
|
||||
&kslist, &kasplist, &kasp);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
kasp = NULL;
|
||||
result = ret;
|
||||
goto check;
|
||||
}
|
||||
|
||||
break;
|
||||
if (strcmp(name, cfg_obj_asstring(obj)) == 0) {
|
||||
kaspobj = obj;
|
||||
dns_kasp_freeze(kasp);
|
||||
}
|
||||
dns_kasp_detach(&kasp);
|
||||
}
|
||||
if (kasp == NULL) {
|
||||
|
||||
/*
|
||||
* Look for the dnssec-policy by name, which is the dnssec-policy
|
||||
* for the zone in question.
|
||||
*/
|
||||
ret = dns_kasplist_find(&kasplist, name, &kasp);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
cfg_obj_log(config, ISC_LOG_ERROR,
|
||||
"no dnssec-policy found for zone '%s'", zname);
|
||||
result = ISC_R_NOTFOUND;
|
||||
goto check;
|
||||
}
|
||||
INSIST(kaspobj != NULL);
|
||||
INSIST(kasp != NULL);
|
||||
|
||||
if (kaspobj == NULL) {
|
||||
kaspobj = kasps == NULL ? config : kasps;
|
||||
}
|
||||
|
||||
if (strcmp(name, "insecure") == 0 || strcmp(name, "default") == 0) {
|
||||
ret = keydirexist(zconfig, "key-directory", origin, dir, name,
|
||||
keydirs, mctx);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
result = ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check key-stores of keys */
|
||||
dns_kasp_freeze(kasp);
|
||||
ISC_LIST_FOREACH(dns_kasp_keys(kasp), kkey, link) {
|
||||
dns_keystore_t *kks = dns_kasp_key_keystore(kkey);
|
||||
dir = dns_keystore_directory(kks, keydir);
|
||||
|
|
@ -2983,18 +3033,10 @@ check_keydir(const cfg_obj_t *config, const cfg_obj_t *zconfig,
|
|||
}
|
||||
}
|
||||
|
||||
dns_kasp_thaw(kasp);
|
||||
done = true;
|
||||
|
||||
check:
|
||||
if (!done) {
|
||||
ret = keydirexist(zconfig, "key-directory", origin, dir, name,
|
||||
keydirs, mctx);
|
||||
if (ret != ISC_R_SUCCESS) {
|
||||
result = ret;
|
||||
}
|
||||
if (default_kasp != NULL) {
|
||||
dns_kasp_detach(&default_kasp);
|
||||
}
|
||||
|
||||
if (kasp != NULL) {
|
||||
dns_kasp_detach(&kasp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,30 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
|
|||
*\li Other errors are possible.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
cfg_kasp_builtinconfig(isc_mem_t *mctx, const char *name,
|
||||
dns_keystorelist_t *keystorelist,
|
||||
dns_kasplist_t *kasplist, dns_kasp_t **kaspp);
|
||||
/*%<
|
||||
* Create built-in KASP.
|
||||
*
|
||||
* If a 'kasplist' is provided, a lookup happens and if a KASP already exists
|
||||
* with the same name, no new KASP is created, and no attach to 'kaspp' happens.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
*\li 'mctx' is a valid memory context.
|
||||
*
|
||||
*\li kaspp != NULL && *kaspp == NULL
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
*\li #ISC_R_SUCCESS If creating and configuring the KASP succeeds.
|
||||
*\li #ISC_R_EXISTS If 'kasplist' already has the default policy.
|
||||
*
|
||||
*\li Other errors are possible.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx,
|
||||
dns_keystorelist_t *keystorelist,
|
||||
|
|
|
|||
|
|
@ -570,10 +570,8 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
|
|||
/* Now configure. */
|
||||
INSIST(DNS_KASP_VALID(kasp));
|
||||
|
||||
if (config != NULL) {
|
||||
koptions = cfg_tuple_get(config, "options");
|
||||
maps[i++] = koptions;
|
||||
}
|
||||
koptions = cfg_tuple_get(config, "options");
|
||||
maps[i++] = koptions;
|
||||
maps[i] = NULL;
|
||||
|
||||
/* Configuration: Signatures */
|
||||
|
|
@ -877,6 +875,100 @@ cleanup:
|
|||
return result;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
cfg_kasp_builtinconfig(isc_mem_t *mctx, const char *name,
|
||||
dns_keystorelist_t *keystorelist,
|
||||
dns_kasplist_t *kasplist, dns_kasp_t **kaspp) {
|
||||
isc_result_t result;
|
||||
dns_kasp_t *kasp = NULL;
|
||||
|
||||
REQUIRE(kaspp != NULL && *kaspp == NULL);
|
||||
REQUIRE(strcmp(name, "default") == 0 || strcmp(name, "insecure") == 0);
|
||||
|
||||
result = dns_kasplist_find(kasplist, name, &kasp);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
dns_kasp_detach(&kasp);
|
||||
return ISC_R_EXISTS;
|
||||
}
|
||||
if (result != ISC_R_NOTFOUND) {
|
||||
return result;
|
||||
}
|
||||
result = ISC_R_SUCCESS;
|
||||
|
||||
/* No kasp with configured name was found in list, create new one. */
|
||||
INSIST(kasp == NULL);
|
||||
dns_kasp_create(mctx, name, &kasp);
|
||||
INSIST(kasp != NULL);
|
||||
|
||||
/* Now configure. */
|
||||
INSIST(DNS_KASP_VALID(kasp));
|
||||
|
||||
/* Configuration: Signatures */
|
||||
dns_kasp_setsigjitter(kasp, parse_duration(DNS_KASP_SIG_JITTER));
|
||||
dns_kasp_setsigrefresh(kasp, parse_duration(DNS_KASP_SIG_REFRESH));
|
||||
dns_kasp_setsigvalidity_dnskey(
|
||||
kasp, parse_duration(DNS_KASP_SIG_VALIDITY_DNSKEY));
|
||||
dns_kasp_setsigvalidity(kasp, parse_duration(DNS_KASP_SIG_VALIDITY));
|
||||
|
||||
/* Configuration: Zone settings */
|
||||
dns_kasp_setinlinesigning(kasp, true);
|
||||
dns_kasp_setmanualmode(kasp, false);
|
||||
dns_kasp_setzonemaxttl(kasp, parse_duration(DNS_KASP_ZONE_MAXTTL));
|
||||
dns_kasp_setzonepropagationdelay(
|
||||
kasp, parse_duration(DNS_KASP_ZONE_PROPDELAY));
|
||||
|
||||
/* Configuration: Parent settings */
|
||||
dns_kasp_setdsttl(kasp, parse_duration(DNS_KASP_DS_TTL));
|
||||
dns_kasp_setparentpropagationdelay(
|
||||
kasp, parse_duration(DNS_KASP_PARENT_PROPDELAY));
|
||||
|
||||
/* Configuration: Keys */
|
||||
dns_kasp_setofflineksk(kasp, false);
|
||||
dns_kasp_setcdnskey(kasp, true);
|
||||
dns_kasp_adddigest(kasp, DNS_DSDIGEST_SHA256);
|
||||
dns_kasp_setdnskeyttl(kasp, parse_duration(DNS_KASP_KEY_TTL));
|
||||
dns_kasp_setpublishsafety(kasp,
|
||||
parse_duration(DNS_KASP_PUBLISH_SAFETY));
|
||||
dns_kasp_setretiresafety(kasp, parse_duration(DNS_KASP_RETIRE_SAFETY));
|
||||
|
||||
dns_kasp_setpurgekeys(kasp, parse_duration(DNS_KASP_PURGE_KEYS));
|
||||
|
||||
if (strcmp(name, "default") == 0) {
|
||||
dns_kasp_key_t *new_key = NULL;
|
||||
dns_kasp_key_create(kasp, &new_key);
|
||||
new_key->role |= DNS_KASP_KEY_ROLE_KSK;
|
||||
new_key->role |= DNS_KASP_KEY_ROLE_ZSK;
|
||||
new_key->lifetime = 0;
|
||||
new_key->algorithm = DST_ALG_ECDSA256;
|
||||
new_key->length = 256;
|
||||
result = dns_keystorelist_find(keystorelist,
|
||||
DNS_KEYSTORE_KEYDIRECTORY,
|
||||
&new_key->keystore);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
dns_kasp_addkey(kasp, new_key);
|
||||
}
|
||||
|
||||
/* Configuration: Denial of existence */
|
||||
dns_kasp_setnsec3(kasp, false);
|
||||
|
||||
/* Append it to the list for future lookups. */
|
||||
ISC_LIST_APPEND(*kasplist, kasp, link);
|
||||
INSIST(!(ISC_LIST_EMPTY(*kasplist)));
|
||||
|
||||
/* Success: Attach the kasp to the pointer and return. */
|
||||
dns_kasp_attach(kasp, kaspp);
|
||||
|
||||
/* Don't detach as kasp is on '*kasplist' */
|
||||
return ISC_R_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
/* Something bad happened, detach (destroys kasp) and return error. */
|
||||
dns_kasp_detach(&kasp);
|
||||
return result;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx,
|
||||
dns_keystorelist_t *keystorelist,
|
||||
|
|
|
|||
Loading…
Reference in a new issue