1 1.14 christos /* $NetBSD: dnssectool.c,v 1.14 2026/09/17 18:00:59 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.7 christos * SPDX-License-Identifier: MPL-2.0 7 1.7 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.5 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.1 christos /*! \file */ 17 1.1 christos 18 1.1 christos /*% 19 1.1 christos * DNSSEC Support Routines. 20 1.1 christos */ 21 1.1 christos 22 1.3 christos #include <inttypes.h> 23 1.3 christos #include <stdbool.h> 24 1.1 christos #include <stdlib.h> 25 1.10 christos #include <unistd.h> 26 1.1 christos 27 1.1 christos #include <isc/base32.h> 28 1.1 christos #include <isc/buffer.h> 29 1.1 christos #include <isc/commandline.h> 30 1.1 christos #include <isc/dir.h> 31 1.1 christos #include <isc/file.h> 32 1.1 christos #include <isc/heap.h> 33 1.1 christos #include <isc/list.h> 34 1.1 christos #include <isc/mem.h> 35 1.9 christos #include <isc/result.h> 36 1.1 christos #include <isc/string.h> 37 1.1 christos #include <isc/time.h> 38 1.11 christos #include <isc/tls.h> 39 1.9 christos #include <isc/tm.h> 40 1.1 christos #include <isc/util.h> 41 1.1 christos 42 1.1 christos #include <dns/db.h> 43 1.1 christos #include <dns/dbiterator.h> 44 1.1 christos #include <dns/dnssec.h> 45 1.1 christos #include <dns/fixedname.h> 46 1.11 christos #include <dns/journal.h> 47 1.1 christos #include <dns/keyvalues.h> 48 1.1 christos #include <dns/log.h> 49 1.1 christos #include <dns/name.h> 50 1.1 christos #include <dns/nsec.h> 51 1.1 christos #include <dns/nsec3.h> 52 1.1 christos #include <dns/rdataclass.h> 53 1.1 christos #include <dns/rdataset.h> 54 1.1 christos #include <dns/rdatasetiter.h> 55 1.4 christos #include <dns/rdatastruct.h> 56 1.1 christos #include <dns/rdatatype.h> 57 1.1 christos #include <dns/secalg.h> 58 1.1 christos #include <dns/time.h> 59 1.1 christos 60 1.1 christos #include "dnssectool.h" 61 1.1 christos 62 1.4 christos #define KEYSTATES_NVALUES 4 63 1.4 christos static const char *keystates[KEYSTATES_NVALUES] = { 64 1.4 christos "hidden", 65 1.4 christos "rumoured", 66 1.4 christos "omnipresent", 67 1.4 christos "unretentive", 68 1.4 christos }; 69 1.4 christos 70 1.4 christos int verbose = 0; 71 1.4 christos bool quiet = false; 72 1.11 christos const char *journal = NULL; 73 1.9 christos dns_dsdigest_t dtype[8]; 74 1.1 christos 75 1.1 christos static fatalcallback_t *fatalcallback = NULL; 76 1.1 christos 77 1.1 christos void 78 1.1 christos fatal(const char *format, ...) { 79 1.1 christos va_list args; 80 1.1 christos 81 1.1 christos fprintf(stderr, "%s: fatal: ", program); 82 1.1 christos va_start(args, format); 83 1.1 christos vfprintf(stderr, format, args); 84 1.1 christos va_end(args); 85 1.1 christos fprintf(stderr, "\n"); 86 1.4 christos if (fatalcallback != NULL) { 87 1.1 christos (*fatalcallback)(); 88 1.4 christos } 89 1.10 christos _exit(EXIT_FAILURE); 90 1.1 christos } 91 1.1 christos 92 1.1 christos void 93 1.1 christos setfatalcallback(fatalcallback_t *callback) { 94 1.1 christos fatalcallback = callback; 95 1.1 christos } 96 1.1 christos 97 1.1 christos void 98 1.1 christos check_result(isc_result_t result, const char *message) { 99 1.4 christos if (result != ISC_R_SUCCESS) { 100 1.1 christos fatal("%s: %s", message, isc_result_totext(result)); 101 1.4 christos } 102 1.1 christos } 103 1.1 christos 104 1.1 christos void 105 1.1 christos vbprintf(int level, const char *fmt, ...) { 106 1.1 christos va_list ap; 107 1.4 christos if (level > verbose) { 108 1.1 christos return; 109 1.4 christos } 110 1.1 christos va_start(ap, fmt); 111 1.1 christos fprintf(stderr, "%s: ", program); 112 1.1 christos vfprintf(stderr, fmt, ap); 113 1.1 christos va_end(ap); 114 1.1 christos } 115 1.1 christos 116 1.1 christos void 117 1.1 christos version(const char *name) { 118 1.11 christos printf("%s %s\n", name, PACKAGE_VERSION); 119 1.10 christos exit(EXIT_SUCCESS); 120 1.1 christos } 121 1.1 christos 122 1.1 christos void 123 1.1 christos sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) { 124 1.1 christos char namestr[DNS_NAME_FORMATSIZE]; 125 1.1 christos char algstr[DNS_NAME_FORMATSIZE]; 126 1.1 christos 127 1.1 christos dns_name_format(&sig->signer, namestr, sizeof(namestr)); 128 1.1 christos dns_secalg_format(sig->algorithm, algstr, sizeof(algstr)); 129 1.1 christos snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid); 130 1.1 christos } 131 1.1 christos 132 1.1 christos void 133 1.1 christos setup_logging(isc_mem_t *mctx, isc_log_t **logp) { 134 1.1 christos isc_logdestination_t destination; 135 1.1 christos isc_logconfig_t *logconfig = NULL; 136 1.1 christos isc_log_t *log = NULL; 137 1.1 christos int level; 138 1.1 christos 139 1.4 christos if (verbose < 0) { 140 1.1 christos verbose = 0; 141 1.4 christos } 142 1.1 christos switch (verbose) { 143 1.1 christos case 0: 144 1.1 christos /* 145 1.1 christos * We want to see warnings about things like out-of-zone 146 1.1 christos * data in the master file even when not verbose. 147 1.1 christos */ 148 1.1 christos level = ISC_LOG_WARNING; 149 1.1 christos break; 150 1.1 christos case 1: 151 1.1 christos level = ISC_LOG_INFO; 152 1.1 christos break; 153 1.1 christos default: 154 1.1 christos level = ISC_LOG_DEBUG(verbose - 2 + 1); 155 1.1 christos break; 156 1.1 christos } 157 1.1 christos 158 1.4 christos isc_log_create(mctx, &log, &logconfig); 159 1.1 christos isc_log_setcontext(log); 160 1.1 christos dns_log_init(log); 161 1.1 christos dns_log_setcontext(log); 162 1.4 christos isc_log_settag(logconfig, program); 163 1.1 christos 164 1.1 christos /* 165 1.1 christos * Set up a channel similar to default_stderr except: 166 1.1 christos * - the logging level is passed in 167 1.1 christos * - the program name and logging level are printed 168 1.1 christos * - no time stamp is printed 169 1.1 christos */ 170 1.1 christos destination.file.stream = stderr; 171 1.1 christos destination.file.name = NULL; 172 1.1 christos destination.file.versions = ISC_LOG_ROLLNEVER; 173 1.1 christos destination.file.maximum_size = 0; 174 1.4 christos isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC, level, 175 1.4 christos &destination, 176 1.4 christos ISC_LOG_PRINTTAG | ISC_LOG_PRINTLEVEL); 177 1.1 christos 178 1.4 christos RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr", NULL, NULL) == 179 1.4 christos ISC_R_SUCCESS); 180 1.1 christos 181 1.1 christos *logp = log; 182 1.1 christos } 183 1.1 christos 184 1.1 christos void 185 1.1 christos cleanup_logging(isc_log_t **logp) { 186 1.1 christos isc_log_t *log; 187 1.1 christos 188 1.1 christos REQUIRE(logp != NULL); 189 1.1 christos 190 1.1 christos log = *logp; 191 1.1 christos *logp = NULL; 192 1.1 christos 193 1.4 christos if (log == NULL) { 194 1.1 christos return; 195 1.4 christos } 196 1.1 christos 197 1.1 christos isc_log_destroy(&log); 198 1.1 christos isc_log_setcontext(NULL); 199 1.1 christos dns_log_setcontext(NULL); 200 1.1 christos } 201 1.1 christos 202 1.1 christos static isc_stdtime_t 203 1.1 christos time_units(isc_stdtime_t offset, char *suffix, const char *str) { 204 1.1 christos switch (suffix[0]) { 205 1.4 christos case 'Y': 206 1.4 christos case 'y': 207 1.11 christos return offset * (365 * 24 * 3600); 208 1.4 christos case 'M': 209 1.4 christos case 'm': 210 1.1 christos switch (suffix[1]) { 211 1.4 christos case 'O': 212 1.4 christos case 'o': 213 1.11 christos return offset * (30 * 24 * 3600); 214 1.4 christos case 'I': 215 1.4 christos case 'i': 216 1.11 christos return offset * 60; 217 1.4 christos case '\0': 218 1.1 christos fatal("'%s' ambiguous: use 'mi' for minutes " 219 1.4 christos "or 'mo' for months", 220 1.4 christos str); 221 1.4 christos default: 222 1.1 christos fatal("time value %s is invalid", str); 223 1.1 christos } 224 1.7 christos UNREACHABLE(); 225 1.1 christos break; 226 1.4 christos case 'W': 227 1.4 christos case 'w': 228 1.11 christos return offset * (7 * 24 * 3600); 229 1.4 christos case 'D': 230 1.4 christos case 'd': 231 1.11 christos return offset * (24 * 3600); 232 1.4 christos case 'H': 233 1.4 christos case 'h': 234 1.11 christos return offset * 3600; 235 1.4 christos case 'S': 236 1.4 christos case 's': 237 1.4 christos case '\0': 238 1.11 christos return offset; 239 1.4 christos default: 240 1.1 christos fatal("time value %s is invalid", str); 241 1.1 christos } 242 1.7 christos UNREACHABLE(); 243 1.11 christos return 0; /* silence compiler warning */ 244 1.1 christos } 245 1.1 christos 246 1.7 christos static bool 247 1.1 christos isnone(const char *str) { 248 1.11 christos return (strcasecmp(str, "none") == 0) || 249 1.11 christos (strcasecmp(str, "never") == 0) || 250 1.11 christos (strcasecmp(str, "unset") == 0); 251 1.1 christos } 252 1.1 christos 253 1.1 christos dns_ttl_t 254 1.1 christos strtottl(const char *str) { 255 1.1 christos const char *orig = str; 256 1.1 christos dns_ttl_t ttl; 257 1.1 christos char *endp; 258 1.1 christos 259 1.4 christos if (isnone(str)) { 260 1.11 christos return (dns_ttl_t)0; 261 1.4 christos } 262 1.1 christos 263 1.1 christos ttl = strtol(str, &endp, 0); 264 1.4 christos if (ttl == 0 && endp == str) { 265 1.1 christos fatal("TTL must be numeric"); 266 1.4 christos } 267 1.1 christos ttl = time_units(ttl, endp, orig); 268 1.11 christos return ttl; 269 1.1 christos } 270 1.1 christos 271 1.4 christos dst_key_state_t 272 1.4 christos strtokeystate(const char *str) { 273 1.4 christos if (isnone(str)) { 274 1.11 christos return DST_KEY_STATE_NA; 275 1.4 christos } 276 1.4 christos 277 1.4 christos for (int i = 0; i < KEYSTATES_NVALUES; i++) { 278 1.4 christos if (keystates[i] != NULL && strcasecmp(str, keystates[i]) == 0) 279 1.4 christos { 280 1.11 christos return (dst_key_state_t)i; 281 1.4 christos } 282 1.4 christos } 283 1.6 christos fatal("unknown key state %s", str); 284 1.4 christos } 285 1.4 christos 286 1.1 christos isc_stdtime_t 287 1.4 christos strtotime(const char *str, int64_t now, int64_t base, bool *setp) { 288 1.3 christos int64_t val, offset; 289 1.1 christos isc_result_t result; 290 1.1 christos const char *orig = str; 291 1.1 christos char *endp; 292 1.1 christos size_t n; 293 1.9 christos struct tm tm; 294 1.1 christos 295 1.1 christos if (isnone(str)) { 296 1.11 christos SET_IF_NOT_NULL(setp, false); 297 1.11 christos return (isc_stdtime_t)0; 298 1.1 christos } 299 1.1 christos 300 1.11 christos SET_IF_NOT_NULL(setp, true); 301 1.1 christos 302 1.4 christos if ((str[0] == '0' || str[0] == '-') && str[1] == '\0') { 303 1.11 christos return (isc_stdtime_t)0; 304 1.4 christos } 305 1.1 christos 306 1.1 christos /* 307 1.1 christos * We accept times in the following formats: 308 1.1 christos * now([+-]offset) 309 1.1 christos * YYYYMMDD([+-]offset) 310 1.1 christos * YYYYMMDDhhmmss([+-]offset) 311 1.9 christos * Day Mon DD HH:MM:SS YYYY([+-]offset) 312 1.9 christos * 1234567890([+-]offset) 313 1.1 christos * [+-]offset 314 1.1 christos */ 315 1.1 christos n = strspn(str, "0123456789"); 316 1.1 christos if ((n == 8u || n == 14u) && 317 1.8 christos (str[n] == '\0' || str[n] == '-' || str[n] == '+')) 318 1.8 christos { 319 1.1 christos char timestr[15]; 320 1.1 christos 321 1.1 christos strlcpy(timestr, str, sizeof(timestr)); 322 1.1 christos timestr[n] = 0; 323 1.4 christos if (n == 8u) { 324 1.1 christos strlcat(timestr, "000000", sizeof(timestr)); 325 1.4 christos } 326 1.1 christos result = dns_time64_fromtext(timestr, &val); 327 1.4 christos if (result != ISC_R_SUCCESS) { 328 1.1 christos fatal("time value %s is invalid: %s", orig, 329 1.1 christos isc_result_totext(result)); 330 1.4 christos } 331 1.1 christos base = val; 332 1.1 christos str += n; 333 1.9 christos } else if (n == 10u && 334 1.9 christos (str[n] == '\0' || str[n] == '-' || str[n] == '+')) 335 1.9 christos { 336 1.9 christos base = strtoll(str, &endp, 0); 337 1.9 christos str += 10; 338 1.1 christos } else if (strncmp(str, "now", 3) == 0) { 339 1.1 christos base = now; 340 1.1 christos str += 3; 341 1.9 christos } else if (str[0] >= 'A' && str[0] <= 'Z') { 342 1.9 christos /* parse ctime() format as written by `dnssec-settime -p` */ 343 1.9 christos endp = isc_tm_strptime(str, "%a %b %d %H:%M:%S %Y", &tm); 344 1.9 christos if (endp != str + 24) { 345 1.9 christos fatal("time value %s is invalid", orig); 346 1.9 christos } 347 1.9 christos base = mktime(&tm); 348 1.9 christos str += 24; 349 1.1 christos } 350 1.1 christos 351 1.4 christos if (str[0] == '\0') { 352 1.11 christos return (isc_stdtime_t)base; 353 1.4 christos } else if (str[0] == '+') { 354 1.1 christos offset = strtol(str + 1, &endp, 0); 355 1.4 christos offset = time_units((isc_stdtime_t)offset, endp, orig); 356 1.1 christos val = base + offset; 357 1.1 christos } else if (str[0] == '-') { 358 1.1 christos offset = strtol(str + 1, &endp, 0); 359 1.4 christos offset = time_units((isc_stdtime_t)offset, endp, orig); 360 1.1 christos val = base - offset; 361 1.4 christos } else { 362 1.1 christos fatal("time value %s is invalid", orig); 363 1.4 christos } 364 1.1 christos 365 1.11 christos return (isc_stdtime_t)val; 366 1.1 christos } 367 1.1 christos 368 1.1 christos dns_rdataclass_t 369 1.1 christos strtoclass(const char *str) { 370 1.1 christos isc_textregion_t r; 371 1.1 christos dns_rdataclass_t rdclass; 372 1.4 christos isc_result_t result; 373 1.1 christos 374 1.4 christos if (str == NULL) { 375 1.11 christos return dns_rdataclass_in; 376 1.4 christos } 377 1.11 christos r.base = UNCONST(str); 378 1.1 christos r.length = strlen(str); 379 1.4 christos result = dns_rdataclass_fromtext(&rdclass, &r); 380 1.4 christos if (result != ISC_R_SUCCESS) { 381 1.1 christos fatal("unknown class %s", str); 382 1.4 christos } 383 1.11 christos return rdclass; 384 1.1 christos } 385 1.1 christos 386 1.1 christos unsigned int 387 1.4 christos strtodsdigest(const char *str) { 388 1.4 christos isc_textregion_t r; 389 1.4 christos dns_dsdigest_t alg; 390 1.4 christos isc_result_t result; 391 1.4 christos 392 1.11 christos r.base = UNCONST(str); 393 1.4 christos r.length = strlen(str); 394 1.4 christos result = dns_dsdigest_fromtext(&alg, &r); 395 1.4 christos if (result != ISC_R_SUCCESS) { 396 1.4 christos fatal("unknown DS algorithm %s", str); 397 1.4 christos } 398 1.11 christos return alg; 399 1.4 christos } 400 1.4 christos 401 1.4 christos static int 402 1.4 christos cmp_dtype(const void *ap, const void *bp) { 403 1.4 christos int a = *(const uint8_t *)ap; 404 1.4 christos int b = *(const uint8_t *)bp; 405 1.11 christos return a - b; 406 1.4 christos } 407 1.4 christos 408 1.4 christos void 409 1.4 christos add_dtype(unsigned int dt) { 410 1.11 christos unsigned int i, n; 411 1.4 christos 412 1.4 christos /* ensure there is space for a zero terminator */ 413 1.4 christos n = sizeof(dtype) / sizeof(dtype[0]) - 1; 414 1.4 christos for (i = 0; i < n; i++) { 415 1.4 christos if (dtype[i] == dt) { 416 1.4 christos return; 417 1.4 christos } 418 1.4 christos if (dtype[i] == 0) { 419 1.4 christos dtype[i] = dt; 420 1.4 christos qsort(dtype, i + 1, 1, cmp_dtype); 421 1.4 christos return; 422 1.4 christos } 423 1.1 christos } 424 1.4 christos fatal("too many -a digest type arguments"); 425 1.1 christos } 426 1.1 christos 427 1.1 christos isc_result_t 428 1.1 christos try_dir(const char *dirname) { 429 1.1 christos isc_result_t result; 430 1.1 christos isc_dir_t d; 431 1.1 christos 432 1.1 christos isc_dir_init(&d); 433 1.1 christos result = isc_dir_open(&d, dirname); 434 1.1 christos if (result == ISC_R_SUCCESS) { 435 1.1 christos isc_dir_close(&d); 436 1.1 christos } 437 1.11 christos return result; 438 1.1 christos } 439 1.1 christos 440 1.1 christos /* 441 1.1 christos * Check private key version compatibility. 442 1.1 christos */ 443 1.1 christos void 444 1.1 christos check_keyversion(dst_key_t *key, char *keystr) { 445 1.1 christos int major, minor; 446 1.1 christos dst_key_getprivateformat(key, &major, &minor); 447 1.1 christos INSIST(major <= DST_MAJOR_VERSION); /* invalid private key */ 448 1.1 christos 449 1.4 christos if (major < DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) { 450 1.1 christos fatal("Key %s has incompatible format version %d.%d, " 451 1.1 christos "use -f to force upgrade to new version.", 452 1.1 christos keystr, major, minor); 453 1.4 christos } 454 1.4 christos if (minor > DST_MINOR_VERSION) { 455 1.1 christos fatal("Key %s has incompatible format version %d.%d, " 456 1.1 christos "use -f to force downgrade to current version.", 457 1.1 christos keystr, major, minor); 458 1.4 christos } 459 1.1 christos } 460 1.1 christos 461 1.1 christos void 462 1.1 christos set_keyversion(dst_key_t *key) { 463 1.1 christos int major, minor; 464 1.1 christos dst_key_getprivateformat(key, &major, &minor); 465 1.1 christos INSIST(major <= DST_MAJOR_VERSION); 466 1.1 christos 467 1.4 christos if (major != DST_MAJOR_VERSION || minor != DST_MINOR_VERSION) { 468 1.1 christos dst_key_setprivateformat(key, DST_MAJOR_VERSION, 469 1.1 christos DST_MINOR_VERSION); 470 1.4 christos } 471 1.1 christos 472 1.1 christos /* 473 1.1 christos * If the key is from a version older than 1.3, set 474 1.1 christos * set the creation date 475 1.1 christos */ 476 1.1 christos if (major < 1 || (major == 1 && minor <= 2)) { 477 1.11 christos isc_stdtime_t now = isc_stdtime_now(); 478 1.1 christos dst_key_settime(key, DST_TIME_CREATED, now); 479 1.1 christos } 480 1.1 christos } 481 1.1 christos 482 1.3 christos bool 483 1.1 christos key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir, 484 1.11 christos isc_mem_t *mctx, uint16_t min, uint16_t max, bool *exact) { 485 1.14 christos REQUIRE((min == 0 && max == 0) || min < max); 486 1.14 christos 487 1.1 christos isc_result_t result; 488 1.3 christos bool conflict = false; 489 1.1 christos dns_dnsseckeylist_t matchkeys; 490 1.1 christos dns_dnsseckey_t *key = NULL; 491 1.3 christos uint16_t id, oldid; 492 1.3 christos uint32_t rid, roldid; 493 1.1 christos dns_secalg_t alg; 494 1.11 christos isc_stdtime_t now = isc_stdtime_now(); 495 1.1 christos 496 1.12 christos SET_IF_NOT_NULL(exact, false); 497 1.1 christos 498 1.1 christos id = dst_key_id(dstkey); 499 1.1 christos rid = dst_key_rid(dstkey); 500 1.1 christos alg = dst_key_alg(dstkey); 501 1.1 christos 502 1.11 christos if (min != max) { 503 1.11 christos if (id < min || id > max) { 504 1.11 christos fprintf(stderr, "Key ID %d outside of [%u..%u]\n", id, 505 1.11 christos min, max); 506 1.11 christos return true; 507 1.11 christos } 508 1.11 christos if (rid < min || rid > max) { 509 1.11 christos fprintf(stderr, 510 1.11 christos "Revoked Key ID %d (for tag %d) outside of " 511 1.11 christos "[%u..%u]\n", 512 1.11 christos rid, id, min, max); 513 1.11 christos return true; 514 1.4 christos } 515 1.1 christos } 516 1.1 christos 517 1.1 christos ISC_LIST_INIT(matchkeys); 518 1.13 christos bool keykey = false; 519 1.13 christos 520 1.13 christos /* 521 1.13 christos * DNSKEY and KEY both use the same file names patterns so 522 1.13 christos * we have to look for both sets of keys. 523 1.13 christos */ 524 1.13 christos again: 525 1.13 christos result = dns_dnssec_findmatchingkeys(name, NULL, dir, NULL, now, keykey, 526 1.13 christos mctx, &matchkeys); 527 1.4 christos if (result == ISC_R_NOTFOUND) { 528 1.13 christos goto try_key; 529 1.4 christos } 530 1.1 christos 531 1.1 christos while (!ISC_LIST_EMPTY(matchkeys) && !conflict) { 532 1.1 christos key = ISC_LIST_HEAD(matchkeys); 533 1.4 christos if (dst_key_alg(key->key) != alg) { 534 1.1 christos goto next; 535 1.4 christos } 536 1.1 christos 537 1.1 christos oldid = dst_key_id(key->key); 538 1.1 christos roldid = dst_key_rid(key->key); 539 1.1 christos 540 1.1 christos if (oldid == rid || roldid == id || id == oldid) { 541 1.3 christos conflict = true; 542 1.1 christos if (id != oldid) { 543 1.4 christos if (verbose > 1) { 544 1.4 christos fprintf(stderr, 545 1.4 christos "Key ID %d could " 546 1.1 christos "collide with %d\n", 547 1.1 christos id, oldid); 548 1.4 christos } 549 1.1 christos } else { 550 1.4 christos if (exact != NULL) { 551 1.3 christos *exact = true; 552 1.4 christos } 553 1.4 christos if (verbose > 1) { 554 1.1 christos fprintf(stderr, "Key ID %d exists\n", 555 1.1 christos id); 556 1.4 christos } 557 1.1 christos } 558 1.1 christos } 559 1.1 christos 560 1.4 christos next: 561 1.1 christos ISC_LIST_UNLINK(matchkeys, key, link); 562 1.1 christos dns_dnsseckey_destroy(mctx, &key); 563 1.1 christos } 564 1.1 christos 565 1.1 christos /* Finish freeing the list */ 566 1.1 christos while (!ISC_LIST_EMPTY(matchkeys)) { 567 1.1 christos key = ISC_LIST_HEAD(matchkeys); 568 1.1 christos ISC_LIST_UNLINK(matchkeys, key, link); 569 1.1 christos dns_dnsseckey_destroy(mctx, &key); 570 1.1 christos } 571 1.1 christos 572 1.13 christos try_key: 573 1.13 christos if (!conflict && !keykey) { 574 1.13 christos keykey = true; 575 1.13 christos goto again; 576 1.13 christos } 577 1.11 christos return conflict; 578 1.1 christos } 579 1.1 christos 580 1.3 christos bool 581 1.13 christos isoptarg(const char *arg, char **argv, void (*usage)(int ret)) { 582 1.1 christos if (!strcasecmp(isc_commandline_argument, arg)) { 583 1.1 christos if (argv[isc_commandline_index] == NULL) { 584 1.1 christos fprintf(stderr, "%s: missing argument -%c %s\n", 585 1.1 christos program, isc_commandline_option, 586 1.1 christos isc_commandline_argument); 587 1.13 christos usage(EXIT_FAILURE); 588 1.1 christos } 589 1.1 christos isc_commandline_argument = argv[isc_commandline_index]; 590 1.4 christos /* skip to next argument */ 591 1.1 christos isc_commandline_index++; 592 1.11 christos return true; 593 1.11 christos } 594 1.11 christos return false; 595 1.11 christos } 596 1.11 christos 597 1.11 christos void 598 1.11 christos loadjournal(isc_mem_t *mctx, dns_db_t *db, const char *file) { 599 1.11 christos dns_journal_t *jnl = NULL; 600 1.11 christos isc_result_t result; 601 1.11 christos 602 1.11 christos result = dns_journal_open(mctx, file, DNS_JOURNAL_READ, &jnl); 603 1.11 christos if (result == ISC_R_NOTFOUND) { 604 1.11 christos fprintf(stderr, "%s: journal file %s not found\n", program, 605 1.11 christos file); 606 1.11 christos goto cleanup; 607 1.11 christos } else if (result != ISC_R_SUCCESS) { 608 1.11 christos fatal("unable to open journal %s: %s\n", file, 609 1.11 christos isc_result_totext(result)); 610 1.11 christos } 611 1.11 christos 612 1.11 christos if (dns_journal_empty(jnl)) { 613 1.11 christos dns_journal_destroy(&jnl); 614 1.11 christos return; 615 1.11 christos } 616 1.11 christos 617 1.11 christos result = dns_journal_rollforward(jnl, db, 0); 618 1.11 christos switch (result) { 619 1.11 christos case ISC_R_SUCCESS: 620 1.11 christos case DNS_R_UPTODATE: 621 1.11 christos break; 622 1.11 christos 623 1.11 christos case ISC_R_NOTFOUND: 624 1.11 christos case ISC_R_RANGE: 625 1.11 christos fatal("journal %s out of sync with zone", file); 626 1.11 christos 627 1.11 christos default: 628 1.11 christos fatal("journal %s: %s\n", file, isc_result_totext(result)); 629 1.11 christos } 630 1.11 christos 631 1.11 christos cleanup: 632 1.11 christos dns_journal_destroy(&jnl); 633 1.11 christos } 634 1.11 christos 635 1.11 christos void 636 1.11 christos kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, isc_log_t *lctx, 637 1.11 christos const char *name, const char *keydir, const char *engine, 638 1.11 christos dns_kasp_t **kaspp) { 639 1.11 christos isc_result_t result = ISC_R_NOTFOUND; 640 1.11 christos const cfg_listelt_t *element; 641 1.11 christos const cfg_obj_t *kasps = NULL; 642 1.11 christos dns_kasp_t *kasp = NULL, *kasp_next; 643 1.11 christos dns_kasplist_t kasplist; 644 1.11 christos const cfg_obj_t *keystores = NULL; 645 1.11 christos dns_keystore_t *ks = NULL, *ks_next; 646 1.11 christos dns_keystorelist_t kslist; 647 1.13 christos unsigned int options = (ISCCFG_KASPCONF_CHECK_ALGORITHMS | 648 1.13 christos ISCCFG_KASPCONF_CHECK_KEYLIST | 649 1.13 christos ISCCFG_KASPCONF_LOG_ERRORS); 650 1.11 christos 651 1.11 christos ISC_LIST_INIT(kasplist); 652 1.11 christos ISC_LIST_INIT(kslist); 653 1.11 christos 654 1.14 christos /* Default key-directory key store. */ 655 1.14 christos result = cfg_keystore_fromconfig(NULL, mctx, lctx, engine, &kslist, 656 1.14 christos &ks); 657 1.14 christos if (result != ISC_R_SUCCESS) { 658 1.14 christos fatal("failed to configure default key-directory key-store: %s", 659 1.14 christos isc_result_totext(result)); 660 1.14 christos } 661 1.14 christos 662 1.11 christos (void)cfg_map_get(config, "key-store", &keystores); 663 1.11 christos for (element = cfg_list_first(keystores); element != NULL; 664 1.11 christos element = cfg_list_next(element)) 665 1.11 christos { 666 1.11 christos cfg_obj_t *kconfig = cfg_listelt_value(element); 667 1.11 christos result = cfg_keystore_fromconfig(kconfig, mctx, lctx, engine, 668 1.11 christos &kslist, NULL); 669 1.11 christos if (result != ISC_R_SUCCESS) { 670 1.11 christos fatal("failed to configure key-store '%s': %s", 671 1.11 christos cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), 672 1.11 christos isc_result_totext(result)); 673 1.11 christos } 674 1.11 christos } 675 1.14 christos 676 1.11 christos INSIST(ks != NULL); 677 1.11 christos if (keydir != NULL) { 678 1.11 christos /* '-K keydir' takes priority */ 679 1.11 christos dns_keystore_setdirectory(ks, keydir); 680 1.11 christos } 681 1.11 christos dns_keystore_detach(&ks); 682 1.11 christos 683 1.11 christos (void)cfg_map_get(config, "dnssec-policy", &kasps); 684 1.11 christos for (element = cfg_list_first(kasps); element != NULL; 685 1.11 christos element = cfg_list_next(element)) 686 1.11 christos { 687 1.11 christos cfg_obj_t *kconfig = cfg_listelt_value(element); 688 1.11 christos kasp = NULL; 689 1.11 christos if (strcmp(cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), 690 1.11 christos name) != 0) 691 1.11 christos { 692 1.11 christos continue; 693 1.11 christos } 694 1.11 christos 695 1.13 christos result = cfg_kasp_fromconfig(kconfig, NULL, options, mctx, lctx, 696 1.11 christos &kslist, &kasplist, &kasp); 697 1.11 christos if (result != ISC_R_SUCCESS) { 698 1.11 christos fatal("failed to configure dnssec-policy '%s': %s", 699 1.11 christos cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), 700 1.11 christos isc_result_totext(result)); 701 1.11 christos } 702 1.11 christos INSIST(kasp != NULL); 703 1.11 christos dns_kasp_freeze(kasp); 704 1.11 christos break; 705 1.11 christos } 706 1.11 christos 707 1.11 christos *kaspp = kasp; 708 1.11 christos 709 1.11 christos /* 710 1.11 christos * Cleanup kasp list. 711 1.11 christos */ 712 1.11 christos for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; kasp = kasp_next) { 713 1.11 christos kasp_next = ISC_LIST_NEXT(kasp, link); 714 1.11 christos ISC_LIST_UNLINK(kasplist, kasp, link); 715 1.11 christos dns_kasp_detach(&kasp); 716 1.11 christos } 717 1.11 christos 718 1.11 christos /* 719 1.11 christos * Cleanup keystore list. 720 1.11 christos */ 721 1.11 christos for (ks = ISC_LIST_HEAD(kslist); ks != NULL; ks = ks_next) { 722 1.11 christos ks_next = ISC_LIST_NEXT(ks, link); 723 1.11 christos ISC_LIST_UNLINK(kslist, ks, link); 724 1.11 christos dns_keystore_detach(&ks); 725 1.1 christos } 726 1.1 christos } 727