1 /* $NetBSD: dnssec-keygen.c,v 1.17 2026/06/19 20:09:58 christos Exp $ */ 2 3 /* 4 * Portions 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 * Portions Copyright (C) Network Associates, Inc. 16 * 17 * Permission to use, copy, modify, and/or distribute this software for any 18 * purpose with or without fee is hereby granted, provided that the above 19 * copyright notice and this permission notice appear in all copies. 20 * 21 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS 22 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 23 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE 24 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 25 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 26 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 27 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 28 */ 29 30 /*! \file */ 31 32 #include <ctype.h> 33 #include <inttypes.h> 34 #include <stdbool.h> 35 #include <stdlib.h> 36 #include <unistd.h> 37 38 #include <openssl/opensslv.h> 39 40 #include <isc/attributes.h> 41 #include <isc/buffer.h> 42 #include <isc/commandline.h> 43 #include <isc/fips.h> 44 #include <isc/mem.h> 45 #include <isc/region.h> 46 #include <isc/result.h> 47 #include <isc/string.h> 48 #include <isc/util.h> 49 50 #include <dns/dnssec.h> 51 #include <dns/fixedname.h> 52 #include <dns/kasp.h> 53 #include <dns/keyvalues.h> 54 #include <dns/log.h> 55 #include <dns/name.h> 56 #include <dns/rdataclass.h> 57 #include <dns/secalg.h> 58 59 #include <dst/dst.h> 60 61 #if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 62 #include <openssl/err.h> 63 #include <openssl/provider.h> 64 #endif 65 66 #include "dnssectool.h" 67 68 const char *program = "dnssec-keygen"; 69 70 /* 71 * These are set here for backwards compatibility. They are 72 * raised to 2048 in FIPS mode. 73 */ 74 static int min_rsa = 1024; 75 static int min_dh = 128; 76 77 isc_log_t *lctx = NULL; 78 79 noreturn static void 80 usage(int ret); 81 82 static void 83 progress(int p); 84 85 struct keygen_ctx { 86 const char *predecessor; 87 const char *policy; 88 const char *configfile; 89 const char *directory; 90 dns_keystore_t *keystore; 91 char *algname; 92 char *nametype; 93 char *type; 94 int protocol; 95 int size; 96 uint16_t tag_min; 97 uint16_t tag_max; 98 int signatory; 99 dns_rdataclass_t rdclass; 100 int options; 101 int dbits; 102 dns_ttl_t ttl; 103 bool wantzsk; 104 bool wantksk; 105 bool wantrev; 106 dns_secalg_t alg; 107 /* timing data */ 108 int prepub; 109 isc_stdtime_t now; 110 isc_stdtime_t publish; 111 isc_stdtime_t activate; 112 isc_stdtime_t inactive; 113 isc_stdtime_t revokekey; 114 isc_stdtime_t deltime; 115 isc_stdtime_t syncadd; 116 isc_stdtime_t syncdel; 117 bool setpub; 118 bool setact; 119 bool setinact; 120 bool setrev; 121 bool setdel; 122 bool setsyncadd; 123 bool setsyncdel; 124 bool unsetpub; 125 bool unsetact; 126 bool unsetinact; 127 bool unsetrev; 128 bool unsetdel; 129 /* how to generate the key */ 130 bool setttl; 131 bool use_nsec3; 132 bool genonly; 133 bool showprogress; 134 bool quiet; 135 bool oldstyle; 136 /* state */ 137 time_t lifetime; 138 bool ksk; 139 bool zsk; 140 }; 141 142 typedef struct keygen_ctx keygen_ctx_t; 143 144 static void 145 usage(int ret) { 146 fprintf(stderr, "Usage:\n"); 147 fprintf(stderr, " %s [options] name\n\n", program); 148 fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); 149 fprintf(stderr, " name: owner of the key\n"); 150 fprintf(stderr, "Options:\n"); 151 fprintf(stderr, " -K <directory>: write keys into directory\n"); 152 fprintf(stderr, " -k <policy>: generate keys for dnssec-policy\n"); 153 fprintf(stderr, " -l <file>: configuration file with dnssec-policy " 154 "statement\n"); 155 fprintf(stderr, " -a <algorithm>:\n"); 156 if (!isc_fips_mode()) { 157 fprintf(stderr, " RSASHA1 (deprecated) | NSEC3RSASHA1 " 158 "(deprecated) |\n"); 159 } 160 fprintf(stderr, " RSASHA256 | RSASHA512 |\n"); 161 fprintf(stderr, " ECDSAP256SHA256 | ECDSAP384SHA384 |\n"); 162 fprintf(stderr, " ED25519 | ED448\n"); 163 fprintf(stderr, " -3: use NSEC3-capable algorithm\n"); 164 fprintf(stderr, " -b <key size in bits>:\n"); 165 if (!isc_fips_mode()) { 166 fprintf(stderr, " RSASHA1 (deprecated) :\t[%d..%d]\n", 167 min_rsa, MAX_RSA); 168 fprintf(stderr, 169 " NSEC3RSASHA1 (deprecated) :\t[%d..%d]\n", 170 min_rsa, MAX_RSA); 171 } 172 fprintf(stderr, " RSASHA256:\t[%d..%d]\n", min_rsa, MAX_RSA); 173 fprintf(stderr, " RSASHA512:\t[%d..%d]\n", min_rsa, MAX_RSA); 174 fprintf(stderr, " ECDSAP256SHA256:\tignored\n"); 175 fprintf(stderr, " ECDSAP384SHA384:\tignored\n"); 176 fprintf(stderr, " ED25519:\tignored\n"); 177 fprintf(stderr, " ED448:\tignored\n"); 178 fprintf(stderr, " (key size defaults are set according to\n" 179 " algorithm and usage (ZSK or KSK)\n"); 180 fprintf(stderr, " -n <nametype>: ZONE | HOST | ENTITY | " 181 "USER | OTHER\n"); 182 fprintf(stderr, " (DNSKEY generation defaults to ZONE)\n"); 183 fprintf(stderr, " -c <class>: (default: IN)\n"); 184 fprintf(stderr, " -d <digest bits> (0 => max, default)\n"); 185 fprintf(stderr, " -E <engine>:\n"); 186 fprintf(stderr, " name of an OpenSSL engine to use\n"); 187 fprintf(stderr, " -f <keyflag>: ZSK | KSK | REVOKE\n"); 188 fprintf(stderr, " -F: FIPS mode\n"); 189 fprintf(stderr, " -L <ttl>: default key TTL\n"); 190 fprintf(stderr, " -M <min>:<max>: allowed Key ID range\n"); 191 fprintf(stderr, " -p <protocol>: (default: 3 [dnssec])\n"); 192 fprintf(stderr, " -s <strength>: strength value this key signs DNS " 193 "records with (default: 0)\n"); 194 fprintf(stderr, " -T <rrtype>: DNSKEY | KEY (default: DNSKEY; " 195 "use KEY for SIG(0))\n"); 196 fprintf(stderr, " -t <type>: " 197 "AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF " 198 "(default: AUTHCONF)\n"); 199 fprintf(stderr, " -h: print usage and exit\n"); 200 fprintf(stderr, " -m <memory debugging mode>:\n"); 201 fprintf(stderr, " usage | trace | record\n"); 202 fprintf(stderr, " -v <level>: set verbosity level (0 - 10)\n"); 203 fprintf(stderr, " -V: print version information\n"); 204 fprintf(stderr, "Timing options:\n"); 205 fprintf(stderr, " -P date/[+-]offset/none: set key publication date " 206 "(default: now)\n"); 207 fprintf(stderr, " -P sync date/[+-]offset/none: set CDS and CDNSKEY " 208 "publication date\n"); 209 fprintf(stderr, " -A date/[+-]offset/none: set key activation date " 210 "(default: now)\n"); 211 fprintf(stderr, " -R date/[+-]offset/none: set key " 212 "revocation date\n"); 213 fprintf(stderr, " -I date/[+-]offset/none: set key " 214 "inactivation date\n"); 215 fprintf(stderr, " -D date/[+-]offset/none: set key deletion date\n"); 216 fprintf(stderr, " -D sync date/[+-]offset/none: set CDS and CDNSKEY " 217 "deletion date\n"); 218 219 fprintf(stderr, " -G: generate key only; do not set -P or -A\n"); 220 fprintf(stderr, " -C: generate a backward-compatible key, omitting " 221 "all dates\n"); 222 fprintf(stderr, " -S <key>: generate a successor to an existing " 223 "key\n"); 224 fprintf(stderr, " -i <interval>: prepublication interval for " 225 "successor key " 226 "(default: 30 days)\n"); 227 fprintf(stderr, "Output:\n"); 228 fprintf(stderr, " K<name>+<alg>+<id>.key, " 229 "K<name>+<alg>+<id>.private\n"); 230 231 exit(ret); 232 } 233 234 static void 235 progress(int p) { 236 char c = '*'; 237 238 switch (p) { 239 case 0: 240 c = '.'; 241 break; 242 case 1: 243 c = '+'; 244 break; 245 case 2: 246 c = '*'; 247 break; 248 case 3: 249 c = ' '; 250 break; 251 default: 252 break; 253 } 254 (void)putc(c, stderr); 255 (void)fflush(stderr); 256 } 257 258 static void 259 keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { 260 char filename[255]; 261 char algstr[DNS_SECALG_FORMATSIZE]; 262 uint16_t flags = 0; 263 int param = 0; 264 bool null_key = false; 265 bool conflict = false; 266 bool show_progress = false; 267 isc_buffer_t buf; 268 dns_name_t *name; 269 dns_fixedname_t fname; 270 isc_result_t ret; 271 dst_key_t *key = NULL; 272 dst_key_t *prevkey = NULL; 273 274 UNUSED(argc); 275 276 dns_secalg_format(ctx->alg, algstr, sizeof(algstr)); 277 278 if (ctx->predecessor == NULL) { 279 if (ctx->prepub == -1) { 280 ctx->prepub = 0; 281 } 282 283 name = dns_fixedname_initname(&fname); 284 isc_buffer_init(&buf, argv[isc_commandline_index], 285 strlen(argv[isc_commandline_index])); 286 isc_buffer_add(&buf, strlen(argv[isc_commandline_index])); 287 ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL); 288 if (ret != ISC_R_SUCCESS) { 289 fatal("invalid key name %s: %s", 290 argv[isc_commandline_index], 291 isc_result_totext(ret)); 292 } 293 294 if (!dst_algorithm_supported(ctx->alg)) { 295 fatal("unsupported algorithm: %s", algstr); 296 } 297 298 if (isc_fips_mode()) { 299 /* verify only in FIPS mode */ 300 switch (ctx->alg) { 301 case DST_ALG_RSASHA1: 302 case DST_ALG_NSEC3RSASHA1: 303 fatal("unsupported algorithm: %s", algstr); 304 default: 305 break; 306 } 307 } 308 309 if (ctx->use_nsec3) { 310 switch (ctx->alg) { 311 case DST_ALG_RSASHA1: 312 ctx->alg = DST_ALG_NSEC3RSASHA1; 313 break; 314 case DST_ALG_NSEC3RSASHA1: 315 case DST_ALG_RSASHA256: 316 case DST_ALG_RSASHA512: 317 case DST_ALG_ECDSA256: 318 case DST_ALG_ECDSA384: 319 case DST_ALG_ED25519: 320 case DST_ALG_ED448: 321 break; 322 default: 323 fatal("algorithm %s is incompatible with NSEC3" 324 ", do not use the -3 option", 325 algstr); 326 } 327 } 328 329 if (ctx->type != NULL && (ctx->options & DST_TYPE_KEY) != 0) { 330 if (strcasecmp(ctx->type, "NOAUTH") == 0) { 331 flags |= DNS_KEYTYPE_NOAUTH; 332 } else if (strcasecmp(ctx->type, "NOCONF") == 0) { 333 flags |= DNS_KEYTYPE_NOCONF; 334 } else if (strcasecmp(ctx->type, "NOAUTHCONF") == 0) { 335 flags |= (DNS_KEYTYPE_NOAUTH | 336 DNS_KEYTYPE_NOCONF); 337 if (ctx->size < 0) { 338 ctx->size = 0; 339 } 340 } else if (strcasecmp(ctx->type, "AUTHCONF") == 0) { 341 /* nothing */ 342 } else { 343 fatal("invalid type %s", ctx->type); 344 } 345 } 346 347 if (ctx->size < 0) { 348 switch (ctx->alg) { 349 case DST_ALG_RSASHA1: 350 case DST_ALG_NSEC3RSASHA1: 351 if (isc_fips_mode()) { 352 fatal("key size not specified (-b " 353 "option)"); 354 } 355 FALLTHROUGH; 356 case DST_ALG_RSASHA256: 357 case DST_ALG_RSASHA512: 358 ctx->size = 2048; 359 if (verbose > 0) { 360 fprintf(stderr, 361 "key size not " 362 "specified; defaulting" 363 " to %d\n", 364 ctx->size); 365 } 366 break; 367 case DST_ALG_ECDSA256: 368 case DST_ALG_ECDSA384: 369 case DST_ALG_ED25519: 370 case DST_ALG_ED448: 371 break; 372 default: 373 fatal("key size not specified (-b option)"); 374 } 375 } 376 377 if (!ctx->oldstyle && ctx->prepub > 0) { 378 if (ctx->setpub && ctx->setact && 379 (ctx->activate - ctx->prepub) < ctx->publish) 380 { 381 fatal("Activation and publication dates " 382 "are closer together than the\n\t" 383 "prepublication interval."); 384 } 385 386 if (!ctx->setpub && !ctx->setact) { 387 ctx->setpub = ctx->setact = true; 388 ctx->publish = ctx->now; 389 ctx->activate = ctx->now + ctx->prepub; 390 } else if (ctx->setpub && !ctx->setact) { 391 ctx->setact = true; 392 ctx->activate = ctx->publish + ctx->prepub; 393 } else if (ctx->setact && !ctx->setpub) { 394 ctx->setpub = true; 395 ctx->publish = ctx->activate - ctx->prepub; 396 } 397 398 if ((ctx->activate - ctx->prepub) < ctx->now) { 399 fatal("Time until activation is shorter " 400 "than the\n\tprepublication interval."); 401 } 402 } 403 } else { 404 char keystr[DST_KEY_FORMATSIZE]; 405 isc_stdtime_t when; 406 int major, minor; 407 408 if (ctx->prepub == -1) { 409 ctx->prepub = (30 * 86400); 410 } 411 412 if (ctx->alg != 0) { 413 fatal("-S and -a cannot be used together"); 414 } 415 if (ctx->size >= 0) { 416 fatal("-S and -b cannot be used together"); 417 } 418 if (ctx->nametype != NULL) { 419 fatal("-S and -n cannot be used together"); 420 } 421 if (ctx->type != NULL) { 422 fatal("-S and -t cannot be used together"); 423 } 424 if (ctx->setpub || ctx->unsetpub) { 425 fatal("-S and -P cannot be used together"); 426 } 427 if (ctx->setact || ctx->unsetact) { 428 fatal("-S and -A cannot be used together"); 429 } 430 if (ctx->use_nsec3) { 431 fatal("-S and -3 cannot be used together"); 432 } 433 if (ctx->oldstyle) { 434 fatal("-S and -C cannot be used together"); 435 } 436 if (ctx->genonly) { 437 fatal("-S and -G cannot be used together"); 438 } 439 440 ret = dst_key_fromnamedfile(ctx->predecessor, ctx->directory, 441 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | 442 DST_TYPE_STATE, 443 mctx, &prevkey); 444 if (ret != ISC_R_SUCCESS) { 445 fatal("Invalid keyfile %s: %s", ctx->predecessor, 446 isc_result_totext(ret)); 447 } 448 if (!dst_key_isprivate(prevkey)) { 449 fatal("%s is not a private key", ctx->predecessor); 450 } 451 452 name = dst_key_name(prevkey); 453 ctx->alg = dst_key_alg(prevkey); 454 ctx->size = dst_key_size(prevkey); 455 flags = dst_key_flags(prevkey); 456 457 dst_key_format(prevkey, keystr, sizeof(keystr)); 458 dst_key_getprivateformat(prevkey, &major, &minor); 459 if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) { 460 fatal("Key %s has incompatible format version %d.%d\n\t" 461 "It is not possible to generate a successor key.", 462 keystr, major, minor); 463 } 464 465 ret = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &when); 466 if (ret != ISC_R_SUCCESS) { 467 fatal("Key %s has no activation date.\n\t" 468 "You must use dnssec-settime -A to set one " 469 "before generating a successor.", 470 keystr); 471 } 472 473 ret = dst_key_gettime(prevkey, DST_TIME_INACTIVE, 474 &ctx->activate); 475 if (ret != ISC_R_SUCCESS) { 476 fatal("Key %s has no inactivation date.\n\t" 477 "You must use dnssec-settime -I to set one " 478 "before generating a successor.", 479 keystr); 480 } 481 482 ctx->publish = ctx->activate - ctx->prepub; 483 if (ctx->publish < ctx->now) { 484 fatal("Key %s becomes inactive\n\t" 485 "sooner than the prepublication period " 486 "for the new key ends.\n\t" 487 "Either change the inactivation date with " 488 "dnssec-settime -I,\n\t" 489 "or use the -i option to set a shorter " 490 "prepublication interval.", 491 keystr); 492 } 493 494 ret = dst_key_gettime(prevkey, DST_TIME_DELETE, &when); 495 if (ret != ISC_R_SUCCESS) { 496 fprintf(stderr, 497 "%s: WARNING: Key %s has no removal " 498 "date;\n\t it will remain in the zone " 499 "indefinitely after rollover.\n\t " 500 "You can use dnssec-settime -D to " 501 "change this.\n", 502 program, keystr); 503 } 504 505 ctx->setpub = ctx->setact = true; 506 } 507 508 switch (ctx->alg) { 509 case DST_ALG_RSASHA1: 510 case DST_ALG_NSEC3RSASHA1: 511 dns_secalg_format(ctx->alg, algstr, sizeof(algstr)); 512 fprintf(stderr, 513 "WARNING: DNSKEY algorithm '%s' is deprecated. Please " 514 "migrate to another algorithm\n", 515 algstr); 516 break; 517 default: 518 break; 519 } 520 521 switch (ctx->alg) { 522 case DST_ALG_RSASHA1: 523 case DST_ALG_NSEC3RSASHA1: 524 if (isc_fips_mode()) { 525 fatal("SHA1 based keys not supported in FIPS mode"); 526 } 527 FALLTHROUGH; 528 case DST_ALG_RSASHA256: 529 case DST_ALG_RSASHA512: 530 if (ctx->size != 0 && 531 (ctx->size < min_rsa || ctx->size > MAX_RSA)) 532 { 533 fatal("RSA key size %d out of range", ctx->size); 534 } 535 break; 536 case DST_ALG_ECDSA256: 537 ctx->size = 256; 538 break; 539 case DST_ALG_ECDSA384: 540 ctx->size = 384; 541 break; 542 case DST_ALG_ED25519: 543 ctx->size = 256; 544 break; 545 case DST_ALG_ED448: 546 ctx->size = 456; 547 break; 548 } 549 550 if (ctx->nametype == NULL) { 551 if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */ 552 fatal("no nametype specified"); 553 } 554 flags |= DNS_KEYOWNER_ZONE; /* DNSKEY */ 555 } else if (strcasecmp(ctx->nametype, "zone") == 0) { 556 flags |= DNS_KEYOWNER_ZONE; 557 } else if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */ 558 if (strcasecmp(ctx->nametype, "host") == 0 || 559 strcasecmp(ctx->nametype, "entity") == 0) 560 { 561 flags |= DNS_KEYOWNER_ENTITY; 562 } else if (strcasecmp(ctx->nametype, "user") == 0) { 563 /* no owner flags */ 564 } else { 565 fatal("invalid KEY nametype %s", ctx->nametype); 566 } 567 } else if (strcasecmp(ctx->nametype, "other") != 0) { /* DNSKEY */ 568 fatal("invalid DNSKEY nametype %s", ctx->nametype); 569 } 570 571 if (ctx->directory == NULL) { 572 ctx->directory = "."; 573 } 574 575 if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */ 576 flags |= ctx->signatory; 577 } else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */ 578 if (ctx->ksk || ctx->wantksk) { 579 flags |= DNS_KEYFLAG_KSK; 580 } 581 if (ctx->wantrev) { 582 flags |= DNS_KEYFLAG_REVOKE; 583 } 584 } 585 586 if (ctx->protocol == -1) { 587 ctx->protocol = DNS_KEYPROTO_DNSSEC; 588 } else if ((ctx->options & DST_TYPE_KEY) == 0 && 589 ctx->protocol != DNS_KEYPROTO_DNSSEC) 590 { 591 fatal("invalid DNSKEY protocol: %d", ctx->protocol); 592 } 593 594 if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) { 595 if (ctx->size > 0) { 596 fatal("specified null key with non-zero size"); 597 } 598 } 599 600 switch (ctx->alg) { 601 case DNS_KEYALG_RSASHA1: 602 case DNS_KEYALG_NSEC3RSASHA1: 603 case DNS_KEYALG_RSASHA256: 604 case DNS_KEYALG_RSASHA512: 605 show_progress = true; 606 break; 607 608 case DST_ALG_ECDSA256: 609 case DST_ALG_ECDSA384: 610 case DST_ALG_ED25519: 611 case DST_ALG_ED448: 612 show_progress = true; 613 break; 614 } 615 616 if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY && 617 (ctx->options & DST_TYPE_KEY) != 0) 618 { 619 null_key = true; 620 } 621 622 isc_buffer_init(&buf, filename, sizeof(filename) - 1); 623 624 do { 625 conflict = false; 626 627 if (!ctx->quiet && show_progress) { 628 fprintf(stderr, "Generating key pair."); 629 } 630 631 if (ctx->keystore != NULL && ctx->policy != NULL) { 632 ret = dns_keystore_keygen( 633 ctx->keystore, name, ctx->policy, ctx->rdclass, 634 mctx, ctx->alg, ctx->size, flags, &key); 635 } else if (!ctx->quiet && show_progress) { 636 ret = dst_key_generate(name, ctx->alg, ctx->size, param, 637 flags, ctx->protocol, 638 ctx->rdclass, NULL, mctx, &key, 639 &progress); 640 } else { 641 ret = dst_key_generate(name, ctx->alg, ctx->size, param, 642 flags, ctx->protocol, 643 ctx->rdclass, NULL, mctx, &key, 644 NULL); 645 } 646 647 if (!ctx->quiet && show_progress) { 648 putc('\n', stderr); 649 fflush(stderr); 650 } 651 652 if (ret != ISC_R_SUCCESS) { 653 char namestr[DNS_NAME_FORMATSIZE]; 654 dns_name_format(name, namestr, sizeof(namestr)); 655 fatal("failed to generate key %s/%s: %s\n", namestr, 656 algstr, isc_result_totext(ret)); 657 } 658 659 dst_key_setbits(key, ctx->dbits); 660 661 /* 662 * Set key timing metadata (unless using -C) 663 * 664 * Creation date is always set to "now". 665 * 666 * For a new key without an explicit predecessor, publish 667 * and activation dates are set to "now" by default, but 668 * can both be overridden. 669 * 670 * For a successor key, activation is set to match the 671 * predecessor's inactivation date. Publish is set to 30 672 * days earlier than that (XXX: this should be configurable). 673 * If either of the resulting dates are in the past, that's 674 * an error; the inactivation date of the predecessor key 675 * must be updated before a successor key can be created. 676 */ 677 if (!ctx->oldstyle) { 678 dst_key_settime(key, DST_TIME_CREATED, ctx->now); 679 680 if (ctx->genonly && (ctx->setpub || ctx->setact)) { 681 fatal("cannot use -G together with " 682 "-P or -A options"); 683 } 684 685 if (ctx->setpub) { 686 dst_key_settime(key, DST_TIME_PUBLISH, 687 ctx->publish); 688 } else if (ctx->setact && !ctx->unsetpub) { 689 dst_key_settime(key, DST_TIME_PUBLISH, 690 ctx->activate - ctx->prepub); 691 } else if (!ctx->genonly && !ctx->unsetpub) { 692 dst_key_settime(key, DST_TIME_PUBLISH, 693 ctx->now); 694 } 695 696 if (ctx->setact) { 697 dst_key_settime(key, DST_TIME_ACTIVATE, 698 ctx->activate); 699 } else if (!ctx->genonly && !ctx->unsetact) { 700 dst_key_settime(key, DST_TIME_ACTIVATE, 701 ctx->now); 702 } 703 704 if (ctx->setrev) { 705 if (!ctx->wantksk) { 706 fprintf(stderr, 707 "%s: warning: Key is " 708 "not flagged as a KSK, but -R " 709 "was used. Revoking a ZSK is " 710 "legal, but undefined.\n", 711 program); 712 } 713 dst_key_settime(key, DST_TIME_REVOKE, 714 ctx->revokekey); 715 } 716 717 if (ctx->setinact) { 718 dst_key_settime(key, DST_TIME_INACTIVE, 719 ctx->inactive); 720 } 721 722 if (ctx->setdel) { 723 if (ctx->setinact && 724 ctx->deltime < ctx->inactive) 725 { 726 fprintf(stderr, 727 "%s: warning: Key is " 728 "scheduled to be deleted " 729 "before it is scheduled to be " 730 "made inactive.\n", 731 program); 732 } 733 dst_key_settime(key, DST_TIME_DELETE, 734 ctx->deltime); 735 } 736 737 if (ctx->setsyncadd) { 738 dst_key_settime(key, DST_TIME_SYNCPUBLISH, 739 ctx->syncadd); 740 } 741 742 if (ctx->setsyncdel) { 743 dst_key_settime(key, DST_TIME_SYNCDELETE, 744 ctx->syncdel); 745 } 746 } else { 747 if (ctx->setpub || ctx->setact || ctx->setrev || 748 ctx->setinact || ctx->setdel || ctx->unsetpub || 749 ctx->unsetact || ctx->unsetrev || ctx->unsetinact || 750 ctx->unsetdel || ctx->genonly || ctx->setsyncadd || 751 ctx->setsyncdel) 752 { 753 fatal("cannot use -C together with " 754 "-P, -A, -R, -I, -D, or -G options"); 755 } 756 /* 757 * Compatibility mode: Private-key-format 758 * should be set to 1.2. 759 */ 760 dst_key_setprivateformat(key, 1, 2); 761 } 762 763 /* Set the default key TTL */ 764 if (ctx->setttl) { 765 dst_key_setttl(key, ctx->ttl); 766 } 767 768 /* Set dnssec-policy related metadata */ 769 if (ctx->policy != NULL) { 770 dst_key_setnum(key, DST_NUM_LIFETIME, ctx->lifetime); 771 dst_key_setbool(key, DST_BOOL_KSK, ctx->ksk); 772 dst_key_setbool(key, DST_BOOL_ZSK, ctx->zsk); 773 } 774 775 /* 776 * Do not overwrite an existing key, or create a key 777 * if there is a risk of ID collision due to this key 778 * or another key being revoked. 779 */ 780 if (key_collision(key, name, ctx->directory, mctx, ctx->tag_min, 781 ctx->tag_max, NULL)) 782 { 783 conflict = true; 784 if (null_key) { 785 dst_key_free(&key); 786 break; 787 } 788 789 if (verbose > 0) { 790 isc_buffer_clear(&buf); 791 ret = dst_key_buildfilename( 792 key, 0, ctx->directory, &buf); 793 if (ret == ISC_R_SUCCESS) { 794 fprintf(stderr, 795 "%s: %s already exists, or " 796 "might collide with another " 797 "key upon revokation. " 798 "Generating a new key\n", 799 program, filename); 800 } 801 } 802 803 dst_key_free(&key); 804 } 805 } while (conflict); 806 807 if (conflict) { 808 fatal("cannot generate a null key due to possible key ID " 809 "collision"); 810 } 811 812 if (ctx->predecessor != NULL && prevkey != NULL) { 813 dst_key_setnum(prevkey, DST_NUM_SUCCESSOR, dst_key_id(key)); 814 dst_key_setnum(key, DST_NUM_PREDECESSOR, dst_key_id(prevkey)); 815 816 ret = dst_key_tofile(prevkey, ctx->options, ctx->directory); 817 if (ret != ISC_R_SUCCESS) { 818 char keystr[DST_KEY_FORMATSIZE]; 819 dst_key_format(prevkey, keystr, sizeof(keystr)); 820 fatal("failed to update predecessor %s: %s\n", keystr, 821 isc_result_totext(ret)); 822 } 823 } 824 825 ret = dst_key_tofile(key, ctx->options, ctx->directory); 826 if (ret != ISC_R_SUCCESS) { 827 char keystr[DST_KEY_FORMATSIZE]; 828 dst_key_format(key, keystr, sizeof(keystr)); 829 fatal("failed to write key %s: %s\n", keystr, 830 isc_result_totext(ret)); 831 } 832 833 isc_buffer_clear(&buf); 834 ret = dst_key_buildfilename(key, 0, NULL, &buf); 835 if (ret != ISC_R_SUCCESS) { 836 fatal("dst_key_buildfilename returned: %s\n", 837 isc_result_totext(ret)); 838 } 839 printf("%s\n", filename); 840 841 dst_key_free(&key); 842 if (prevkey != NULL) { 843 dst_key_free(&prevkey); 844 } 845 } 846 847 static void 848 check_keystore_options(keygen_ctx_t *ctx) { 849 ctx->directory = dns_keystore_directory(ctx->keystore, NULL); 850 if (ctx->directory != NULL) { 851 isc_result_t ret = try_dir(ctx->directory); 852 if (ret != ISC_R_SUCCESS) { 853 fatal("cannot open directory %s: %s", ctx->directory, 854 isc_result_totext(ret)); 855 } 856 } 857 } 858 859 int 860 main(int argc, char **argv) { 861 char *algname = NULL, *freeit = NULL; 862 char *classname = NULL; 863 char *endp; 864 isc_mem_t *mctx = NULL; 865 isc_result_t ret; 866 isc_textregion_t r; 867 const char *engine = NULL; 868 unsigned char c; 869 int ch; 870 bool set_fips_mode = false; 871 #if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 872 OSSL_PROVIDER *fips = NULL, *base = NULL; 873 #endif 874 875 keygen_ctx_t ctx = { 876 .options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC, 877 .prepub = -1, 878 .protocol = -1, 879 .size = -1, 880 .now = isc_stdtime_now(), 881 }; 882 883 if (argc == 1) { 884 usage(EXIT_FAILURE); 885 } 886 887 isc_commandline_errprint = false; 888 889 /* 890 * Process memory debugging argument first. 891 */ 892 #define CMDLINE_FLAGS \ 893 "3A:a:b:Cc:D:d:E:Ff:GhI:i:K:k:L:l:M:m:n:P:p:qR:r:S:s:" \ 894 "T:t:v:V" 895 while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { 896 switch (ch) { 897 case 'm': 898 if (strcasecmp(isc_commandline_argument, "record") == 0) 899 { 900 isc_mem_debugging |= ISC_MEM_DEBUGRECORD; 901 } 902 if (strcasecmp(isc_commandline_argument, "trace") == 0) 903 { 904 isc_mem_debugging |= ISC_MEM_DEBUGTRACE; 905 } 906 if (strcasecmp(isc_commandline_argument, "usage") == 0) 907 { 908 isc_mem_debugging |= ISC_MEM_DEBUGUSAGE; 909 } 910 break; 911 default: 912 break; 913 } 914 } 915 isc_commandline_reset = true; 916 917 isc_mem_create(&mctx); 918 919 while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { 920 switch (ch) { 921 case '3': 922 ctx.use_nsec3 = true; 923 break; 924 case 'a': 925 algname = isc_commandline_argument; 926 break; 927 case 'b': 928 ctx.size = strtol(isc_commandline_argument, &endp, 10); 929 if (*endp != '\0' || ctx.size < 0) { 930 fatal("-b requires a non-negative number"); 931 } 932 break; 933 case 'C': 934 ctx.oldstyle = true; 935 break; 936 case 'c': 937 classname = isc_commandline_argument; 938 break; 939 case 'd': 940 ctx.dbits = strtol(isc_commandline_argument, &endp, 10); 941 if (*endp != '\0' || ctx.dbits < 0) { 942 fatal("-d requires a non-negative number"); 943 } 944 break; 945 case 'E': 946 engine = isc_commandline_argument; 947 break; 948 case 'f': 949 c = (unsigned char)(isc_commandline_argument[0]); 950 if (toupper(c) == 'K') { 951 ctx.wantksk = true; 952 } else if (toupper(c) == 'Z') { 953 ctx.wantzsk = true; 954 } else if (toupper(c) == 'R') { 955 ctx.wantrev = true; 956 } else { 957 fatal("unknown flag '%s'", 958 isc_commandline_argument); 959 } 960 break; 961 case 'K': 962 ctx.directory = isc_commandline_argument; 963 ret = try_dir(ctx.directory); 964 if (ret != ISC_R_SUCCESS) { 965 fatal("cannot open directory %s: %s", 966 ctx.directory, isc_result_totext(ret)); 967 } 968 break; 969 case 'k': 970 ctx.policy = isc_commandline_argument; 971 break; 972 case 'L': 973 ctx.ttl = strtottl(isc_commandline_argument); 974 ctx.setttl = true; 975 break; 976 case 'l': 977 ctx.configfile = isc_commandline_argument; 978 break; 979 case 'n': 980 ctx.nametype = isc_commandline_argument; 981 break; 982 case 'M': { 983 unsigned long ul; 984 ctx.tag_min = ul = strtoul(isc_commandline_argument, 985 &endp, 10); 986 if (*endp != ':' || ul > 0xffff) { 987 fatal("-M range invalid"); 988 } 989 ctx.tag_max = ul = strtoul(endp + 1, &endp, 10); 990 if (*endp != '\0' || ul > 0xffff || 991 ctx.tag_max <= ctx.tag_min) 992 { 993 fatal("-M range invalid"); 994 } 995 break; 996 } 997 case 'm': 998 break; 999 case 'p': 1000 ctx.protocol = strtol(isc_commandline_argument, &endp, 1001 10); 1002 if (*endp != '\0' || ctx.protocol < 0 || 1003 ctx.protocol > 255) 1004 { 1005 fatal("-p must be followed by a number " 1006 "[0..255]"); 1007 } 1008 break; 1009 case 'q': 1010 ctx.quiet = true; 1011 break; 1012 case 'r': 1013 fatal("The -r option has been deprecated.\n" 1014 "System random data is always used.\n"); 1015 break; 1016 case 's': 1017 ctx.signatory = strtol(isc_commandline_argument, &endp, 1018 10); 1019 if (*endp != '\0' || ctx.signatory < 0 || 1020 ctx.signatory > 15) 1021 { 1022 fatal("-s must be followed by a number " 1023 "[0..15]"); 1024 } 1025 break; 1026 case 'T': 1027 if (strcasecmp(isc_commandline_argument, "KEY") == 0) { 1028 ctx.options |= DST_TYPE_KEY; 1029 } else if (strcasecmp(isc_commandline_argument, 1030 "DNSKE" 1031 "Y") == 0) 1032 { 1033 /* default behavior */ 1034 } else { 1035 fatal("unknown type '%s'", 1036 isc_commandline_argument); 1037 } 1038 break; 1039 case 't': 1040 ctx.type = isc_commandline_argument; 1041 break; 1042 case 'v': 1043 endp = NULL; 1044 verbose = strtol(isc_commandline_argument, &endp, 0); 1045 if (*endp != '\0') { 1046 fatal("-v must be followed by a number"); 1047 } 1048 break; 1049 case 'G': 1050 ctx.genonly = true; 1051 break; 1052 case 'P': 1053 /* -Psync ? */ 1054 if (isoptarg("sync", argv, usage)) { 1055 if (ctx.setsyncadd) { 1056 fatal("-P sync specified more than " 1057 "once"); 1058 } 1059 1060 ctx.syncadd = strtotime( 1061 isc_commandline_argument, ctx.now, 1062 ctx.now, &ctx.setsyncadd); 1063 break; 1064 } 1065 (void)isoptarg("dnskey", argv, usage); 1066 if (ctx.setpub || ctx.unsetpub) { 1067 fatal("-P specified more than once"); 1068 } 1069 1070 ctx.publish = strtotime(isc_commandline_argument, 1071 ctx.now, ctx.now, &ctx.setpub); 1072 ctx.unsetpub = !ctx.setpub; 1073 break; 1074 case 'A': 1075 if (ctx.setact || ctx.unsetact) { 1076 fatal("-A specified more than once"); 1077 } 1078 1079 ctx.activate = strtotime(isc_commandline_argument, 1080 ctx.now, ctx.now, &ctx.setact); 1081 ctx.unsetact = !ctx.setact; 1082 break; 1083 case 'R': 1084 if (ctx.setrev || ctx.unsetrev) { 1085 fatal("-R specified more than once"); 1086 } 1087 1088 ctx.revokekey = strtotime(isc_commandline_argument, 1089 ctx.now, ctx.now, 1090 &ctx.setrev); 1091 ctx.unsetrev = !ctx.setrev; 1092 break; 1093 case 'I': 1094 if (ctx.setinact || ctx.unsetinact) { 1095 fatal("-I specified more than once"); 1096 } 1097 1098 ctx.inactive = strtotime(isc_commandline_argument, 1099 ctx.now, ctx.now, 1100 &ctx.setinact); 1101 ctx.unsetinact = !ctx.setinact; 1102 break; 1103 case 'D': 1104 /* -Dsync ? */ 1105 if (isoptarg("sync", argv, usage)) { 1106 if (ctx.setsyncdel) { 1107 fatal("-D sync specified more than " 1108 "once"); 1109 } 1110 1111 ctx.syncdel = strtotime( 1112 isc_commandline_argument, ctx.now, 1113 ctx.now, &ctx.setsyncdel); 1114 break; 1115 } 1116 (void)isoptarg("dnskey", argv, usage); 1117 if (ctx.setdel || ctx.unsetdel) { 1118 fatal("-D specified more than once"); 1119 } 1120 1121 ctx.deltime = strtotime(isc_commandline_argument, 1122 ctx.now, ctx.now, &ctx.setdel); 1123 ctx.unsetdel = !ctx.setdel; 1124 break; 1125 case 'S': 1126 ctx.predecessor = isc_commandline_argument; 1127 break; 1128 case 'i': 1129 ctx.prepub = strtottl(isc_commandline_argument); 1130 break; 1131 case 'F': 1132 set_fips_mode = true; 1133 break; 1134 case '?': 1135 if (isc_commandline_option != '?') { 1136 fprintf(stderr, "%s: invalid argument -%c\n", 1137 program, isc_commandline_option); 1138 } 1139 /* Does not return. */ 1140 usage(EXIT_FAILURE); 1141 1142 case 'h': 1143 /* Does not return. */ 1144 usage(EXIT_SUCCESS); 1145 1146 case 'V': 1147 /* Does not return. */ 1148 version(program); 1149 1150 default: 1151 fprintf(stderr, "%s: unhandled option -%c\n", program, 1152 isc_commandline_option); 1153 exit(EXIT_FAILURE); 1154 } 1155 } 1156 1157 if (!isatty(0)) { 1158 ctx.quiet = true; 1159 } 1160 1161 if (set_fips_mode) { 1162 #if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 1163 fips = OSSL_PROVIDER_load(NULL, "fips"); 1164 if (fips == NULL) { 1165 ERR_clear_error(); 1166 fatal("Failed to load FIPS provider"); 1167 } 1168 base = OSSL_PROVIDER_load(NULL, "base"); 1169 if (base == NULL) { 1170 OSSL_PROVIDER_unload(fips); 1171 ERR_clear_error(); 1172 fatal("Failed to load base provider"); 1173 } 1174 #endif 1175 if (!isc_fips_mode()) { 1176 if (isc_fips_set_mode(1) != ISC_R_SUCCESS) { 1177 fatal("setting FIPS mode failed"); 1178 } 1179 } 1180 } 1181 1182 ret = dst_lib_init(mctx, engine); 1183 if (ret != ISC_R_SUCCESS) { 1184 fatal("could not initialize dst: %s", isc_result_totext(ret)); 1185 } 1186 1187 /* 1188 * After dst_lib_init which will set FIPS mode if requested 1189 * at build time. The minumums are both raised to 2048. 1190 */ 1191 if (isc_fips_mode()) { 1192 min_rsa = min_dh = 2048; 1193 } 1194 1195 setup_logging(mctx, &lctx); 1196 1197 ctx.rdclass = strtoclass(classname); 1198 1199 if (ctx.configfile == NULL || ctx.configfile[0] == '\0') { 1200 ctx.configfile = NAMED_CONFFILE; 1201 } 1202 1203 if (ctx.predecessor == NULL) { 1204 if (argc < isc_commandline_index + 1) { 1205 fatal("the key name was not specified"); 1206 } 1207 if (argc > isc_commandline_index + 1) { 1208 fatal("extraneous arguments"); 1209 } 1210 } 1211 1212 if (ctx.predecessor == NULL && ctx.policy == NULL) { 1213 if (algname == NULL) { 1214 fatal("no algorithm specified"); 1215 } 1216 r.base = algname; 1217 r.length = strlen(algname); 1218 ret = dns_secalg_fromtext(&ctx.alg, &r); 1219 if (ret != ISC_R_SUCCESS) { 1220 fatal("unknown algorithm %s", algname); 1221 } 1222 if (!dst_algorithm_supported(ctx.alg)) { 1223 fatal("unsupported algorithm: %s", algname); 1224 } 1225 } 1226 1227 if (ctx.policy != NULL) { 1228 if (ctx.nametype != NULL) { 1229 fatal("-k and -n cannot be used together"); 1230 } 1231 if (ctx.predecessor != NULL) { 1232 fatal("-k and -S cannot be used together"); 1233 } 1234 if (ctx.oldstyle) { 1235 fatal("-k and -C cannot be used together"); 1236 } 1237 if (ctx.setttl) { 1238 fatal("-k and -L cannot be used together"); 1239 } 1240 if (ctx.prepub > 0) { 1241 fatal("-k and -i cannot be used together"); 1242 } 1243 if (ctx.size != -1) { 1244 fatal("-k and -b cannot be used together"); 1245 } 1246 if (ctx.wantrev) { 1247 fatal("-k and -fR cannot be used together"); 1248 } 1249 if (ctx.options & DST_TYPE_KEY) { 1250 fatal("-k and -T KEY cannot be used together"); 1251 } 1252 if (ctx.use_nsec3) { 1253 fatal("-k and -3 cannot be used together"); 1254 } 1255 1256 ctx.options |= DST_TYPE_STATE; 1257 1258 if (strcmp(ctx.policy, "default") == 0) { 1259 ctx.use_nsec3 = false; 1260 ctx.alg = DST_ALG_ECDSA256; 1261 ctx.size = 0; 1262 ctx.ttl = 3600; 1263 ctx.setttl = true; 1264 ctx.ksk = true; 1265 ctx.zsk = true; 1266 ctx.lifetime = 0; 1267 ctx.tag_min = 0; 1268 ctx.tag_max = 0xffff; 1269 1270 keygen(&ctx, mctx, argc, argv); 1271 } else { 1272 cfg_parser_t *parser = NULL; 1273 cfg_obj_t *config = NULL; 1274 dns_kasp_t *kasp = NULL; 1275 dns_kasp_key_t *kaspkey = NULL; 1276 1277 RUNTIME_CHECK(cfg_parser_create(mctx, lctx, &parser) == 1278 ISC_R_SUCCESS); 1279 if (cfg_parse_file(parser, ctx.configfile, 1280 &cfg_type_namedconf, 1281 &config) != ISC_R_SUCCESS) 1282 { 1283 fatal("unable to load dnssec-policy '%s' from " 1284 "'%s'", 1285 ctx.policy, ctx.configfile); 1286 } 1287 1288 kasp_from_conf(config, mctx, lctx, ctx.policy, 1289 ctx.directory, engine, &kasp); 1290 if (kasp == NULL) { 1291 fatal("failed to load dnssec-policy '%s'", 1292 ctx.policy); 1293 } 1294 if (ISC_LIST_EMPTY(dns_kasp_keys(kasp))) { 1295 fatal("dnssec-policy '%s' has no keys " 1296 "configured", 1297 ctx.policy); 1298 } 1299 1300 ctx.ttl = dns_kasp_dnskeyttl(kasp); 1301 ctx.setttl = true; 1302 1303 for (kaspkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); 1304 kaspkey != NULL; 1305 kaspkey = ISC_LIST_NEXT(kaspkey, link)) 1306 { 1307 ctx.use_nsec3 = false; 1308 ctx.alg = dns_kasp_key_algorithm(kaspkey); 1309 ctx.size = dns_kasp_key_size(kaspkey); 1310 ctx.ksk = dns_kasp_key_ksk(kaspkey); 1311 ctx.zsk = dns_kasp_key_zsk(kaspkey); 1312 ctx.lifetime = dns_kasp_key_lifetime(kaspkey); 1313 ctx.keystore = dns_kasp_key_keystore(kaspkey); 1314 if (ctx.keystore != NULL) { 1315 check_keystore_options(&ctx); 1316 } 1317 ctx.tag_min = dns_kasp_key_tagmin(kaspkey); 1318 ctx.tag_max = dns_kasp_key_tagmax(kaspkey); 1319 if ((ctx.ksk && !ctx.wantksk && ctx.wantzsk) || 1320 (ctx.zsk && !ctx.wantzsk && ctx.wantksk)) 1321 { 1322 continue; 1323 } 1324 keygen(&ctx, mctx, argc, argv); 1325 } 1326 1327 dns_kasp_detach(&kasp); 1328 cfg_obj_destroy(parser, &config); 1329 cfg_parser_destroy(&parser); 1330 } 1331 } else { 1332 keygen(&ctx, mctx, argc, argv); 1333 } 1334 1335 cleanup_logging(&lctx); 1336 dst_lib_destroy(); 1337 if (verbose > 10) { 1338 isc_mem_stats(mctx, stdout); 1339 } 1340 isc_mem_destroy(&mctx); 1341 1342 #if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 1343 if (base != NULL) { 1344 OSSL_PROVIDER_unload(base); 1345 } 1346 if (fips != NULL) { 1347 OSSL_PROVIDER_unload(fips); 1348 } 1349 #endif 1350 if (freeit != NULL) { 1351 free(freeit); 1352 } 1353 1354 return 0; 1355 } 1356