1 /* $NetBSD: dnssec-settime.c,v 1.12 2015/07/08 17:28:55 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2009-2015 Internet Systems Consortium, Inc. ("ISC") 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /*! \file */ 20 21 #include <config.h> 22 23 #include <stdlib.h> 24 #include <unistd.h> 25 #include <errno.h> 26 #include <time.h> 27 28 #include <isc/buffer.h> 29 #include <isc/commandline.h> 30 #include <isc/entropy.h> 31 #include <isc/file.h> 32 #include <isc/hash.h> 33 #include <isc/mem.h> 34 #include <isc/print.h> 35 #include <isc/string.h> 36 #include <isc/util.h> 37 38 #include <dns/keyvalues.h> 39 #include <dns/result.h> 40 #include <dns/log.h> 41 42 #include <dst/dst.h> 43 44 #ifdef PKCS11CRYPTO 45 #include <pk11/result.h> 46 #endif 47 48 #include "dnssectool.h" 49 50 const char *program = "dnssec-settime"; 51 int verbose; 52 53 static isc_mem_t *mctx = NULL; 54 55 ISC_PLATFORM_NORETURN_PRE static void 56 usage(void) ISC_PLATFORM_NORETURN_POST; 57 58 static void 59 usage(void) { 60 fprintf(stderr, "Usage:\n"); 61 fprintf(stderr, " %s [options] keyfile\n\n", program); 62 fprintf(stderr, "Version: %s\n", VERSION); 63 fprintf(stderr, "General options:\n"); 64 #if defined(PKCS11CRYPTO) 65 fprintf(stderr, " -E engine: specify PKCS#11 provider " 66 "(default: %s)\n", PK11_LIB_LOCATION); 67 #elif defined(USE_PKCS11) 68 fprintf(stderr, " -E engine: specify OpenSSL engine " 69 "(default \"pkcs11\")\n"); 70 #else 71 fprintf(stderr, " -E engine: specify OpenSSL engine\n"); 72 #endif 73 fprintf(stderr, " -f: force update of old-style " 74 "keys\n"); 75 fprintf(stderr, " -K directory: set key file location\n"); 76 fprintf(stderr, " -L ttl: set default key TTL\n"); 77 fprintf(stderr, " -v level: set level of verbosity\n"); 78 fprintf(stderr, " -V: print version information\n"); 79 fprintf(stderr, " -h: help\n"); 80 fprintf(stderr, "Timing options:\n"); 81 fprintf(stderr, " -P date/[+-]offset/none: set/unset key " 82 "publication date\n"); 83 fprintf(stderr, " -A date/[+-]offset/none: set/unset key " 84 "activation date\n"); 85 fprintf(stderr, " -R date/[+-]offset/none: set/unset key " 86 "revocation date\n"); 87 fprintf(stderr, " -I date/[+-]offset/none: set/unset key " 88 "inactivation date\n"); 89 fprintf(stderr, " -D date/[+-]offset/none: set/unset key " 90 "deletion date\n"); 91 fprintf(stderr, "Printing options:\n"); 92 fprintf(stderr, " -p C/P/A/R/I/D/all: print a particular time " 93 "value or values\n"); 94 fprintf(stderr, " -u: print times in unix epoch " 95 "format\n"); 96 fprintf(stderr, "Output:\n"); 97 fprintf(stderr, " K<name>+<alg>+<new id>.key, " 98 "K<name>+<alg>+<new id>.private\n"); 99 100 exit (-1); 101 } 102 103 static void 104 printtime(dst_key_t *key, int type, const char *tag, isc_boolean_t epoch, 105 FILE *stream) 106 { 107 isc_result_t result; 108 const char *output = NULL; 109 isc_stdtime_t when; 110 111 if (tag != NULL) 112 fprintf(stream, "%s: ", tag); 113 114 result = dst_key_gettime(key, type, &when); 115 if (result == ISC_R_NOTFOUND) { 116 fprintf(stream, "UNSET\n"); 117 } else if (epoch) { 118 fprintf(stream, "%d\n", (int) when); 119 } else { 120 time_t time = when; 121 output = ctime(&time); 122 fprintf(stream, "%s", output); 123 } 124 } 125 126 int 127 main(int argc, char **argv) { 128 isc_result_t result; 129 #ifdef USE_PKCS11 130 const char *engine = PKCS11_ENGINE; 131 #else 132 const char *engine = NULL; 133 #endif 134 char *filename = NULL, *directory = NULL; 135 char newname[1024]; 136 char keystr[DST_KEY_FORMATSIZE]; 137 char *endp, *p; 138 int ch; 139 isc_entropy_t *ectx = NULL; 140 const char *predecessor = NULL; 141 dst_key_t *prevkey = NULL; 142 dst_key_t *key = NULL; 143 isc_buffer_t buf; 144 dns_name_t *name = NULL; 145 dns_secalg_t alg = 0; 146 unsigned int size = 0; 147 isc_uint16_t flags = 0; 148 int prepub = -1; 149 dns_ttl_t ttl = 0; 150 isc_stdtime_t now; 151 isc_stdtime_t pub = 0, act = 0, rev = 0, inact = 0, del = 0; 152 isc_stdtime_t prevact = 0, previnact = 0, prevdel = 0; 153 isc_boolean_t setpub = ISC_FALSE, setact = ISC_FALSE; 154 isc_boolean_t setrev = ISC_FALSE, setinact = ISC_FALSE; 155 isc_boolean_t setdel = ISC_FALSE, setttl = ISC_FALSE; 156 isc_boolean_t unsetpub = ISC_FALSE, unsetact = ISC_FALSE; 157 isc_boolean_t unsetrev = ISC_FALSE, unsetinact = ISC_FALSE; 158 isc_boolean_t unsetdel = ISC_FALSE; 159 isc_boolean_t printcreate = ISC_FALSE, printpub = ISC_FALSE; 160 isc_boolean_t printact = ISC_FALSE, printrev = ISC_FALSE; 161 isc_boolean_t printinact = ISC_FALSE, printdel = ISC_FALSE; 162 isc_boolean_t force = ISC_FALSE; 163 isc_boolean_t epoch = ISC_FALSE; 164 isc_boolean_t changed = ISC_FALSE; 165 isc_log_t *log = NULL; 166 167 if (argc == 1) 168 usage(); 169 170 result = isc_mem_create(0, 0, &mctx); 171 if (result != ISC_R_SUCCESS) 172 fatal("Out of memory"); 173 174 setup_logging(mctx, &log); 175 176 #ifdef PKCS11CRYPTO 177 pk11_result_register(); 178 #endif 179 dns_result_register(); 180 181 isc_commandline_errprint = ISC_FALSE; 182 183 isc_stdtime_get(&now); 184 185 #define CMDLINE_FLAGS "A:D:E:fhI:i:K:L:P:p:R:S:uv:V" 186 while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { 187 switch (ch) { 188 case 'E': 189 engine = isc_commandline_argument; 190 break; 191 case 'f': 192 force = ISC_TRUE; 193 break; 194 case 'p': 195 p = isc_commandline_argument; 196 if (!strcasecmp(p, "all")) { 197 printcreate = ISC_TRUE; 198 printpub = ISC_TRUE; 199 printact = ISC_TRUE; 200 printrev = ISC_TRUE; 201 printinact = ISC_TRUE; 202 printdel = ISC_TRUE; 203 break; 204 } 205 206 do { 207 switch (*p++) { 208 case 'C': 209 printcreate = ISC_TRUE; 210 break; 211 case 'P': 212 printpub = ISC_TRUE; 213 break; 214 case 'A': 215 printact = ISC_TRUE; 216 break; 217 case 'R': 218 printrev = ISC_TRUE; 219 break; 220 case 'I': 221 printinact = ISC_TRUE; 222 break; 223 case 'D': 224 printdel = ISC_TRUE; 225 break; 226 case ' ': 227 break; 228 default: 229 usage(); 230 break; 231 } 232 } while (*p != '\0'); 233 break; 234 case 'u': 235 epoch = ISC_TRUE; 236 break; 237 case 'K': 238 /* 239 * We don't have to copy it here, but do it to 240 * simplify cleanup later 241 */ 242 directory = isc_mem_strdup(mctx, 243 isc_commandline_argument); 244 if (directory == NULL) { 245 fatal("Failed to allocate memory for " 246 "directory"); 247 } 248 break; 249 case 'L': 250 ttl = strtottl(isc_commandline_argument); 251 setttl = ISC_TRUE; 252 break; 253 case 'v': 254 verbose = strtol(isc_commandline_argument, &endp, 0); 255 if (*endp != '\0') 256 fatal("-v must be followed by a number"); 257 break; 258 case 'P': 259 if (setpub || unsetpub) 260 fatal("-P specified more than once"); 261 262 changed = ISC_TRUE; 263 pub = strtotime(isc_commandline_argument, 264 now, now, &setpub); 265 unsetpub = !setpub; 266 break; 267 case 'A': 268 if (setact || unsetact) 269 fatal("-A specified more than once"); 270 271 changed = ISC_TRUE; 272 act = strtotime(isc_commandline_argument, 273 now, now, &setact); 274 unsetact = !setact; 275 break; 276 case 'R': 277 if (setrev || unsetrev) 278 fatal("-R specified more than once"); 279 280 changed = ISC_TRUE; 281 rev = strtotime(isc_commandline_argument, 282 now, now, &setrev); 283 unsetrev = !setrev; 284 break; 285 case 'I': 286 if (setinact || unsetinact) 287 fatal("-I specified more than once"); 288 289 changed = ISC_TRUE; 290 inact = strtotime(isc_commandline_argument, 291 now, now, &setinact); 292 unsetinact = !setinact; 293 break; 294 case 'D': 295 if (setdel || unsetdel) 296 fatal("-D specified more than once"); 297 298 changed = ISC_TRUE; 299 del = strtotime(isc_commandline_argument, 300 now, now, &setdel); 301 unsetdel = !setdel; 302 break; 303 case 'S': 304 predecessor = isc_commandline_argument; 305 break; 306 case 'i': 307 prepub = strtottl(isc_commandline_argument); 308 break; 309 case '?': 310 if (isc_commandline_option != '?') 311 fprintf(stderr, "%s: invalid argument -%c\n", 312 program, isc_commandline_option); 313 /* Falls into */ 314 case 'h': 315 /* Does not return. */ 316 usage(); 317 318 case 'V': 319 /* Does not return. */ 320 version(program); 321 322 default: 323 fprintf(stderr, "%s: unhandled option -%c\n", 324 program, isc_commandline_option); 325 exit(1); 326 } 327 } 328 329 if (argc < isc_commandline_index + 1 || 330 argv[isc_commandline_index] == NULL) 331 fatal("The key file name was not specified"); 332 if (argc > isc_commandline_index + 1) 333 fatal("Extraneous arguments"); 334 335 if (ectx == NULL) 336 setup_entropy(mctx, NULL, &ectx); 337 result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE); 338 if (result != ISC_R_SUCCESS) 339 fatal("Could not initialize hash"); 340 result = dst_lib_init2(mctx, ectx, engine, 341 ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY); 342 if (result != ISC_R_SUCCESS) 343 fatal("Could not initialize dst: %s", 344 isc_result_totext(result)); 345 isc_entropy_stopcallbacksources(ectx); 346 347 if (predecessor != NULL) { 348 int major, minor; 349 350 if (prepub == -1) 351 prepub = (30 * 86400); 352 353 if (setpub || unsetpub) 354 fatal("-S and -P cannot be used together"); 355 if (setact || unsetact) 356 fatal("-S and -A cannot be used together"); 357 358 result = dst_key_fromnamedfile(predecessor, directory, 359 DST_TYPE_PUBLIC | 360 DST_TYPE_PRIVATE, 361 mctx, &prevkey); 362 if (result != ISC_R_SUCCESS) 363 fatal("Invalid keyfile %s: %s", 364 filename, isc_result_totext(result)); 365 if (!dst_key_isprivate(prevkey) && !dst_key_isexternal(prevkey)) 366 fatal("%s is not a private key", filename); 367 368 name = dst_key_name(prevkey); 369 alg = dst_key_alg(prevkey); 370 size = dst_key_size(prevkey); 371 flags = dst_key_flags(prevkey); 372 373 dst_key_format(prevkey, keystr, sizeof(keystr)); 374 dst_key_getprivateformat(prevkey, &major, &minor); 375 if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) 376 fatal("Predecessor has incompatible format " 377 "version %d.%d\n\t", major, minor); 378 379 result = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &prevact); 380 if (result != ISC_R_SUCCESS) 381 fatal("Predecessor has no activation date. " 382 "You must set one before\n\t" 383 "generating a successor."); 384 385 result = dst_key_gettime(prevkey, DST_TIME_INACTIVE, 386 &previnact); 387 if (result != ISC_R_SUCCESS) 388 fatal("Predecessor has no inactivation date. " 389 "You must set one before\n\t" 390 "generating a successor."); 391 392 pub = prevact - prepub; 393 if (pub < now && prepub != 0) 394 fatal("Predecessor will become inactive before the\n\t" 395 "prepublication period ends. Either change " 396 "its inactivation date,\n\t" 397 "or use the -i option to set a shorter " 398 "prepublication interval."); 399 400 result = dst_key_gettime(prevkey, DST_TIME_DELETE, &prevdel); 401 if (result != ISC_R_SUCCESS) 402 fprintf(stderr, "%s: warning: Predecessor has no " 403 "removal date;\n\t" 404 "it will remain in the zone " 405 "indefinitely after rollover.\n", 406 program); 407 else if (prevdel < previnact) 408 fprintf(stderr, "%s: warning: Predecessor is " 409 "scheduled to be deleted\n\t" 410 "before it is scheduled to be " 411 "inactive.\n", program); 412 413 changed = setpub = setact = ISC_TRUE; 414 dst_key_free(&prevkey); 415 } else { 416 if (prepub < 0) 417 prepub = 0; 418 419 if (prepub > 0) { 420 if (setpub && setact && (act - prepub) < pub) 421 fatal("Activation and publication dates " 422 "are closer together than the\n\t" 423 "prepublication interval."); 424 425 if (setpub && !setact) { 426 setact = ISC_TRUE; 427 act = pub + prepub; 428 } else if (setact && !setpub) { 429 setpub = ISC_TRUE; 430 pub = act - prepub; 431 } 432 433 if ((act - prepub) < now) 434 fatal("Time until activation is shorter " 435 "than the\n\tprepublication interval."); 436 } 437 } 438 439 if (directory != NULL) { 440 filename = argv[isc_commandline_index]; 441 } else { 442 result = isc_file_splitpath(mctx, argv[isc_commandline_index], 443 &directory, &filename); 444 if (result != ISC_R_SUCCESS) 445 fatal("cannot process filename %s: %s", 446 argv[isc_commandline_index], 447 isc_result_totext(result)); 448 } 449 450 result = dst_key_fromnamedfile(filename, directory, 451 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, 452 mctx, &key); 453 if (result != ISC_R_SUCCESS) 454 fatal("Invalid keyfile %s: %s", 455 filename, isc_result_totext(result)); 456 457 if (!dst_key_isprivate(key) && !dst_key_isexternal(key)) 458 fatal("%s is not a private key", filename); 459 460 dst_key_format(key, keystr, sizeof(keystr)); 461 462 if (predecessor != NULL) { 463 if (!dns_name_equal(name, dst_key_name(key))) 464 fatal("Key name mismatch"); 465 if (alg != dst_key_alg(key)) 466 fatal("Key algorithm mismatch"); 467 if (size != dst_key_size(key)) 468 fatal("Key size mismatch"); 469 if (flags != dst_key_flags(key)) 470 fatal("Key flags mismatch"); 471 } 472 473 prevdel = previnact = 0; 474 if ((setdel && setinact && del < inact) || 475 (dst_key_gettime(key, DST_TIME_INACTIVE, 476 &previnact) == ISC_R_SUCCESS && 477 setdel && !setinact && del < previnact) || 478 (dst_key_gettime(key, DST_TIME_DELETE, 479 &prevdel) == ISC_R_SUCCESS && 480 setinact && !setdel && prevdel < inact) || 481 (!setdel && !setinact && prevdel < previnact)) 482 fprintf(stderr, "%s: warning: Key is scheduled to " 483 "be deleted before it is\n\t" 484 "scheduled to be inactive.\n", 485 program); 486 487 if (force) 488 set_keyversion(key); 489 else 490 check_keyversion(key, keystr); 491 492 if (verbose > 2) 493 fprintf(stderr, "%s: %s\n", program, keystr); 494 495 /* 496 * Set time values. 497 */ 498 if (setpub) 499 dst_key_settime(key, DST_TIME_PUBLISH, pub); 500 else if (unsetpub) 501 dst_key_unsettime(key, DST_TIME_PUBLISH); 502 503 if (setact) 504 dst_key_settime(key, DST_TIME_ACTIVATE, act); 505 else if (unsetact) 506 dst_key_unsettime(key, DST_TIME_ACTIVATE); 507 508 if (setrev) { 509 if ((dst_key_flags(key) & DNS_KEYFLAG_REVOKE) != 0) 510 fprintf(stderr, "%s: warning: Key %s is already " 511 "revoked; changing the revocation date " 512 "will not affect this.\n", 513 program, keystr); 514 if ((dst_key_flags(key) & DNS_KEYFLAG_KSK) == 0) 515 fprintf(stderr, "%s: warning: Key %s is not flagged as " 516 "a KSK, but -R was used. Revoking a " 517 "ZSK is legal, but undefined.\n", 518 program, keystr); 519 dst_key_settime(key, DST_TIME_REVOKE, rev); 520 } else if (unsetrev) { 521 if ((dst_key_flags(key) & DNS_KEYFLAG_REVOKE) != 0) 522 fprintf(stderr, "%s: warning: Key %s is already " 523 "revoked; removing the revocation date " 524 "will not affect this.\n", 525 program, keystr); 526 dst_key_unsettime(key, DST_TIME_REVOKE); 527 } 528 529 if (setinact) 530 dst_key_settime(key, DST_TIME_INACTIVE, inact); 531 else if (unsetinact) 532 dst_key_unsettime(key, DST_TIME_INACTIVE); 533 534 if (setdel) 535 dst_key_settime(key, DST_TIME_DELETE, del); 536 else if (unsetdel) 537 dst_key_unsettime(key, DST_TIME_DELETE); 538 539 if (setttl) 540 dst_key_setttl(key, ttl); 541 542 /* 543 * No metadata changes were made but we're forcing an upgrade 544 * to the new format anyway: use "-P now -A now" as the default 545 */ 546 if (force && !changed) { 547 dst_key_settime(key, DST_TIME_PUBLISH, now); 548 dst_key_settime(key, DST_TIME_ACTIVATE, now); 549 changed = ISC_TRUE; 550 } 551 552 if (!changed && setttl) 553 changed = ISC_TRUE; 554 555 /* 556 * Print out time values, if -p was used. 557 */ 558 if (printcreate) 559 printtime(key, DST_TIME_CREATED, "Created", epoch, stdout); 560 561 if (printpub) 562 printtime(key, DST_TIME_PUBLISH, "Publish", epoch, stdout); 563 564 if (printact) 565 printtime(key, DST_TIME_ACTIVATE, "Activate", epoch, stdout); 566 567 if (printrev) 568 printtime(key, DST_TIME_REVOKE, "Revoke", epoch, stdout); 569 570 if (printinact) 571 printtime(key, DST_TIME_INACTIVE, "Inactive", epoch, stdout); 572 573 if (printdel) 574 printtime(key, DST_TIME_DELETE, "Delete", epoch, stdout); 575 576 if (changed) { 577 isc_buffer_init(&buf, newname, sizeof(newname)); 578 result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, 579 &buf); 580 if (result != ISC_R_SUCCESS) { 581 fatal("Failed to build public key filename: %s", 582 isc_result_totext(result)); 583 } 584 585 result = dst_key_tofile(key, DST_TYPE_PUBLIC|DST_TYPE_PRIVATE, 586 directory); 587 if (result != ISC_R_SUCCESS) { 588 dst_key_format(key, keystr, sizeof(keystr)); 589 fatal("Failed to write key %s: %s", keystr, 590 isc_result_totext(result)); 591 } 592 593 printf("%s\n", newname); 594 595 isc_buffer_clear(&buf); 596 result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, 597 &buf); 598 if (result != ISC_R_SUCCESS) { 599 fatal("Failed to build private key filename: %s", 600 isc_result_totext(result)); 601 } 602 printf("%s\n", newname); 603 } 604 605 dst_key_free(&key); 606 dst_lib_destroy(); 607 isc_hash_destroy(); 608 cleanup_entropy(&ectx); 609 if (verbose > 10) 610 isc_mem_stats(mctx, stdout); 611 cleanup_logging(&log); 612 isc_mem_free(mctx, directory); 613 isc_mem_destroy(&mctx); 614 615 return (0); 616 } 617