2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 01:59:26 +00:00
bind/lib/isccfg/kaspconf.c
Matthijs Mekking 63c5b453e0 Add manual-mode config option
Add a new option 'manual-mode' to 'dnssec-policy'. The intended
use is that if it is enabled, it will not automatically move to the
next state transition (RUMOURED, UNRETENTIVE), only after manual
confirmation. The intended state transition should be logged.
2025-08-21 16:00:19 +02:00

868 lines
22 KiB
C

/*
* 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.
*/
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <isc/crypto.h>
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/region.h>
#include <isc/result.h>
#include <isc/string.h>
#include <isc/types.h>
#include <isc/util.h>
#include <dns/kasp.h>
#include <dns/keystore.h>
#include <dns/keyvalues.h>
#include <dns/nsec3.h>
#include <dns/secalg.h>
#include <dns/ttl.h>
#include <dst/dst.h>
#include <isccfg/cfg.h>
#include <isccfg/duration.h>
#include <isccfg/kaspconf.h>
#include <isccfg/namedconf.h>
#define DEFAULT_NSEC3PARAM_ITER 0
#define DEFAULT_NSEC3PARAM_SALTLEN 0
/*
* Utility function for getting a configuration option.
*/
static isc_result_t
confget(cfg_obj_t const *const *maps, const char *name, const cfg_obj_t **obj) {
for (size_t i = 0;; i++) {
if (maps[i] == NULL) {
return ISC_R_NOTFOUND;
}
if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) {
return ISC_R_SUCCESS;
}
}
}
/*
* Utility function for parsing durations from string.
*/
static uint32_t
parse_duration(const char *str) {
uint32_t time = 0;
isccfg_duration_t duration;
isc_result_t result;
isc_textregion_t tr;
tr.base = UNCONST(str);
tr.length = strlen(tr.base);
result = isccfg_parse_duration(&tr, &duration);
if (result == ISC_R_SUCCESS) {
time = isccfg_duration_toseconds(&duration);
}
return time;
}
/*
* Utility function for configuring durations.
*/
static uint32_t
get_duration(const cfg_obj_t **maps, const char *option, const char *dfl) {
const cfg_obj_t *obj;
isc_result_t result;
obj = NULL;
result = confget(maps, option, &obj);
if (result == ISC_R_NOTFOUND) {
return parse_duration(dfl);
}
INSIST(result == ISC_R_SUCCESS);
return cfg_obj_asduration(obj);
}
/*
* Utility function for configuring strings.
*/
static const char *
get_string(const cfg_obj_t **maps, const char *option) {
const cfg_obj_t *obj;
isc_result_t result;
obj = NULL;
result = confget(maps, option, &obj);
if (result == ISC_R_NOTFOUND) {
return NULL;
}
INSIST(result == ISC_R_SUCCESS);
return cfg_obj_asstring(obj);
}
/*
* Create a new kasp key derived from configuration.
*/
static isc_result_t
cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
bool check_algorithms, bool offline_ksk,
dns_keystorelist_t *keystorelist,
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. */
dns_kasp_key_create(kasp, &key);
if (config == NULL) {
/* We are creating a key reference for the default kasp. */
INSIST(!offline_ksk);
key->role |= DNS_KASP_KEY_ROLE_KSK | DNS_KASP_KEY_ROLE_ZSK;
key->lifetime = 0; /* unlimited */
key->algorithm = DST_ALG_ECDSA256;
key->length = -1;
result = dns_keystorelist_find(keystorelist,
DNS_KEYSTORE_KEYDIRECTORY,
&key->keystore);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
} else {
const char *rolestr = NULL;
const char *keydir = NULL;
const cfg_obj_t *obj = NULL;
isc_consttextregion_t alg;
bool error = false;
rolestr = cfg_obj_asstring(cfg_tuple_get(config, "role"));
if (strcmp(rolestr, "ksk") == 0) {
key->role |= DNS_KASP_KEY_ROLE_KSK;
} else if (strcmp(rolestr, "zsk") == 0) {
key->role |= DNS_KASP_KEY_ROLE_ZSK;
} else if (strcmp(rolestr, "csk") == 0) {
if (offline_ksk) {
cfg_obj_log(
config, ISC_LOG_ERROR,
"dnssec-policy: csk keys are not "
"allowed when offline-ksk is enabled");
result = ISC_R_FAILURE;
goto cleanup;
}
key->role |= DNS_KASP_KEY_ROLE_KSK;
key->role |= DNS_KASP_KEY_ROLE_ZSK;
}
obj = cfg_tuple_get(config, "keystorage");
if (cfg_obj_isstring(obj)) {
keydir = cfg_obj_asstring(obj);
}
if (keydir == NULL) {
keydir = DNS_KEYSTORE_KEYDIRECTORY;
}
result = dns_keystorelist_find(keystorelist, keydir,
&key->keystore);
if (result == ISC_R_NOTFOUND) {
cfg_obj_log(obj, ISC_LOG_ERROR,
"dnssec-policy: keystore %s does not exist",
keydir);
result = ISC_R_FAILURE;
goto cleanup;
} else if (result != ISC_R_SUCCESS) {
cfg_obj_log(obj, ISC_LOG_ERROR,
"dnssec-policy: bad keystore %s", keydir);
result = ISC_R_FAILURE;
goto cleanup;
}
INSIST(key->keystore != NULL);
key->lifetime = 0; /* unlimited */
obj = cfg_tuple_get(config, "lifetime");
if (cfg_obj_isduration(obj)) {
key->lifetime = cfg_obj_asduration(obj);
}
if (key->lifetime > 0) {
if (key->lifetime < 30 * (24 * 3600)) {
cfg_obj_log(obj, ISC_LOG_WARNING,
"dnssec-policy: key lifetime is "
"shorter than 30 days");
}
if ((key->role & DNS_KASP_KEY_ROLE_KSK) != 0 &&
key->lifetime <= ksk_min_lifetime)
{
error = true;
}
if ((key->role & DNS_KASP_KEY_ROLE_ZSK) != 0 &&
key->lifetime <= zsk_min_lifetime)
{
error = true;
}
if (error) {
cfg_obj_log(obj, ISC_LOG_ERROR,
"dnssec-policy: key lifetime is "
"shorter than the time it takes to "
"do a rollover");
result = ISC_R_FAILURE;
goto cleanup;
}
}
obj = cfg_tuple_get(config, "algorithm");
alg.base = cfg_obj_asstring(obj);
alg.length = strlen(alg.base);
result = dst_algorithm_fromtext(&key->algorithm,
(isc_textregion_t *)&alg);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(obj, ISC_LOG_ERROR,
"dnssec-policy: bad algorithm %s",
alg.base);
result = DNS_R_BADALG;
goto cleanup;
}
if (check_algorithms && isc_crypto_fips_mode() &&
(key->algorithm == DST_ALG_RSASHA1 ||
key->algorithm == DST_ALG_NSEC3RSASHA1))
{
cfg_obj_log(obj, ISC_LOG_ERROR,
"dnssec-policy: algorithm %s not supported "
"in FIPS mode",
alg.base);
result = DNS_R_BADALG;
goto cleanup;
}
if (check_algorithms &&
!dst_algorithm_supported(key->algorithm))
{
cfg_obj_log(obj, ISC_LOG_ERROR,
"dnssec-policy: algorithm %s not supported",
alg.base);
result = DNS_R_BADALG;
goto cleanup;
}
switch (key->algorithm) {
case DST_ALG_RSASHA1:
case DST_ALG_NSEC3RSASHA1:
cfg_obj_log(obj, ISC_LOG_WARNING,
"dnssec-policy: DNSSEC algorithm %s is "
"deprecated",
alg.base);
break;
default:
break;
}
obj = cfg_tuple_get(config, "length");
if (cfg_obj_isuint32(obj)) {
uint32_t min, size;
size = cfg_obj_asuint32(obj);
switch (key->algorithm) {
case DST_ALG_RSASHA1:
case DST_ALG_NSEC3RSASHA1:
case DST_ALG_RSASHA256:
case DST_ALG_RSASHA512:
case DST_ALG_RSASHA256PRIVATEOID:
case DST_ALG_RSASHA512PRIVATEOID:
if (isc_crypto_fips_mode()) {
min = 2048;
} else {
min = DST_ALG_RSASHA512 ? 1024 : 512;
}
if (size < min || size > 4096) {
cfg_obj_log(obj, ISC_LOG_ERROR,
"dnssec-policy: key with "
"algorithm %s has invalid "
"key length %u",
alg.base, size);
result = ISC_R_RANGE;
goto cleanup;
}
break;
case DST_ALG_ECDSA256:
case DST_ALG_ECDSA384:
case DST_ALG_ED25519:
case DST_ALG_ED448:
cfg_obj_log(obj, ISC_LOG_WARNING,
"dnssec-policy: key algorithm %s "
"has predefined length; ignoring "
"length value %u",
alg.base, size);
default:
break;
}
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);
return ISC_R_SUCCESS;
cleanup:
dns_kasp_key_destroy(key);
return result;
}
static isc_result_t
cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp) {
unsigned int min_keysize = 4096;
const cfg_obj_t *obj = NULL;
uint32_t iter = DEFAULT_NSEC3PARAM_ITER;
uint32_t saltlen = DEFAULT_NSEC3PARAM_SALTLEN;
uint32_t badalg = 0;
bool optout = false;
/* How many iterations. */
obj = cfg_tuple_get(config, "iterations");
if (cfg_obj_isuint32(obj)) {
iter = cfg_obj_asuint32(obj);
}
dns_kasp_freeze(kasp);
ISC_LIST_FOREACH(dns_kasp_keys(kasp), kkey, link) {
unsigned int keysize = dns_kasp_key_size(kkey);
uint32_t keyalg = dns_kasp_key_algorithm(kkey);
if (keysize < min_keysize) {
min_keysize = keysize;
}
/* NSEC3 cannot be used with certain key algorithms. */
if (keyalg == DST_ALG_RSAMD5 || keyalg == DST_ALG_DSA ||
keyalg == DST_ALG_RSASHA1)
{
badalg = keyalg;
}
}
dns_kasp_thaw(kasp);
if (badalg > 0) {
char algstr[DNS_SECALG_FORMATSIZE];
dns_secalg_format((dns_secalg_t)badalg, algstr, sizeof(algstr));
cfg_obj_log(
obj, ISC_LOG_ERROR,
"dnssec-policy: cannot use nsec3 with algorithm '%s'",
algstr);
return DNS_R_NSEC3BADALG;
}
if (iter != DEFAULT_NSEC3PARAM_ITER) {
cfg_obj_log(obj, ISC_LOG_ERROR,
"dnssec-policy: nsec3 iterations value %u "
"not allowed, must be zero",
iter);
return DNS_R_NSEC3ITERRANGE;
}
/* Opt-out? */
obj = cfg_tuple_get(config, "optout");
if (cfg_obj_isboolean(obj)) {
optout = cfg_obj_asboolean(obj);
}
/* Salt */
obj = cfg_tuple_get(config, "salt-length");
if (cfg_obj_isuint32(obj)) {
saltlen = cfg_obj_asuint32(obj);
}
if (saltlen > 0xff) {
cfg_obj_log(obj, ISC_LOG_ERROR,
"dnssec-policy: nsec3 salt length %u too high",
saltlen);
return DNS_R_NSEC3SALTRANGE;
}
dns_kasp_setnsec3param(kasp, iter, optout, saltlen);
return ISC_R_SUCCESS;
}
static isc_result_t
add_digest(dns_kasp_t *kasp, const cfg_obj_t *digest) {
isc_result_t result = ISC_R_SUCCESS;
isc_textregion_t r;
dns_dsdigest_t alg;
const char *str = cfg_obj_asstring(digest);
r.base = UNCONST(str);
r.length = strlen(str);
result = dns_dsdigest_fromtext(&alg, &r);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(digest, ISC_LOG_ERROR,
"dnssec-policy: bad cds digest-type %s", str);
result = DNS_R_BADALG;
} else if (!dst_ds_digest_supported(alg)) {
cfg_obj_log(digest, ISC_LOG_ERROR,
"dnssec-policy: unsupported cds "
"digest-type %s",
str);
result = DST_R_UNSUPPORTEDALG;
} else {
if (alg == DNS_DSDIGEST_SHA1) {
cfg_obj_log(
digest, ISC_LOG_WARNING,
"dnssec-policy: deprecated CDS digest-type %s",
str);
}
dns_kasp_adddigest(kasp, alg);
}
return result;
}
isc_result_t
cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
bool check_algorithms, isc_mem_t *mctx,
dns_keystorelist_t *keystorelist, dns_kasplist_t *kasplist,
dns_kasp_t **kaspp) {
isc_result_t result;
const cfg_obj_t *maps[2];
const cfg_obj_t *koptions = NULL;
const cfg_obj_t *keys = NULL;
const cfg_obj_t *nsec3 = NULL;
const cfg_obj_t *inlinesigning = NULL;
const cfg_obj_t *cds = NULL;
const cfg_obj_t *obj = NULL;
const char *kaspname = NULL;
dns_kasp_t *kasp = NULL;
size_t i = 0;
uint32_t sigjitter = 0, sigrefresh = 0, sigvalidity = 0;
uint32_t dnskeyttl = 0, dsttl = 0, maxttl = 0;
uint32_t publishsafety = 0, retiresafety = 0;
uint32_t zonepropdelay = 0, parentpropdelay = 0;
uint32_t ipub = 0, iret = 0;
uint32_t ksk_min_lifetime = 0, zsk_min_lifetime = 0;
bool offline_ksk = false, manual_mode = false;
REQUIRE(config != NULL);
REQUIRE(kaspp != NULL && *kaspp == NULL);
kaspname = cfg_obj_asstring(cfg_tuple_get(config, "name"));
INSIST(kaspname != NULL);
cfg_obj_log(config, ISC_LOG_DEBUG(1), "dnssec-policy: load policy '%s'",
kaspname);
result = dns_kasplist_find(kasplist, kaspname, &kasp);
if (result == ISC_R_SUCCESS) {
cfg_obj_log(
config, ISC_LOG_ERROR,
"dnssec-policy: duplicately named policy found '%s'",
kaspname);
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, kaspname, &kasp);
INSIST(kasp != NULL);
/* Now configure. */
INSIST(DNS_KASP_VALID(kasp));
if (config != NULL) {
koptions = cfg_tuple_get(config, "options");
maps[i++] = koptions;
}
maps[i] = NULL;
/* Configuration: Signatures */
sigjitter = get_duration(maps, "signatures-jitter",
DNS_KASP_SIG_JITTER);
dns_kasp_setsigjitter(kasp, sigjitter);
sigrefresh = get_duration(maps, "signatures-refresh",
DNS_KASP_SIG_REFRESH);
dns_kasp_setsigrefresh(kasp, sigrefresh);
sigvalidity = get_duration(maps, "signatures-validity-dnskey",
DNS_KASP_SIG_VALIDITY_DNSKEY);
if (sigrefresh >= (sigvalidity * 0.9)) {
cfg_obj_log(
config, ISC_LOG_ERROR,
"dnssec-policy: policy '%s' signatures-refresh must be "
"at most 90%% of the signatures-validity-dnskey",
kaspname);
result = ISC_R_FAILURE;
}
dns_kasp_setsigvalidity_dnskey(kasp, sigvalidity);
if (sigjitter > sigvalidity) {
cfg_obj_log(
config, ISC_LOG_ERROR,
"dnssec-policy: policy '%s' signatures-jitter cannot "
"be larger than signatures-validity-dnskey",
kaspname);
result = ISC_R_FAILURE;
}
sigvalidity = get_duration(maps, "signatures-validity",
DNS_KASP_SIG_VALIDITY);
if (sigrefresh >= (sigvalidity * 0.9)) {
cfg_obj_log(
config, ISC_LOG_ERROR,
"dnssec-policy: policy '%s' signatures-refresh must be "
"at most 90%% of the signatures-validity",
kaspname);
result = ISC_R_FAILURE;
}
dns_kasp_setsigvalidity(kasp, sigvalidity);
if (sigjitter > sigvalidity) {
cfg_obj_log(
config, ISC_LOG_ERROR,
"dnssec-policy: policy '%s' signatures-jitter cannot "
"be larger than signatures-validity",
kaspname);
result = ISC_R_FAILURE;
}
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/* Configuration: Zone settings */
(void)confget(maps, "inline-signing", &inlinesigning);
if (inlinesigning != NULL && cfg_obj_isboolean(inlinesigning)) {
dns_kasp_setinlinesigning(kasp,
cfg_obj_asboolean(inlinesigning));
} else {
dns_kasp_setinlinesigning(kasp, true);
}
obj = NULL;
(void)confget(maps, "manual-mode", &obj);
if (obj != NULL) {
manual_mode = cfg_obj_asboolean(obj);
}
dns_kasp_setmanualmode(kasp, manual_mode);
maxttl = get_duration(maps, "max-zone-ttl", DNS_KASP_ZONE_MAXTTL);
dns_kasp_setzonemaxttl(kasp, maxttl);
zonepropdelay = get_duration(maps, "zone-propagation-delay",
DNS_KASP_ZONE_PROPDELAY);
dns_kasp_setzonepropagationdelay(kasp, zonepropdelay);
/* Configuration: Parent settings */
dsttl = get_duration(maps, "parent-ds-ttl", DNS_KASP_DS_TTL);
dns_kasp_setdsttl(kasp, dsttl);
parentpropdelay = get_duration(maps, "parent-propagation-delay",
DNS_KASP_PARENT_PROPDELAY);
dns_kasp_setparentpropagationdelay(kasp, parentpropdelay);
/* Configuration: Keys */
obj = NULL;
(void)confget(maps, "offline-ksk", &obj);
if (obj != NULL) {
offline_ksk = cfg_obj_asboolean(obj);
}
dns_kasp_setofflineksk(kasp, offline_ksk);
obj = NULL;
(void)confget(maps, "cdnskey", &obj);
if (obj != NULL) {
dns_kasp_setcdnskey(kasp, cfg_obj_asboolean(obj));
} else {
dns_kasp_setcdnskey(kasp, true);
}
(void)confget(maps, "cds-digest-types", &cds);
if (cds != NULL) {
CFG_LIST_FOREACH(cds, element) {
result = add_digest(kasp, cfg_listelt_value(element));
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
}
} else {
dns_kasp_adddigest(kasp, DNS_DSDIGEST_SHA256);
}
dnskeyttl = get_duration(maps, "dnskey-ttl", DNS_KASP_KEY_TTL);
dns_kasp_setdnskeyttl(kasp, dnskeyttl);
publishsafety = get_duration(maps, "publish-safety",
DNS_KASP_PUBLISH_SAFETY);
dns_kasp_setpublishsafety(kasp, publishsafety);
retiresafety = get_duration(maps, "retire-safety",
DNS_KASP_RETIRE_SAFETY);
dns_kasp_setretiresafety(kasp, retiresafety);
dns_kasp_setpurgekeys(
kasp, get_duration(maps, "purge-keys", DNS_KASP_PURGE_KEYS));
ipub = dnskeyttl + publishsafety + zonepropdelay;
iret = dsttl + retiresafety + parentpropdelay;
ksk_min_lifetime = ISC_MAX(ipub, iret);
iret = (sigvalidity - sigrefresh) + maxttl + retiresafety +
zonepropdelay;
zsk_min_lifetime = ISC_MAX(ipub, iret);
(void)confget(maps, "keys", &keys);
if (keys != NULL) {
char role[DST_MAX_ALGS] = { 0 };
bool warn[DST_MAX_ALGS][2] = { { false } };
CFG_LIST_FOREACH(keys, element) {
cfg_obj_t *kobj = cfg_listelt_value(element);
result = cfg_kaspkey_fromconfig(
kobj, kasp, check_algorithms, offline_ksk,
keystorelist, ksk_min_lifetime,
zsk_min_lifetime);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(kobj, ISC_LOG_ERROR,
"dnssec-policy: failed to "
"configure keys (%s)",
isc_result_totext(result));
goto cleanup;
}
}
dns_kasp_freeze(kasp);
ISC_LIST_FOREACH(dns_kasp_keys(kasp), kkey, link) {
uint32_t keyalg = dns_kasp_key_algorithm(kkey);
INSIST(keyalg < ARRAY_SIZE(role));
if (dns_kasp_key_zsk(kkey)) {
if ((role[keyalg] & DNS_KASP_KEY_ROLE_ZSK) != 0)
{
warn[keyalg][0] = true;
}
role[keyalg] |= DNS_KASP_KEY_ROLE_ZSK;
}
if (dns_kasp_key_ksk(kkey)) {
if ((role[keyalg] & DNS_KASP_KEY_ROLE_KSK) != 0)
{
warn[keyalg][1] = true;
}
role[keyalg] |= DNS_KASP_KEY_ROLE_KSK;
}
}
dns_kasp_thaw(kasp);
for (i = 0; i < ARRAY_SIZE(role); i++) {
if (role[i] == 0) {
continue;
}
if (role[i] !=
(DNS_KASP_KEY_ROLE_ZSK | DNS_KASP_KEY_ROLE_KSK))
{
cfg_obj_log(keys, ISC_LOG_ERROR,
"dnssec-policy: algorithm %zu "
"requires both KSK and ZSK roles",
i);
result = ISC_R_FAILURE;
}
if (warn[i][0]) {
cfg_obj_log(keys, ISC_LOG_WARNING,
"dnssec-policy: algorithm %zu has "
"multiple keys with ZSK role",
i);
}
if (warn[i][1]) {
cfg_obj_log(keys, ISC_LOG_WARNING,
"dnssec-policy: algorithm %zu has "
"multiple keys with KSK role",
i);
}
}
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
} else if (default_kasp) {
/*
* If there are no specific keys configured in the policy,
* inherit from the default policy (except for the built-in
* "insecure" policy).
*/
ISC_LIST_FOREACH(dns_kasp_keys(default_kasp), key, link) {
dns_kasp_key_t *new_key = NULL;
/* Create a new key reference. */
dns_kasp_key_create(kasp, &new_key);
if (dns_kasp_key_ksk(key)) {
new_key->role |= DNS_KASP_KEY_ROLE_KSK;
}
if (dns_kasp_key_zsk(key)) {
new_key->role |= DNS_KASP_KEY_ROLE_ZSK;
}
new_key->lifetime = dns_kasp_key_lifetime(key);
new_key->algorithm = dns_kasp_key_algorithm(key);
new_key->length = dns_kasp_key_size(key);
result = dns_keystorelist_find(
keystorelist, DNS_KEYSTORE_KEYDIRECTORY,
&new_key->keystore);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(config, ISC_LOG_ERROR,
"dnssec-policy: failed to "
"find keystore (%s)",
isc_result_totext(result));
goto cleanup;
}
dns_kasp_addkey(kasp, new_key);
}
}
if (strcmp(kaspname, "insecure") == 0) {
/* "dnssec-policy insecure": key list must be empty */
INSIST(dns_kasp_keylist_empty(kasp));
} else if (default_kasp != NULL) {
/* There must be keys configured. */
INSIST(!(dns_kasp_keylist_empty(kasp)));
}
/* Configuration: NSEC3 */
(void)confget(maps, "nsec3param", &nsec3);
if (nsec3 == NULL) {
if (default_kasp != NULL && dns_kasp_nsec3(default_kasp)) {
dns_kasp_setnsec3param(
kasp, dns_kasp_nsec3iter(default_kasp),
dns_kasp_nsec3flags(default_kasp) == 0x01,
dns_kasp_nsec3saltlen(default_kasp));
} else {
dns_kasp_setnsec3(kasp, false);
}
} else {
dns_kasp_setnsec3(kasp, true);
result = cfg_nsec3param_fromconfig(nsec3, kasp);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
}
/* 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,
dns_keystore_t **kspp) {
isc_result_t result;
const cfg_obj_t *maps[2];
const cfg_obj_t *koptions = NULL;
const char *name = NULL;
const char *keydirectory = DNS_KEYSTORE_KEYDIRECTORY;
dns_keystore_t *keystore = NULL;
int i = 0;
if (config != NULL) {
name = cfg_obj_asstring(cfg_tuple_get(config, "name"));
} else {
name = keydirectory;
}
INSIST(name != NULL);
result = dns_keystorelist_find(keystorelist, name, &keystore);
if (result == ISC_R_SUCCESS) {
cfg_obj_log(config, ISC_LOG_ERROR,
"key-store: duplicate key-store found '%s'", name);
dns_keystore_detach(&keystore);
return ISC_R_EXISTS;
}
if (result != ISC_R_NOTFOUND) {
cfg_obj_log(config, ISC_LOG_ERROR,
"key-store: lookup '%s' failed: %s", name,
isc_result_totext(result));
return result;
}
/*
* No key-store with configured name was found in list, create new one.
*/
INSIST(keystore == NULL);
dns_keystore_create(mctx, name, &keystore);
INSIST(keystore != NULL);
/* Now configure. */
INSIST(DNS_KEYSTORE_VALID(keystore));
if (config != NULL) {
koptions = cfg_tuple_get(config, "options");
maps[i++] = koptions;
maps[i] = NULL;
dns_keystore_setdirectory(keystore,
get_string(maps, "directory"));
dns_keystore_setpkcs11uri(keystore,
get_string(maps, "pkcs11-uri"));
}
/* Append it to the list for future lookups. */
ISC_LIST_APPEND(*keystorelist, keystore, link);
INSIST(!(ISC_LIST_EMPTY(*keystorelist)));
/* Success: Attach the keystore to the pointer and return. */
if (kspp != NULL) {
INSIST(*kspp == NULL);
dns_keystore_attach(keystore, kspp);
}
/* Don't detach as keystore is on '*keystorelist' */
return ISC_R_SUCCESS;
}