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