xref: /netbsd-src/external/mpl/bind/dist/bin/dnssec/dnssec-revoke.c (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
1 /*	$NetBSD: dnssec-revoke.c,v 1.9 2024/02/21 22:51:03 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 <inttypes.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 
23 #include <isc/attributes.h>
24 #include <isc/buffer.h>
25 #include <isc/commandline.h>
26 #include <isc/file.h>
27 #include <isc/hash.h>
28 #include <isc/mem.h>
29 #include <isc/print.h>
30 #include <isc/result.h>
31 #include <isc/string.h>
32 #include <isc/util.h>
33 
34 #include <dns/keyvalues.h>
35 
36 #include <dst/dst.h>
37 
38 #include "dnssectool.h"
39 
40 const char *program = "dnssec-revoke";
41 
42 static isc_mem_t *mctx = NULL;
43 
44 noreturn static void
45 usage(void);
46 
47 static void
48 usage(void) {
49 	fprintf(stderr, "Usage:\n");
50 	fprintf(stderr, "    %s [options] keyfile\n\n", program);
51 	fprintf(stderr, "Version: %s\n", PACKAGE_VERSION);
52 	fprintf(stderr, "    -E engine:    specify OpenSSL engine\n");
53 	fprintf(stderr, "    -f:           force overwrite\n");
54 	fprintf(stderr, "    -h:           help\n");
55 	fprintf(stderr, "    -K directory: use directory for key files\n");
56 	fprintf(stderr, "    -r:           remove old keyfiles after "
57 			"creating revoked version\n");
58 	fprintf(stderr, "    -v level:     set level of verbosity\n");
59 	fprintf(stderr, "    -V:           print version information\n");
60 	fprintf(stderr, "Output:\n");
61 	fprintf(stderr, "     K<name>+<alg>+<new id>.key, "
62 			"K<name>+<alg>+<new id>.private\n");
63 
64 	exit(-1);
65 }
66 
67 int
68 main(int argc, char **argv) {
69 	isc_result_t result;
70 	const char *engine = NULL;
71 	char const *filename = NULL;
72 	char *dir = NULL;
73 	char newname[1024], oldname[1024];
74 	char keystr[DST_KEY_FORMATSIZE];
75 	char *endp;
76 	int ch;
77 	dst_key_t *key = NULL;
78 	uint32_t flags;
79 	isc_buffer_t buf;
80 	bool force = false;
81 	bool removefile = false;
82 	bool id = false;
83 
84 	if (argc == 1) {
85 		usage();
86 	}
87 
88 	isc_mem_create(&mctx);
89 
90 	isc_commandline_errprint = false;
91 
92 	while ((ch = isc_commandline_parse(argc, argv, "E:fK:rRhv:V")) != -1) {
93 		switch (ch) {
94 		case 'E':
95 			engine = isc_commandline_argument;
96 			break;
97 		case 'f':
98 			force = true;
99 			break;
100 		case 'K':
101 			/*
102 			 * We don't have to copy it here, but do it to
103 			 * simplify cleanup later
104 			 */
105 			dir = isc_mem_strdup(mctx, isc_commandline_argument);
106 			break;
107 		case 'r':
108 			removefile = true;
109 			break;
110 		case 'R':
111 			id = true;
112 			break;
113 		case 'v':
114 			verbose = strtol(isc_commandline_argument, &endp, 0);
115 			if (*endp != '\0') {
116 				fatal("-v must be followed by a number");
117 			}
118 			break;
119 		case '?':
120 			if (isc_commandline_option != '?') {
121 				fprintf(stderr, "%s: invalid argument -%c\n",
122 					program, isc_commandline_option);
123 			}
124 			FALLTHROUGH;
125 		case 'h':
126 			/* Does not return. */
127 			usage();
128 
129 		case 'V':
130 			/* Does not return. */
131 			version(program);
132 
133 		default:
134 			fprintf(stderr, "%s: unhandled option -%c\n", program,
135 				isc_commandline_option);
136 			exit(1);
137 		}
138 	}
139 
140 	if (argc < isc_commandline_index + 1 ||
141 	    argv[isc_commandline_index] == NULL)
142 	{
143 		fatal("The key file name was not specified");
144 	}
145 	if (argc > isc_commandline_index + 1) {
146 		fatal("Extraneous arguments");
147 	}
148 
149 	if (dir != NULL) {
150 		filename = argv[isc_commandline_index];
151 	} else {
152 		result = isc_file_splitpath(mctx, argv[isc_commandline_index],
153 					    &dir, &filename);
154 		if (result != ISC_R_SUCCESS) {
155 			fatal("cannot process filename %s: %s",
156 			      argv[isc_commandline_index],
157 			      isc_result_totext(result));
158 		}
159 		if (strcmp(dir, ".") == 0) {
160 			isc_mem_free(mctx, dir);
161 			dir = NULL;
162 		}
163 	}
164 
165 	result = dst_lib_init(mctx, engine);
166 	if (result != ISC_R_SUCCESS) {
167 		fatal("Could not initialize dst: %s",
168 		      isc_result_totext(result));
169 	}
170 
171 	result = dst_key_fromnamedfile(
172 		filename, dir, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, mctx, &key);
173 	if (result != ISC_R_SUCCESS) {
174 		fatal("Invalid keyfile name %s: %s", filename,
175 		      isc_result_totext(result));
176 	}
177 
178 	if (id) {
179 		fprintf(stdout, "%u\n", dst_key_rid(key));
180 		goto cleanup;
181 	}
182 	dst_key_format(key, keystr, sizeof(keystr));
183 
184 	if (verbose > 2) {
185 		fprintf(stderr, "%s: %s\n", program, keystr);
186 	}
187 
188 	if (force) {
189 		set_keyversion(key);
190 	} else {
191 		check_keyversion(key, keystr);
192 	}
193 
194 	flags = dst_key_flags(key);
195 	if ((flags & DNS_KEYFLAG_REVOKE) == 0) {
196 		isc_stdtime_t now;
197 
198 		if ((flags & DNS_KEYFLAG_KSK) == 0) {
199 			fprintf(stderr,
200 				"%s: warning: Key is not flagged "
201 				"as a KSK. Revoking a ZSK is "
202 				"legal, but undefined.\n",
203 				program);
204 		}
205 
206 		isc_stdtime_get(&now);
207 		dst_key_settime(key, DST_TIME_REVOKE, now);
208 
209 		dst_key_setflags(key, flags | DNS_KEYFLAG_REVOKE);
210 
211 		isc_buffer_init(&buf, newname, sizeof(newname));
212 		dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf);
213 
214 		if (access(newname, F_OK) == 0 && !force) {
215 			fatal("Key file %s already exists; "
216 			      "use -f to force overwrite",
217 			      newname);
218 		}
219 
220 		result = dst_key_tofile(key, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
221 					dir);
222 		if (result != ISC_R_SUCCESS) {
223 			dst_key_format(key, keystr, sizeof(keystr));
224 			fatal("Failed to write key %s: %s", keystr,
225 			      isc_result_totext(result));
226 		}
227 
228 		isc_buffer_clear(&buf);
229 		dst_key_buildfilename(key, 0, dir, &buf);
230 		printf("%s\n", newname);
231 
232 		/*
233 		 * Remove old key file, if told to (and if
234 		 * it isn't the same as the new file)
235 		 */
236 		if (removefile) {
237 			isc_buffer_init(&buf, oldname, sizeof(oldname));
238 			dst_key_setflags(key, flags & ~DNS_KEYFLAG_REVOKE);
239 			dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
240 			if (strcmp(oldname, newname) == 0) {
241 				goto cleanup;
242 			}
243 			(void)unlink(oldname);
244 			isc_buffer_clear(&buf);
245 			dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf);
246 			(void)unlink(oldname);
247 		}
248 	} else {
249 		dst_key_format(key, keystr, sizeof(keystr));
250 		fatal("Key %s is already revoked", keystr);
251 	}
252 
253 cleanup:
254 	dst_key_free(&key);
255 	dst_lib_destroy();
256 	if (verbose > 10) {
257 		isc_mem_stats(mctx, stdout);
258 	}
259 	if (dir != NULL) {
260 		isc_mem_free(mctx, dir);
261 	}
262 	isc_mem_destroy(&mctx);
263 
264 	return (0);
265 }
266