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