1262445Serwin/* 2262445Serwin * Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC") 3262445Serwin * 4262445Serwin * Permission to use, copy, modify, and/or distribute this software for any 5262445Serwin * purpose with or without fee is hereby granted, provided that the above 6262445Serwin * copyright notice and this permission notice appear in all copies. 7262445Serwin * 8262445Serwin * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9262445Serwin * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10262445Serwin * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11262445Serwin * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12262445Serwin * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13262445Serwin * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14262445Serwin * PERFORMANCE OF THIS SOFTWARE. 15262445Serwin */ 16262445Serwin 17262445Serwin/*! \file */ 18262445Serwin 19262445Serwin#include <config.h> 20262445Serwin 21262445Serwin#include <stdlib.h> 22262445Serwin 23262445Serwin#include <isc/buffer.h> 24262445Serwin#include <isc/commandline.h> 25262445Serwin#include <isc/entropy.h> 26262445Serwin#include <isc/hash.h> 27262445Serwin#include <isc/mem.h> 28262445Serwin#include <isc/print.h> 29262445Serwin#include <isc/string.h> 30262445Serwin#include <isc/util.h> 31262445Serwin 32262445Serwin#include <dns/callbacks.h> 33262445Serwin#include <dns/db.h> 34262445Serwin#include <dns/dbiterator.h> 35262445Serwin#include <dns/ds.h> 36262445Serwin#include <dns/fixedname.h> 37262445Serwin#include <dns/keyvalues.h> 38262445Serwin#include <dns/log.h> 39262445Serwin#include <dns/master.h> 40262445Serwin#include <dns/name.h> 41262445Serwin#include <dns/rdata.h> 42262445Serwin#include <dns/rdataclass.h> 43262445Serwin#include <dns/rdataset.h> 44262445Serwin#include <dns/rdatasetiter.h> 45262445Serwin#include <dns/rdatatype.h> 46262445Serwin#include <dns/result.h> 47262445Serwin 48262445Serwin#include <dst/dst.h> 49262445Serwin 50262445Serwin#include "dnssectool.h" 51262445Serwin 52262445Serwin#ifndef PATH_MAX 53262445Serwin#define PATH_MAX 1024 /* AIX, WIN32, and others don't define this. */ 54262445Serwin#endif 55262445Serwin 56262445Serwinconst char *program = "dnssec-importkey"; 57262445Serwinint verbose; 58262445Serwin 59262445Serwinstatic dns_rdataclass_t rdclass; 60262445Serwinstatic dns_fixedname_t fixed; 61262445Serwinstatic dns_name_t *name = NULL; 62262445Serwinstatic isc_mem_t *mctx = NULL; 63262445Serwinstatic isc_boolean_t setpub = ISC_FALSE, setdel = ISC_FALSE; 64262445Serwinstatic isc_boolean_t setttl = ISC_FALSE; 65262445Serwinstatic isc_stdtime_t pub = 0, del = 0; 66262445Serwinstatic dns_ttl_t ttl = 0; 67262445Serwin 68262445Serwinstatic isc_result_t 69262445Serwininitname(char *setname) { 70262445Serwin isc_result_t result; 71262445Serwin isc_buffer_t buf; 72262445Serwin 73262445Serwin dns_fixedname_init(&fixed); 74262445Serwin name = dns_fixedname_name(&fixed); 75262445Serwin 76262445Serwin isc_buffer_init(&buf, setname, strlen(setname)); 77262445Serwin isc_buffer_add(&buf, strlen(setname)); 78262445Serwin result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL); 79262445Serwin return (result); 80262445Serwin} 81262445Serwin 82262445Serwinstatic void 83262445Serwindb_load_from_stream(dns_db_t *db, FILE *fp) { 84262445Serwin isc_result_t result; 85262445Serwin dns_rdatacallbacks_t callbacks; 86262445Serwin 87262445Serwin dns_rdatacallbacks_init(&callbacks); 88262445Serwin result = dns_db_beginload(db, &callbacks.add, &callbacks.add_private); 89262445Serwin if (result != ISC_R_SUCCESS) 90262445Serwin fatal("dns_db_beginload failed: %s", isc_result_totext(result)); 91262445Serwin 92262445Serwin result = dns_master_loadstream(fp, name, name, rdclass, 0, 93262445Serwin &callbacks, mctx); 94262445Serwin if (result != ISC_R_SUCCESS) 95262445Serwin fatal("can't load from input: %s", isc_result_totext(result)); 96262445Serwin 97262445Serwin result = dns_db_endload(db, &callbacks.add_private); 98262445Serwin if (result != ISC_R_SUCCESS) 99262445Serwin fatal("dns_db_endload failed: %s", isc_result_totext(result)); 100262445Serwin} 101262445Serwin 102262445Serwinstatic isc_result_t 103262445Serwinloadset(const char *filename, dns_rdataset_t *rdataset) { 104262445Serwin isc_result_t result; 105262445Serwin dns_db_t *db = NULL; 106262445Serwin dns_dbnode_t *node = NULL; 107262445Serwin char setname[DNS_NAME_FORMATSIZE]; 108262445Serwin 109262445Serwin dns_name_format(name, setname, sizeof(setname)); 110262445Serwin 111262445Serwin result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, 112262445Serwin rdclass, 0, NULL, &db); 113262445Serwin if (result != ISC_R_SUCCESS) 114262445Serwin fatal("can't create database"); 115262445Serwin 116262445Serwin if (strcmp(filename, "-") == 0) { 117262445Serwin db_load_from_stream(db, stdin); 118262445Serwin filename = "input"; 119262445Serwin } else { 120262445Serwin result = dns_db_load3(db, filename, dns_masterformat_text, 121262445Serwin DNS_MASTER_NOTTL); 122262445Serwin if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) 123262445Serwin fatal("can't load %s: %s", filename, 124262445Serwin isc_result_totext(result)); 125262445Serwin } 126262445Serwin 127262445Serwin result = dns_db_findnode(db, name, ISC_FALSE, &node); 128262445Serwin if (result != ISC_R_SUCCESS) 129262445Serwin fatal("can't find %s node in %s", setname, filename); 130262445Serwin 131262445Serwin result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 132262445Serwin 0, 0, rdataset, NULL); 133262445Serwin 134262445Serwin if (result == ISC_R_NOTFOUND) 135262445Serwin fatal("no DNSKEY RR for %s in %s", setname, filename); 136262445Serwin else if (result != ISC_R_SUCCESS) 137262445Serwin fatal("dns_db_findrdataset"); 138262445Serwin 139262445Serwin if (node != NULL) 140262445Serwin dns_db_detachnode(db, &node); 141262445Serwin if (db != NULL) 142262445Serwin dns_db_detach(&db); 143262445Serwin return (result); 144262445Serwin} 145262445Serwin 146262445Serwinstatic void 147262445Serwinloadkey(char *filename, unsigned char *key_buf, unsigned int key_buf_size, 148262445Serwin dns_rdata_t *rdata) 149262445Serwin{ 150262445Serwin isc_result_t result; 151262445Serwin dst_key_t *key = NULL; 152262445Serwin isc_buffer_t keyb; 153262445Serwin isc_region_t r; 154262445Serwin 155262445Serwin dns_rdata_init(rdata); 156262445Serwin 157262445Serwin isc_buffer_init(&keyb, key_buf, key_buf_size); 158262445Serwin 159262445Serwin result = dst_key_fromnamedfile(filename, NULL, DST_TYPE_PUBLIC, 160262445Serwin mctx, &key); 161262445Serwin if (result != ISC_R_SUCCESS) 162262445Serwin fatal("invalid keyfile name %s: %s", 163262445Serwin filename, isc_result_totext(result)); 164262445Serwin 165262445Serwin if (verbose > 2) { 166262445Serwin char keystr[DST_KEY_FORMATSIZE]; 167262445Serwin 168262445Serwin dst_key_format(key, keystr, sizeof(keystr)); 169262445Serwin fprintf(stderr, "%s: %s\n", program, keystr); 170262445Serwin } 171262445Serwin 172262445Serwin result = dst_key_todns(key, &keyb); 173262445Serwin if (result != ISC_R_SUCCESS) 174262445Serwin fatal("can't decode key"); 175262445Serwin 176262445Serwin isc_buffer_usedregion(&keyb, &r); 177262445Serwin dns_rdata_fromregion(rdata, dst_key_class(key), 178262445Serwin dns_rdatatype_dnskey, &r); 179262445Serwin 180262445Serwin rdclass = dst_key_class(key); 181262445Serwin 182262445Serwin dns_fixedname_init(&fixed); 183262445Serwin name = dns_fixedname_name(&fixed); 184262445Serwin result = dns_name_copy(dst_key_name(key), name, NULL); 185262445Serwin if (result != ISC_R_SUCCESS) 186262445Serwin fatal("can't copy name"); 187262445Serwin 188262445Serwin dst_key_free(&key); 189262445Serwin} 190262445Serwin 191262445Serwinstatic void 192262445Serwinemit(const char *dir, dns_rdata_t *rdata) { 193262445Serwin isc_result_t result; 194262445Serwin char keystr[DST_KEY_FORMATSIZE]; 195262445Serwin char pubname[1024]; 196262445Serwin char priname[1024]; 197262445Serwin isc_buffer_t buf; 198262445Serwin dst_key_t *key = NULL, *tmp = NULL; 199262445Serwin 200262445Serwin isc_buffer_init(&buf, rdata->data, rdata->length); 201262445Serwin isc_buffer_add(&buf, rdata->length); 202262445Serwin result = dst_key_fromdns(name, rdclass, &buf, mctx, &key); 203262445Serwin if (result != ISC_R_SUCCESS) { 204262445Serwin fatal("dst_key_fromdns: %s", isc_result_totext(result)); 205262445Serwin } 206262445Serwin 207262445Serwin isc_buffer_init(&buf, pubname, sizeof(pubname)); 208262445Serwin result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf); 209262445Serwin if (result != ISC_R_SUCCESS) { 210262445Serwin fatal("Failed to build public key filename: %s", 211262445Serwin isc_result_totext(result)); 212262445Serwin } 213262445Serwin isc_buffer_init(&buf, priname, sizeof(priname)); 214262445Serwin result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf); 215262445Serwin if (result != ISC_R_SUCCESS) { 216262445Serwin fatal("Failed to build private key filename: %s", 217262445Serwin isc_result_totext(result)); 218262445Serwin } 219262445Serwin 220262445Serwin result = dst_key_fromfile(dst_key_name(key), dst_key_id(key), 221262445Serwin dst_key_alg(key), 222262445Serwin DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, 223262445Serwin dir, mctx, &tmp); 224262445Serwin if (result == ISC_R_SUCCESS) { 225262445Serwin if (dst_key_isprivate(tmp) && !dst_key_isexternal(tmp)) 226262445Serwin fatal("Private key already exists in %s", priname); 227262445Serwin dst_key_free(&tmp); 228262445Serwin } 229262445Serwin 230262445Serwin dst_key_setexternal(key, ISC_TRUE); 231262445Serwin if (setpub) 232262445Serwin dst_key_settime(key, DST_TIME_PUBLISH, pub); 233262445Serwin if (setdel) 234262445Serwin dst_key_settime(key, DST_TIME_DELETE, del); 235262445Serwin if (setttl) 236262445Serwin dst_key_setttl(key, ttl); 237262445Serwin 238262445Serwin result = dst_key_tofile(key, DST_TYPE_PUBLIC|DST_TYPE_PRIVATE, 239262445Serwin dir); 240262445Serwin if (result != ISC_R_SUCCESS) { 241262445Serwin dst_key_format(key, keystr, sizeof(keystr)); 242262445Serwin fatal("Failed to write key %s: %s", keystr, 243262445Serwin isc_result_totext(result)); 244262445Serwin } 245262445Serwin printf("%s\n", pubname); 246262445Serwin 247262445Serwin isc_buffer_clear(&buf); 248262445Serwin result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf); 249262445Serwin if (result != ISC_R_SUCCESS) { 250262445Serwin fatal("Failed to build private key filename: %s", 251262445Serwin isc_result_totext(result)); 252262445Serwin } 253262445Serwin printf("%s\n", priname); 254262445Serwin dst_key_free(&key); 255262445Serwin} 256262445Serwin 257262445SerwinISC_PLATFORM_NORETURN_PRE static void 258262445Serwinusage(void) ISC_PLATFORM_NORETURN_POST; 259262445Serwin 260262445Serwinstatic void 261262445Serwinusage(void) { 262262445Serwin fprintf(stderr, "Usage:\n"); 263262445Serwin fprintf(stderr, " %s options [-K dir] keyfile\n\n", program); 264262445Serwin fprintf(stderr, " %s options -f file [keyname]\n\n", program); 265262445Serwin fprintf(stderr, "Version: %s\n", VERSION); 266262445Serwin fprintf(stderr, "Options:\n"); 267262445Serwin fprintf(stderr, " -f file: read key from zone file\n"); 268262445Serwin fprintf(stderr, " -K <directory>: directory in which to store " 269262445Serwin "the key files\n"); 270262445Serwin fprintf(stderr, " -L ttl: set default key TTL\n"); 271262445Serwin fprintf(stderr, " -v <verbose level>\n"); 272262445Serwin fprintf(stderr, " -h: print usage and exit\n"); 273262445Serwin fprintf(stderr, "Timing options:\n"); 274262445Serwin fprintf(stderr, " -P date/[+-]offset/none: set/unset key " 275262445Serwin "publication date\n"); 276262445Serwin fprintf(stderr, " -D date/[+-]offset/none: set/unset key " 277262445Serwin "deletion date\n"); 278262445Serwin 279262445Serwin exit (-1); 280262445Serwin} 281262445Serwin 282262445Serwinint 283262445Serwinmain(int argc, char **argv) { 284262445Serwin char *classname = NULL; 285262445Serwin char *filename = NULL, *dir = NULL, *namestr; 286262445Serwin char *endp; 287262445Serwin int ch; 288262445Serwin isc_result_t result; 289262445Serwin isc_log_t *log = NULL; 290262445Serwin isc_entropy_t *ectx = NULL; 291262445Serwin dns_rdataset_t rdataset; 292262445Serwin dns_rdata_t rdata; 293262445Serwin isc_stdtime_t now; 294262445Serwin 295262445Serwin dns_rdata_init(&rdata); 296262445Serwin isc_stdtime_get(&now); 297262445Serwin 298262445Serwin if (argc == 1) 299262445Serwin usage(); 300262445Serwin 301262445Serwin result = isc_mem_create(0, 0, &mctx); 302262445Serwin if (result != ISC_R_SUCCESS) 303262445Serwin fatal("out of memory"); 304262445Serwin 305262445Serwin dns_result_register(); 306262445Serwin 307262445Serwin isc_commandline_errprint = ISC_FALSE; 308262445Serwin 309262445Serwin#define CMDLINE_FLAGS "D:f:hK:L:P:v:" 310262445Serwin while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { 311262445Serwin switch (ch) { 312262445Serwin case 'D': 313262445Serwin if (setdel) 314262445Serwin fatal("-D specified more than once"); 315262445Serwin 316262445Serwin setdel = ISC_TRUE; 317262445Serwin del = strtotime(isc_commandline_argument, now, now); 318262445Serwin break; 319262445Serwin case 'K': 320262445Serwin dir = isc_commandline_argument; 321262445Serwin if (strlen(dir) == 0U) 322262445Serwin fatal("directory must be non-empty string"); 323262445Serwin break; 324262445Serwin case 'L': 325262445Serwin if (strcmp(isc_commandline_argument, "none") == 0) 326262445Serwin ttl = 0; 327262445Serwin else 328262445Serwin ttl = strtottl(isc_commandline_argument); 329262445Serwin setttl = ISC_TRUE; 330262445Serwin break; 331262445Serwin case 'P': 332262445Serwin if (setpub) 333262445Serwin fatal("-P specified more than once"); 334262445Serwin setpub = ISC_TRUE; 335262445Serwin pub = strtotime(isc_commandline_argument, now, now); 336262445Serwin break; 337262445Serwin case 'f': 338262445Serwin filename = isc_commandline_argument; 339262445Serwin break; 340262445Serwin case 'v': 341262445Serwin verbose = strtol(isc_commandline_argument, &endp, 0); 342262445Serwin if (*endp != '\0') 343262445Serwin fatal("-v must be followed by a number"); 344262445Serwin break; 345262445Serwin case '?': 346262445Serwin if (isc_commandline_option != '?') 347262445Serwin fprintf(stderr, "%s: invalid argument -%c\n", 348262445Serwin program, isc_commandline_option); 349262445Serwin /* FALLTHROUGH */ 350262445Serwin case 'h': 351262445Serwin usage(); 352262445Serwin 353262445Serwin default: 354262445Serwin fprintf(stderr, "%s: unhandled option -%c\n", 355262445Serwin program, isc_commandline_option); 356262445Serwin exit(1); 357262445Serwin } 358262445Serwin } 359262445Serwin 360262445Serwin rdclass = strtoclass(classname); 361262445Serwin 362262445Serwin if (argc < isc_commandline_index + 1 && filename == NULL) 363262445Serwin fatal("the key file name was not specified"); 364262445Serwin if (argc > isc_commandline_index + 1) 365262445Serwin fatal("extraneous arguments"); 366262445Serwin 367262445Serwin if (ectx == NULL) 368262445Serwin setup_entropy(mctx, NULL, &ectx); 369262445Serwin result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE); 370262445Serwin if (result != ISC_R_SUCCESS) 371262445Serwin fatal("could not initialize hash"); 372262445Serwin result = dst_lib_init(mctx, ectx, 373262445Serwin ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY); 374262445Serwin if (result != ISC_R_SUCCESS) 375262445Serwin fatal("could not initialize dst: %s", 376262445Serwin isc_result_totext(result)); 377262445Serwin isc_entropy_stopcallbacksources(ectx); 378262445Serwin 379262445Serwin setup_logging(verbose, mctx, &log); 380262445Serwin 381262445Serwin dns_rdataset_init(&rdataset); 382262445Serwin 383262445Serwin if (filename != NULL) { 384262445Serwin if (argc < isc_commandline_index + 1) { 385262445Serwin /* using filename as zone name */ 386262445Serwin namestr = filename; 387262445Serwin } else 388262445Serwin namestr = argv[isc_commandline_index]; 389262445Serwin 390262445Serwin result = initname(namestr); 391262445Serwin if (result != ISC_R_SUCCESS) 392262445Serwin fatal("could not initialize name %s", namestr); 393262445Serwin 394262445Serwin result = loadset(filename, &rdataset); 395262445Serwin 396262445Serwin if (result != ISC_R_SUCCESS) 397262445Serwin fatal("could not load DNSKEY set: %s\n", 398262445Serwin isc_result_totext(result)); 399262445Serwin 400262445Serwin for (result = dns_rdataset_first(&rdataset); 401262445Serwin result == ISC_R_SUCCESS; 402262445Serwin result = dns_rdataset_next(&rdataset)) { 403262445Serwin 404262445Serwin dns_rdata_init(&rdata); 405262445Serwin dns_rdataset_current(&rdataset, &rdata); 406262445Serwin emit(dir, &rdata); 407262445Serwin } 408262445Serwin } else { 409262445Serwin unsigned char key_buf[DST_KEY_MAXSIZE]; 410262445Serwin 411262445Serwin loadkey(argv[isc_commandline_index], key_buf, 412262445Serwin DST_KEY_MAXSIZE, &rdata); 413262445Serwin 414262445Serwin emit(dir, &rdata); 415262445Serwin } 416262445Serwin 417262445Serwin if (dns_rdataset_isassociated(&rdataset)) 418262445Serwin dns_rdataset_disassociate(&rdataset); 419262445Serwin cleanup_logging(&log); 420262445Serwin dst_lib_destroy(); 421262445Serwin isc_hash_destroy(); 422262445Serwin cleanup_entropy(&ectx); 423262445Serwin dns_name_destroy(); 424262445Serwin if (verbose > 10) 425262445Serwin isc_mem_stats(mctx, stdout); 426262445Serwin isc_mem_destroy(&mctx); 427262445Serwin 428262445Serwin fflush(stdout); 429262445Serwin if (ferror(stdout)) { 430262445Serwin fprintf(stderr, "write error\n"); 431262445Serwin return (1); 432262445Serwin } else 433262445Serwin return (0); 434262445Serwin} 435