Update repub branch u/fanf2/patch to rebasing branch u/fanf2/rebasing revision v9_15_...
[ipreg/bind9.git] / bin / dnssec / dnssec-dsfromkey.c
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 http://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 <inttypes.h>
15 #include <stdbool.h>
16 #include <stdlib.h>
17
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/string.h>
24 #include <isc/util.h>
25
26 #include <dns/callbacks.h>
27 #include <dns/db.h>
28 #include <dns/dbiterator.h>
29 #include <dns/ds.h>
30 #include <dns/fixedname.h>
31 #include <dns/keyvalues.h>
32 #include <dns/log.h>
33 #include <dns/master.h>
34 #include <dns/name.h>
35 #include <dns/rdata.h>
36 #include <dns/rdataclass.h>
37 #include <dns/rdataset.h>
38 #include <dns/rdatasetiter.h>
39 #include <dns/rdatatype.h>
40 #include <dns/result.h>
41
42 #include <dst/dst.h>
43
44 #if USE_PKCS11
45 #include <pk11/result.h>
46 #endif
47
48 #include "dnssectool.h"
49
50 const char *program = "dnssec-dsfromkey";
51
52 static dns_rdataclass_t rdclass;
53 static dns_fixedname_t fixed;
54 static dns_name_t *name = NULL;
55 static isc_mem_t *mctx = NULL;
56 static uint32_t ttl;
57 static bool emitttl = false;
58
59 static isc_result_t
60 initname(char *setname) {
61 isc_result_t result;
62 isc_buffer_t buf;
63
64 name = dns_fixedname_initname(&fixed);
65
66 isc_buffer_init(&buf, setname, strlen(setname));
67 isc_buffer_add(&buf, strlen(setname));
68 result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
69 return (result);
70 }
71
72 static void
73 db_load_from_stream(dns_db_t *db, FILE *fp) {
74 isc_result_t result;
75 dns_rdatacallbacks_t callbacks;
76
77 dns_rdatacallbacks_init(&callbacks);
78 result = dns_db_beginload(db, &callbacks);
79 if (result != ISC_R_SUCCESS)
80 fatal("dns_db_beginload failed: %s", isc_result_totext(result));
81
82 result = dns_master_loadstream(fp, name, name, rdclass, 0,
83 &callbacks, mctx);
84 if (result != ISC_R_SUCCESS)
85 fatal("can't load from input: %s", isc_result_totext(result));
86
87 result = dns_db_endload(db, &callbacks);
88 if (result != ISC_R_SUCCESS)
89 fatal("dns_db_endload failed: %s", isc_result_totext(result));
90 }
91
92 static isc_result_t
93 loadset(const char *filename, dns_rdataset_t *rdataset) {
94 isc_result_t result;
95 dns_db_t *db = NULL;
96 dns_dbnode_t *node = NULL;
97 char setname[DNS_NAME_FORMATSIZE];
98
99 dns_name_format(name, setname, sizeof(setname));
100
101 result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
102 rdclass, 0, NULL, &db);
103 if (result != ISC_R_SUCCESS)
104 fatal("can't create database");
105
106 if (strcmp(filename, "-") == 0) {
107 db_load_from_stream(db, stdin);
108 filename = "input";
109 } else {
110 result = dns_db_load(db, filename, dns_masterformat_text, 0);
111 if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
112 fatal("can't load %s: %s", filename,
113 isc_result_totext(result));
114 }
115
116 result = dns_db_findnode(db, name, false, &node);
117 if (result != ISC_R_SUCCESS)
118 fatal("can't find %s node in %s", setname, filename);
119
120 result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey,
121 0, 0, rdataset, NULL);
122
123 if (result == ISC_R_NOTFOUND)
124 fatal("no DNSKEY RR for %s in %s", setname, filename);
125 else if (result != ISC_R_SUCCESS)
126 fatal("dns_db_findrdataset");
127
128 if (node != NULL)
129 dns_db_detachnode(db, &node);
130 if (db != NULL)
131 dns_db_detach(&db);
132 return (result);
133 }
134
135 static isc_result_t
136 loadkeyset(char *dirname, dns_rdataset_t *rdataset) {
137 isc_result_t result;
138 char filename[PATH_MAX + 1];
139 isc_buffer_t buf;
140
141 dns_rdataset_init(rdataset);
142
143 isc_buffer_init(&buf, filename, sizeof(filename));
144 if (dirname != NULL) {
145 /* allow room for a trailing slash */
146 if (strlen(dirname) >= isc_buffer_availablelength(&buf))
147 return (ISC_R_NOSPACE);
148 isc_buffer_putstr(&buf, dirname);
149 if (dirname[strlen(dirname) - 1] != '/')
150 isc_buffer_putstr(&buf, "/");
151 }
152
153 if (isc_buffer_availablelength(&buf) < 7)
154 return (ISC_R_NOSPACE);
155 isc_buffer_putstr(&buf, "keyset-");
156
157 result = dns_name_tofilenametext(name, false, &buf);
158 check_result(result, "dns_name_tofilenametext()");
159 if (isc_buffer_availablelength(&buf) == 0)
160 return (ISC_R_NOSPACE);
161 isc_buffer_putuint8(&buf, 0);
162
163 return (loadset(filename, rdataset));
164 }
165
166 static void
167 loadkey(char *filename, unsigned char *key_buf, unsigned int key_buf_size,
168 dns_rdata_t *rdata)
169 {
170 isc_result_t result;
171 dst_key_t *key = NULL;
172 isc_buffer_t keyb;
173 isc_region_t r;
174
175 dns_rdata_init(rdata);
176
177 isc_buffer_init(&keyb, key_buf, key_buf_size);
178
179 result = dst_key_fromnamedfile(filename, NULL, DST_TYPE_PUBLIC,
180 mctx, &key);
181 if (result != ISC_R_SUCCESS)
182 fatal("can't load %s.key: %s",
183 filename, isc_result_totext(result));
184
185 if (verbose > 2) {
186 char keystr[DST_KEY_FORMATSIZE];
187
188 dst_key_format(key, keystr, sizeof(keystr));
189 fprintf(stderr, "%s: %s\n", program, keystr);
190 }
191
192 result = dst_key_todns(key, &keyb);
193 if (result != ISC_R_SUCCESS)
194 fatal("can't decode key");
195
196 isc_buffer_usedregion(&keyb, &r);
197 dns_rdata_fromregion(rdata, dst_key_class(key),
198 dns_rdatatype_dnskey, &r);
199
200 rdclass = dst_key_class(key);
201
202 name = dns_fixedname_initname(&fixed);
203 result = dns_name_copy(dst_key_name(key), name, NULL);
204 if (result != ISC_R_SUCCESS)
205 fatal("can't copy name");
206
207 dst_key_free(&key);
208 }
209
210 static void
211 logkey(dns_rdata_t *rdata)
212 {
213 isc_result_t result;
214 dst_key_t *key = NULL;
215 isc_buffer_t buf;
216 char keystr[DST_KEY_FORMATSIZE];
217
218 isc_buffer_init(&buf, rdata->data, rdata->length);
219 isc_buffer_add(&buf, rdata->length);
220 result = dst_key_fromdns(name, rdclass, &buf, mctx, &key);
221 if (result != ISC_R_SUCCESS)
222 return;
223
224 dst_key_format(key, keystr, sizeof(keystr));
225 fprintf(stderr, "%s: %s\n", program, keystr);
226
227 dst_key_free(&key);
228 }
229
230 static void
231 emit(dns_dsdigest_t dt, bool showall, char *lookaside,
232 bool cds, dns_rdata_t *rdata)
233 {
234 isc_result_t result;
235 unsigned char buf[DNS_DS_BUFFERSIZE];
236 char text_buf[DST_KEY_MAXTEXTSIZE];
237 char name_buf[DNS_NAME_MAXWIRE];
238 char class_buf[10];
239 isc_buffer_t textb, nameb, classb;
240 isc_region_t r;
241 dns_rdata_t ds;
242 dns_rdata_dnskey_t dnskey;
243
244 isc_buffer_init(&textb, text_buf, sizeof(text_buf));
245 isc_buffer_init(&nameb, name_buf, sizeof(name_buf));
246 isc_buffer_init(&classb, class_buf, sizeof(class_buf));
247
248 dns_rdata_init(&ds);
249
250 result = dns_rdata_tostruct(rdata, &dnskey, NULL);
251 if (result != ISC_R_SUCCESS)
252 fatal("can't convert DNSKEY");
253
254 if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && !showall)
255 return;
256
257 result = dns_ds_buildrdata(name, rdata, dt, buf, &ds);
258 if (result != ISC_R_SUCCESS)
259 fatal("can't build record");
260
261 result = dns_name_totext(name, false, &nameb);
262 if (result != ISC_R_SUCCESS)
263 fatal("can't print name");
264
265 /* Add lookaside origin, if set */
266 if (lookaside != NULL) {
267 if (isc_buffer_availablelength(&nameb) < strlen(lookaside))
268 fatal("DLV origin '%s' is too long", lookaside);
269 isc_buffer_putstr(&nameb, lookaside);
270 if (lookaside[strlen(lookaside) - 1] != '.') {
271 if (isc_buffer_availablelength(&nameb) < 1)
272 fatal("DLV origin '%s' is too long", lookaside);
273 isc_buffer_putstr(&nameb, ".");
274 }
275 }
276
277 result = dns_rdata_tofmttext(&ds, (dns_name_t *) NULL, 0, 0, 0, "",
278 &textb);
279
280 if (result != ISC_R_SUCCESS)
281 fatal("can't print rdata");
282
283 result = dns_rdataclass_totext(rdclass, &classb);
284 if (result != ISC_R_SUCCESS)
285 fatal("can't print class");
286
287 isc_buffer_usedregion(&nameb, &r);
288 printf("%.*s ", (int)r.length, r.base);
289
290 if (emitttl)
291 printf("%u ", ttl);
292
293 isc_buffer_usedregion(&classb, &r);
294 printf("%.*s", (int)r.length, r.base);
295
296 if (lookaside == NULL) {
297 if (cds)
298 printf(" CDS ");
299 else
300 printf(" DS ");
301 } else
302 printf(" DLV ");
303
304 isc_buffer_usedregion(&textb, &r);
305 printf("%.*s\n", (int)r.length, r.base);
306 }
307
308 static void
309 emits(bool showall, char *lookaside, bool cds, dns_rdata_t *rdata) {
310 unsigned i, n;
311
312 n = sizeof(dtype)/sizeof(dtype[0]);
313 for (i = 0; i < n; i++) {
314 if (dtype[i] != 0) {
315 emit(dtype[i], showall, lookaside, cds, rdata);
316 }
317 }
318 }
319
320 ISC_PLATFORM_NORETURN_PRE static void
321 usage(void) ISC_PLATFORM_NORETURN_POST;
322
323 static void
324 usage(void) {
325 fprintf(stderr, "Usage:\n");
326 fprintf(stderr, " %s [options] keyfile\n\n", program);
327 fprintf(stderr, " %s [options] -f zonefile [zonename]\n\n", program);
328 fprintf(stderr, " %s [options] -s dnsname\n\n", program);
329 fprintf(stderr, " %s [-h|-V]\n\n", program);
330 fprintf(stderr, "Version: %s\n", VERSION);
331 fprintf(stderr, "Options:\n"
332 " -1: digest algorithm SHA-1\n"
333 " -2: digest algorithm SHA-256\n"
334 " -a algorithm: digest algorithm (SHA-1, SHA-256 or SHA-384)\n"
335 " -A: include all keys in DS set, not just KSKs (-f only)\n"
336 " -c class: rdata class for DS set (default IN) (-f or -s only)\n"
337 " -C: print CDS records\n"
338 " -f zonefile: read keys from a zone file\n"
339 " -h: print help information\n"
340 " -K directory: where to find key or keyset files\n"
341 " -l zone: print DLV records in the given lookaside zone\n"
342 " -s: read keys from keyset-<dnsname> file\n"
343 " -T: TTL of output records (omitted by default)\n"
344 " -v level: verbosity\n"
345 " -V: print version information\n");
346 fprintf(stderr, "Output: DS, DLV, or CDS RRs\n");
347
348 exit (-1);
349 }
350
351 int
352 main(int argc, char **argv) {
353 char *classname = NULL;
354 char *filename = NULL, *dir = NULL, *namestr;
355 char *lookaside = NULL;
356 char *endp;
357 int ch;
358 bool cds = false;
359 bool usekeyset = false;
360 bool showall = false;
361 isc_result_t result;
362 isc_log_t *log = NULL;
363 dns_rdataset_t rdataset;
364 dns_rdata_t rdata;
365
366 dns_rdata_init(&rdata);
367
368 if (argc == 1) {
369 usage();
370 }
371
372 result = isc_mem_create(0, 0, &mctx);
373 if (result != ISC_R_SUCCESS) {
374 fatal("out of memory");
375 }
376
377 #if USE_PKCS11
378 pk11_result_register();
379 #endif
380 dns_result_register();
381
382 isc_commandline_errprint = false;
383
384 #define OPTIONS "12Aa:Cc:d:Ff:K:l:sT:v:hV"
385 while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) {
386 switch (ch) {
387 case '1':
388 add_dtype(DNS_DSDIGEST_SHA1);
389 break;
390 case '2':
391 add_dtype(DNS_DSDIGEST_SHA256);
392 break;
393 case 'A':
394 showall = true;
395 break;
396 case 'a':
397 add_dtype(strtodsdigest(isc_commandline_argument));
398 break;
399 case 'C':
400 if (lookaside != NULL)
401 fatal("lookaside and CDS are mutually"
402 " exclusive");
403 cds = true;
404 break;
405 case 'c':
406 classname = isc_commandline_argument;
407 break;
408 case 'd':
409 fprintf(stderr, "%s: the -d option is deprecated; "
410 "use -K\n", program);
411 /* fall through */
412 case 'K':
413 dir = isc_commandline_argument;
414 if (strlen(dir) == 0U)
415 fatal("directory must be non-empty string");
416 break;
417 case 'f':
418 filename = isc_commandline_argument;
419 break;
420 case 'l':
421 if (cds)
422 fatal("lookaside and CDS are mutually"
423 " exclusive");
424 lookaside = isc_commandline_argument;
425 if (strlen(lookaside) == 0U)
426 fatal("lookaside must be a non-empty string");
427 break;
428 case 's':
429 usekeyset = true;
430 break;
431 case 'T':
432 emitttl = true;
433 ttl = strtottl(isc_commandline_argument);
434 break;
435 case 'v':
436 verbose = strtol(isc_commandline_argument, &endp, 0);
437 if (*endp != '\0')
438 fatal("-v must be followed by a number");
439 break;
440 case 'F':
441 /* Reserved for FIPS mode */
442 /* FALLTHROUGH */
443 case '?':
444 if (isc_commandline_option != '?')
445 fprintf(stderr, "%s: invalid argument -%c\n",
446 program, isc_commandline_option);
447 /* FALLTHROUGH */
448 case 'h':
449 /* Does not return. */
450 usage();
451
452 case 'V':
453 /* Does not return. */
454 version(program);
455
456 default:
457 fprintf(stderr, "%s: unhandled option -%c\n",
458 program, isc_commandline_option);
459 exit(1);
460 }
461 }
462
463 rdclass = strtoclass(classname);
464
465 if (usekeyset && filename != NULL) {
466 fatal("cannot use both -s and -f");
467 }
468
469 /* When not using -f, -A is implicit */
470 if (filename == NULL) {
471 showall = true;
472 }
473
474 /* Default digest type if none specified. */
475 if (dtype[0] == 0) {
476 dtype[0] = DNS_DSDIGEST_SHA256;
477 }
478
479 if (argc < isc_commandline_index + 1 && filename == NULL) {
480 fatal("the key file name was not specified");
481 }
482 if (argc > isc_commandline_index + 1) {
483 fatal("extraneous arguments");
484 }
485
486 result = dst_lib_init(mctx, NULL);
487 if (result != ISC_R_SUCCESS) {
488 fatal("could not initialize dst: %s",
489 isc_result_totext(result));
490 }
491
492 setup_logging(mctx, &log);
493
494 dns_rdataset_init(&rdataset);
495
496 if (usekeyset || filename != NULL) {
497 if (argc < isc_commandline_index + 1 && filename != NULL) {
498 /* using zone name as the zone file name */
499 namestr = filename;
500 } else {
501 namestr = argv[isc_commandline_index];
502 }
503
504 result = initname(namestr);
505 if (result != ISC_R_SUCCESS) {
506 fatal("could not initialize name %s", namestr);
507 }
508
509 if (usekeyset) {
510 result = loadkeyset(dir, &rdataset);
511 } else {
512 result = loadset(filename, &rdataset);
513 }
514
515 if (result != ISC_R_SUCCESS) {
516 fatal("could not load DNSKEY set: %s\n",
517 isc_result_totext(result));
518 }
519
520 for (result = dns_rdataset_first(&rdataset);
521 result == ISC_R_SUCCESS;
522 result = dns_rdataset_next(&rdataset))
523 {
524 dns_rdata_init(&rdata);
525 dns_rdataset_current(&rdataset, &rdata);
526
527 if (verbose > 2) {
528 logkey(&rdata);
529 }
530
531 emits(showall, lookaside, cds, &rdata);
532 }
533 } else {
534 unsigned char key_buf[DST_KEY_MAXSIZE];
535
536 loadkey(argv[isc_commandline_index], key_buf,
537 DST_KEY_MAXSIZE, &rdata);
538
539 emits(showall, lookaside, cds, &rdata);
540 }
541
542 if (dns_rdataset_isassociated(&rdataset)) {
543 dns_rdataset_disassociate(&rdataset);
544 }
545 cleanup_logging(&log);
546 dst_lib_destroy();
547 dns_name_destroy();
548 if (verbose > 10) {
549 isc_mem_stats(mctx, stdout);
550 }
551 isc_mem_destroy(&mctx);
552
553 fflush(stdout);
554 if (ferror(stdout)) {
555 fprintf(stderr, "write error\n");
556 return (1);
557 } else {
558 return (0);
559 }
560 }