xref: /netbsd-src/external/mpl/bind/dist/lib/isccfg/kaspconf.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: kaspconf.c,v 1.9 2025/01/26 16:25:45 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 #include <inttypes.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19 
20 #include <isc/fips.h>
21 #include <isc/mem.h>
22 #include <isc/region.h>
23 #include <isc/result.h>
24 #include <isc/string.h>
25 #include <isc/types.h>
26 #include <isc/util.h>
27 
28 #include <dns/kasp.h>
29 #include <dns/keystore.h>
30 #include <dns/keyvalues.h>
31 #include <dns/log.h>
32 #include <dns/nsec3.h>
33 #include <dns/secalg.h>
34 #include <dns/ttl.h>
35 
36 #include <isccfg/cfg.h>
37 #include <isccfg/duration.h>
38 #include <isccfg/kaspconf.h>
39 #include <isccfg/namedconf.h>
40 
41 #define DEFAULT_NSEC3PARAM_ITER	   0
42 #define DEFAULT_NSEC3PARAM_SALTLEN 0
43 
44 /*
45  * Utility function for getting a configuration option.
46  */
47 static isc_result_t
48 confget(cfg_obj_t const *const *maps, const char *name, const cfg_obj_t **obj) {
49 	for (size_t i = 0;; i++) {
50 		if (maps[i] == NULL) {
51 			return ISC_R_NOTFOUND;
52 		}
53 		if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) {
54 			return ISC_R_SUCCESS;
55 		}
56 	}
57 }
58 
59 /*
60  * Utility function for parsing durations from string.
61  */
62 static uint32_t
63 parse_duration(const char *str) {
64 	uint32_t time = 0;
65 	isccfg_duration_t duration;
66 	isc_result_t result;
67 	isc_textregion_t tr;
68 
69 	tr.base = UNCONST(str);
70 	tr.length = strlen(tr.base);
71 	result = isccfg_parse_duration(&tr, &duration);
72 	if (result == ISC_R_SUCCESS) {
73 		time = isccfg_duration_toseconds(&duration);
74 	}
75 	return time;
76 }
77 
78 /*
79  * Utility function for configuring durations.
80  */
81 static uint32_t
82 get_duration(const cfg_obj_t **maps, const char *option, const char *dfl) {
83 	const cfg_obj_t *obj;
84 	isc_result_t result;
85 	obj = NULL;
86 
87 	result = confget(maps, option, &obj);
88 	if (result == ISC_R_NOTFOUND) {
89 		return parse_duration(dfl);
90 	}
91 	INSIST(result == ISC_R_SUCCESS);
92 	return cfg_obj_asduration(obj);
93 }
94 
95 /*
96  * Utility function for configuring strings.
97  */
98 static const char *
99 get_string(const cfg_obj_t **maps, const char *option) {
100 	const cfg_obj_t *obj;
101 	isc_result_t result;
102 	obj = NULL;
103 
104 	result = confget(maps, option, &obj);
105 	if (result == ISC_R_NOTFOUND) {
106 		return NULL;
107 	}
108 	INSIST(result == ISC_R_SUCCESS);
109 	return cfg_obj_asstring(obj);
110 }
111 
112 /*
113  * Create a new kasp key derived from configuration.
114  */
115 static isc_result_t
116 cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
117 		       bool check_algorithms, isc_log_t *logctx,
118 		       bool offline_ksk, dns_keystorelist_t *keystorelist,
119 		       uint32_t ksk_min_lifetime, uint32_t zsk_min_lifetime) {
120 	isc_result_t result;
121 	dns_kasp_key_t *key = NULL;
122 	const cfg_obj_t *tagrange = NULL;
123 
124 	/* Create a new key reference. */
125 	result = dns_kasp_key_create(kasp, &key);
126 	if (result != ISC_R_SUCCESS) {
127 		return result;
128 	}
129 
130 	if (config == NULL) {
131 		/* We are creating a key reference for the default kasp. */
132 		INSIST(!offline_ksk);
133 		key->role |= DNS_KASP_KEY_ROLE_KSK | DNS_KASP_KEY_ROLE_ZSK;
134 		key->lifetime = 0; /* unlimited */
135 		key->algorithm = DNS_KEYALG_ECDSA256;
136 		key->length = -1;
137 		result = dns_keystorelist_find(keystorelist,
138 					       DNS_KEYSTORE_KEYDIRECTORY,
139 					       &key->keystore);
140 		if (result != ISC_R_SUCCESS) {
141 			goto cleanup;
142 		}
143 	} else {
144 		const char *rolestr = NULL;
145 		const char *keydir = NULL;
146 		const cfg_obj_t *obj = NULL;
147 		isc_consttextregion_t alg;
148 		bool error = false;
149 
150 		rolestr = cfg_obj_asstring(cfg_tuple_get(config, "role"));
151 		if (strcmp(rolestr, "ksk") == 0) {
152 			key->role |= DNS_KASP_KEY_ROLE_KSK;
153 		} else if (strcmp(rolestr, "zsk") == 0) {
154 			key->role |= DNS_KASP_KEY_ROLE_ZSK;
155 		} else if (strcmp(rolestr, "csk") == 0) {
156 			if (offline_ksk) {
157 				cfg_obj_log(
158 					config, logctx, ISC_LOG_ERROR,
159 					"dnssec-policy: csk keys are not "
160 					"allowed when offline-ksk is enabled");
161 				result = ISC_R_FAILURE;
162 				goto cleanup;
163 			}
164 			key->role |= DNS_KASP_KEY_ROLE_KSK;
165 			key->role |= DNS_KASP_KEY_ROLE_ZSK;
166 		}
167 
168 		obj = cfg_tuple_get(config, "keystorage");
169 		if (cfg_obj_isstring(obj)) {
170 			keydir = cfg_obj_asstring(obj);
171 		}
172 		if (keydir == NULL) {
173 			keydir = DNS_KEYSTORE_KEYDIRECTORY;
174 		}
175 		result = dns_keystorelist_find(keystorelist, keydir,
176 					       &key->keystore);
177 		if (result == ISC_R_NOTFOUND) {
178 			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
179 				    "dnssec-policy: keystore %s does not exist",
180 				    keydir);
181 			result = ISC_R_FAILURE;
182 			goto cleanup;
183 		} else if (result != ISC_R_SUCCESS) {
184 			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
185 				    "dnssec-policy: bad keystore %s", keydir);
186 			result = ISC_R_FAILURE;
187 			goto cleanup;
188 		}
189 		INSIST(key->keystore != NULL);
190 
191 		key->lifetime = 0; /* unlimited */
192 		obj = cfg_tuple_get(config, "lifetime");
193 		if (cfg_obj_isduration(obj)) {
194 			key->lifetime = cfg_obj_asduration(obj);
195 		}
196 		if (key->lifetime > 0) {
197 			if (key->lifetime < 30 * (24 * 3600)) {
198 				cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
199 					    "dnssec-policy: key lifetime is "
200 					    "shorter than 30 days");
201 			}
202 			if ((key->role & DNS_KASP_KEY_ROLE_KSK) != 0 &&
203 			    key->lifetime <= ksk_min_lifetime)
204 			{
205 				error = true;
206 			}
207 			if ((key->role & DNS_KASP_KEY_ROLE_ZSK) != 0 &&
208 			    key->lifetime <= zsk_min_lifetime)
209 			{
210 				error = true;
211 			}
212 			if (error) {
213 				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
214 					    "dnssec-policy: key lifetime is "
215 					    "shorter than the time it takes to "
216 					    "do a rollover");
217 				result = ISC_R_FAILURE;
218 				goto cleanup;
219 			}
220 		}
221 
222 		obj = cfg_tuple_get(config, "algorithm");
223 		alg.base = cfg_obj_asstring(obj);
224 		alg.length = strlen(alg.base);
225 		result = dns_secalg_fromtext(&key->algorithm,
226 					     (isc_textregion_t *)&alg);
227 		if (result != ISC_R_SUCCESS) {
228 			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
229 				    "dnssec-policy: bad algorithm %s",
230 				    alg.base);
231 			result = DNS_R_BADALG;
232 			goto cleanup;
233 		}
234 
235 		if (check_algorithms && isc_fips_mode() &&
236 		    (key->algorithm == DNS_KEYALG_RSASHA1 ||
237 		     key->algorithm == DNS_KEYALG_NSEC3RSASHA1))
238 		{
239 			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
240 				    "dnssec-policy: algorithm %s not supported "
241 				    "in FIPS mode",
242 				    alg.base);
243 			result = DNS_R_BADALG;
244 			goto cleanup;
245 		}
246 
247 		if (check_algorithms &&
248 		    !dst_algorithm_supported(key->algorithm))
249 		{
250 			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
251 				    "dnssec-policy: algorithm %s not supported",
252 				    alg.base);
253 			result = DNS_R_BADALG;
254 			goto cleanup;
255 		}
256 
257 		obj = cfg_tuple_get(config, "length");
258 		if (cfg_obj_isuint32(obj)) {
259 			uint32_t min, size;
260 			size = cfg_obj_asuint32(obj);
261 
262 			switch (key->algorithm) {
263 			case DNS_KEYALG_RSASHA1:
264 			case DNS_KEYALG_NSEC3RSASHA1:
265 			case DNS_KEYALG_RSASHA256:
266 			case DNS_KEYALG_RSASHA512:
267 				if (isc_fips_mode()) {
268 					min = 2048;
269 				} else {
270 					min = DNS_KEYALG_RSASHA512 ? 1024 : 512;
271 				}
272 				if (size < min || size > 4096) {
273 					cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
274 						    "dnssec-policy: key with "
275 						    "algorithm %s has invalid "
276 						    "key length %u",
277 						    alg.base, size);
278 					result = ISC_R_RANGE;
279 					goto cleanup;
280 				}
281 				break;
282 			case DNS_KEYALG_ECDSA256:
283 			case DNS_KEYALG_ECDSA384:
284 			case DNS_KEYALG_ED25519:
285 			case DNS_KEYALG_ED448:
286 				cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
287 					    "dnssec-policy: key algorithm %s "
288 					    "has predefined length; ignoring "
289 					    "length value %u",
290 					    alg.base, size);
291 			default:
292 				break;
293 			}
294 
295 			key->length = size;
296 		}
297 
298 		tagrange = cfg_tuple_get(config, "tag-range");
299 		if (cfg_obj_istuple(tagrange)) {
300 			uint32_t tag_min = 0, tag_max = 0xffff;
301 			obj = cfg_tuple_get(tagrange, "tag-min");
302 			tag_min = cfg_obj_asuint32(obj);
303 			if (tag_min > 0xffff) {
304 				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
305 					    "dnssec-policy: tag-min "
306 					    "too big");
307 				result = ISC_R_RANGE;
308 				goto cleanup;
309 			}
310 			obj = cfg_tuple_get(tagrange, "tag-max");
311 			tag_max = cfg_obj_asuint32(obj);
312 			if (tag_max > 0xffff) {
313 				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
314 					    "dnssec-policy: tag-max "
315 					    "too big");
316 				result = ISC_R_RANGE;
317 				goto cleanup;
318 			}
319 			if (tag_min >= tag_max) {
320 				cfg_obj_log(
321 					obj, logctx, ISC_LOG_ERROR,
322 					"dnssec-policy: tag-min >= tag_max");
323 				result = ISC_R_RANGE;
324 				goto cleanup;
325 			}
326 			key->tag_min = tag_min;
327 			key->tag_max = tag_max;
328 		}
329 	}
330 
331 	dns_kasp_addkey(kasp, key);
332 	return ISC_R_SUCCESS;
333 
334 cleanup:
335 
336 	dns_kasp_key_destroy(key);
337 	return result;
338 }
339 
340 static isc_result_t
341 cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
342 			  isc_log_t *logctx) {
343 	dns_kasp_key_t *kkey;
344 	unsigned int min_keysize = 4096;
345 	const cfg_obj_t *obj = NULL;
346 	uint32_t iter = DEFAULT_NSEC3PARAM_ITER;
347 	uint32_t saltlen = DEFAULT_NSEC3PARAM_SALTLEN;
348 	uint32_t badalg = 0;
349 	bool optout = false;
350 
351 	/* How many iterations. */
352 	obj = cfg_tuple_get(config, "iterations");
353 	if (cfg_obj_isuint32(obj)) {
354 		iter = cfg_obj_asuint32(obj);
355 	}
356 	dns_kasp_freeze(kasp);
357 	for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL;
358 	     kkey = ISC_LIST_NEXT(kkey, link))
359 	{
360 		unsigned int keysize = dns_kasp_key_size(kkey);
361 		uint32_t keyalg = dns_kasp_key_algorithm(kkey);
362 
363 		if (keysize < min_keysize) {
364 			min_keysize = keysize;
365 		}
366 
367 		/* NSEC3 cannot be used with certain key algorithms. */
368 		if (keyalg == DNS_KEYALG_RSAMD5 || keyalg == DNS_KEYALG_DSA ||
369 		    keyalg == DNS_KEYALG_RSASHA1)
370 		{
371 			badalg = keyalg;
372 		}
373 	}
374 	dns_kasp_thaw(kasp);
375 
376 	if (badalg > 0) {
377 		char algstr[DNS_SECALG_FORMATSIZE];
378 		dns_secalg_format((dns_secalg_t)badalg, algstr, sizeof(algstr));
379 		cfg_obj_log(
380 			obj, logctx, ISC_LOG_ERROR,
381 			"dnssec-policy: cannot use nsec3 with algorithm '%s'",
382 			algstr);
383 		return DNS_R_NSEC3BADALG;
384 	}
385 
386 	if (iter != DEFAULT_NSEC3PARAM_ITER) {
387 		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
388 			    "dnssec-policy: nsec3 iterations value %u "
389 			    "not allowed, must be zero",
390 			    iter);
391 		return DNS_R_NSEC3ITERRANGE;
392 	}
393 
394 	/* Opt-out? */
395 	obj = cfg_tuple_get(config, "optout");
396 	if (cfg_obj_isboolean(obj)) {
397 		optout = cfg_obj_asboolean(obj);
398 	}
399 
400 	/* Salt */
401 	obj = cfg_tuple_get(config, "salt-length");
402 	if (cfg_obj_isuint32(obj)) {
403 		saltlen = cfg_obj_asuint32(obj);
404 	}
405 	if (saltlen > 0xff) {
406 		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
407 			    "dnssec-policy: nsec3 salt length %u too high",
408 			    saltlen);
409 		return DNS_R_NSEC3SALTRANGE;
410 	}
411 
412 	dns_kasp_setnsec3param(kasp, iter, optout, saltlen);
413 	return ISC_R_SUCCESS;
414 }
415 
416 static isc_result_t
417 add_digest(dns_kasp_t *kasp, const cfg_obj_t *digest, isc_log_t *logctx) {
418 	isc_result_t result = ISC_R_SUCCESS;
419 	isc_textregion_t r;
420 	dns_dsdigest_t alg;
421 	const char *str = cfg_obj_asstring(digest);
422 
423 	r.base = UNCONST(str);
424 	r.length = strlen(str);
425 	result = dns_dsdigest_fromtext(&alg, &r);
426 	if (result != ISC_R_SUCCESS) {
427 		cfg_obj_log(digest, logctx, ISC_LOG_ERROR,
428 			    "dnssec-policy: bad cds digest-type %s", str);
429 		result = DNS_R_BADALG;
430 	} else if (!dst_ds_digest_supported(alg)) {
431 		cfg_obj_log(digest, logctx, ISC_LOG_ERROR,
432 			    "dnssec-policy: unsupported cds "
433 			    "digest-type %s",
434 			    str);
435 		result = DST_R_UNSUPPORTEDALG;
436 	} else {
437 		dns_kasp_adddigest(kasp, alg);
438 	}
439 	return result;
440 }
441 
442 isc_result_t
443 cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
444 		    bool check_algorithms, isc_mem_t *mctx, isc_log_t *logctx,
445 		    dns_keystorelist_t *keystorelist, dns_kasplist_t *kasplist,
446 		    dns_kasp_t **kaspp) {
447 	isc_result_t result;
448 	const cfg_obj_t *maps[2];
449 	const cfg_obj_t *koptions = NULL;
450 	const cfg_obj_t *keys = NULL;
451 	const cfg_obj_t *nsec3 = NULL;
452 	const cfg_obj_t *inlinesigning = NULL;
453 	const cfg_obj_t *cds = NULL;
454 	const cfg_obj_t *obj = NULL;
455 	const cfg_listelt_t *element = NULL;
456 	const char *kaspname = NULL;
457 	dns_kasp_t *kasp = NULL;
458 	size_t i = 0;
459 	uint32_t sigjitter = 0, sigrefresh = 0, sigvalidity = 0;
460 	uint32_t dnskeyttl = 0, dsttl = 0, maxttl = 0;
461 	uint32_t publishsafety = 0, retiresafety = 0;
462 	uint32_t zonepropdelay = 0, parentpropdelay = 0;
463 	uint32_t ipub = 0, iret = 0;
464 	uint32_t ksk_min_lifetime = 0, zsk_min_lifetime = 0;
465 	bool offline_ksk = false;
466 
467 	REQUIRE(config != NULL);
468 	REQUIRE(kaspp != NULL && *kaspp == NULL);
469 
470 	kaspname = cfg_obj_asstring(cfg_tuple_get(config, "name"));
471 	INSIST(kaspname != NULL);
472 
473 	cfg_obj_log(config, logctx, ISC_LOG_DEBUG(1),
474 		    "dnssec-policy: load policy '%s'", kaspname);
475 
476 	result = dns_kasplist_find(kasplist, kaspname, &kasp);
477 
478 	if (result == ISC_R_SUCCESS) {
479 		cfg_obj_log(
480 			config, logctx, ISC_LOG_ERROR,
481 			"dnssec-policy: duplicately named policy found '%s'",
482 			kaspname);
483 		dns_kasp_detach(&kasp);
484 		return ISC_R_EXISTS;
485 	}
486 	if (result != ISC_R_NOTFOUND) {
487 		return result;
488 	}
489 
490 	/* No kasp with configured name was found in list, create new one. */
491 	INSIST(kasp == NULL);
492 	result = dns_kasp_create(mctx, kaspname, &kasp);
493 	if (result != ISC_R_SUCCESS) {
494 		return result;
495 	}
496 	INSIST(kasp != NULL);
497 
498 	/* Now configure. */
499 	INSIST(DNS_KASP_VALID(kasp));
500 
501 	if (config != NULL) {
502 		koptions = cfg_tuple_get(config, "options");
503 		maps[i++] = koptions;
504 	}
505 	maps[i] = NULL;
506 
507 	/* Configuration: Signatures */
508 	sigjitter = get_duration(maps, "signatures-jitter",
509 				 DNS_KASP_SIG_JITTER);
510 	dns_kasp_setsigjitter(kasp, sigjitter);
511 
512 	sigrefresh = get_duration(maps, "signatures-refresh",
513 				  DNS_KASP_SIG_REFRESH);
514 	dns_kasp_setsigrefresh(kasp, sigrefresh);
515 
516 	sigvalidity = get_duration(maps, "signatures-validity-dnskey",
517 				   DNS_KASP_SIG_VALIDITY_DNSKEY);
518 	if (sigrefresh >= (sigvalidity * 0.9)) {
519 		cfg_obj_log(
520 			config, logctx, ISC_LOG_ERROR,
521 			"dnssec-policy: policy '%s' signatures-refresh must be "
522 			"at most 90%% of the signatures-validity-dnskey",
523 			kaspname);
524 		result = ISC_R_FAILURE;
525 	}
526 	dns_kasp_setsigvalidity_dnskey(kasp, sigvalidity);
527 
528 	if (sigjitter > sigvalidity) {
529 		cfg_obj_log(
530 			config, logctx, ISC_LOG_ERROR,
531 			"dnssec-policy: policy '%s' signatures-jitter cannot "
532 			"be larger than signatures-validity-dnskey",
533 			kaspname);
534 		result = ISC_R_FAILURE;
535 	}
536 
537 	sigvalidity = get_duration(maps, "signatures-validity",
538 				   DNS_KASP_SIG_VALIDITY);
539 	if (sigrefresh >= (sigvalidity * 0.9)) {
540 		cfg_obj_log(
541 			config, logctx, ISC_LOG_ERROR,
542 			"dnssec-policy: policy '%s' signatures-refresh must be "
543 			"at most 90%% of the signatures-validity",
544 			kaspname);
545 		result = ISC_R_FAILURE;
546 	}
547 	dns_kasp_setsigvalidity(kasp, sigvalidity);
548 
549 	if (sigjitter > sigvalidity) {
550 		cfg_obj_log(
551 			config, logctx, ISC_LOG_ERROR,
552 			"dnssec-policy: policy '%s' signatures-jitter cannot "
553 			"be larger than signatures-validity",
554 			kaspname);
555 		result = ISC_R_FAILURE;
556 	}
557 
558 	if (result != ISC_R_SUCCESS) {
559 		goto cleanup;
560 	}
561 
562 	/* Configuration: Zone settings */
563 	(void)confget(maps, "inline-signing", &inlinesigning);
564 	if (inlinesigning != NULL && cfg_obj_isboolean(inlinesigning)) {
565 		dns_kasp_setinlinesigning(kasp,
566 					  cfg_obj_asboolean(inlinesigning));
567 	} else {
568 		dns_kasp_setinlinesigning(kasp, true);
569 	}
570 
571 	maxttl = get_duration(maps, "max-zone-ttl", DNS_KASP_ZONE_MAXTTL);
572 	dns_kasp_setzonemaxttl(kasp, maxttl);
573 
574 	zonepropdelay = get_duration(maps, "zone-propagation-delay",
575 				     DNS_KASP_ZONE_PROPDELAY);
576 	dns_kasp_setzonepropagationdelay(kasp, zonepropdelay);
577 
578 	/* Configuration: Parent settings */
579 	dsttl = get_duration(maps, "parent-ds-ttl", DNS_KASP_DS_TTL);
580 	dns_kasp_setdsttl(kasp, dsttl);
581 
582 	parentpropdelay = get_duration(maps, "parent-propagation-delay",
583 				       DNS_KASP_PARENT_PROPDELAY);
584 	dns_kasp_setparentpropagationdelay(kasp, parentpropdelay);
585 
586 	/* Configuration: Keys */
587 	obj = NULL;
588 	(void)confget(maps, "offline-ksk", &obj);
589 	if (obj != NULL) {
590 		offline_ksk = cfg_obj_asboolean(obj);
591 	}
592 	dns_kasp_setofflineksk(kasp, offline_ksk);
593 
594 	obj = NULL;
595 	(void)confget(maps, "cdnskey", &obj);
596 	if (obj != NULL) {
597 		dns_kasp_setcdnskey(kasp, cfg_obj_asboolean(obj));
598 	} else {
599 		dns_kasp_setcdnskey(kasp, true);
600 	}
601 
602 	(void)confget(maps, "cds-digest-types", &cds);
603 	if (cds != NULL) {
604 		for (element = cfg_list_first(cds); element != NULL;
605 		     element = cfg_list_next(element))
606 		{
607 			result = add_digest(kasp, cfg_listelt_value(element),
608 					    logctx);
609 			if (result != ISC_R_SUCCESS) {
610 				goto cleanup;
611 			}
612 		}
613 	} else {
614 		dns_kasp_adddigest(kasp, DNS_DSDIGEST_SHA256);
615 	}
616 
617 	dnskeyttl = get_duration(maps, "dnskey-ttl", DNS_KASP_KEY_TTL);
618 	dns_kasp_setdnskeyttl(kasp, dnskeyttl);
619 
620 	publishsafety = get_duration(maps, "publish-safety",
621 				     DNS_KASP_PUBLISH_SAFETY);
622 	dns_kasp_setpublishsafety(kasp, publishsafety);
623 
624 	retiresafety = get_duration(maps, "retire-safety",
625 				    DNS_KASP_RETIRE_SAFETY);
626 	dns_kasp_setretiresafety(kasp, retiresafety);
627 
628 	dns_kasp_setpurgekeys(
629 		kasp, get_duration(maps, "purge-keys", DNS_KASP_PURGE_KEYS));
630 
631 	ipub = dnskeyttl + publishsafety + zonepropdelay;
632 	iret = dsttl + retiresafety + parentpropdelay;
633 	ksk_min_lifetime = ISC_MAX(ipub, iret);
634 
635 	iret = (sigvalidity - sigrefresh) + maxttl + retiresafety +
636 	       zonepropdelay;
637 	zsk_min_lifetime = ISC_MAX(ipub, iret);
638 
639 	(void)confget(maps, "keys", &keys);
640 	if (keys != NULL) {
641 		char role[256] = { 0 };
642 		bool warn[256][2] = { { false } };
643 		dns_kasp_key_t *kkey = NULL;
644 
645 		for (element = cfg_list_first(keys); element != NULL;
646 		     element = cfg_list_next(element))
647 		{
648 			cfg_obj_t *kobj = cfg_listelt_value(element);
649 			result = cfg_kaspkey_fromconfig(
650 				kobj, kasp, check_algorithms, logctx,
651 				offline_ksk, keystorelist, ksk_min_lifetime,
652 				zsk_min_lifetime);
653 			if (result != ISC_R_SUCCESS) {
654 				cfg_obj_log(kobj, logctx, ISC_LOG_ERROR,
655 					    "dnssec-policy: failed to "
656 					    "configure keys (%s)",
657 					    isc_result_totext(result));
658 				goto cleanup;
659 			}
660 		}
661 		dns_kasp_freeze(kasp);
662 		for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL;
663 		     kkey = ISC_LIST_NEXT(kkey, link))
664 		{
665 			uint32_t keyalg = dns_kasp_key_algorithm(kkey);
666 			INSIST(keyalg < ARRAY_SIZE(role));
667 
668 			if (dns_kasp_key_zsk(kkey)) {
669 				if ((role[keyalg] & DNS_KASP_KEY_ROLE_ZSK) != 0)
670 				{
671 					warn[keyalg][0] = true;
672 				}
673 				role[keyalg] |= DNS_KASP_KEY_ROLE_ZSK;
674 			}
675 
676 			if (dns_kasp_key_ksk(kkey)) {
677 				if ((role[keyalg] & DNS_KASP_KEY_ROLE_KSK) != 0)
678 				{
679 					warn[keyalg][1] = true;
680 				}
681 				role[keyalg] |= DNS_KASP_KEY_ROLE_KSK;
682 			}
683 		}
684 		dns_kasp_thaw(kasp);
685 		for (i = 0; i < ARRAY_SIZE(role); i++) {
686 			if (role[i] == 0) {
687 				continue;
688 			}
689 			if (role[i] !=
690 			    (DNS_KASP_KEY_ROLE_ZSK | DNS_KASP_KEY_ROLE_KSK))
691 			{
692 				cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
693 					    "dnssec-policy: algorithm %zu "
694 					    "requires both KSK and ZSK roles",
695 					    i);
696 				result = ISC_R_FAILURE;
697 			}
698 			if (warn[i][0]) {
699 				cfg_obj_log(keys, logctx, ISC_LOG_WARNING,
700 					    "dnssec-policy: algorithm %zu has "
701 					    "multiple keys with ZSK role",
702 					    i);
703 			}
704 			if (warn[i][1]) {
705 				cfg_obj_log(keys, logctx, ISC_LOG_WARNING,
706 					    "dnssec-policy: algorithm %zu has "
707 					    "multiple keys with KSK role",
708 					    i);
709 			}
710 		}
711 		if (result != ISC_R_SUCCESS) {
712 			goto cleanup;
713 		}
714 	} else if (default_kasp) {
715 		dns_kasp_key_t *key, *new_key;
716 		/*
717 		 * If there are no specific keys configured in the policy,
718 		 * inherit from the default policy (except for the built-in
719 		 * "insecure" policy).
720 		 */
721 		for (key = ISC_LIST_HEAD(dns_kasp_keys(default_kasp));
722 		     key != NULL; key = ISC_LIST_NEXT(key, link))
723 		{
724 			/* Create a new key reference. */
725 			new_key = NULL;
726 			result = dns_kasp_key_create(kasp, &new_key);
727 			if (result != ISC_R_SUCCESS) {
728 				cfg_obj_log(config, logctx, ISC_LOG_ERROR,
729 					    "dnssec-policy: failed to "
730 					    "configure keys (%s)",
731 					    isc_result_totext(result));
732 				goto cleanup;
733 			}
734 			if (dns_kasp_key_ksk(key)) {
735 				new_key->role |= DNS_KASP_KEY_ROLE_KSK;
736 			}
737 			if (dns_kasp_key_zsk(key)) {
738 				new_key->role |= DNS_KASP_KEY_ROLE_ZSK;
739 			}
740 			new_key->lifetime = dns_kasp_key_lifetime(key);
741 			new_key->algorithm = dns_kasp_key_algorithm(key);
742 			new_key->length = dns_kasp_key_size(key);
743 			result = dns_keystorelist_find(
744 				keystorelist, DNS_KEYSTORE_KEYDIRECTORY,
745 				&new_key->keystore);
746 			if (result != ISC_R_SUCCESS) {
747 				cfg_obj_log(config, logctx, ISC_LOG_ERROR,
748 					    "dnssec-policy: failed to "
749 					    "find keystore (%s)",
750 					    isc_result_totext(result));
751 				goto cleanup;
752 			}
753 			dns_kasp_addkey(kasp, new_key);
754 		}
755 	}
756 
757 	if (strcmp(kaspname, "insecure") == 0) {
758 		/* "dnssec-policy insecure": key list must be empty */
759 		INSIST(dns_kasp_keylist_empty(kasp));
760 	} else if (default_kasp != NULL) {
761 		/* There must be keys configured. */
762 		INSIST(!(dns_kasp_keylist_empty(kasp)));
763 	}
764 
765 	/* Configuration: NSEC3 */
766 	(void)confget(maps, "nsec3param", &nsec3);
767 	if (nsec3 == NULL) {
768 		if (default_kasp != NULL && dns_kasp_nsec3(default_kasp)) {
769 			dns_kasp_setnsec3param(
770 				kasp, dns_kasp_nsec3iter(default_kasp),
771 				(dns_kasp_nsec3flags(default_kasp) == 0x01),
772 				dns_kasp_nsec3saltlen(default_kasp));
773 		} else {
774 			dns_kasp_setnsec3(kasp, false);
775 		}
776 	} else {
777 		dns_kasp_setnsec3(kasp, true);
778 		result = cfg_nsec3param_fromconfig(nsec3, kasp, logctx);
779 		if (result != ISC_R_SUCCESS) {
780 			goto cleanup;
781 		}
782 	}
783 
784 	/* Append it to the list for future lookups. */
785 	ISC_LIST_APPEND(*kasplist, kasp, link);
786 	INSIST(!(ISC_LIST_EMPTY(*kasplist)));
787 
788 	/* Success: Attach the kasp to the pointer and return. */
789 	dns_kasp_attach(kasp, kaspp);
790 
791 	/* Don't detach as kasp is on '*kasplist' */
792 	return ISC_R_SUCCESS;
793 
794 cleanup:
795 
796 	/* Something bad happened, detach (destroys kasp) and return error. */
797 	dns_kasp_detach(&kasp);
798 	return result;
799 }
800 
801 isc_result_t
802 cfg_keystore_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx,
803 			isc_log_t *logctx, const char *engine,
804 			dns_keystorelist_t *keystorelist,
805 			dns_keystore_t **kspp) {
806 	isc_result_t result;
807 	const cfg_obj_t *maps[2];
808 	const cfg_obj_t *koptions = NULL;
809 	const char *name = NULL;
810 	const char *keydirectory = DNS_KEYSTORE_KEYDIRECTORY;
811 	dns_keystore_t *keystore = NULL;
812 	int i = 0;
813 
814 	if (config != NULL) {
815 		name = cfg_obj_asstring(cfg_tuple_get(config, "name"));
816 	} else {
817 		name = keydirectory;
818 	}
819 	INSIST(name != NULL);
820 
821 	result = dns_keystorelist_find(keystorelist, name, &keystore);
822 
823 	if (result == ISC_R_SUCCESS) {
824 		cfg_obj_log(config, logctx, ISC_LOG_ERROR,
825 			    "key-store: duplicate key-store found '%s'", name);
826 		dns_keystore_detach(&keystore);
827 		return ISC_R_EXISTS;
828 	}
829 	if (result != ISC_R_NOTFOUND) {
830 		cfg_obj_log(config, logctx, ISC_LOG_ERROR,
831 			    "key-store: lookup '%s' failed: %s", name,
832 			    isc_result_totext(result));
833 		return result;
834 	}
835 
836 	/*
837 	 * No key-store with configured name was found in list, create new one.
838 	 */
839 	INSIST(keystore == NULL);
840 	result = dns_keystore_create(mctx, name, engine, &keystore);
841 	if (result != ISC_R_SUCCESS) {
842 		return result;
843 	}
844 	INSIST(keystore != NULL);
845 
846 	/* Now configure. */
847 	INSIST(DNS_KEYSTORE_VALID(keystore));
848 
849 	if (config != NULL) {
850 		koptions = cfg_tuple_get(config, "options");
851 		maps[i++] = koptions;
852 		maps[i] = NULL;
853 		dns_keystore_setdirectory(keystore,
854 					  get_string(maps, "directory"));
855 		dns_keystore_setpkcs11uri(keystore,
856 					  get_string(maps, "pkcs11-uri"));
857 	}
858 
859 	/* Append it to the list for future lookups. */
860 	ISC_LIST_APPEND(*keystorelist, keystore, link);
861 	INSIST(!(ISC_LIST_EMPTY(*keystorelist)));
862 
863 	/* Success: Attach the keystore to the pointer and return. */
864 	if (kspp != NULL) {
865 		INSIST(*kspp == NULL);
866 		dns_keystore_attach(keystore, kspp);
867 	}
868 
869 	/* Don't detach as keystore is on '*keystorelist' */
870 	return ISC_R_SUCCESS;
871 }
872