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