dnssectool.c revision 234010
1/* 2 * Copyright (C) 2004, 2005, 2007, 2009-2011 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2000, 2001, 2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id: dnssectool.c,v 1.60.162.3 2011/10/21 03:56:32 marka Exp $ */ 19 20/*! \file */ 21 22/*% 23 * DNSSEC Support Routines. 24 */ 25 26#include <config.h> 27 28#include <stdlib.h> 29 30#include <isc/buffer.h> 31#include <isc/dir.h> 32#include <isc/entropy.h> 33#include <isc/list.h> 34#include <isc/mem.h> 35#include <isc/string.h> 36#include <isc/time.h> 37#include <isc/util.h> 38#include <isc/print.h> 39 40#include <dns/dnssec.h> 41#include <dns/keyvalues.h> 42#include <dns/log.h> 43#include <dns/name.h> 44#include <dns/rdatastruct.h> 45#include <dns/rdataclass.h> 46#include <dns/rdatatype.h> 47#include <dns/result.h> 48#include <dns/secalg.h> 49#include <dns/time.h> 50 51#include "dnssectool.h" 52 53extern int verbose; 54extern const char *program; 55 56typedef struct entropysource entropysource_t; 57 58struct entropysource { 59 isc_entropysource_t *source; 60 isc_mem_t *mctx; 61 ISC_LINK(entropysource_t) link; 62}; 63 64static ISC_LIST(entropysource_t) sources; 65static fatalcallback_t *fatalcallback = NULL; 66 67void 68fatal(const char *format, ...) { 69 va_list args; 70 71 fprintf(stderr, "%s: fatal: ", program); 72 va_start(args, format); 73 vfprintf(stderr, format, args); 74 va_end(args); 75 fprintf(stderr, "\n"); 76 if (fatalcallback != NULL) 77 (*fatalcallback)(); 78 exit(1); 79} 80 81void 82setfatalcallback(fatalcallback_t *callback) { 83 fatalcallback = callback; 84} 85 86void 87check_result(isc_result_t result, const char *message) { 88 if (result != ISC_R_SUCCESS) 89 fatal("%s: %s", message, isc_result_totext(result)); 90} 91 92void 93vbprintf(int level, const char *fmt, ...) { 94 va_list ap; 95 if (level > verbose) 96 return; 97 va_start(ap, fmt); 98 fprintf(stderr, "%s: ", program); 99 vfprintf(stderr, fmt, ap); 100 va_end(ap); 101} 102 103void 104type_format(const dns_rdatatype_t type, char *cp, unsigned int size) { 105 isc_buffer_t b; 106 isc_region_t r; 107 isc_result_t result; 108 109 isc_buffer_init(&b, cp, size - 1); 110 result = dns_rdatatype_totext(type, &b); 111 check_result(result, "dns_rdatatype_totext()"); 112 isc_buffer_usedregion(&b, &r); 113 r.base[r.length] = 0; 114} 115 116void 117sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) { 118 char namestr[DNS_NAME_FORMATSIZE]; 119 char algstr[DNS_NAME_FORMATSIZE]; 120 121 dns_name_format(&sig->signer, namestr, sizeof(namestr)); 122 dns_secalg_format(sig->algorithm, algstr, sizeof(algstr)); 123 snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid); 124} 125 126void 127setup_logging(int verbose, isc_mem_t *mctx, isc_log_t **logp) { 128 isc_result_t result; 129 isc_logdestination_t destination; 130 isc_logconfig_t *logconfig = NULL; 131 isc_log_t *log = NULL; 132 int level; 133 134 if (verbose < 0) 135 verbose = 0; 136 switch (verbose) { 137 case 0: 138 /* 139 * We want to see warnings about things like out-of-zone 140 * data in the master file even when not verbose. 141 */ 142 level = ISC_LOG_WARNING; 143 break; 144 case 1: 145 level = ISC_LOG_INFO; 146 break; 147 default: 148 level = ISC_LOG_DEBUG(verbose - 2 + 1); 149 break; 150 } 151 152 RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS); 153 isc_log_setcontext(log); 154 dns_log_init(log); 155 dns_log_setcontext(log); 156 157 RUNTIME_CHECK(isc_log_settag(logconfig, program) == ISC_R_SUCCESS); 158 159 /* 160 * Set up a channel similar to default_stderr except: 161 * - the logging level is passed in 162 * - the program name and logging level are printed 163 * - no time stamp is printed 164 */ 165 destination.file.stream = stderr; 166 destination.file.name = NULL; 167 destination.file.versions = ISC_LOG_ROLLNEVER; 168 destination.file.maximum_size = 0; 169 result = isc_log_createchannel(logconfig, "stderr", 170 ISC_LOG_TOFILEDESC, 171 level, 172 &destination, 173 ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL); 174 check_result(result, "isc_log_createchannel()"); 175 176 RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr", 177 NULL, NULL) == ISC_R_SUCCESS); 178 179 *logp = log; 180} 181 182void 183cleanup_logging(isc_log_t **logp) { 184 isc_log_t *log; 185 186 REQUIRE(logp != NULL); 187 188 log = *logp; 189 if (log == NULL) 190 return; 191 isc_log_destroy(&log); 192 isc_log_setcontext(NULL); 193 dns_log_setcontext(NULL); 194 logp = NULL; 195} 196 197void 198setup_entropy(isc_mem_t *mctx, const char *randomfile, isc_entropy_t **ectx) { 199 isc_result_t result; 200 isc_entropysource_t *source = NULL; 201 entropysource_t *elt; 202 int usekeyboard = ISC_ENTROPY_KEYBOARDMAYBE; 203 204 REQUIRE(ectx != NULL); 205 206 if (*ectx == NULL) { 207 result = isc_entropy_create(mctx, ectx); 208 if (result != ISC_R_SUCCESS) 209 fatal("could not create entropy object"); 210 ISC_LIST_INIT(sources); 211 } 212 213 if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) { 214 usekeyboard = ISC_ENTROPY_KEYBOARDYES; 215 randomfile = NULL; 216 } 217 218 result = isc_entropy_usebestsource(*ectx, &source, randomfile, 219 usekeyboard); 220 221 if (result != ISC_R_SUCCESS) 222 fatal("could not initialize entropy source: %s", 223 isc_result_totext(result)); 224 225 if (source != NULL) { 226 elt = isc_mem_get(mctx, sizeof(*elt)); 227 if (elt == NULL) 228 fatal("out of memory"); 229 elt->source = source; 230 elt->mctx = mctx; 231 ISC_LINK_INIT(elt, link); 232 ISC_LIST_APPEND(sources, elt, link); 233 } 234} 235 236void 237cleanup_entropy(isc_entropy_t **ectx) { 238 entropysource_t *source; 239 while (!ISC_LIST_EMPTY(sources)) { 240 source = ISC_LIST_HEAD(sources); 241 ISC_LIST_UNLINK(sources, source, link); 242 isc_entropy_destroysource(&source->source); 243 isc_mem_put(source->mctx, source, sizeof(*source)); 244 } 245 isc_entropy_detach(ectx); 246} 247 248static isc_stdtime_t 249time_units(isc_stdtime_t offset, char *suffix, const char *str) { 250 switch (suffix[0]) { 251 case 'Y': case 'y': 252 return (offset * (365 * 24 * 3600)); 253 case 'M': case 'm': 254 switch (suffix[1]) { 255 case 'O': case 'o': 256 return (offset * (30 * 24 * 3600)); 257 case 'I': case 'i': 258 return (offset * 60); 259 case '\0': 260 fatal("'%s' ambiguous: use 'mi' for minutes " 261 "or 'mo' for months", str); 262 default: 263 fatal("time value %s is invalid", str); 264 } 265 /* NOTREACHED */ 266 break; 267 case 'W': case 'w': 268 return (offset * (7 * 24 * 3600)); 269 case 'D': case 'd': 270 return (offset * (24 * 3600)); 271 case 'H': case 'h': 272 return (offset * 3600); 273 case 'S': case 's': case '\0': 274 return (offset); 275 default: 276 fatal("time value %s is invalid", str); 277 } 278 /* NOTREACHED */ 279 return(0); /* silence compiler warning */ 280} 281 282dns_ttl_t 283strtottl(const char *str) { 284 const char *orig = str; 285 dns_ttl_t ttl; 286 char *endp; 287 288 ttl = strtol(str, &endp, 0); 289 if (ttl == 0 && endp == str) 290 fatal("TTL must be numeric"); 291 ttl = time_units(ttl, endp, orig); 292 return (ttl); 293} 294 295isc_stdtime_t 296strtotime(const char *str, isc_int64_t now, isc_int64_t base) { 297 isc_int64_t val, offset; 298 isc_result_t result; 299 const char *orig = str; 300 char *endp; 301 302 if ((str[0] == '0' || str[0] == '-') && str[1] == '\0') 303 return ((isc_stdtime_t) 0); 304 305 if (strncmp(str, "now", 3) == 0) { 306 base = now; 307 str += 3; 308 } 309 310 if (str[0] == '\0') 311 return ((isc_stdtime_t) base); 312 else if (str[0] == '+') { 313 offset = strtol(str + 1, &endp, 0); 314 offset = time_units((isc_stdtime_t) offset, endp, orig); 315 val = base + offset; 316 } else if (str[0] == '-') { 317 offset = strtol(str + 1, &endp, 0); 318 offset = time_units((isc_stdtime_t) offset, endp, orig); 319 val = base - offset; 320 } else if (strlen(str) == 8U) { 321 char timestr[15]; 322 sprintf(timestr, "%s000000", str); 323 result = dns_time64_fromtext(timestr, &val); 324 if (result != ISC_R_SUCCESS) 325 fatal("time value %s is invalid: %s", orig, 326 isc_result_totext(result)); 327 } else if (strlen(str) > 14U) { 328 fatal("time value %s is invalid", orig); 329 } else { 330 result = dns_time64_fromtext(str, &val); 331 if (result != ISC_R_SUCCESS) 332 fatal("time value %s is invalid: %s", orig, 333 isc_result_totext(result)); 334 } 335 336 return ((isc_stdtime_t) val); 337} 338 339dns_rdataclass_t 340strtoclass(const char *str) { 341 isc_textregion_t r; 342 dns_rdataclass_t rdclass; 343 isc_result_t ret; 344 345 if (str == NULL) 346 return dns_rdataclass_in; 347 DE_CONST(str, r.base); 348 r.length = strlen(str); 349 ret = dns_rdataclass_fromtext(&rdclass, &r); 350 if (ret != ISC_R_SUCCESS) 351 fatal("unknown class %s", str); 352 return (rdclass); 353} 354 355isc_result_t 356try_dir(const char *dirname) { 357 isc_result_t result; 358 isc_dir_t d; 359 360 isc_dir_init(&d); 361 result = isc_dir_open(&d, dirname); 362 if (result == ISC_R_SUCCESS) { 363 isc_dir_close(&d); 364 } 365 return (result); 366} 367 368/* 369 * Check private key version compatibility. 370 */ 371void 372check_keyversion(dst_key_t *key, char *keystr) { 373 int major, minor; 374 dst_key_getprivateformat(key, &major, &minor); 375 INSIST(major <= DST_MAJOR_VERSION); /* invalid private key */ 376 377 if (major < DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) 378 fatal("Key %s has incompatible format version %d.%d, " 379 "use -f to force upgrade to new version.", 380 keystr, major, minor); 381 if (minor > DST_MINOR_VERSION) 382 fatal("Key %s has incompatible format version %d.%d, " 383 "use -f to force downgrade to current version.", 384 keystr, major, minor); 385} 386 387void 388set_keyversion(dst_key_t *key) { 389 int major, minor; 390 dst_key_getprivateformat(key, &major, &minor); 391 INSIST(major <= DST_MAJOR_VERSION); 392 393 if (major != DST_MAJOR_VERSION || minor != DST_MINOR_VERSION) 394 dst_key_setprivateformat(key, DST_MAJOR_VERSION, 395 DST_MINOR_VERSION); 396 397 /* 398 * If the key is from a version older than 1.3, set 399 * set the creation date 400 */ 401 if (major < 1 || (major == 1 && minor <= 2)) { 402 isc_stdtime_t now; 403 isc_stdtime_get(&now); 404 dst_key_settime(key, DST_TIME_CREATED, now); 405 } 406} 407 408isc_boolean_t 409key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir, 410 isc_mem_t *mctx, isc_boolean_t *exact) 411{ 412 isc_result_t result; 413 isc_boolean_t conflict = ISC_FALSE; 414 dns_dnsseckeylist_t matchkeys; 415 dns_dnsseckey_t *key = NULL; 416 isc_uint16_t id, oldid; 417 isc_uint32_t rid, roldid; 418 dns_secalg_t alg; 419 420 if (exact != NULL) 421 *exact = ISC_FALSE; 422 423 id = dst_key_id(dstkey); 424 rid = dst_key_rid(dstkey); 425 alg = dst_key_alg(dstkey); 426 427 ISC_LIST_INIT(matchkeys); 428 result = dns_dnssec_findmatchingkeys(name, dir, mctx, &matchkeys); 429 if (result == ISC_R_NOTFOUND) 430 return (ISC_FALSE); 431 432 while (!ISC_LIST_EMPTY(matchkeys) && !conflict) { 433 key = ISC_LIST_HEAD(matchkeys); 434 if (dst_key_alg(key->key) != alg) 435 goto next; 436 437 oldid = dst_key_id(key->key); 438 roldid = dst_key_rid(key->key); 439 440 if (oldid == rid || roldid == id || id == oldid) { 441 conflict = ISC_TRUE; 442 if (id != oldid) { 443 if (verbose > 1) 444 fprintf(stderr, "Key ID %d could " 445 "collide with %d\n", 446 id, oldid); 447 } else { 448 if (exact != NULL) 449 *exact = ISC_TRUE; 450 if (verbose > 1) 451 fprintf(stderr, "Key ID %d exists\n", 452 id); 453 } 454 } 455 456 next: 457 ISC_LIST_UNLINK(matchkeys, key, link); 458 dns_dnsseckey_destroy(mctx, &key); 459 } 460 461 /* Finish freeing the list */ 462 while (!ISC_LIST_EMPTY(matchkeys)) { 463 key = ISC_LIST_HEAD(matchkeys); 464 ISC_LIST_UNLINK(matchkeys, key, link); 465 dns_dnsseckey_destroy(mctx, &key); 466 } 467 468 return (conflict); 469} 470