4 * Copyright (C) 2008, 2009 Internet Systems Consortium, Inc. ("ISC")
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
19 /* Id: dnssec-dsfromkey.c,v 1.16 2009/10/12 20:48:10 each Exp */
27 #include <isc/buffer.h>
28 #include <isc/commandline.h>
29 #include <isc/entropy.h>
32 #include <isc/print.h>
33 #include <isc/string.h>
37 #include <dns/dbiterator.h>
39 #include <dns/fixedname.h>
41 #include <dns/keyvalues.h>
42 #include <dns/master.h>
44 #include <dns/rdata.h>
45 #include <dns/rdataclass.h>
46 #include <dns/rdataset.h>
47 #include <dns/rdatasetiter.h>
48 #include <dns/rdatatype.h>
49 #include <dns/result.h>
53 #include "dnssectool.h"
56 #define PATH_MAX 1024 /* AIX, WIN32, and others don't define this. */
59 const char *program
= "dnssec-dsfromkey";
62 static dns_rdataclass_t rdclass
;
63 static dns_fixedname_t fixed
;
64 static dns_name_t
*name
= NULL
;
65 static isc_mem_t
*mctx
= NULL
;
68 initname(char *setname
) {
72 dns_fixedname_init(&fixed
);
73 name
= dns_fixedname_name(&fixed
);
75 isc_buffer_init(&buf
, setname
, strlen(setname
));
76 isc_buffer_add(&buf
, strlen(setname
));
77 result
= dns_name_fromtext(name
, &buf
, dns_rootname
, 0, NULL
);
82 loadsetfromfile(char *filename
, dns_rdataset_t
*rdataset
) {
85 dns_dbnode_t
*node
= NULL
;
86 char setname
[DNS_NAME_FORMATSIZE
];
88 dns_name_format(name
, setname
, sizeof(setname
));
90 result
= dns_db_create(mctx
, "rbt", name
, dns_dbtype_zone
,
91 rdclass
, 0, NULL
, &db
);
92 if (result
!= ISC_R_SUCCESS
)
93 fatal("can't create database");
95 result
= dns_db_load(db
, filename
);
96 if (result
!= ISC_R_SUCCESS
&& result
!= DNS_R_SEENINCLUDE
)
97 fatal("can't load %s: %s", filename
, isc_result_totext(result
));
99 result
= dns_db_findnode(db
, name
, ISC_FALSE
, &node
);
100 if (result
!= ISC_R_SUCCESS
)
101 fatal("can't find %s node in %s", setname
, filename
);
103 result
= dns_db_findrdataset(db
, node
, NULL
, dns_rdatatype_dnskey
,
104 0, 0, rdataset
, NULL
);
106 if (result
== ISC_R_NOTFOUND
)
107 fatal("no DNSKEY RR for %s in %s", setname
, filename
);
108 else if (result
!= ISC_R_SUCCESS
)
109 fatal("dns_db_findrdataset");
112 dns_db_detachnode(db
, &node
);
119 loadkeyset(char *dirname
, dns_rdataset_t
*rdataset
) {
121 char filename
[PATH_MAX
+ 1];
124 dns_rdataset_init(rdataset
);
126 isc_buffer_init(&buf
, filename
, sizeof(filename
));
127 if (dirname
!= NULL
) {
128 /* allow room for a trailing slash */
129 if (strlen(dirname
) >= isc_buffer_availablelength(&buf
))
130 return (ISC_R_NOSPACE
);
131 isc_buffer_putstr(&buf
, dirname
);
132 if (dirname
[strlen(dirname
) - 1] != '/')
133 isc_buffer_putstr(&buf
, "/");
136 if (isc_buffer_availablelength(&buf
) < 7)
137 return (ISC_R_NOSPACE
);
138 isc_buffer_putstr(&buf
, "keyset-");
140 result
= dns_name_tofilenametext(name
, ISC_FALSE
, &buf
);
141 check_result(result
, "dns_name_tofilenametext()");
142 if (isc_buffer_availablelength(&buf
) == 0)
143 return (ISC_R_NOSPACE
);
144 isc_buffer_putuint8(&buf
, 0);
146 return (loadsetfromfile(filename
, rdataset
));
150 loadkey(char *filename
, unsigned char *key_buf
, unsigned int key_buf_size
,
154 dst_key_t
*key
= NULL
;
158 dns_rdata_init(rdata
);
160 isc_buffer_init(&keyb
, key_buf
, key_buf_size
);
162 result
= dst_key_fromnamedfile(filename
, NULL
, DST_TYPE_PUBLIC
,
164 if (result
!= ISC_R_SUCCESS
)
165 fatal("invalid keyfile name %s: %s",
166 filename
, isc_result_totext(result
));
169 char keystr
[DST_KEY_FORMATSIZE
];
171 dst_key_format(key
, keystr
, sizeof(keystr
));
172 fprintf(stderr
, "%s: %s\n", program
, keystr
);
175 result
= dst_key_todns(key
, &keyb
);
176 if (result
!= ISC_R_SUCCESS
)
177 fatal("can't decode key");
179 isc_buffer_usedregion(&keyb
, &r
);
180 dns_rdata_fromregion(rdata
, dst_key_class(key
),
181 dns_rdatatype_dnskey
, &r
);
183 rdclass
= dst_key_class(key
);
185 dns_fixedname_init(&fixed
);
186 name
= dns_fixedname_name(&fixed
);
187 result
= dns_name_copy(dst_key_name(key
), name
, NULL
);
188 if (result
!= ISC_R_SUCCESS
)
189 fatal("can't copy name");
195 logkey(dns_rdata_t
*rdata
)
198 dst_key_t
*key
= NULL
;
200 char keystr
[DST_KEY_FORMATSIZE
];
202 isc_buffer_init(&buf
, rdata
->data
, rdata
->length
);
203 isc_buffer_add(&buf
, rdata
->length
);
204 result
= dst_key_fromdns(name
, rdclass
, &buf
, mctx
, &key
);
205 if (result
!= ISC_R_SUCCESS
)
208 dst_key_format(key
, keystr
, sizeof(keystr
));
209 fprintf(stderr
, "%s: %s\n", program
, keystr
);
215 emit(unsigned int dtype
, isc_boolean_t showall
, char *lookaside
,
219 unsigned char buf
[DNS_DS_BUFFERSIZE
];
220 char text_buf
[DST_KEY_MAXTEXTSIZE
];
221 char name_buf
[DNS_NAME_MAXWIRE
];
223 isc_buffer_t textb
, nameb
, classb
;
226 dns_rdata_dnskey_t dnskey
;
228 isc_buffer_init(&textb
, text_buf
, sizeof(text_buf
));
229 isc_buffer_init(&nameb
, name_buf
, sizeof(name_buf
));
230 isc_buffer_init(&classb
, class_buf
, sizeof(class_buf
));
234 result
= dns_rdata_tostruct(rdata
, &dnskey
, NULL
);
235 if (result
!= ISC_R_SUCCESS
)
236 fatal("can't convert DNSKEY");
238 if ((dnskey
.flags
& DNS_KEYFLAG_KSK
) == 0 && !showall
)
241 result
= dns_ds_buildrdata(name
, rdata
, dtype
, buf
, &ds
);
242 if (result
!= ISC_R_SUCCESS
)
243 fatal("can't build record");
245 result
= dns_name_totext(name
, ISC_FALSE
, &nameb
);
246 if (result
!= ISC_R_SUCCESS
)
247 fatal("can't print name");
249 /* Add lookaside origin, if set */
250 if (lookaside
!= NULL
) {
251 if (isc_buffer_availablelength(&nameb
) < strlen(lookaside
))
252 fatal("DLV origin '%s' is too long", lookaside
);
253 isc_buffer_putstr(&nameb
, lookaside
);
254 if (lookaside
[strlen(lookaside
) - 1] != '.') {
255 if (isc_buffer_availablelength(&nameb
) < 1)
256 fatal("DLV origin '%s' is too long", lookaside
);
257 isc_buffer_putstr(&nameb
, ".");
261 result
= dns_rdata_totext(&ds
, (dns_name_t
*) NULL
, &textb
);
262 if (result
!= ISC_R_SUCCESS
)
263 fatal("can't print rdata");
265 result
= dns_rdataclass_totext(rdclass
, &classb
);
266 if (result
!= ISC_R_SUCCESS
)
267 fatal("can't print class");
269 isc_buffer_usedregion(&nameb
, &r
);
270 fwrite(r
.base
, 1, r
.length
, stdout
);
274 isc_buffer_usedregion(&classb
, &r
);
275 fwrite(r
.base
, 1, r
.length
, stdout
);
277 if (lookaside
== NULL
)
282 isc_buffer_usedregion(&textb
, &r
);
283 fwrite(r
.base
, 1, r
.length
, stdout
);
287 ISC_PLATFORM_NORETURN_PRE
static void
288 usage(void) ISC_PLATFORM_NORETURN_POST
;
292 fprintf(stderr
, "Usage:\n");
293 fprintf(stderr
, " %s options [-K dir] keyfile\n\n", program
);
294 fprintf(stderr
, " %s options [-K dir] [-c class] -s dnsname\n\n",
296 fprintf(stderr
, " %s options -f zonefile (as zone name)\n\n", program
);
297 fprintf(stderr
, " %s options -f zonefile zonename\n\n", program
);
298 fprintf(stderr
, "Version: %s\n", VERSION
);
299 fprintf(stderr
, "Options:\n");
300 fprintf(stderr
, " -v <verbose level>\n");
301 fprintf(stderr
, " -K <directory>: directory in which to find "
302 "key file or keyset file\n");
303 fprintf(stderr
, " -a algorithm: digest algorithm "
304 "(SHA-1 or SHA-256)\n");
305 fprintf(stderr
, " -1: use SHA-1\n");
306 fprintf(stderr
, " -2: use SHA-256\n");
307 fprintf(stderr
, " -l: add lookaside zone and print DLV records\n");
308 fprintf(stderr
, " -s: read keyset from keyset-<dnsname> file\n");
309 fprintf(stderr
, " -c class: rdata class for DS set (default: IN)\n");
310 fprintf(stderr
, " -f file: read keyset from zone file\n");
311 fprintf(stderr
, " -A: when used with -f, "
312 "include all keys in DS set, not just KSKs\n");
313 fprintf(stderr
, "Output: DS or DLV RRs\n");
319 main(int argc
, char **argv
) {
320 char *algname
= NULL
, *classname
= NULL
;
321 char *filename
= NULL
, *dir
= NULL
, *namestr
;
322 char *lookaside
= NULL
;
325 unsigned int dtype
= DNS_DSDIGEST_SHA1
;
326 isc_boolean_t both
= ISC_TRUE
;
327 isc_boolean_t usekeyset
= ISC_FALSE
;
328 isc_boolean_t showall
= ISC_FALSE
;
330 isc_log_t
*log
= NULL
;
331 isc_entropy_t
*ectx
= NULL
;
332 dns_rdataset_t rdataset
;
335 dns_rdata_init(&rdata
);
340 result
= isc_mem_create(0, 0, &mctx
);
341 if (result
!= ISC_R_SUCCESS
)
342 fatal("out of memory");
344 dns_result_register();
346 isc_commandline_errprint
= ISC_FALSE
;
348 while ((ch
= isc_commandline_parse(argc
, argv
,
349 "12Aa:c:d:Ff:K:l:sv:h")) != -1) {
352 dtype
= DNS_DSDIGEST_SHA1
;
356 dtype
= DNS_DSDIGEST_SHA256
;
363 algname
= isc_commandline_argument
;
367 classname
= isc_commandline_argument
;
370 fprintf(stderr
, "%s: the -d option is deprecated; "
371 "use -K\n", program
);
374 dir
= isc_commandline_argument
;
375 if (strlen(dir
) == 0U)
376 fatal("directory must be non-empty string");
379 filename
= isc_commandline_argument
;
382 lookaside
= isc_commandline_argument
;
383 if (strlen(lookaside
) == 0U)
384 fatal("lookaside must be a non-empty string");
387 usekeyset
= ISC_TRUE
;
390 verbose
= strtol(isc_commandline_argument
, &endp
, 0);
392 fatal("-v must be followed by a number");
395 /* Reserved for FIPS mode */
398 if (isc_commandline_option
!= '?')
399 fprintf(stderr
, "%s: invalid argument -%c\n",
400 program
, isc_commandline_option
);
406 fprintf(stderr
, "%s: unhandled option -%c\n",
407 program
, isc_commandline_option
);
412 if (algname
!= NULL
) {
413 if (strcasecmp(algname
, "SHA1") == 0 ||
414 strcasecmp(algname
, "SHA-1") == 0)
415 dtype
= DNS_DSDIGEST_SHA1
;
416 else if (strcasecmp(algname
, "SHA256") == 0 ||
417 strcasecmp(algname
, "SHA-256") == 0)
418 dtype
= DNS_DSDIGEST_SHA256
;
420 fatal("unknown algorithm %s", algname
);
423 rdclass
= strtoclass(classname
);
425 if (usekeyset
&& filename
!= NULL
)
426 fatal("cannot use both -s and -f");
428 /* When not using -f, -A is implicit */
429 if (filename
== NULL
)
432 if (argc
< isc_commandline_index
+ 1 && filename
== NULL
)
433 fatal("the key file name was not specified");
434 if (argc
> isc_commandline_index
+ 1)
435 fatal("extraneous arguments");
438 setup_entropy(mctx
, NULL
, &ectx
);
439 result
= isc_hash_create(mctx
, ectx
, DNS_NAME_MAXWIRE
);
440 if (result
!= ISC_R_SUCCESS
)
441 fatal("could not initialize hash");
442 result
= dst_lib_init(mctx
, ectx
,
443 ISC_ENTROPY_BLOCKING
| ISC_ENTROPY_GOODONLY
);
444 if (result
!= ISC_R_SUCCESS
)
445 fatal("could not initialize dst: %s",
446 isc_result_totext(result
));
447 isc_entropy_stopcallbacksources(ectx
);
449 setup_logging(verbose
, mctx
, &log
);
451 dns_rdataset_init(&rdataset
);
453 if (usekeyset
|| filename
!= NULL
) {
454 if (argc
< isc_commandline_index
+ 1 && filename
!= NULL
) {
455 /* using zone name as the zone file name */
458 namestr
= argv
[isc_commandline_index
];
460 result
= initname(namestr
);
461 if (result
!= ISC_R_SUCCESS
)
462 fatal("could not initialize name %s", namestr
);
465 result
= loadkeyset(dir
, &rdataset
);
467 result
= loadsetfromfile(filename
, &rdataset
);
469 if (result
!= ISC_R_SUCCESS
)
470 fatal("could not load DNSKEY set: %s\n",
471 isc_result_totext(result
));
473 for (result
= dns_rdataset_first(&rdataset
);
474 result
== ISC_R_SUCCESS
;
475 result
= dns_rdataset_next(&rdataset
)) {
476 dns_rdata_init(&rdata
);
477 dns_rdataset_current(&rdataset
, &rdata
);
483 emit(DNS_DSDIGEST_SHA1
, showall
, lookaside
,
485 emit(DNS_DSDIGEST_SHA256
, showall
, lookaside
,
488 emit(dtype
, showall
, lookaside
, &rdata
);
491 unsigned char key_buf
[DST_KEY_MAXSIZE
];
493 loadkey(argv
[isc_commandline_index
], key_buf
,
494 DST_KEY_MAXSIZE
, &rdata
);
497 emit(DNS_DSDIGEST_SHA1
, showall
, lookaside
, &rdata
);
498 emit(DNS_DSDIGEST_SHA256
, showall
, lookaside
, &rdata
);
500 emit(dtype
, showall
, lookaside
, &rdata
);
503 if (dns_rdataset_isassociated(&rdataset
))
504 dns_rdataset_disassociate(&rdataset
);
505 cleanup_logging(&log
);
508 cleanup_entropy(&ectx
);
511 isc_mem_stats(mctx
, stdout
);
512 isc_mem_destroy(&mctx
);
515 if (ferror(stdout
)) {
516 fprintf(stderr
, "write error\n");