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