1 /* $NetBSD: kasp.c,v 1.9 2026/01/29 18:37:49 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 /*! \file */ 17 18 #include <string.h> 19 20 #include <isc/assertions.h> 21 #include <isc/buffer.h> 22 #include <isc/file.h> 23 #include <isc/hex.h> 24 #include <isc/log.h> 25 #include <isc/mem.h> 26 #include <isc/util.h> 27 28 #include <dns/kasp.h> 29 #include <dns/keyvalues.h> 30 #include <dns/log.h> 31 32 #include <dst/dst.h> 33 34 /* Default TTLsig (maximum zone ttl) */ 35 #define DEFAULT_TTLSIG 604800 /* one week */ 36 37 isc_result_t 38 dns_kasp_create(isc_mem_t *mctx, const char *name, dns_kasp_t **kaspp) { 39 dns_kasp_t *kasp; 40 dns_kasp_t k = { 41 .magic = DNS_KASP_MAGIC, 42 .digests = ISC_LIST_INITIALIZER, 43 .keys = ISC_LIST_INITIALIZER, 44 .link = ISC_LINK_INITIALIZER, 45 }; 46 47 REQUIRE(name != NULL); 48 REQUIRE(kaspp != NULL && *kaspp == NULL); 49 50 kasp = isc_mem_get(mctx, sizeof(*kasp)); 51 *kasp = k; 52 53 kasp->mctx = NULL; 54 isc_mem_attach(mctx, &kasp->mctx); 55 kasp->name = isc_mem_strdup(mctx, name); 56 isc_mutex_init(&kasp->lock); 57 isc_refcount_init(&kasp->references, 1); 58 59 *kaspp = kasp; 60 return ISC_R_SUCCESS; 61 } 62 63 void 64 dns_kasp_attach(dns_kasp_t *source, dns_kasp_t **targetp) { 65 REQUIRE(DNS_KASP_VALID(source)); 66 REQUIRE(targetp != NULL && *targetp == NULL); 67 68 isc_refcount_increment(&source->references); 69 *targetp = source; 70 } 71 72 static void 73 destroy(dns_kasp_t *kasp) { 74 dns_kasp_key_t *key, *key_next; 75 dns_kasp_digest_t *digest, *digest_next; 76 77 REQUIRE(!ISC_LINK_LINKED(kasp, link)); 78 79 for (key = ISC_LIST_HEAD(kasp->keys); key != NULL; key = key_next) { 80 key_next = ISC_LIST_NEXT(key, link); 81 ISC_LIST_UNLINK(kasp->keys, key, link); 82 dns_kasp_key_destroy(key); 83 } 84 INSIST(ISC_LIST_EMPTY(kasp->keys)); 85 86 for (digest = ISC_LIST_HEAD(kasp->digests); digest != NULL; 87 digest = digest_next) 88 { 89 digest_next = ISC_LIST_NEXT(digest, link); 90 ISC_LIST_UNLINK(kasp->digests, digest, link); 91 isc_mem_put(kasp->mctx, digest, sizeof(*digest)); 92 } 93 INSIST(ISC_LIST_EMPTY(kasp->digests)); 94 95 isc_mutex_destroy(&kasp->lock); 96 isc_mem_free(kasp->mctx, kasp->name); 97 isc_mem_putanddetach(&kasp->mctx, kasp, sizeof(*kasp)); 98 } 99 100 void 101 dns_kasp_detach(dns_kasp_t **kaspp) { 102 REQUIRE(kaspp != NULL && DNS_KASP_VALID(*kaspp)); 103 104 dns_kasp_t *kasp = *kaspp; 105 *kaspp = NULL; 106 107 if (isc_refcount_decrement(&kasp->references) == 1) { 108 destroy(kasp); 109 } 110 } 111 112 const char * 113 dns_kasp_getname(dns_kasp_t *kasp) { 114 REQUIRE(DNS_KASP_VALID(kasp)); 115 116 return kasp->name; 117 } 118 119 void 120 dns_kasp_freeze(dns_kasp_t *kasp) { 121 REQUIRE(DNS_KASP_VALID(kasp)); 122 REQUIRE(!kasp->frozen); 123 124 kasp->frozen = true; 125 } 126 127 void 128 dns_kasp_thaw(dns_kasp_t *kasp) { 129 REQUIRE(DNS_KASP_VALID(kasp)); 130 REQUIRE(kasp->frozen); 131 132 kasp->frozen = false; 133 } 134 135 uint32_t 136 dns_kasp_signdelay(dns_kasp_t *kasp) { 137 REQUIRE(DNS_KASP_VALID(kasp)); 138 REQUIRE(kasp->frozen); 139 140 return kasp->signatures_validity - kasp->signatures_refresh; 141 } 142 143 uint32_t 144 dns_kasp_sigjitter(dns_kasp_t *kasp) { 145 REQUIRE(DNS_KASP_VALID(kasp)); 146 REQUIRE(kasp->frozen); 147 148 return kasp->signatures_jitter; 149 } 150 151 void 152 dns_kasp_setsigjitter(dns_kasp_t *kasp, uint32_t value) { 153 REQUIRE(DNS_KASP_VALID(kasp)); 154 REQUIRE(!kasp->frozen); 155 156 kasp->signatures_jitter = value; 157 } 158 159 uint32_t 160 dns_kasp_sigrefresh(dns_kasp_t *kasp) { 161 REQUIRE(DNS_KASP_VALID(kasp)); 162 REQUIRE(kasp->frozen); 163 164 return kasp->signatures_refresh; 165 } 166 167 void 168 dns_kasp_setsigrefresh(dns_kasp_t *kasp, uint32_t value) { 169 REQUIRE(DNS_KASP_VALID(kasp)); 170 REQUIRE(!kasp->frozen); 171 172 kasp->signatures_refresh = value; 173 } 174 175 uint32_t 176 dns_kasp_sigvalidity(dns_kasp_t *kasp) { 177 REQUIRE(DNS_KASP_VALID(kasp)); 178 REQUIRE(kasp->frozen); 179 180 return kasp->signatures_validity; 181 } 182 183 void 184 dns_kasp_setsigvalidity(dns_kasp_t *kasp, uint32_t value) { 185 REQUIRE(DNS_KASP_VALID(kasp)); 186 REQUIRE(!kasp->frozen); 187 188 kasp->signatures_validity = value; 189 } 190 191 uint32_t 192 dns_kasp_sigvalidity_dnskey(dns_kasp_t *kasp) { 193 REQUIRE(DNS_KASP_VALID(kasp)); 194 REQUIRE(kasp->frozen); 195 196 return kasp->signatures_validity_dnskey; 197 } 198 199 void 200 dns_kasp_setsigvalidity_dnskey(dns_kasp_t *kasp, uint32_t value) { 201 REQUIRE(DNS_KASP_VALID(kasp)); 202 REQUIRE(!kasp->frozen); 203 204 kasp->signatures_validity_dnskey = value; 205 } 206 207 dns_ttl_t 208 dns_kasp_dnskeyttl(dns_kasp_t *kasp) { 209 REQUIRE(DNS_KASP_VALID(kasp)); 210 REQUIRE(kasp->frozen); 211 212 return kasp->dnskey_ttl; 213 } 214 215 void 216 dns_kasp_setdnskeyttl(dns_kasp_t *kasp, dns_ttl_t ttl) { 217 REQUIRE(DNS_KASP_VALID(kasp)); 218 REQUIRE(!kasp->frozen); 219 220 kasp->dnskey_ttl = ttl; 221 } 222 223 uint32_t 224 dns_kasp_purgekeys(dns_kasp_t *kasp) { 225 REQUIRE(DNS_KASP_VALID(kasp)); 226 REQUIRE(kasp->frozen); 227 228 return kasp->purge_keys; 229 } 230 231 void 232 dns_kasp_setpurgekeys(dns_kasp_t *kasp, uint32_t value) { 233 REQUIRE(DNS_KASP_VALID(kasp)); 234 REQUIRE(!kasp->frozen); 235 236 kasp->purge_keys = value; 237 } 238 239 uint32_t 240 dns_kasp_publishsafety(dns_kasp_t *kasp) { 241 REQUIRE(DNS_KASP_VALID(kasp)); 242 REQUIRE(kasp->frozen); 243 244 return kasp->publish_safety; 245 } 246 247 void 248 dns_kasp_setpublishsafety(dns_kasp_t *kasp, uint32_t value) { 249 REQUIRE(DNS_KASP_VALID(kasp)); 250 REQUIRE(!kasp->frozen); 251 252 kasp->publish_safety = value; 253 } 254 255 uint32_t 256 dns_kasp_retiresafety(dns_kasp_t *kasp) { 257 REQUIRE(DNS_KASP_VALID(kasp)); 258 REQUIRE(kasp->frozen); 259 260 return kasp->retire_safety; 261 } 262 263 void 264 dns_kasp_setretiresafety(dns_kasp_t *kasp, uint32_t value) { 265 REQUIRE(DNS_KASP_VALID(kasp)); 266 REQUIRE(!kasp->frozen); 267 268 kasp->retire_safety = value; 269 } 270 271 bool 272 dns_kasp_inlinesigning(dns_kasp_t *kasp) { 273 REQUIRE(DNS_KASP_VALID(kasp)); 274 REQUIRE(kasp->frozen); 275 276 return kasp->inline_signing; 277 } 278 279 void 280 dns_kasp_setinlinesigning(dns_kasp_t *kasp, bool value) { 281 REQUIRE(DNS_KASP_VALID(kasp)); 282 REQUIRE(!kasp->frozen); 283 284 kasp->inline_signing = value; 285 } 286 287 bool 288 dns_kasp_manualmode(dns_kasp_t *kasp) { 289 REQUIRE(DNS_KASP_VALID(kasp)); 290 REQUIRE(kasp->frozen); 291 292 return kasp->manual_mode; 293 } 294 295 void 296 dns_kasp_setmanualmode(dns_kasp_t *kasp, bool value) { 297 REQUIRE(DNS_KASP_VALID(kasp)); 298 REQUIRE(!kasp->frozen); 299 300 kasp->manual_mode = value; 301 } 302 303 dns_ttl_t 304 dns_kasp_zonemaxttl(dns_kasp_t *kasp, bool fallback) { 305 REQUIRE(DNS_KASP_VALID(kasp)); 306 REQUIRE(kasp->frozen); 307 308 if (kasp->zone_max_ttl == 0 && fallback) { 309 return DEFAULT_TTLSIG; 310 } 311 return kasp->zone_max_ttl; 312 } 313 314 void 315 dns_kasp_setzonemaxttl(dns_kasp_t *kasp, dns_ttl_t ttl) { 316 REQUIRE(DNS_KASP_VALID(kasp)); 317 REQUIRE(!kasp->frozen); 318 319 kasp->zone_max_ttl = ttl; 320 } 321 322 uint32_t 323 dns_kasp_zonepropagationdelay(dns_kasp_t *kasp) { 324 REQUIRE(DNS_KASP_VALID(kasp)); 325 REQUIRE(kasp->frozen); 326 327 return kasp->zone_propagation_delay; 328 } 329 330 void 331 dns_kasp_setzonepropagationdelay(dns_kasp_t *kasp, uint32_t value) { 332 REQUIRE(DNS_KASP_VALID(kasp)); 333 REQUIRE(!kasp->frozen); 334 335 kasp->zone_propagation_delay = value; 336 } 337 338 dns_ttl_t 339 dns_kasp_dsttl(dns_kasp_t *kasp) { 340 REQUIRE(DNS_KASP_VALID(kasp)); 341 REQUIRE(kasp->frozen); 342 343 return kasp->parent_ds_ttl; 344 } 345 346 void 347 dns_kasp_setdsttl(dns_kasp_t *kasp, dns_ttl_t ttl) { 348 REQUIRE(DNS_KASP_VALID(kasp)); 349 REQUIRE(!kasp->frozen); 350 351 kasp->parent_ds_ttl = ttl; 352 } 353 354 uint32_t 355 dns_kasp_parentpropagationdelay(dns_kasp_t *kasp) { 356 REQUIRE(DNS_KASP_VALID(kasp)); 357 REQUIRE(kasp->frozen); 358 359 return kasp->parent_propagation_delay; 360 } 361 362 void 363 dns_kasp_setparentpropagationdelay(dns_kasp_t *kasp, uint32_t value) { 364 REQUIRE(DNS_KASP_VALID(kasp)); 365 REQUIRE(!kasp->frozen); 366 367 kasp->parent_propagation_delay = value; 368 } 369 370 isc_result_t 371 dns_kasplist_find(dns_kasplist_t *list, const char *name, dns_kasp_t **kaspp) { 372 dns_kasp_t *kasp = NULL; 373 374 REQUIRE(kaspp != NULL && *kaspp == NULL); 375 376 if (list == NULL) { 377 return ISC_R_NOTFOUND; 378 } 379 380 for (kasp = ISC_LIST_HEAD(*list); kasp != NULL; 381 kasp = ISC_LIST_NEXT(kasp, link)) 382 { 383 if (strcmp(kasp->name, name) == 0) { 384 break; 385 } 386 } 387 388 if (kasp == NULL) { 389 return ISC_R_NOTFOUND; 390 } 391 392 dns_kasp_attach(kasp, kaspp); 393 return ISC_R_SUCCESS; 394 } 395 396 dns_kasp_keylist_t 397 dns_kasp_keys(dns_kasp_t *kasp) { 398 REQUIRE(DNS_KASP_VALID(kasp)); 399 REQUIRE(kasp->frozen); 400 401 return kasp->keys; 402 } 403 404 bool 405 dns_kasp_keylist_empty(dns_kasp_t *kasp) { 406 REQUIRE(DNS_KASP_VALID(kasp)); 407 408 return ISC_LIST_EMPTY(kasp->keys); 409 } 410 411 void 412 dns_kasp_addkey(dns_kasp_t *kasp, dns_kasp_key_t *key) { 413 REQUIRE(DNS_KASP_VALID(kasp)); 414 REQUIRE(!kasp->frozen); 415 REQUIRE(key != NULL); 416 417 ISC_LIST_APPEND(kasp->keys, key, link); 418 } 419 420 isc_result_t 421 dns_kasp_key_create(dns_kasp_t *kasp, dns_kasp_key_t **keyp) { 422 dns_kasp_key_t *key = NULL; 423 dns_kasp_key_t k = { .tag_max = 0xffff, .length = -1 }; 424 425 REQUIRE(DNS_KASP_VALID(kasp)); 426 REQUIRE(keyp != NULL && *keyp == NULL); 427 428 key = isc_mem_get(kasp->mctx, sizeof(*key)); 429 *key = k; 430 431 key->mctx = NULL; 432 isc_mem_attach(kasp->mctx, &key->mctx); 433 434 ISC_LINK_INIT(key, link); 435 436 *keyp = key; 437 return ISC_R_SUCCESS; 438 } 439 440 void 441 dns_kasp_key_destroy(dns_kasp_key_t *key) { 442 REQUIRE(key != NULL); 443 444 if (key->keystore != NULL) { 445 dns_keystore_detach(&key->keystore); 446 } 447 isc_mem_putanddetach(&key->mctx, key, sizeof(*key)); 448 } 449 450 uint32_t 451 dns_kasp_key_algorithm(dns_kasp_key_t *key) { 452 REQUIRE(key != NULL); 453 454 return key->algorithm; 455 } 456 457 unsigned int 458 dns_kasp_key_size(dns_kasp_key_t *key) { 459 unsigned int size = 0; 460 unsigned int min = 0; 461 462 REQUIRE(key != NULL); 463 464 switch (key->algorithm) { 465 case DNS_KEYALG_RSASHA1: 466 case DNS_KEYALG_NSEC3RSASHA1: 467 case DNS_KEYALG_RSASHA256: 468 case DNS_KEYALG_RSASHA512: 469 min = (key->algorithm == DNS_KEYALG_RSASHA512) ? 1024 : 512; 470 if (key->length > -1) { 471 size = (unsigned int)key->length; 472 if (size < min) { 473 size = min; 474 } 475 if (size > 4096) { 476 size = 4096; 477 } 478 } else { 479 size = 2048; 480 } 481 break; 482 case DNS_KEYALG_ECDSA256: 483 size = 256; 484 break; 485 case DNS_KEYALG_ECDSA384: 486 size = 384; 487 break; 488 case DNS_KEYALG_ED25519: 489 size = 256; 490 break; 491 case DNS_KEYALG_ED448: 492 size = 456; 493 break; 494 default: 495 /* unsupported */ 496 break; 497 } 498 return size; 499 } 500 501 uint32_t 502 dns_kasp_key_lifetime(dns_kasp_key_t *key) { 503 REQUIRE(key != NULL); 504 505 return key->lifetime; 506 } 507 508 dns_keystore_t * 509 dns_kasp_key_keystore(dns_kasp_key_t *key) { 510 REQUIRE(key != NULL); 511 512 return key->keystore; 513 } 514 515 bool 516 dns_kasp_key_ksk(dns_kasp_key_t *key) { 517 REQUIRE(key != NULL); 518 519 return key->role & DNS_KASP_KEY_ROLE_KSK; 520 } 521 522 bool 523 dns_kasp_key_zsk(dns_kasp_key_t *key) { 524 REQUIRE(key != NULL); 525 526 return key->role & DNS_KASP_KEY_ROLE_ZSK; 527 } 528 529 uint16_t 530 dns_kasp_key_tagmin(dns_kasp_key_t *key) { 531 REQUIRE(key != NULL); 532 return key->tag_min; 533 } 534 535 uint16_t 536 dns_kasp_key_tagmax(dns_kasp_key_t *key) { 537 REQUIRE(key != NULL); 538 return key->tag_min; 539 } 540 541 bool 542 dns_kasp_key_match(dns_kasp_key_t *key, dns_dnsseckey_t *dkey) { 543 isc_result_t ret; 544 bool role = false; 545 546 REQUIRE(key != NULL); 547 REQUIRE(dkey != NULL); 548 549 /* Matching algorithms? */ 550 if (dst_key_alg(dkey->key) != dns_kasp_key_algorithm(key)) { 551 return false; 552 } 553 /* Matching length? */ 554 if (dst_key_size(dkey->key) != dns_kasp_key_size(key)) { 555 return false; 556 } 557 /* Matching role? */ 558 ret = dst_key_getbool(dkey->key, DST_BOOL_KSK, &role); 559 if (ret != ISC_R_SUCCESS || role != dns_kasp_key_ksk(key)) { 560 return false; 561 } 562 ret = dst_key_getbool(dkey->key, DST_BOOL_ZSK, &role); 563 if (ret != ISC_R_SUCCESS || role != dns_kasp_key_zsk(key)) { 564 return false; 565 } 566 /* Valid key tag range? */ 567 uint16_t id = dst_key_id(dkey->key); 568 uint16_t rid = dst_key_rid(dkey->key); 569 if (id < key->tag_min || id > key->tag_max) { 570 return false; 571 } 572 if (rid < key->tag_min || rid > key->tag_max) { 573 return false; 574 } 575 576 /* Found a match. */ 577 return true; 578 } 579 580 void 581 dns_kasp_key_format(dns_kasp_key_t *key, char *cp, unsigned int size) { 582 REQUIRE(key != NULL); 583 REQUIRE(cp != NULL); 584 585 char algstr[DNS_NAME_FORMATSIZE]; 586 bool csk = dns_kasp_key_ksk(key) && dns_kasp_key_zsk(key); 587 const char *rolestr = (csk ? "csk" 588 : (dns_kasp_key_ksk(key) ? "ksk" : "zsk")); 589 590 dns_secalg_format((dns_secalg_t)key->algorithm, algstr, sizeof(algstr)); 591 snprintf(cp, size, "%s algorithm:%s length:%u tag-range:%u-%u", rolestr, 592 algstr, dns_kasp_key_size(key), key->tag_min, key->tag_max); 593 } 594 595 uint8_t 596 dns_kasp_nsec3iter(dns_kasp_t *kasp) { 597 REQUIRE(kasp != NULL); 598 REQUIRE(kasp->frozen); 599 REQUIRE(kasp->nsec3); 600 601 return kasp->nsec3param.iterations; 602 } 603 604 uint8_t 605 dns_kasp_nsec3flags(dns_kasp_t *kasp) { 606 REQUIRE(kasp != NULL); 607 REQUIRE(kasp->frozen); 608 REQUIRE(kasp->nsec3); 609 610 if (kasp->nsec3param.optout) { 611 return 0x01; 612 } 613 return 0x00; 614 } 615 616 uint8_t 617 dns_kasp_nsec3saltlen(dns_kasp_t *kasp) { 618 REQUIRE(kasp != NULL); 619 REQUIRE(kasp->frozen); 620 REQUIRE(kasp->nsec3); 621 622 return kasp->nsec3param.saltlen; 623 } 624 625 bool 626 dns_kasp_nsec3(dns_kasp_t *kasp) { 627 REQUIRE(kasp != NULL); 628 REQUIRE(kasp->frozen); 629 630 return kasp->nsec3; 631 } 632 633 void 634 dns_kasp_setnsec3(dns_kasp_t *kasp, bool nsec3) { 635 REQUIRE(kasp != NULL); 636 REQUIRE(!kasp->frozen); 637 638 kasp->nsec3 = nsec3; 639 } 640 641 void 642 dns_kasp_setnsec3param(dns_kasp_t *kasp, uint8_t iter, bool optout, 643 uint8_t saltlen) { 644 REQUIRE(kasp != NULL); 645 REQUIRE(!kasp->frozen); 646 REQUIRE(kasp->nsec3); 647 648 kasp->nsec3param.iterations = iter; 649 kasp->nsec3param.optout = optout; 650 kasp->nsec3param.saltlen = saltlen; 651 } 652 653 bool 654 dns_kasp_offlineksk(dns_kasp_t *kasp) { 655 REQUIRE(kasp != NULL); 656 REQUIRE(kasp->frozen); 657 658 return kasp->offlineksk; 659 } 660 661 void 662 dns_kasp_setofflineksk(dns_kasp_t *kasp, bool offlineksk) { 663 REQUIRE(kasp != NULL); 664 REQUIRE(!kasp->frozen); 665 666 kasp->offlineksk = offlineksk; 667 } 668 669 bool 670 dns_kasp_cdnskey(dns_kasp_t *kasp) { 671 REQUIRE(kasp != NULL); 672 REQUIRE(kasp->frozen); 673 674 return kasp->cdnskey; 675 } 676 677 void 678 dns_kasp_setcdnskey(dns_kasp_t *kasp, bool cdnskey) { 679 REQUIRE(kasp != NULL); 680 REQUIRE(!kasp->frozen); 681 682 kasp->cdnskey = cdnskey; 683 } 684 685 dns_kasp_digestlist_t 686 dns_kasp_digests(dns_kasp_t *kasp) { 687 REQUIRE(DNS_KASP_VALID(kasp)); 688 REQUIRE(kasp->frozen); 689 690 return kasp->digests; 691 } 692 693 void 694 dns_kasp_adddigest(dns_kasp_t *kasp, dns_dsdigest_t alg) { 695 dns_kasp_digest_t *digest; 696 697 REQUIRE(DNS_KASP_VALID(kasp)); 698 REQUIRE(!kasp->frozen); 699 700 /* Suppress unsupported algorithms */ 701 if (!dst_ds_digest_supported(alg)) { 702 return; 703 } 704 705 /* Suppress duplicates */ 706 for (dns_kasp_digest_t *d = ISC_LIST_HEAD(kasp->digests); d != NULL; 707 d = ISC_LIST_NEXT(d, link)) 708 { 709 if (d->digest == alg) { 710 return; 711 } 712 } 713 714 digest = isc_mem_get(kasp->mctx, sizeof(*digest)); 715 digest->digest = alg; 716 ISC_LINK_INIT(digest, link); 717 ISC_LIST_APPEND(kasp->digests, digest, link); 718 } 719