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