xref: /netbsd-src/external/mpl/bind/dist/bin/dnssec/dnssec-importkey.c (revision 2dd295436a0082eb4f8d294f4aa73c223413d0f2)
1 /*	$NetBSD: dnssec-importkey.c,v 1.7 2022/09/23 12:15:21 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 <stdbool.h>
19 #include <stdlib.h>
20 
21 #include <isc/buffer.h>
22 #include <isc/commandline.h>
23 #include <isc/hash.h>
24 #include <isc/mem.h>
25 #include <isc/print.h>
26 #include <isc/string.h>
27 #include <isc/util.h>
28 
29 #include <dns/callbacks.h>
30 #include <dns/db.h>
31 #include <dns/dbiterator.h>
32 #include <dns/ds.h>
33 #include <dns/fixedname.h>
34 #include <dns/keyvalues.h>
35 #include <dns/log.h>
36 #include <dns/master.h>
37 #include <dns/name.h>
38 #include <dns/rdata.h>
39 #include <dns/rdataclass.h>
40 #include <dns/rdataset.h>
41 #include <dns/rdatasetiter.h>
42 #include <dns/rdatatype.h>
43 #include <dns/result.h>
44 
45 #include <dst/dst.h>
46 
47 #if USE_PKCS11
48 #include <pk11/result.h>
49 #endif /* if USE_PKCS11 */
50 
51 #include "dnssectool.h"
52 
53 const char *program = "dnssec-importkey";
54 
55 static dns_rdataclass_t rdclass;
56 static dns_fixedname_t fixed;
57 static dns_name_t *name = NULL;
58 static isc_mem_t *mctx = NULL;
59 static bool setpub = false, setdel = false;
60 static bool setttl = false;
61 static isc_stdtime_t pub = 0, del = 0;
62 static dns_ttl_t ttl = 0;
63 static isc_stdtime_t syncadd = 0, syncdel = 0;
64 static bool setsyncadd = false;
65 static bool setsyncdel = false;
66 
67 static isc_result_t
68 initname(char *setname) {
69 	isc_result_t result;
70 	isc_buffer_t buf;
71 
72 	name = dns_fixedname_initname(&fixed);
73 
74 	isc_buffer_init(&buf, setname, strlen(setname));
75 	isc_buffer_add(&buf, strlen(setname));
76 	result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
77 	return (result);
78 }
79 
80 static void
81 db_load_from_stream(dns_db_t *db, FILE *fp) {
82 	isc_result_t result;
83 	dns_rdatacallbacks_t callbacks;
84 
85 	dns_rdatacallbacks_init(&callbacks);
86 	result = dns_db_beginload(db, &callbacks);
87 	if (result != ISC_R_SUCCESS) {
88 		fatal("dns_db_beginload failed: %s", isc_result_totext(result));
89 	}
90 
91 	result = dns_master_loadstream(fp, name, name, rdclass, 0, &callbacks,
92 				       mctx);
93 	if (result != ISC_R_SUCCESS) {
94 		fatal("can't load from input: %s", isc_result_totext(result));
95 	}
96 
97 	result = dns_db_endload(db, &callbacks);
98 	if (result != ISC_R_SUCCESS) {
99 		fatal("dns_db_endload failed: %s", isc_result_totext(result));
100 	}
101 }
102 
103 static isc_result_t
104 loadset(const char *filename, dns_rdataset_t *rdataset) {
105 	isc_result_t result;
106 	dns_db_t *db = NULL;
107 	dns_dbnode_t *node = NULL;
108 	char setname[DNS_NAME_FORMATSIZE];
109 
110 	dns_name_format(name, setname, sizeof(setname));
111 
112 	result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0,
113 			       NULL, &db);
114 	if (result != ISC_R_SUCCESS) {
115 		fatal("can't create database");
116 	}
117 
118 	if (strcmp(filename, "-") == 0) {
119 		db_load_from_stream(db, stdin);
120 		filename = "input";
121 	} else {
122 		result = dns_db_load(db, filename, dns_masterformat_text,
123 				     DNS_MASTER_NOTTL);
124 		if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) {
125 			fatal("can't load %s: %s", filename,
126 			      isc_result_totext(result));
127 		}
128 	}
129 
130 	result = dns_db_findnode(db, name, false, &node);
131 	if (result != ISC_R_SUCCESS) {
132 		fatal("can't find %s node in %s", setname, filename);
133 	}
134 
135 	result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, 0,
136 				     rdataset, NULL);
137 
138 	if (result == ISC_R_NOTFOUND) {
139 		fatal("no DNSKEY RR for %s in %s", setname, filename);
140 	} else if (result != ISC_R_SUCCESS) {
141 		fatal("dns_db_findrdataset");
142 	}
143 
144 	if (node != NULL) {
145 		dns_db_detachnode(db, &node);
146 	}
147 	if (db != NULL) {
148 		dns_db_detach(&db);
149 	}
150 	return (result);
151 }
152 
153 static void
154 loadkey(char *filename, unsigned char *key_buf, unsigned int key_buf_size,
155 	dns_rdata_t *rdata) {
156 	isc_result_t result;
157 	dst_key_t *key = NULL;
158 	isc_buffer_t keyb;
159 	isc_region_t r;
160 
161 	dns_rdata_init(rdata);
162 
163 	isc_buffer_init(&keyb, key_buf, key_buf_size);
164 
165 	result = dst_key_fromnamedfile(filename, NULL, DST_TYPE_PUBLIC, mctx,
166 				       &key);
167 	if (result != ISC_R_SUCCESS) {
168 		fatal("invalid keyfile name %s: %s", filename,
169 		      isc_result_totext(result));
170 	}
171 
172 	if (verbose > 2) {
173 		char keystr[DST_KEY_FORMATSIZE];
174 
175 		dst_key_format(key, keystr, sizeof(keystr));
176 		fprintf(stderr, "%s: %s\n", program, keystr);
177 	}
178 
179 	result = dst_key_todns(key, &keyb);
180 	if (result != ISC_R_SUCCESS) {
181 		fatal("can't decode key");
182 	}
183 
184 	isc_buffer_usedregion(&keyb, &r);
185 	dns_rdata_fromregion(rdata, dst_key_class(key), dns_rdatatype_dnskey,
186 			     &r);
187 
188 	rdclass = dst_key_class(key);
189 
190 	name = dns_fixedname_initname(&fixed);
191 	dns_name_copynf(dst_key_name(key), name);
192 
193 	dst_key_free(&key);
194 }
195 
196 static void
197 emit(const char *dir, dns_rdata_t *rdata) {
198 	isc_result_t result;
199 	char keystr[DST_KEY_FORMATSIZE];
200 	char pubname[1024];
201 	char priname[1024];
202 	isc_buffer_t buf;
203 	dst_key_t *key = NULL, *tmp = NULL;
204 
205 	isc_buffer_init(&buf, rdata->data, rdata->length);
206 	isc_buffer_add(&buf, rdata->length);
207 	result = dst_key_fromdns(name, rdclass, &buf, mctx, &key);
208 	if (result != ISC_R_SUCCESS) {
209 		fatal("dst_key_fromdns: %s", isc_result_totext(result));
210 	}
211 
212 	isc_buffer_init(&buf, pubname, sizeof(pubname));
213 	result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf);
214 	if (result != ISC_R_SUCCESS) {
215 		fatal("Failed to build public key filename: %s",
216 		      isc_result_totext(result));
217 	}
218 	isc_buffer_init(&buf, priname, sizeof(priname));
219 	result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
220 	if (result != ISC_R_SUCCESS) {
221 		fatal("Failed to build private key filename: %s",
222 		      isc_result_totext(result));
223 	}
224 
225 	result = dst_key_fromfile(
226 		dst_key_name(key), dst_key_id(key), dst_key_alg(key),
227 		DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, dir, mctx, &tmp);
228 	if (result == ISC_R_SUCCESS) {
229 		if (dst_key_isprivate(tmp) && !dst_key_isexternal(tmp)) {
230 			fatal("Private key already exists in %s", priname);
231 		}
232 		dst_key_free(&tmp);
233 	}
234 
235 	dst_key_setexternal(key, true);
236 	if (setpub) {
237 		dst_key_settime(key, DST_TIME_PUBLISH, pub);
238 	}
239 	if (setdel) {
240 		dst_key_settime(key, DST_TIME_DELETE, del);
241 	}
242 	if (setsyncadd) {
243 		dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncadd);
244 	}
245 	if (setsyncdel) {
246 		dst_key_settime(key, DST_TIME_SYNCDELETE, syncdel);
247 	}
248 
249 	if (setttl) {
250 		dst_key_setttl(key, ttl);
251 	}
252 
253 	result = dst_key_tofile(key, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, dir);
254 	if (result != ISC_R_SUCCESS) {
255 		dst_key_format(key, keystr, sizeof(keystr));
256 		fatal("Failed to write key %s: %s", keystr,
257 		      isc_result_totext(result));
258 	}
259 	printf("%s\n", pubname);
260 
261 	isc_buffer_clear(&buf);
262 	result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
263 	if (result != ISC_R_SUCCESS) {
264 		fatal("Failed to build private key filename: %s",
265 		      isc_result_totext(result));
266 	}
267 	printf("%s\n", priname);
268 	dst_key_free(&key);
269 }
270 
271 ISC_PLATFORM_NORETURN_PRE static void
272 usage(void) ISC_PLATFORM_NORETURN_POST;
273 
274 static void
275 usage(void) {
276 	fprintf(stderr, "Usage:\n");
277 	fprintf(stderr, "    %s options [-K dir] keyfile\n\n", program);
278 	fprintf(stderr, "    %s options -f file [keyname]\n\n", program);
279 	fprintf(stderr, "Version: %s\n", VERSION);
280 	fprintf(stderr, "Options:\n");
281 	fprintf(stderr, "    -f file: read key from zone file\n");
282 	fprintf(stderr, "    -K <directory>: directory in which to store "
283 			"the key files\n");
284 	fprintf(stderr, "    -L ttl:             set default key TTL\n");
285 	fprintf(stderr, "    -v <verbose level>\n");
286 	fprintf(stderr, "    -V: print version information\n");
287 	fprintf(stderr, "    -h: print usage and exit\n");
288 	fprintf(stderr, "Timing options:\n");
289 	fprintf(stderr, "    -P date/[+-]offset/none: set/unset key "
290 			"publication date\n");
291 	fprintf(stderr, "    -P sync date/[+-]offset/none: set/unset "
292 			"CDS and CDNSKEY publication date\n");
293 	fprintf(stderr, "    -D date/[+-]offset/none: set/unset key "
294 			"deletion date\n");
295 	fprintf(stderr, "    -D sync date/[+-]offset/none: set/unset "
296 			"CDS and CDNSKEY deletion date\n");
297 
298 	exit(-1);
299 }
300 
301 int
302 main(int argc, char **argv) {
303 	char *classname = NULL;
304 	char *filename = NULL, *dir = NULL, *namestr;
305 	char *endp;
306 	int ch;
307 	isc_result_t result;
308 	isc_log_t *log = NULL;
309 	dns_rdataset_t rdataset;
310 	dns_rdata_t rdata;
311 	isc_stdtime_t now;
312 
313 	dns_rdata_init(&rdata);
314 	isc_stdtime_get(&now);
315 
316 	if (argc == 1) {
317 		usage();
318 	}
319 
320 	isc_mem_create(&mctx);
321 
322 #if USE_PKCS11
323 	pk11_result_register();
324 #endif /* if USE_PKCS11 */
325 	dns_result_register();
326 
327 	isc_commandline_errprint = false;
328 
329 #define CMDLINE_FLAGS "D:f:hK:L:P:v:V"
330 	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
331 		switch (ch) {
332 		case 'D':
333 			/* -Dsync ? */
334 			if (isoptarg("sync", argv, usage)) {
335 				if (setsyncdel) {
336 					fatal("-D sync specified more than "
337 					      "once");
338 				}
339 
340 				syncdel = strtotime(isc_commandline_argument,
341 						    now, now, &setsyncdel);
342 				break;
343 			}
344 			/* -Ddnskey ? */
345 			(void)isoptarg("dnskey", argv, usage);
346 			if (setdel) {
347 				fatal("-D specified more than once");
348 			}
349 
350 			del = strtotime(isc_commandline_argument, now, now,
351 					&setdel);
352 			break;
353 		case 'K':
354 			dir = isc_commandline_argument;
355 			if (strlen(dir) == 0U) {
356 				fatal("directory must be non-empty string");
357 			}
358 			break;
359 		case 'L':
360 			ttl = strtottl(isc_commandline_argument);
361 			setttl = true;
362 			break;
363 		case 'P':
364 			/* -Psync ? */
365 			if (isoptarg("sync", argv, usage)) {
366 				if (setsyncadd) {
367 					fatal("-P sync specified more than "
368 					      "once");
369 				}
370 
371 				syncadd = strtotime(isc_commandline_argument,
372 						    now, now, &setsyncadd);
373 				break;
374 			}
375 			/* -Pdnskey ? */
376 			(void)isoptarg("dnskey", argv, usage);
377 			if (setpub) {
378 				fatal("-P specified more than once");
379 			}
380 
381 			pub = strtotime(isc_commandline_argument, now, now,
382 					&setpub);
383 			break;
384 		case 'f':
385 			filename = isc_commandline_argument;
386 			break;
387 		case 'v':
388 			verbose = strtol(isc_commandline_argument, &endp, 0);
389 			if (*endp != '\0') {
390 				fatal("-v must be followed by a number");
391 			}
392 			break;
393 		case '?':
394 			if (isc_commandline_option != '?') {
395 				fprintf(stderr, "%s: invalid argument -%c\n",
396 					program, isc_commandline_option);
397 			}
398 			FALLTHROUGH;
399 		case 'h':
400 			/* Does not return. */
401 			usage();
402 
403 		case 'V':
404 			/* Does not return. */
405 			version(program);
406 
407 		default:
408 			fprintf(stderr, "%s: unhandled option -%c\n", program,
409 				isc_commandline_option);
410 			exit(1);
411 		}
412 	}
413 
414 	rdclass = strtoclass(classname);
415 
416 	if (argc < isc_commandline_index + 1 && filename == NULL) {
417 		fatal("the key file name was not specified");
418 	}
419 	if (argc > isc_commandline_index + 1) {
420 		fatal("extraneous arguments");
421 	}
422 
423 	result = dst_lib_init(mctx, NULL);
424 	if (result != ISC_R_SUCCESS) {
425 		fatal("could not initialize dst: %s",
426 		      isc_result_totext(result));
427 	}
428 
429 	setup_logging(mctx, &log);
430 
431 	dns_rdataset_init(&rdataset);
432 
433 	if (filename != NULL) {
434 		if (argc < isc_commandline_index + 1) {
435 			/* using filename as zone name */
436 			namestr = filename;
437 		} else {
438 			namestr = argv[isc_commandline_index];
439 		}
440 
441 		result = initname(namestr);
442 		if (result != ISC_R_SUCCESS) {
443 			fatal("could not initialize name %s", namestr);
444 		}
445 
446 		result = loadset(filename, &rdataset);
447 
448 		if (result != ISC_R_SUCCESS) {
449 			fatal("could not load DNSKEY set: %s\n",
450 			      isc_result_totext(result));
451 		}
452 
453 		for (result = dns_rdataset_first(&rdataset);
454 		     result == ISC_R_SUCCESS;
455 		     result = dns_rdataset_next(&rdataset))
456 		{
457 			dns_rdata_init(&rdata);
458 			dns_rdataset_current(&rdataset, &rdata);
459 			emit(dir, &rdata);
460 		}
461 	} else {
462 		unsigned char key_buf[DST_KEY_MAXSIZE];
463 
464 		loadkey(argv[isc_commandline_index], key_buf, DST_KEY_MAXSIZE,
465 			&rdata);
466 
467 		emit(dir, &rdata);
468 	}
469 
470 	if (dns_rdataset_isassociated(&rdataset)) {
471 		dns_rdataset_disassociate(&rdataset);
472 	}
473 	cleanup_logging(&log);
474 	dst_lib_destroy();
475 	if (verbose > 10) {
476 		isc_mem_stats(mctx, stdout);
477 	}
478 	isc_mem_destroy(&mctx);
479 
480 	fflush(stdout);
481 	if (ferror(stdout)) {
482 		fprintf(stderr, "write error\n");
483 		return (1);
484 	} else {
485 		return (0);
486 	}
487 }
488