xref: /netbsd-src/external/mpl/bind/dist/bin/dnssec/dnssec-settime.c (revision 782713e6c126f1866c6d9cfdee4ceb49483b5828)
1 /*	$NetBSD: dnssec-settime.c,v 1.7 2023/01/25 21:43:23 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <errno.h>
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stdlib.h>
22 #include <time.h>
23 #include <unistd.h>
24 
25 #include <isc/buffer.h>
26 #include <isc/commandline.h>
27 #include <isc/file.h>
28 #include <isc/hash.h>
29 #include <isc/mem.h>
30 #include <isc/print.h>
31 #include <isc/string.h>
32 #include <isc/time.h>
33 #include <isc/util.h>
34 
35 #include <dns/keyvalues.h>
36 #include <dns/log.h>
37 #include <dns/result.h>
38 
39 #include <dst/dst.h>
40 
41 #if USE_PKCS11
42 #include <pk11/result.h>
43 #endif /* if USE_PKCS11 */
44 
45 #include "dnssectool.h"
46 
47 const char *program = "dnssec-settime";
48 
49 static isc_mem_t *mctx = NULL;
50 
51 ISC_PLATFORM_NORETURN_PRE static void
52 usage(void) ISC_PLATFORM_NORETURN_POST;
53 
54 static void
55 usage(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 #if USE_PKCS11
61 	fprintf(stderr,
62 		"    -E engine:          specify PKCS#11 provider "
63 		"(default: %s)\n",
64 		PK11_LIB_LOCATION);
65 #elif defined(USE_PKCS11)
66 	fprintf(stderr, "    -E engine:          specify OpenSSL engine "
67 			"(default \"pkcs11\")\n");
68 #else  /* if USE_PKCS11 */
69 	fprintf(stderr, "    -E engine:          specify OpenSSL engine\n");
70 #endif /* if USE_PKCS11 */
71 	fprintf(stderr, "    -f:                 force update of old-style "
72 			"keys\n");
73 	fprintf(stderr, "    -K directory:       set key file location\n");
74 	fprintf(stderr, "    -L ttl:             set default key TTL\n");
75 	fprintf(stderr, "    -v level:           set level of verbosity\n");
76 	fprintf(stderr, "    -V:                 print version information\n");
77 	fprintf(stderr, "    -h:                 help\n");
78 	fprintf(stderr, "Timing options:\n");
79 	fprintf(stderr, "    -P date/[+-]offset/none: set/unset key "
80 			"publication date\n");
81 	fprintf(stderr, "    -P ds date/[+-]offset/none: set/unset "
82 			"DS publication date\n");
83 	fprintf(stderr, "    -P sync date/[+-]offset/none: set/unset "
84 			"CDS and CDNSKEY publication date\n");
85 	fprintf(stderr, "    -A date/[+-]offset/none: set/unset key "
86 			"activation date\n");
87 	fprintf(stderr, "    -R date/[+-]offset/none: set/unset key "
88 			"revocation date\n");
89 	fprintf(stderr, "    -I date/[+-]offset/none: set/unset key "
90 			"inactivation date\n");
91 	fprintf(stderr, "    -D date/[+-]offset/none: set/unset key "
92 			"deletion date\n");
93 	fprintf(stderr, "    -D ds date/[+-]offset/none: set/unset "
94 			"DS deletion date\n");
95 	fprintf(stderr, "    -D sync date/[+-]offset/none: set/unset "
96 			"CDS and CDNSKEY deletion date\n");
97 	fprintf(stderr, "    -S <key>: generate a successor to an existing "
98 			"key\n");
99 	fprintf(stderr, "    -i <interval>: prepublication interval for "
100 			"successor key "
101 			"(default: 30 days)\n");
102 	fprintf(stderr, "Key state options:\n");
103 	fprintf(stderr, "    -s: update key state file (default no)\n");
104 	fprintf(stderr, "    -g state: set the goal state for this key\n");
105 	fprintf(stderr, "    -d state date/[+-]offset: set the DS state\n");
106 	fprintf(stderr, "    -k state date/[+-]offset: set the DNSKEY state\n");
107 	fprintf(stderr, "    -r state date/[+-]offset: set the RRSIG (KSK) "
108 			"state\n");
109 	fprintf(stderr, "    -z state date/[+-]offset: set the RRSIG (ZSK) "
110 			"state\n");
111 	fprintf(stderr, "Printing options:\n");
112 	fprintf(stderr, "    -p C/P/Psync/A/R/I/D/Dsync/all: print a "
113 			"particular time value or values\n");
114 	fprintf(stderr, "    -u:                 print times in unix epoch "
115 			"format\n");
116 	fprintf(stderr, "Output:\n");
117 	fprintf(stderr, "     K<name>+<alg>+<new id>.key, "
118 			"K<name>+<alg>+<new id>.private\n");
119 
120 	exit(-1);
121 }
122 
123 static void
124 printtime(dst_key_t *key, int type, const char *tag, bool epoch, FILE *stream) {
125 	isc_result_t result;
126 	isc_stdtime_t when;
127 
128 	if (tag != NULL) {
129 		fprintf(stream, "%s: ", tag);
130 	}
131 
132 	result = dst_key_gettime(key, type, &when);
133 	if (result == ISC_R_NOTFOUND) {
134 		fprintf(stream, "UNSET\n");
135 	} else if (epoch) {
136 		fprintf(stream, "%d\n", (int)when);
137 	} else {
138 		time_t now = when;
139 		struct tm t, *tm = localtime_r(&now, &t);
140 		unsigned int flen;
141 		char timebuf[80];
142 
143 		if (tm == NULL) {
144 			fprintf(stream, "INVALID\n");
145 			return;
146 		}
147 
148 		flen = strftime(timebuf, sizeof(timebuf),
149 				"%a %b %e %H:%M:%S %Y", tm);
150 		INSIST(flen > 0U && flen < sizeof(timebuf));
151 		fprintf(stream, "%s\n", timebuf);
152 	}
153 }
154 
155 static void
156 writekey(dst_key_t *key, const char *directory, bool write_state) {
157 	char newname[1024];
158 	char keystr[DST_KEY_FORMATSIZE];
159 	isc_buffer_t buf;
160 	isc_result_t result;
161 	int options = DST_TYPE_PUBLIC | DST_TYPE_PRIVATE;
162 
163 	if (write_state) {
164 		options |= DST_TYPE_STATE;
165 	}
166 
167 	isc_buffer_init(&buf, newname, sizeof(newname));
168 	result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &buf);
169 	if (result != ISC_R_SUCCESS) {
170 		fatal("Failed to build public key filename: %s",
171 		      isc_result_totext(result));
172 	}
173 
174 	result = dst_key_tofile(key, options, directory);
175 	if (result != ISC_R_SUCCESS) {
176 		dst_key_format(key, keystr, sizeof(keystr));
177 		fatal("Failed to write key %s: %s", keystr,
178 		      isc_result_totext(result));
179 	}
180 	printf("%s\n", newname);
181 
182 	isc_buffer_clear(&buf);
183 	result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &buf);
184 	if (result != ISC_R_SUCCESS) {
185 		fatal("Failed to build private key filename: %s",
186 		      isc_result_totext(result));
187 	}
188 	printf("%s\n", newname);
189 
190 	if (write_state) {
191 		isc_buffer_clear(&buf);
192 		result = dst_key_buildfilename(key, DST_TYPE_STATE, directory,
193 					       &buf);
194 		if (result != ISC_R_SUCCESS) {
195 			fatal("Failed to build key state filename: %s",
196 			      isc_result_totext(result));
197 		}
198 		printf("%s\n", newname);
199 	}
200 }
201 
202 int
203 main(int argc, char **argv) {
204 	isc_result_t result;
205 	const char *engine = NULL;
206 	const char *filename = NULL;
207 	char *directory = NULL;
208 	char keystr[DST_KEY_FORMATSIZE];
209 	char *endp, *p;
210 	int ch;
211 	const char *predecessor = NULL;
212 	dst_key_t *prevkey = NULL;
213 	dst_key_t *key = NULL;
214 	dns_name_t *name = NULL;
215 	dns_secalg_t alg = 0;
216 	unsigned int size = 0;
217 	uint16_t flags = 0;
218 	int prepub = -1;
219 	int options;
220 	dns_ttl_t ttl = 0;
221 	isc_stdtime_t now;
222 	isc_stdtime_t dstime = 0, dnskeytime = 0;
223 	isc_stdtime_t krrsigtime = 0, zrrsigtime = 0;
224 	isc_stdtime_t pub = 0, act = 0, rev = 0, inact = 0, del = 0;
225 	isc_stdtime_t prevact = 0, previnact = 0, prevdel = 0;
226 	dst_key_state_t goal = DST_KEY_STATE_NA;
227 	dst_key_state_t ds = DST_KEY_STATE_NA;
228 	dst_key_state_t dnskey = DST_KEY_STATE_NA;
229 	dst_key_state_t krrsig = DST_KEY_STATE_NA;
230 	dst_key_state_t zrrsig = DST_KEY_STATE_NA;
231 	bool setgoal = false, setds = false, setdnskey = false;
232 	bool setkrrsig = false, setzrrsig = false;
233 	bool setdstime = false, setdnskeytime = false;
234 	bool setkrrsigtime = false, setzrrsigtime = false;
235 	bool setpub = false, setact = false;
236 	bool setrev = false, setinact = false;
237 	bool setdel = false, setttl = false;
238 	bool unsetpub = false, unsetact = false;
239 	bool unsetrev = false, unsetinact = false;
240 	bool unsetdel = false;
241 	bool printcreate = false, printpub = false;
242 	bool printact = false, printrev = false;
243 	bool printinact = false, printdel = false;
244 	bool force = false;
245 	bool epoch = false;
246 	bool changed = false;
247 	bool write_state = false;
248 	isc_log_t *log = NULL;
249 	isc_stdtime_t syncadd = 0, syncdel = 0;
250 	bool unsetsyncadd = false, setsyncadd = false;
251 	bool unsetsyncdel = false, setsyncdel = false;
252 	bool printsyncadd = false, printsyncdel = false;
253 	isc_stdtime_t dsadd = 0, dsdel = 0;
254 	bool unsetdsadd = false, setdsadd = false;
255 	bool unsetdsdel = false, setdsdel = false;
256 	bool printdsadd = false, printdsdel = false;
257 
258 	options = DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE;
259 
260 	if (argc == 1) {
261 		usage();
262 	}
263 
264 	isc_mem_create(&mctx);
265 
266 	setup_logging(mctx, &log);
267 
268 #if USE_PKCS11
269 	pk11_result_register();
270 #endif /* if USE_PKCS11 */
271 	dns_result_register();
272 
273 	isc_commandline_errprint = false;
274 
275 	isc_stdtime_get(&now);
276 
277 #define CMDLINE_FLAGS "A:D:d:E:fg:hI:i:K:k:L:P:p:R:r:S:suv:Vz:"
278 	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
279 		switch (ch) {
280 		case 'A':
281 			if (setact || unsetact) {
282 				fatal("-A specified more than once");
283 			}
284 
285 			changed = true;
286 			act = strtotime(isc_commandline_argument, now, now,
287 					&setact);
288 			unsetact = !setact;
289 			break;
290 		case 'D':
291 			/* -Dsync ? */
292 			if (isoptarg("sync", argv, usage)) {
293 				if (unsetsyncdel || setsyncdel) {
294 					fatal("-D sync specified more than "
295 					      "once");
296 				}
297 
298 				changed = true;
299 				syncdel = strtotime(isc_commandline_argument,
300 						    now, now, &setsyncdel);
301 				unsetsyncdel = !setsyncdel;
302 				break;
303 			}
304 			/* -Dds ? */
305 			if (isoptarg("ds", argv, usage)) {
306 				if (unsetdsdel || setdsdel) {
307 					fatal("-D ds specified more than once");
308 				}
309 
310 				changed = true;
311 				dsdel = strtotime(isc_commandline_argument, now,
312 						  now, &setdsdel);
313 				unsetdsdel = !setdsdel;
314 				break;
315 			}
316 			/* -Ddnskey ? */
317 			(void)isoptarg("dnskey", argv, usage);
318 			if (setdel || unsetdel) {
319 				fatal("-D specified more than once");
320 			}
321 
322 			changed = true;
323 			del = strtotime(isc_commandline_argument, now, now,
324 					&setdel);
325 			unsetdel = !setdel;
326 			break;
327 		case 'd':
328 			if (setds) {
329 				fatal("-d specified more than once");
330 			}
331 
332 			ds = strtokeystate(isc_commandline_argument);
333 			setds = true;
334 			/* time */
335 			(void)isoptarg(isc_commandline_argument, argv, usage);
336 			dstime = strtotime(isc_commandline_argument, now, now,
337 					   &setdstime);
338 			break;
339 		case 'E':
340 			engine = isc_commandline_argument;
341 			break;
342 		case 'f':
343 			force = true;
344 			break;
345 		case 'g':
346 			if (setgoal) {
347 				fatal("-g specified more than once");
348 			}
349 
350 			goal = strtokeystate(isc_commandline_argument);
351 			if (goal != DST_KEY_STATE_NA &&
352 			    goal != DST_KEY_STATE_HIDDEN &&
353 			    goal != DST_KEY_STATE_OMNIPRESENT)
354 			{
355 				fatal("-g must be either none, hidden, or "
356 				      "omnipresent");
357 			}
358 			setgoal = true;
359 			break;
360 		case '?':
361 			if (isc_commandline_option != '?') {
362 				fprintf(stderr, "%s: invalid argument -%c\n",
363 					program, isc_commandline_option);
364 			}
365 			FALLTHROUGH;
366 		case 'h':
367 			/* Does not return. */
368 			usage();
369 		case 'I':
370 			if (setinact || unsetinact) {
371 				fatal("-I specified more than once");
372 			}
373 
374 			changed = true;
375 			inact = strtotime(isc_commandline_argument, now, now,
376 					  &setinact);
377 			unsetinact = !setinact;
378 			break;
379 		case 'i':
380 			prepub = strtottl(isc_commandline_argument);
381 			break;
382 		case 'K':
383 			/*
384 			 * We don't have to copy it here, but do it to
385 			 * simplify cleanup later
386 			 */
387 			directory = isc_mem_strdup(mctx,
388 						   isc_commandline_argument);
389 			break;
390 		case 'k':
391 			if (setdnskey) {
392 				fatal("-k specified more than once");
393 			}
394 
395 			dnskey = strtokeystate(isc_commandline_argument);
396 			setdnskey = true;
397 			/* time */
398 			(void)isoptarg(isc_commandline_argument, argv, usage);
399 			dnskeytime = strtotime(isc_commandline_argument, now,
400 					       now, &setdnskeytime);
401 			break;
402 		case 'L':
403 			ttl = strtottl(isc_commandline_argument);
404 			setttl = true;
405 			break;
406 		case 'P':
407 			/* -Psync ? */
408 			if (isoptarg("sync", argv, usage)) {
409 				if (unsetsyncadd || setsyncadd) {
410 					fatal("-P sync specified more than "
411 					      "once");
412 				}
413 
414 				changed = true;
415 				syncadd = strtotime(isc_commandline_argument,
416 						    now, now, &setsyncadd);
417 				unsetsyncadd = !setsyncadd;
418 				break;
419 			}
420 			/* -Pds ? */
421 			if (isoptarg("ds", argv, usage)) {
422 				if (unsetdsadd || setdsadd) {
423 					fatal("-P ds specified more than once");
424 				}
425 
426 				changed = true;
427 				dsadd = strtotime(isc_commandline_argument, now,
428 						  now, &setdsadd);
429 				unsetdsadd = !setdsadd;
430 				break;
431 			}
432 			/* -Pdnskey ? */
433 			(void)isoptarg("dnskey", argv, usage);
434 			if (setpub || unsetpub) {
435 				fatal("-P specified more than once");
436 			}
437 
438 			changed = true;
439 			pub = strtotime(isc_commandline_argument, now, now,
440 					&setpub);
441 			unsetpub = !setpub;
442 			break;
443 		case 'p':
444 			p = isc_commandline_argument;
445 			if (!strcasecmp(p, "all")) {
446 				printcreate = true;
447 				printpub = true;
448 				printact = true;
449 				printrev = true;
450 				printinact = true;
451 				printdel = true;
452 				printsyncadd = true;
453 				printsyncdel = true;
454 				printdsadd = true;
455 				printdsdel = true;
456 				break;
457 			}
458 
459 			do {
460 				switch (*p++) {
461 				case 'A':
462 					printact = true;
463 					break;
464 				case 'C':
465 					printcreate = true;
466 					break;
467 				case 'D':
468 					if (!strncmp(p, "sync", 4)) {
469 						p += 4;
470 						printsyncdel = true;
471 						break;
472 					}
473 					if (!strncmp(p, "ds", 2)) {
474 						p += 2;
475 						printdsdel = true;
476 						break;
477 					}
478 					printdel = true;
479 					break;
480 				case 'I':
481 					printinact = true;
482 					break;
483 				case 'P':
484 					if (!strncmp(p, "sync", 4)) {
485 						p += 4;
486 						printsyncadd = true;
487 						break;
488 					}
489 					if (!strncmp(p, "ds", 2)) {
490 						p += 2;
491 						printdsadd = true;
492 						break;
493 					}
494 					printpub = true;
495 					break;
496 				case 'R':
497 					printrev = true;
498 					break;
499 				case ' ':
500 					break;
501 				default:
502 					usage();
503 					break;
504 				}
505 			} while (*p != '\0');
506 			break;
507 		case 'R':
508 			if (setrev || unsetrev) {
509 				fatal("-R specified more than once");
510 			}
511 
512 			changed = true;
513 			rev = strtotime(isc_commandline_argument, now, now,
514 					&setrev);
515 			unsetrev = !setrev;
516 			break;
517 		case 'r':
518 			if (setkrrsig) {
519 				fatal("-r specified more than once");
520 			}
521 
522 			krrsig = strtokeystate(isc_commandline_argument);
523 			setkrrsig = true;
524 			/* time */
525 			(void)isoptarg(isc_commandline_argument, argv, usage);
526 			krrsigtime = strtotime(isc_commandline_argument, now,
527 					       now, &setkrrsigtime);
528 			break;
529 		case 'S':
530 			predecessor = isc_commandline_argument;
531 			break;
532 		case 's':
533 			write_state = true;
534 			break;
535 		case 'u':
536 			epoch = true;
537 			break;
538 		case 'V':
539 			/* Does not return. */
540 			version(program);
541 		case 'v':
542 			verbose = strtol(isc_commandline_argument, &endp, 0);
543 			if (*endp != '\0') {
544 				fatal("-v must be followed by a number");
545 			}
546 			break;
547 		case 'z':
548 			if (setzrrsig) {
549 				fatal("-z specified more than once");
550 			}
551 
552 			zrrsig = strtokeystate(isc_commandline_argument);
553 			setzrrsig = true;
554 			(void)isoptarg(isc_commandline_argument, argv, usage);
555 			zrrsigtime = strtotime(isc_commandline_argument, now,
556 					       now, &setzrrsigtime);
557 			break;
558 
559 		default:
560 			fprintf(stderr, "%s: unhandled option -%c\n", program,
561 				isc_commandline_option);
562 			exit(1);
563 		}
564 	}
565 
566 	if (argc < isc_commandline_index + 1 ||
567 	    argv[isc_commandline_index] == NULL)
568 	{
569 		fatal("The key file name was not specified");
570 	}
571 	if (argc > isc_commandline_index + 1) {
572 		fatal("Extraneous arguments");
573 	}
574 
575 	if ((setgoal || setds || setdnskey || setkrrsig || setzrrsig) &&
576 	    !write_state)
577 	{
578 		fatal("Options -g, -d, -k, -r and -z require -s to be set");
579 	}
580 
581 	result = dst_lib_init(mctx, engine);
582 	if (result != ISC_R_SUCCESS) {
583 		fatal("Could not initialize dst: %s",
584 		      isc_result_totext(result));
585 	}
586 
587 	if (predecessor != NULL) {
588 		int major, minor;
589 
590 		if (prepub == -1) {
591 			prepub = (30 * 86400);
592 		}
593 
594 		if (setpub || unsetpub) {
595 			fatal("-S and -P cannot be used together");
596 		}
597 		if (setact || unsetact) {
598 			fatal("-S and -A cannot be used together");
599 		}
600 
601 		result = dst_key_fromnamedfile(predecessor, directory, options,
602 					       mctx, &prevkey);
603 		if (result != ISC_R_SUCCESS) {
604 			fatal("Invalid keyfile %s: %s", filename,
605 			      isc_result_totext(result));
606 		}
607 		if (!dst_key_isprivate(prevkey) && !dst_key_isexternal(prevkey))
608 		{
609 			fatal("%s is not a private key", filename);
610 		}
611 
612 		name = dst_key_name(prevkey);
613 		alg = dst_key_alg(prevkey);
614 		size = dst_key_size(prevkey);
615 		flags = dst_key_flags(prevkey);
616 
617 		dst_key_format(prevkey, keystr, sizeof(keystr));
618 		dst_key_getprivateformat(prevkey, &major, &minor);
619 		if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) {
620 			fatal("Predecessor has incompatible format "
621 			      "version %d.%d\n\t",
622 			      major, minor);
623 		}
624 
625 		result = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &prevact);
626 		if (result != ISC_R_SUCCESS) {
627 			fatal("Predecessor has no activation date. "
628 			      "You must set one before\n\t"
629 			      "generating a successor.");
630 		}
631 
632 		result = dst_key_gettime(prevkey, DST_TIME_INACTIVE,
633 					 &previnact);
634 		if (result != ISC_R_SUCCESS) {
635 			fatal("Predecessor has no inactivation date. "
636 			      "You must set one before\n\t"
637 			      "generating a successor.");
638 		}
639 
640 		pub = previnact - prepub;
641 		act = previnact;
642 
643 		if ((previnact - prepub) < now && prepub != 0) {
644 			fatal("Time until predecessor inactivation is\n\t"
645 			      "shorter than the prepublication interval.  "
646 			      "Either change\n\t"
647 			      "predecessor inactivation date, or use the -i "
648 			      "option to set\n\t"
649 			      "a shorter prepublication interval.");
650 		}
651 
652 		result = dst_key_gettime(prevkey, DST_TIME_DELETE, &prevdel);
653 		if (result != ISC_R_SUCCESS) {
654 			fprintf(stderr,
655 				"%s: warning: Predecessor has no "
656 				"removal date;\n\t"
657 				"it will remain in the zone "
658 				"indefinitely after rollover.\n",
659 				program);
660 		} else if (prevdel < previnact) {
661 			fprintf(stderr,
662 				"%s: warning: Predecessor is "
663 				"scheduled to be deleted\n\t"
664 				"before it is scheduled to be "
665 				"inactive.\n",
666 				program);
667 		}
668 
669 		changed = setpub = setact = true;
670 	} else {
671 		if (prepub < 0) {
672 			prepub = 0;
673 		}
674 
675 		if (prepub > 0) {
676 			if (setpub && setact && (act - prepub) < pub) {
677 				fatal("Activation and publication dates "
678 				      "are closer together than the\n\t"
679 				      "prepublication interval.");
680 			}
681 
682 			if (setpub && !setact) {
683 				setact = true;
684 				act = pub + prepub;
685 			} else if (setact && !setpub) {
686 				setpub = true;
687 				pub = act - prepub;
688 			}
689 
690 			if ((act - prepub) < now) {
691 				fatal("Time until activation is shorter "
692 				      "than the\n\tprepublication interval.");
693 			}
694 		}
695 	}
696 
697 	if (directory != NULL) {
698 		filename = argv[isc_commandline_index];
699 	} else {
700 		result = isc_file_splitpath(mctx, argv[isc_commandline_index],
701 					    &directory, &filename);
702 		if (result != ISC_R_SUCCESS) {
703 			fatal("cannot process filename %s: %s",
704 			      argv[isc_commandline_index],
705 			      isc_result_totext(result));
706 		}
707 	}
708 
709 	result = dst_key_fromnamedfile(filename, directory, options, mctx,
710 				       &key);
711 	if (result != ISC_R_SUCCESS) {
712 		fatal("Invalid keyfile %s: %s", filename,
713 		      isc_result_totext(result));
714 	}
715 
716 	if (!dst_key_isprivate(key) && !dst_key_isexternal(key)) {
717 		fatal("%s is not a private key", filename);
718 	}
719 
720 	dst_key_format(key, keystr, sizeof(keystr));
721 
722 	if (predecessor != NULL) {
723 		if (!dns_name_equal(name, dst_key_name(key))) {
724 			fatal("Key name mismatch");
725 		}
726 		if (alg != dst_key_alg(key)) {
727 			fatal("Key algorithm mismatch");
728 		}
729 		if (size != dst_key_size(key)) {
730 			fatal("Key size mismatch");
731 		}
732 		if (flags != dst_key_flags(key)) {
733 			fatal("Key flags mismatch");
734 		}
735 	}
736 
737 	prevdel = previnact = 0;
738 	if ((setdel && setinact && del < inact) ||
739 	    (dst_key_gettime(key, DST_TIME_INACTIVE, &previnact) ==
740 		     ISC_R_SUCCESS &&
741 	     setdel && !setinact && !unsetinact && del < previnact) ||
742 	    (dst_key_gettime(key, DST_TIME_DELETE, &prevdel) == ISC_R_SUCCESS &&
743 	     setinact && !setdel && !unsetdel && prevdel < inact) ||
744 	    (!setdel && !unsetdel && !setinact && !unsetinact && prevdel != 0 &&
745 	     prevdel < previnact))
746 	{
747 		fprintf(stderr,
748 			"%s: warning: Key is scheduled to "
749 			"be deleted before it is\n\t"
750 			"scheduled to be inactive.\n",
751 			program);
752 	}
753 
754 	if (force) {
755 		set_keyversion(key);
756 	} else {
757 		check_keyversion(key, keystr);
758 	}
759 
760 	if (verbose > 2) {
761 		fprintf(stderr, "%s: %s\n", program, keystr);
762 	}
763 
764 	/*
765 	 * Set time values.
766 	 */
767 	if (setpub) {
768 		dst_key_settime(key, DST_TIME_PUBLISH, pub);
769 	} else if (unsetpub) {
770 		dst_key_unsettime(key, DST_TIME_PUBLISH);
771 	}
772 
773 	if (setact) {
774 		dst_key_settime(key, DST_TIME_ACTIVATE, act);
775 	} else if (unsetact) {
776 		dst_key_unsettime(key, DST_TIME_ACTIVATE);
777 	}
778 
779 	if (setrev) {
780 		if ((dst_key_flags(key) & DNS_KEYFLAG_REVOKE) != 0) {
781 			fprintf(stderr,
782 				"%s: warning: Key %s is already "
783 				"revoked; changing the revocation date "
784 				"will not affect this.\n",
785 				program, keystr);
786 		}
787 		if ((dst_key_flags(key) & DNS_KEYFLAG_KSK) == 0) {
788 			fprintf(stderr,
789 				"%s: warning: Key %s is not flagged as "
790 				"a KSK, but -R was used.  Revoking a "
791 				"ZSK is legal, but undefined.\n",
792 				program, keystr);
793 		}
794 		dst_key_settime(key, DST_TIME_REVOKE, rev);
795 	} else if (unsetrev) {
796 		if ((dst_key_flags(key) & DNS_KEYFLAG_REVOKE) != 0) {
797 			fprintf(stderr,
798 				"%s: warning: Key %s is already "
799 				"revoked; removing the revocation date "
800 				"will not affect this.\n",
801 				program, keystr);
802 		}
803 		dst_key_unsettime(key, DST_TIME_REVOKE);
804 	}
805 
806 	if (setinact) {
807 		dst_key_settime(key, DST_TIME_INACTIVE, inact);
808 	} else if (unsetinact) {
809 		dst_key_unsettime(key, DST_TIME_INACTIVE);
810 	}
811 
812 	if (setdel) {
813 		dst_key_settime(key, DST_TIME_DELETE, del);
814 	} else if (unsetdel) {
815 		dst_key_unsettime(key, DST_TIME_DELETE);
816 	}
817 
818 	if (setsyncadd) {
819 		dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncadd);
820 	} else if (unsetsyncadd) {
821 		dst_key_unsettime(key, DST_TIME_SYNCPUBLISH);
822 	}
823 
824 	if (setsyncdel) {
825 		dst_key_settime(key, DST_TIME_SYNCDELETE, syncdel);
826 	} else if (unsetsyncdel) {
827 		dst_key_unsettime(key, DST_TIME_SYNCDELETE);
828 	}
829 
830 	if (setdsadd) {
831 		dst_key_settime(key, DST_TIME_DSPUBLISH, dsadd);
832 	} else if (unsetdsadd) {
833 		dst_key_unsettime(key, DST_TIME_DSPUBLISH);
834 	}
835 
836 	if (setdsdel) {
837 		dst_key_settime(key, DST_TIME_DSDELETE, dsdel);
838 	} else if (unsetdsdel) {
839 		dst_key_unsettime(key, DST_TIME_DSDELETE);
840 	}
841 
842 	if (setttl) {
843 		dst_key_setttl(key, ttl);
844 	}
845 
846 	if (predecessor != NULL && prevkey != NULL) {
847 		dst_key_setnum(prevkey, DST_NUM_SUCCESSOR, dst_key_id(key));
848 		dst_key_setnum(key, DST_NUM_PREDECESSOR, dst_key_id(prevkey));
849 	}
850 
851 	/*
852 	 * No metadata changes were made but we're forcing an upgrade
853 	 * to the new format anyway: use "-P now -A now" as the default
854 	 */
855 	if (force && !changed) {
856 		dst_key_settime(key, DST_TIME_PUBLISH, now);
857 		dst_key_settime(key, DST_TIME_ACTIVATE, now);
858 		changed = true;
859 	}
860 
861 	/*
862 	 * Make sure the key state goals are written.
863 	 */
864 	if (write_state) {
865 		if (setgoal) {
866 			if (goal == DST_KEY_STATE_NA) {
867 				dst_key_unsetstate(key, DST_KEY_GOAL);
868 			} else {
869 				dst_key_setstate(key, DST_KEY_GOAL, goal);
870 			}
871 			changed = true;
872 		}
873 		if (setds) {
874 			if (ds == DST_KEY_STATE_NA) {
875 				dst_key_unsetstate(key, DST_KEY_DS);
876 				dst_key_unsettime(key, DST_TIME_DS);
877 			} else {
878 				dst_key_setstate(key, DST_KEY_DS, ds);
879 				dst_key_settime(key, DST_TIME_DS, dstime);
880 			}
881 			changed = true;
882 		}
883 		if (setdnskey) {
884 			if (dnskey == DST_KEY_STATE_NA) {
885 				dst_key_unsetstate(key, DST_KEY_DNSKEY);
886 				dst_key_unsettime(key, DST_TIME_DNSKEY);
887 			} else {
888 				dst_key_setstate(key, DST_KEY_DNSKEY, dnskey);
889 				dst_key_settime(key, DST_TIME_DNSKEY,
890 						dnskeytime);
891 			}
892 			changed = true;
893 		}
894 		if (setkrrsig) {
895 			if (krrsig == DST_KEY_STATE_NA) {
896 				dst_key_unsetstate(key, DST_KEY_KRRSIG);
897 				dst_key_unsettime(key, DST_TIME_KRRSIG);
898 			} else {
899 				dst_key_setstate(key, DST_KEY_KRRSIG, krrsig);
900 				dst_key_settime(key, DST_TIME_KRRSIG,
901 						krrsigtime);
902 			}
903 			changed = true;
904 		}
905 		if (setzrrsig) {
906 			if (zrrsig == DST_KEY_STATE_NA) {
907 				dst_key_unsetstate(key, DST_KEY_ZRRSIG);
908 				dst_key_unsettime(key, DST_TIME_ZRRSIG);
909 			} else {
910 				dst_key_setstate(key, DST_KEY_ZRRSIG, zrrsig);
911 				dst_key_settime(key, DST_TIME_ZRRSIG,
912 						zrrsigtime);
913 			}
914 			changed = true;
915 		}
916 	}
917 
918 	if (!changed && setttl) {
919 		changed = true;
920 	}
921 
922 	/*
923 	 * Print out time values, if -p was used.
924 	 */
925 	if (printcreate) {
926 		printtime(key, DST_TIME_CREATED, "Created", epoch, stdout);
927 	}
928 
929 	if (printpub) {
930 		printtime(key, DST_TIME_PUBLISH, "Publish", epoch, stdout);
931 	}
932 
933 	if (printact) {
934 		printtime(key, DST_TIME_ACTIVATE, "Activate", epoch, stdout);
935 	}
936 
937 	if (printrev) {
938 		printtime(key, DST_TIME_REVOKE, "Revoke", epoch, stdout);
939 	}
940 
941 	if (printinact) {
942 		printtime(key, DST_TIME_INACTIVE, "Inactive", epoch, stdout);
943 	}
944 
945 	if (printdel) {
946 		printtime(key, DST_TIME_DELETE, "Delete", epoch, stdout);
947 	}
948 
949 	if (printsyncadd) {
950 		printtime(key, DST_TIME_SYNCPUBLISH, "SYNC Publish", epoch,
951 			  stdout);
952 	}
953 
954 	if (printsyncdel) {
955 		printtime(key, DST_TIME_SYNCDELETE, "SYNC Delete", epoch,
956 			  stdout);
957 	}
958 
959 	if (printdsadd) {
960 		printtime(key, DST_TIME_DSPUBLISH, "DS Publish", epoch, stdout);
961 	}
962 
963 	if (printdsdel) {
964 		printtime(key, DST_TIME_DSDELETE, "DS Delete", epoch, stdout);
965 	}
966 
967 	if (changed) {
968 		writekey(key, directory, write_state);
969 		if (predecessor != NULL && prevkey != NULL) {
970 			writekey(prevkey, directory, write_state);
971 		}
972 	}
973 
974 	if (prevkey != NULL) {
975 		dst_key_free(&prevkey);
976 	}
977 	dst_key_free(&key);
978 	dst_lib_destroy();
979 	if (verbose > 10) {
980 		isc_mem_stats(mctx, stdout);
981 	}
982 	cleanup_logging(&log);
983 	isc_mem_free(mctx, directory);
984 	isc_mem_destroy(&mctx);
985 
986 	return (0);
987 }
988