Update repub branch u/fanf2/patch to rebasing branch u/fanf2/rebasing revision v9_15_...
[ipreg/bind9.git] / bin / dnssec / dnssec-dsfromkey.c
CommitLineData
582f8b9a 1/*
843d3896 2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
582f8b9a 3 *
0c27b3fe
MA
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/.
843d3896
OS
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
582f8b9a
MA
10 */
11
582f8b9a
MA
12/*! \file */
13
cb6a185c 14#include <inttypes.h>
994e6569 15#include <stdbool.h>
582f8b9a
MA
16#include <stdlib.h>
17
18#include <isc/buffer.h>
19#include <isc/commandline.h>
582f8b9a
MA
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
0a824926 26#include <dns/callbacks.h>
582f8b9a
MA
27#include <dns/db.h>
28#include <dns/dbiterator.h>
29#include <dns/ds.h>
30#include <dns/fixedname.h>
553ead32 31#include <dns/keyvalues.h>
0a824926 32#include <dns/log.h>
553ead32 33#include <dns/master.h>
582f8b9a
MA
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
c3b8130f 44#if USE_PKCS11
acbb301e
EH
45#include <pk11/result.h>
46#endif
47
582f8b9a
MA
48#include "dnssectool.h"
49
50const char *program = "dnssec-dsfromkey";
582f8b9a
MA
51
52static dns_rdataclass_t rdclass;
b272d38c
EH
53static dns_fixedname_t fixed;
54static dns_name_t *name = NULL;
b272d38c 55static isc_mem_t *mctx = NULL;
cb6a185c 56static uint32_t ttl;
994e6569 57static bool emitttl = false;
582f8b9a 58
553ead32
EH
59static isc_result_t
60initname(char *setname) {
61 isc_result_t result;
62 isc_buffer_t buf;
582f8b9a 63
4df4a8e7 64 name = dns_fixedname_initname(&fixed);
26d8ffe7
AU
65
66 isc_buffer_init(&buf, setname, strlen(setname));
67 isc_buffer_add(&buf, strlen(setname));
307d2084 68 result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
553ead32
EH
69 return (result);
70}
582f8b9a 71
0a824926
MA
72static void
73db_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);
7829fad4 78 result = dns_db_beginload(db, &callbacks);
0a824926
MA
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));
06140f73 86
7829fad4 87 result = dns_db_endload(db, &callbacks);
0a824926
MA
88 if (result != ISC_R_SUCCESS)
89 fatal("dns_db_endload failed: %s", isc_result_totext(result));
90}
91
553ead32 92static isc_result_t
0a824926 93loadset(const char *filename, dns_rdataset_t *rdataset) {
553ead32
EH
94 isc_result_t result;
95 dns_db_t *db = NULL;
96 dns_dbnode_t *node = NULL;
97 char setname[DNS_NAME_FORMATSIZE];
b272d38c 98
553ead32 99 dns_name_format(name, setname, sizeof(setname));
582f8b9a
MA
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
0a824926
MA
106 if (strcmp(filename, "-") == 0) {
107 db_load_from_stream(db, stdin);
108 filename = "input";
109 } else {
275a6a3b 110 result = dns_db_load(db, filename, dns_masterformat_text, 0);
0a824926
MA
111 if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
112 fatal("can't load %s: %s", filename,
113 isc_result_totext(result));
114 }
582f8b9a 115
994e6569 116 result = dns_db_findnode(db, name, false, &node);
582f8b9a
MA
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,
553ead32
EH
121 0, 0, rdataset, NULL);
122
582f8b9a
MA
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");
553ead32
EH
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
135static isc_result_t
136loadkeyset(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
994e6569 157 result = dns_name_tofilenametext(name, false, &buf);
553ead32
EH
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
0a824926 163 return (loadset(filename, rdataset));
582f8b9a
MA
164}
165
166static void
52dec699
MA
167loadkey(char *filename, unsigned char *key_buf, unsigned int key_buf_size,
168 dns_rdata_t *rdata)
582f8b9a
MA
169{
170 isc_result_t result;
171 dst_key_t *key = NULL;
582f8b9a
MA
172 isc_buffer_t keyb;
173 isc_region_t r;
174
582f8b9a
MA
175 dns_rdata_init(rdata);
176
52dec699 177 isc_buffer_init(&keyb, key_buf, key_buf_size);
582f8b9a 178
553ead32
EH
179 result = dst_key_fromnamedfile(filename, NULL, DST_TYPE_PUBLIC,
180 mctx, &key);
582f8b9a 181 if (result != ISC_R_SUCCESS)
c150f686 182 fatal("can't load %s.key: %s",
582f8b9a
MA
183 filename, isc_result_totext(result));
184
185 if (verbose > 2) {
77b8f88f 186 char keystr[DST_KEY_FORMATSIZE];
582f8b9a 187
77b8f88f 188 dst_key_format(key, keystr, sizeof(keystr));
582f8b9a
MA
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
4df4a8e7 202 name = dns_fixedname_initname(&fixed);
582f8b9a
MA
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
210static void
211logkey(dns_rdata_t *rdata)
212{
213 isc_result_t result;
214 dst_key_t *key = NULL;
215 isc_buffer_t buf;
77b8f88f 216 char keystr[DST_KEY_FORMATSIZE];
582f8b9a
MA
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
77b8f88f 224 dst_key_format(key, keystr, sizeof(keystr));
582f8b9a
MA
225 fprintf(stderr, "%s: %s\n", program, keystr);
226
227 dst_key_free(&key);
228}
229
230static void
796a6c4e 231emit(dns_dsdigest_t dt, bool showall, char *lookaside,
994e6569 232 bool cds, dns_rdata_t *rdata)
582f8b9a 233{
553ead32
EH
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;
582f8b9a
MA
243
244 isc_buffer_init(&textb, text_buf, sizeof(text_buf));
b272d38c 245 isc_buffer_init(&nameb, name_buf, sizeof(name_buf));
582f8b9a
MA
246 isc_buffer_init(&classb, class_buf, sizeof(class_buf));
247
248 dns_rdata_init(&ds);
249
553ead32
EH
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
796a6c4e 257 result = dns_ds_buildrdata(name, rdata, dt, buf, &ds);
582f8b9a 258 if (result != ISC_R_SUCCESS)
b272d38c
EH
259 fatal("can't build record");
260
994e6569 261 result = dns_name_totext(name, false, &nameb);
b272d38c
EH
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 }
582f8b9a 276
b13b4520
MA
277 result = dns_rdata_tofmttext(&ds, (dns_name_t *) NULL, 0, 0, 0, "",
278 &textb);
279
582f8b9a 280 if (result != ISC_R_SUCCESS)
b272d38c 281 fatal("can't print rdata");
582f8b9a
MA
282
283 result = dns_rdataclass_totext(rdclass, &classb);
284 if (result != ISC_R_SUCCESS)
b272d38c 285 fatal("can't print class");
582f8b9a 286
b272d38c 287 isc_buffer_usedregion(&nameb, &r);
ab6c20f9 288 printf("%.*s ", (int)r.length, r.base);
582f8b9a 289
e785f9c1 290 if (emitttl)
b1c6de54
MA
291 printf("%u ", ttl);
292
582f8b9a 293 isc_buffer_usedregion(&classb, &r);
ab6c20f9 294 printf("%.*s", (int)r.length, r.base);
582f8b9a 295
598b5026
MA
296 if (lookaside == NULL) {
297 if (cds)
298 printf(" CDS ");
299 else
300 printf(" DS ");
301 } else
b272d38c 302 printf(" DLV ");
582f8b9a
MA
303
304 isc_buffer_usedregion(&textb, &r);
ab6c20f9 305 printf("%.*s\n", (int)r.length, r.base);
582f8b9a
MA
306}
307
796a6c4e
TF
308static void
309emits(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
debd489a
FD
320ISC_PLATFORM_NORETURN_PRE static void
321usage(void) ISC_PLATFORM_NORETURN_POST;
322
582f8b9a
MA
323static void
324usage(void) {
325 fprintf(stderr, "Usage:\n");
6ca8e130
TF
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);
582f8b9a 330 fprintf(stderr, "Version: %s\n", VERSION);
6ca8e130
TF
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");
582f8b9a
MA
347
348 exit (-1);
349}
350
351int
352main(int argc, char **argv) {
ba37674d 353 char *classname = NULL;
553ead32 354 char *filename = NULL, *dir = NULL, *namestr;
b272d38c
EH
355 char *lookaside = NULL;
356 char *endp;
357 int ch;
796a6c4e
TF
358 bool cds = false;
359 bool usekeyset = false;
360 bool showall = false;
b272d38c 361 isc_result_t result;
553ead32 362 isc_log_t *log = NULL;
553ead32 363 dns_rdataset_t rdataset;
b272d38c 364 dns_rdata_t rdata;
582f8b9a
MA
365
366 dns_rdata_init(&rdata);
367
796a6c4e 368 if (argc == 1) {
582f8b9a 369 usage();
796a6c4e 370 }
582f8b9a
MA
371
372 result = isc_mem_create(0, 0, &mctx);
796a6c4e 373 if (result != ISC_R_SUCCESS) {
582f8b9a 374 fatal("out of memory");
796a6c4e 375 }
582f8b9a 376
c3b8130f 377#if USE_PKCS11
acbb301e
EH
378 pk11_result_register();
379#endif
582f8b9a
MA
380 dns_result_register();
381
994e6569 382 isc_commandline_errprint = false;
582f8b9a 383
598b5026
MA
384#define OPTIONS "12Aa:Cc:d:Ff:K:l:sT:v:hV"
385 while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) {
582f8b9a
MA
386 switch (ch) {
387 case '1':
796a6c4e 388 add_dtype(DNS_DSDIGEST_SHA1);
582f8b9a
MA
389 break;
390 case '2':
796a6c4e 391 add_dtype(DNS_DSDIGEST_SHA256);
582f8b9a 392 break;
553ead32 393 case 'A':
994e6569 394 showall = true;
553ead32 395 break;
582f8b9a 396 case 'a':
796a6c4e 397 add_dtype(strtodsdigest(isc_commandline_argument));
582f8b9a 398 break;
598b5026
MA
399 case 'C':
400 if (lookaside != NULL)
401 fatal("lookaside and CDS are mutually"
402 " exclusive");
994e6569 403 cds = true;
598b5026 404 break;
582f8b9a
MA
405 case 'c':
406 classname = isc_commandline_argument;
407 break;
408 case 'd':
553ead32
EH
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;
50eab6c2 414 if (strlen(dir) == 0U)
553ead32
EH
415 fatal("directory must be non-empty string");
416 break;
417 case 'f':
418 filename = isc_commandline_argument;
b272d38c
EH
419 break;
420 case 'l':
598b5026
MA
421 if (cds)
422 fatal("lookaside and CDS are mutually"
423 " exclusive");
b272d38c 424 lookaside = isc_commandline_argument;
50eab6c2 425 if (strlen(lookaside) == 0U)
b272d38c 426 fatal("lookaside must be a non-empty string");
582f8b9a
MA
427 break;
428 case 's':
994e6569 429 usekeyset = true;
582f8b9a 430 break;
b1c6de54 431 case 'T':
994e6569 432 emitttl = true;
ba37674d 433 ttl = strtottl(isc_commandline_argument);
b1c6de54 434 break;
582f8b9a
MA
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;
ddac1a2b
FD
440 case 'F':
441 /* Reserved for FIPS mode */
442 /* FALLTHROUGH */
582f8b9a
MA
443 case '?':
444 if (isc_commandline_option != '?')
445 fprintf(stderr, "%s: invalid argument -%c\n",
446 program, isc_commandline_option);
ddac1a2b 447 /* FALLTHROUGH */
582f8b9a 448 case 'h':
42782931 449 /* Does not return. */
582f8b9a
MA
450 usage();
451
42782931
MS
452 case 'V':
453 /* Does not return. */
454 version(program);
455
582f8b9a
MA
456 default:
457 fprintf(stderr, "%s: unhandled option -%c\n",
458 program, isc_commandline_option);
459 exit(1);
460 }
461 }
462
582f8b9a
MA
463 rdclass = strtoclass(classname);
464
796a6c4e 465 if (usekeyset && filename != NULL) {
553ead32 466 fatal("cannot use both -s and -f");
796a6c4e 467 }
553ead32
EH
468
469 /* When not using -f, -A is implicit */
796a6c4e 470 if (filename == NULL) {
994e6569 471 showall = true;
796a6c4e 472 }
553ead32 473
796a6c4e
TF
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) {
582f8b9a 480 fatal("the key file name was not specified");
796a6c4e
TF
481 }
482 if (argc > isc_commandline_index + 1) {
582f8b9a 483 fatal("extraneous arguments");
796a6c4e 484 }
582f8b9a 485
3a4f820d 486 result = dst_lib_init(mctx, NULL);
796a6c4e 487 if (result != ISC_R_SUCCESS) {
8b78c993
FD
488 fatal("could not initialize dst: %s",
489 isc_result_totext(result));
796a6c4e 490 }
582f8b9a 491
11463c0a 492 setup_logging(mctx, &log);
582f8b9a 493
553ead32
EH
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;
796a6c4e 500 } else {
553ead32 501 namestr = argv[isc_commandline_index];
796a6c4e 502 }
582f8b9a 503
553ead32 504 result = initname(namestr);
796a6c4e 505 if (result != ISC_R_SUCCESS) {
553ead32 506 fatal("could not initialize name %s", namestr);
796a6c4e 507 }
553ead32 508
796a6c4e 509 if (usekeyset) {
553ead32 510 result = loadkeyset(dir, &rdataset);
796a6c4e 511 } else {
0a824926 512 result = loadset(filename, &rdataset);
796a6c4e 513 }
553ead32 514
796a6c4e 515 if (result != ISC_R_SUCCESS) {
553ead32
EH
516 fatal("could not load DNSKEY set: %s\n",
517 isc_result_totext(result));
796a6c4e 518 }
553ead32
EH
519
520 for (result = dns_rdataset_first(&rdataset);
582f8b9a 521 result == ISC_R_SUCCESS;
796a6c4e
TF
522 result = dns_rdataset_next(&rdataset))
523 {
582f8b9a 524 dns_rdata_init(&rdata);
553ead32 525 dns_rdataset_current(&rdataset, &rdata);
582f8b9a 526
796a6c4e 527 if (verbose > 2) {
582f8b9a 528 logkey(&rdata);
796a6c4e 529 }
582f8b9a 530
796a6c4e 531 emits(showall, lookaside, cds, &rdata);
582f8b9a
MA
532 }
533 } else {
52dec699
MA
534 unsigned char key_buf[DST_KEY_MAXSIZE];
535
536 loadkey(argv[isc_commandline_index], key_buf,
537 DST_KEY_MAXSIZE, &rdata);
582f8b9a 538
796a6c4e 539 emits(showall, lookaside, cds, &rdata);
582f8b9a
MA
540 }
541
796a6c4e 542 if (dns_rdataset_isassociated(&rdataset)) {
553ead32 543 dns_rdataset_disassociate(&rdataset);
796a6c4e 544 }
582f8b9a 545 cleanup_logging(&log);
586e65ea 546 dst_lib_destroy();
582f8b9a 547 dns_name_destroy();
796a6c4e 548 if (verbose > 10) {
582f8b9a 549 isc_mem_stats(mctx, stdout);
796a6c4e 550 }
582f8b9a
MA
551 isc_mem_destroy(&mctx);
552
98b2be76
MA
553 fflush(stdout);
554 if (ferror(stdout)) {
555 fprintf(stderr, "write error\n");
556 return (1);
796a6c4e 557 } else {
98b2be76 558 return (0);
796a6c4e 559 }
582f8b9a 560}