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