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