1
0
Fork 0
bind9/lib/isccfg/kaspconf.c
Daniel Baumann f66ff7eae6
Adding upstream version 1:9.20.9.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 13:32:37 +02:00

869 lines
23 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/fips.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/log.h>
#include <dns/nsec3.h>
#include <dns/secalg.h>
#include <dns/ttl.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, isc_log_t *logctx,
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. */
result = dns_kasp_key_create(kasp, &key);
if (result != ISC_R_SUCCESS) {
return result;
}
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 = DNS_KEYALG_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, logctx, 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, logctx, 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, logctx, 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, logctx, 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, logctx, 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 = dns_secalg_fromtext(&key->algorithm,
(isc_textregion_t *)&alg);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
"dnssec-policy: bad algorithm %s",
alg.base);
result = DNS_R_BADALG;
goto cleanup;
}
if (check_algorithms && isc_fips_mode() &&
(key->algorithm == DNS_KEYALG_RSASHA1 ||
key->algorithm == DNS_KEYALG_NSEC3RSASHA1))
{
cfg_obj_log(obj, logctx, 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, logctx, ISC_LOG_ERROR,
"dnssec-policy: algorithm %s not supported",
alg.base);
result = DNS_R_BADALG;
goto cleanup;
}
obj = cfg_tuple_get(config, "length");
if (cfg_obj_isuint32(obj)) {
uint32_t min, size;
size = cfg_obj_asuint32(obj);
switch (key->algorithm) {
case DNS_KEYALG_RSASHA1:
case DNS_KEYALG_NSEC3RSASHA1:
case DNS_KEYALG_RSASHA256:
case DNS_KEYALG_RSASHA512:
if (isc_fips_mode()) {
min = 2048;
} else {
min = DNS_KEYALG_RSASHA512 ? 1024 : 512;
}
if (size < min || size > 4096) {
cfg_obj_log(obj, logctx, 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 DNS_KEYALG_ECDSA256:
case DNS_KEYALG_ECDSA384:
case DNS_KEYALG_ED25519:
case DNS_KEYALG_ED448:
cfg_obj_log(obj, logctx, 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, logctx, 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, logctx, ISC_LOG_ERROR,
"dnssec-policy: tag-max "
"too big");
result = ISC_R_RANGE;
goto cleanup;
}
if (tag_min >= tag_max) {
cfg_obj_log(
obj, logctx, 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,
isc_log_t *logctx) {
dns_kasp_key_t *kkey;
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);
for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL;
kkey = ISC_LIST_NEXT(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 == DNS_KEYALG_RSAMD5 || keyalg == DNS_KEYALG_DSA ||
keyalg == DNS_KEYALG_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, logctx, 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, logctx, 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, logctx, 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_log_t *logctx) {
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, logctx, 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, logctx, ISC_LOG_ERROR,
"dnssec-policy: unsupported cds "
"digest-type %s",
str);
result = DST_R_UNSUPPORTEDALG;
} else {
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, isc_log_t *logctx,
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 cfg_listelt_t *element = 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;
REQUIRE(config != NULL);
REQUIRE(kaspp != NULL && *kaspp == NULL);
kaspname = cfg_obj_asstring(cfg_tuple_get(config, "name"));
INSIST(kaspname != NULL);
cfg_obj_log(config, logctx, 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, logctx, 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;
}
/* No kasp with configured name was found in list, create new one. */
INSIST(kasp == NULL);
result = dns_kasp_create(mctx, kaspname, &kasp);
if (result != ISC_R_SUCCESS) {
return result;
}
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, logctx, 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, logctx, 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, logctx, 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, logctx, 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);
}
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) {
for (element = cfg_list_first(cds); element != NULL;
element = cfg_list_next(element))
{
result = add_digest(kasp, cfg_listelt_value(element),
logctx);
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[256] = { 0 };
bool warn[256][2] = { { false } };
dns_kasp_key_t *kkey = NULL;
for (element = cfg_list_first(keys); element != NULL;
element = cfg_list_next(element))
{
cfg_obj_t *kobj = cfg_listelt_value(element);
result = cfg_kaspkey_fromconfig(
kobj, kasp, check_algorithms, logctx,
offline_ksk, keystorelist, ksk_min_lifetime,
zsk_min_lifetime);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(kobj, logctx, ISC_LOG_ERROR,
"dnssec-policy: failed to "
"configure keys (%s)",
isc_result_totext(result));
goto cleanup;
}
}
dns_kasp_freeze(kasp);
for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL;
kkey = ISC_LIST_NEXT(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, logctx, 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, logctx, ISC_LOG_WARNING,
"dnssec-policy: algorithm %zu has "
"multiple keys with ZSK role",
i);
}
if (warn[i][1]) {
cfg_obj_log(keys, logctx, 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) {
dns_kasp_key_t *key, *new_key;
/*
* If there are no specific keys configured in the policy,
* inherit from the default policy (except for the built-in
* "insecure" policy).
*/
for (key = ISC_LIST_HEAD(dns_kasp_keys(default_kasp));
key != NULL; key = ISC_LIST_NEXT(key, link))
{
/* Create a new key reference. */
new_key = NULL;
result = dns_kasp_key_create(kasp, &new_key);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(config, logctx, ISC_LOG_ERROR,
"dnssec-policy: failed to "
"configure keys (%s)",
isc_result_totext(result));
goto cleanup;
}
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, logctx, 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, logctx);
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,
isc_log_t *logctx, const char *engine,
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, logctx, 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, logctx, 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);
result = dns_keystore_create(mctx, name, engine, &keystore);
if (result != ISC_R_SUCCESS) {
return result;
}
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;
}