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