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