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