1 /*
2 * Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /*! \file */
18
19 #include <config.h>
20
21 #include <stdlib.h>
22
23 #include <isc/buffer.h>
24 #include <isc/commandline.h>
25 #include <isc/entropy.h>
26 #include <isc/hash.h>
27 #include <isc/mem.h>
28 #include <isc/print.h>
29 #include <isc/string.h>
30 #include <isc/util.h>
31
32 #include <dns/callbacks.h>
33 #include <dns/db.h>
34 #include <dns/dbiterator.h>
35 #include <dns/ds.h>
36 #include <dns/fixedname.h>
37 #include <dns/keyvalues.h>
38 #include <dns/log.h>
39 #include <dns/master.h>
40 #include <dns/name.h>
41 #include <dns/rdata.h>
42 #include <dns/rdataclass.h>
43 #include <dns/rdataset.h>
44 #include <dns/rdatasetiter.h>
45 #include <dns/rdatatype.h>
46 #include <dns/result.h>
47
48 #include <dst/dst.h>
49
50 #include "dnssectool.h"
51
52 #ifndef PATH_MAX
53 #define PATH_MAX 1024 /* AIX, WIN32, and others don't define this. */
54 #endif
55
56 const char *program = "dnssec-importkey";
57 int verbose;
58
59 static dns_rdataclass_t rdclass;
60 static dns_fixedname_t fixed;
61 static dns_name_t *name = NULL;
62 static isc_mem_t *mctx = NULL;
63 static isc_boolean_t setpub = ISC_FALSE, setdel = ISC_FALSE;
64 static isc_boolean_t setttl = ISC_FALSE;
65 static isc_stdtime_t pub = 0, del = 0;
66 static dns_ttl_t ttl = 0;
67
68 static isc_result_t
initname(char * setname)69 initname(char *setname) {
70 isc_result_t result;
71 isc_buffer_t buf;
72
73 dns_fixedname_init(&fixed);
74 name = dns_fixedname_name(&fixed);
75
76 isc_buffer_init(&buf, setname, strlen(setname));
77 isc_buffer_add(&buf, strlen(setname));
78 result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
79 return (result);
80 }
81
82 static void
db_load_from_stream(dns_db_t * db,FILE * fp)83 db_load_from_stream(dns_db_t *db, FILE *fp) {
84 isc_result_t result;
85 dns_rdatacallbacks_t callbacks;
86
87 dns_rdatacallbacks_init(&callbacks);
88 result = dns_db_beginload(db, &callbacks.add, &callbacks.add_private);
89 if (result != ISC_R_SUCCESS)
90 fatal("dns_db_beginload failed: %s", isc_result_totext(result));
91
92 result = dns_master_loadstream(fp, name, name, rdclass, 0,
93 &callbacks, mctx);
94 if (result != ISC_R_SUCCESS)
95 fatal("can't load from input: %s", isc_result_totext(result));
96
97 result = dns_db_endload(db, &callbacks.add_private);
98 if (result != ISC_R_SUCCESS)
99 fatal("dns_db_endload failed: %s", isc_result_totext(result));
100 }
101
102 static isc_result_t
loadset(const char * filename,dns_rdataset_t * rdataset)103 loadset(const char *filename, dns_rdataset_t *rdataset) {
104 isc_result_t result;
105 dns_db_t *db = NULL;
106 dns_dbnode_t *node = NULL;
107 char setname[DNS_NAME_FORMATSIZE];
108
109 dns_name_format(name, setname, sizeof(setname));
110
111 result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
112 rdclass, 0, NULL, &db);
113 if (result != ISC_R_SUCCESS)
114 fatal("can't create database");
115
116 if (strcmp(filename, "-") == 0) {
117 db_load_from_stream(db, stdin);
118 filename = "input";
119 } else {
120 result = dns_db_load3(db, filename, dns_masterformat_text,
121 DNS_MASTER_NOTTL);
122 if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
123 fatal("can't load %s: %s", filename,
124 isc_result_totext(result));
125 }
126
127 result = dns_db_findnode(db, name, ISC_FALSE, &node);
128 if (result != ISC_R_SUCCESS)
129 fatal("can't find %s node in %s", setname, filename);
130
131 result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey,
132 0, 0, rdataset, NULL);
133
134 if (result == ISC_R_NOTFOUND)
135 fatal("no DNSKEY RR for %s in %s", setname, filename);
136 else if (result != ISC_R_SUCCESS)
137 fatal("dns_db_findrdataset");
138
139 if (node != NULL)
140 dns_db_detachnode(db, &node);
141 if (db != NULL)
142 dns_db_detach(&db);
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 {
150 isc_result_t result;
151 dst_key_t *key = NULL;
152 isc_buffer_t keyb;
153 isc_region_t r;
154
155 dns_rdata_init(rdata);
156
157 isc_buffer_init(&keyb, key_buf, key_buf_size);
158
159 result = dst_key_fromnamedfile(filename, NULL, DST_TYPE_PUBLIC,
160 mctx, &key);
161 if (result != ISC_R_SUCCESS)
162 fatal("invalid keyfile name %s: %s",
163 filename, isc_result_totext(result));
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 isc_buffer_usedregion(&keyb, &r);
177 dns_rdata_fromregion(rdata, dst_key_class(key),
178 dns_rdatatype_dnskey, &r);
179
180 rdclass = dst_key_class(key);
181
182 dns_fixedname_init(&fixed);
183 name = dns_fixedname_name(&fixed);
184 result = dns_name_copy(dst_key_name(key), name, NULL);
185 if (result != ISC_R_SUCCESS)
186 fatal("can't copy name");
187
188 dst_key_free(&key);
189 }
190
191 static void
emit(const char * dir,dns_rdata_t * rdata)192 emit(const char *dir, dns_rdata_t *rdata) {
193 isc_result_t result;
194 char keystr[DST_KEY_FORMATSIZE];
195 char pubname[1024];
196 char priname[1024];
197 isc_buffer_t buf;
198 dst_key_t *key = NULL, *tmp = NULL;
199
200 isc_buffer_init(&buf, rdata->data, rdata->length);
201 isc_buffer_add(&buf, rdata->length);
202 result = dst_key_fromdns(name, rdclass, &buf, mctx, &key);
203 if (result != ISC_R_SUCCESS) {
204 fatal("dst_key_fromdns: %s", isc_result_totext(result));
205 }
206
207 isc_buffer_init(&buf, pubname, sizeof(pubname));
208 result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf);
209 if (result != ISC_R_SUCCESS) {
210 fatal("Failed to build public key filename: %s",
211 isc_result_totext(result));
212 }
213 isc_buffer_init(&buf, priname, sizeof(priname));
214 result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
215 if (result != ISC_R_SUCCESS) {
216 fatal("Failed to build private key filename: %s",
217 isc_result_totext(result));
218 }
219
220 result = dst_key_fromfile(dst_key_name(key), dst_key_id(key),
221 dst_key_alg(key),
222 DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
223 dir, mctx, &tmp);
224 if (result == ISC_R_SUCCESS) {
225 if (dst_key_isprivate(tmp) && !dst_key_isexternal(tmp))
226 fatal("Private key already exists in %s", priname);
227 dst_key_free(&tmp);
228 }
229
230 dst_key_setexternal(key, ISC_TRUE);
231 if (setpub)
232 dst_key_settime(key, DST_TIME_PUBLISH, pub);
233 if (setdel)
234 dst_key_settime(key, DST_TIME_DELETE, del);
235 if (setttl)
236 dst_key_setttl(key, ttl);
237
238 result = dst_key_tofile(key, DST_TYPE_PUBLIC|DST_TYPE_PRIVATE,
239 dir);
240 if (result != ISC_R_SUCCESS) {
241 dst_key_format(key, keystr, sizeof(keystr));
242 fatal("Failed to write key %s: %s", keystr,
243 isc_result_totext(result));
244 }
245 printf("%s\n", pubname);
246
247 isc_buffer_clear(&buf);
248 result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
249 if (result != ISC_R_SUCCESS) {
250 fatal("Failed to build private key filename: %s",
251 isc_result_totext(result));
252 }
253 printf("%s\n", priname);
254 dst_key_free(&key);
255 }
256
257 ISC_PLATFORM_NORETURN_PRE static void
258 usage(void) ISC_PLATFORM_NORETURN_POST;
259
260 static void
usage(void)261 usage(void) {
262 fprintf(stderr, "Usage:\n");
263 fprintf(stderr, " %s options [-K dir] keyfile\n\n", program);
264 fprintf(stderr, " %s options -f file [keyname]\n\n", program);
265 fprintf(stderr, "Version: %s\n", VERSION);
266 fprintf(stderr, "Options:\n");
267 fprintf(stderr, " -f file: read key from zone file\n");
268 fprintf(stderr, " -K <directory>: directory in which to store "
269 "the key files\n");
270 fprintf(stderr, " -L ttl: set default key TTL\n");
271 fprintf(stderr, " -v <verbose level>\n");
272 fprintf(stderr, " -V: print version information\n");
273 fprintf(stderr, " -h: print usage and exit\n");
274 fprintf(stderr, "Timing options:\n");
275 fprintf(stderr, " -P date/[+-]offset/none: set/unset key "
276 "publication date\n");
277 fprintf(stderr, " -D date/[+-]offset/none: set/unset key "
278 "deletion date\n");
279
280 exit (-1);
281 }
282
283 int
main(int argc,char ** argv)284 main(int argc, char **argv) {
285 char *classname = NULL;
286 char *filename = NULL, *dir = NULL, *namestr;
287 char *endp;
288 int ch;
289 isc_result_t result;
290 isc_log_t *log = NULL;
291 isc_entropy_t *ectx = NULL;
292 dns_rdataset_t rdataset;
293 dns_rdata_t rdata;
294 isc_stdtime_t now;
295
296 dns_rdata_init(&rdata);
297 isc_stdtime_get(&now);
298
299 if (argc == 1)
300 usage();
301
302 result = isc_mem_create(0, 0, &mctx);
303 if (result != ISC_R_SUCCESS)
304 fatal("out of memory");
305
306 dns_result_register();
307
308 isc_commandline_errprint = ISC_FALSE;
309
310 #define CMDLINE_FLAGS "D:f:hK:L:P:v:V"
311 while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
312 switch (ch) {
313 case 'D':
314 if (setdel)
315 fatal("-D specified more than once");
316
317 del = strtotime(isc_commandline_argument,
318 now, now, &setdel);
319 break;
320 case 'K':
321 dir = isc_commandline_argument;
322 if (strlen(dir) == 0U)
323 fatal("directory must be non-empty string");
324 break;
325 case 'L':
326 ttl = strtottl(isc_commandline_argument);
327 setttl = ISC_TRUE;
328 break;
329 case 'P':
330 if (setpub)
331 fatal("-P specified more than once");
332
333 pub = strtotime(isc_commandline_argument,
334 now, now, &setpub);
335 break;
336 case 'f':
337 filename = isc_commandline_argument;
338 break;
339 case 'v':
340 verbose = strtol(isc_commandline_argument, &endp, 0);
341 if (*endp != '\0')
342 fatal("-v must be followed by a number");
343 break;
344 case '?':
345 if (isc_commandline_option != '?')
346 fprintf(stderr, "%s: invalid argument -%c\n",
347 program, isc_commandline_option);
348 /* FALLTHROUGH */
349 case 'h':
350 /* Does not return. */
351 usage();
352
353 case 'V':
354 /* Does not return. */
355 version(program);
356
357 default:
358 fprintf(stderr, "%s: unhandled option -%c\n",
359 program, isc_commandline_option);
360 exit(1);
361 }
362 }
363
364 rdclass = strtoclass(classname);
365
366 if (argc < isc_commandline_index + 1 && filename == NULL)
367 fatal("the key file name was not specified");
368 if (argc > isc_commandline_index + 1)
369 fatal("extraneous arguments");
370
371 if (ectx == NULL)
372 setup_entropy(mctx, NULL, &ectx);
373 result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
374 if (result != ISC_R_SUCCESS)
375 fatal("could not initialize hash");
376 result = dst_lib_init(mctx, ectx,
377 ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
378 if (result != ISC_R_SUCCESS)
379 fatal("could not initialize dst: %s",
380 isc_result_totext(result));
381 isc_entropy_stopcallbacksources(ectx);
382
383 setup_logging(mctx, &log);
384
385 dns_rdataset_init(&rdataset);
386
387 if (filename != NULL) {
388 if (argc < isc_commandline_index + 1) {
389 /* using filename as zone name */
390 namestr = filename;
391 } else
392 namestr = argv[isc_commandline_index];
393
394 result = initname(namestr);
395 if (result != ISC_R_SUCCESS)
396 fatal("could not initialize name %s", namestr);
397
398 result = loadset(filename, &rdataset);
399
400 if (result != ISC_R_SUCCESS)
401 fatal("could not load DNSKEY set: %s\n",
402 isc_result_totext(result));
403
404 for (result = dns_rdataset_first(&rdataset);
405 result == ISC_R_SUCCESS;
406 result = dns_rdataset_next(&rdataset)) {
407
408 dns_rdata_init(&rdata);
409 dns_rdataset_current(&rdataset, &rdata);
410 emit(dir, &rdata);
411 }
412 } else {
413 unsigned char key_buf[DST_KEY_MAXSIZE];
414
415 loadkey(argv[isc_commandline_index], key_buf,
416 DST_KEY_MAXSIZE, &rdata);
417
418 emit(dir, &rdata);
419 }
420
421 if (dns_rdataset_isassociated(&rdataset))
422 dns_rdataset_disassociate(&rdataset);
423 cleanup_logging(&log);
424 dst_lib_destroy();
425 isc_hash_destroy();
426 cleanup_entropy(&ectx);
427 dns_name_destroy();
428 if (verbose > 10)
429 isc_mem_stats(mctx, stdout);
430 isc_mem_destroy(&mctx);
431
432 fflush(stdout);
433 if (ferror(stdout)) {
434 fprintf(stderr, "write error\n");
435 return (1);
436 } else
437 return (0);
438 }
439