Update repub branch u/fanf2/patch to rebasing branch u/fanf2/rebasing revision v9_15_...
[ipreg/bind9.git] / bin / dnssec / dnssectool.c
CommitLineData
b1d234eb 1/*
843d3896 2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
40f53fa8 3 *
0c27b3fe
MA
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
843d3896
OS
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
b1d234eb
BW
10 */
11
ab023a65
RA
12/*! \file */
13
14/*%
15 * DNSSEC Support Routines.
16 */
9c3531d7 17
cb6a185c 18#include <inttypes.h>
994e6569 19#include <stdbool.h>
b1d234eb
BW
20#include <stdlib.h>
21
358dfaee
WK
22#ifdef _WIN32
23#include <Winsock2.h>
24#endif
25
ad127d83 26#include <isc/base32.h>
b1d234eb 27#include <isc/buffer.h>
e939674d 28#include <isc/commandline.h>
8f0502e9 29#include <isc/dir.h>
5ac42705 30#include <isc/file.h>
ad127d83 31#include <isc/heap.h>
3f543c37
BW
32#include <isc/list.h>
33#include <isc/mem.h>
586e65ea 34#include <isc/platform.h>
e939674d 35#include <isc/print.h>
b1d234eb 36#include <isc/string.h>
49d233a0 37#include <isc/time.h>
b6a65886 38#include <isc/tm.h>
b1d234eb
BW
39#include <isc/util.h>
40
ad127d83
MA
41#include <dns/db.h>
42#include <dns/dbiterator.h>
8a198fa7 43#include <dns/dnssec.h>
ad127d83 44#include <dns/fixedname.h>
8a198fa7 45#include <dns/keyvalues.h>
b1d234eb
BW
46#include <dns/log.h>
47#include <dns/name.h>
ad127d83
MA
48#include <dns/nsec.h>
49#include <dns/nsec3.h>
565d0dc2 50#include <dns/rdatastruct.h>
39504d45 51#include <dns/rdataclass.h>
ad127d83
MA
52#include <dns/rdataset.h>
53#include <dns/rdatasetiter.h>
b1d234eb
BW
54#include <dns/rdatatype.h>
55#include <dns/result.h>
56#include <dns/secalg.h>
42848e71 57#include <dns/time.h>
b1d234eb
BW
58
59#include "dnssectool.h"
60
a177b07d 61int verbose;
796a6c4e 62uint8_t dtype[8];
b1d234eb 63
c9c630ee 64static fatalcallback_t *fatalcallback = NULL;
49d233a0 65
b1d234eb 66void
7efc8c3f 67fatal(const char *format, ...) {
b1d234eb
BW
68 va_list args;
69
2534a73a 70 fprintf(stderr, "%s: fatal: ", program);
b1d234eb
BW
71 va_start(args, format);
72 vfprintf(stderr, format, args);
73 va_end(args);
74 fprintf(stderr, "\n");
c9c630ee
BW
75 if (fatalcallback != NULL)
76 (*fatalcallback)();
b1d234eb
BW
77 exit(1);
78}
79
c9c630ee
BW
80void
81setfatalcallback(fatalcallback_t *callback) {
82 fatalcallback = callback;
83}
84
b1d234eb 85void
7efc8c3f 86check_result(isc_result_t result, const char *message) {
8771fbe4
BW
87 if (result != ISC_R_SUCCESS)
88 fatal("%s: %s", message, isc_result_totext(result));
b1d234eb
BW
89}
90
91void
92vbprintf(int level, const char *fmt, ...) {
93 va_list ap;
94 if (level > verbose)
95 return;
96 va_start(ap, fmt);
97 fprintf(stderr, "%s: ", program);
98 vfprintf(stderr, fmt, ap);
99 va_end(ap);
100}
101
42782931 102void
11463c0a
EH
103version(const char *name) {
104 fprintf(stderr, "%s %s\n", name, VERSION);
42782931
MS
105 exit(0);
106}
107
4fc4813e 108void
93d6dfaf 109sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) {
4fc4813e
BW
110 char namestr[DNS_NAME_FORMATSIZE];
111 char algstr[DNS_NAME_FORMATSIZE];
565d0dc2 112
2f734e0a 113 dns_name_format(&sig->signer, namestr, sizeof(namestr));
77b8f88f 114 dns_secalg_format(sig->algorithm, algstr, sizeof(algstr));
4fc4813e
BW
115 snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid);
116}
565d0dc2 117
b1d234eb 118void
11463c0a 119setup_logging(isc_mem_t *mctx, isc_log_t **logp) {
b1d234eb
BW
120 isc_result_t result;
121 isc_logdestination_t destination;
3955d85b
AG
122 isc_logconfig_t *logconfig = NULL;
123 isc_log_t *log = NULL;
b1d234eb
BW
124 int level;
125
7c386b0c
MA
126 if (verbose < 0)
127 verbose = 0;
b1d234eb 128 switch (verbose) {
78838d3e
BW
129 case 0:
130 /*
b1d234eb
BW
131 * We want to see warnings about things like out-of-zone
132 * data in the master file even when not verbose.
133 */
134 level = ISC_LOG_WARNING;
135 break;
136 case 1:
137 level = ISC_LOG_INFO;
138 break;
139 default:
140 level = ISC_LOG_DEBUG(verbose - 2 + 1);
141 break;
142 }
40f53fa8 143
b1d234eb
BW
144 RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
145 isc_log_setcontext(log);
146 dns_log_init(log);
147 dns_log_setcontext(log);
148
7efc8c3f
DL
149 RUNTIME_CHECK(isc_log_settag(logconfig, program) == ISC_R_SUCCESS);
150
b1d234eb
BW
151 /*
152 * Set up a channel similar to default_stderr except:
153 * - the logging level is passed in
154 * - the program name and logging level are printed
155 * - no time stamp is printed
156 */
157 destination.file.stream = stderr;
158 destination.file.name = NULL;
159 destination.file.versions = ISC_LOG_ROLLNEVER;
160 destination.file.maximum_size = 0;
161 result = isc_log_createchannel(logconfig, "stderr",
162 ISC_LOG_TOFILEDESC,
163 level,
164 &destination,
165 ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL);
166 check_result(result, "isc_log_createchannel()");
40f53fa8 167
b1d234eb
BW
168 RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
169 NULL, NULL) == ISC_R_SUCCESS);
170
171 *logp = log;
172}
173
db503b6d
BW
174void
175cleanup_logging(isc_log_t **logp) {
176 isc_log_t *log;
177
178 REQUIRE(logp != NULL);
179
180 log = *logp;
89ff72be
MA
181 *logp = NULL;
182
db503b6d
BW
183 if (log == NULL)
184 return;
89ff72be 185
db503b6d
BW
186 isc_log_destroy(&log);
187 isc_log_setcontext(NULL);
188 dns_log_setcontext(NULL);
db503b6d
BW
189}
190
26d8ffe7 191static isc_stdtime_t
eab9975b
EH
192time_units(isc_stdtime_t offset, char *suffix, const char *str) {
193 switch (suffix[0]) {
553ead32
EH
194 case 'Y': case 'y':
195 return (offset * (365 * 24 * 3600));
196 case 'M': case 'm':
eab9975b
EH
197 switch (suffix[1]) {
198 case 'O': case 'o':
199 return (offset * (30 * 24 * 3600));
200 case 'I': case 'i':
201 return (offset * 60);
202 case '\0':
203 fatal("'%s' ambiguous: use 'mi' for minutes "
204 "or 'mo' for months", str);
205 default:
206 fatal("time value %s is invalid", str);
207 }
12107993 208 /* NOTREACHED */
eab9975b 209 break;
553ead32
EH
210 case 'W': case 'w':
211 return (offset * (7 * 24 * 3600));
212 case 'D': case 'd':
213 return (offset * (24 * 3600));
214 case 'H': case 'h':
215 return (offset * 3600);
216 case 'S': case 's': case '\0':
217 return (offset);
218 default:
219 fatal("time value %s is invalid", str);
220 }
12107993 221 /* NOTREACHED */
553ead32
EH
222 return(0); /* silence compiler warning */
223}
224
994e6569 225static inline bool
a165a17a 226isnone(const char *str) {
994e6569
OS
227 return ((strcasecmp(str, "none") == 0) ||
228 (strcasecmp(str, "never") == 0));
a165a17a
EH
229}
230
eab9975b
EH
231dns_ttl_t
232strtottl(const char *str) {
233 const char *orig = str;
234 dns_ttl_t ttl;
235 char *endp;
236
a165a17a
EH
237 if (isnone(str))
238 return ((dns_ttl_t) 0);
239
eab9975b 240 ttl = strtol(str, &endp, 0);
d7201de0
AU
241 if (ttl == 0 && endp == str)
242 fatal("TTL must be numeric");
eab9975b
EH
243 ttl = time_units(ttl, endp, orig);
244 return (ttl);
245}
246
42848e71 247isc_stdtime_t
cb6a185c 248strtotime(const char *str, int64_t now, int64_t base,
994e6569 249 bool *setp)
a165a17a 250{
cb6a185c 251 int64_t val, offset;
42848e71 252 isc_result_t result;
553ead32 253 const char *orig = str;
42848e71 254 char *endp;
dbb01276 255 size_t n;
b6a65886 256 struct tm tm;
42848e71 257
a165a17a
EH
258 if (isnone(str)) {
259 if (setp != NULL)
994e6569 260 *setp = false;
a165a17a
EH
261 return ((isc_stdtime_t) 0);
262 }
263
264 if (setp != NULL)
994e6569 265 *setp = true;
a165a17a 266
50eab6c2 267 if ((str[0] == '0' || str[0] == '-') && str[1] == '\0')
553ead32
EH
268 return ((isc_stdtime_t) 0);
269
7ace3277
MA
270 /*
271 * We accept times in the following formats:
272 * now([+-]offset)
273 * YYYYMMDD([+-]offset)
274 * YYYYMMDDhhmmss([+-]offset)
b6a65886 275 * Dow Mon DD HH:MM:SS YYYY
7ace3277
MA
276 * [+-]offset
277 */
278 n = strspn(str, "0123456789");
5deedd70 279 if ((n == 8u || n == 14u) &&
7ace3277
MA
280 (str[n] == '\0' || str[n] == '-' || str[n] == '+'))
281 {
282 char timestr[15];
283
284 strlcpy(timestr, str, sizeof(timestr));
285 timestr[n] = 0;
5deedd70 286 if (n == 8u)
7ace3277
MA
287 strlcat(timestr, "000000", sizeof(timestr));
288 result = dns_time64_fromtext(timestr, &val);
289 if (result != ISC_R_SUCCESS)
290 fatal("time value %s is invalid: %s", orig,
291 isc_result_totext(result));
292 base = val;
293 str += n;
294 } else if (strncmp(str, "now", 3) == 0) {
553ead32
EH
295 base = now;
296 str += 3;
b6a65886
TF
297 } else {
298 /* parse ctime() format as written by `dnssec-settime -p` */
299 endp = isc_tm_strptime(str, "%a %b %d %H:%M:%S %Y", &tm);
300 if (endp != str + 24) {
301 fatal("time value %s is invalid", orig);
302 }
303 base = mktime(&tm);
553ead32
EH
304 }
305
306 if (str[0] == '\0')
307 return ((isc_stdtime_t) base);
308 else if (str[0] == '+') {
42848e71 309 offset = strtol(str + 1, &endp, 0);
9f8d002a 310 offset = time_units((isc_stdtime_t) offset, endp, orig);
42848e71 311 val = base + offset;
553ead32
EH
312 } else if (str[0] == '-') {
313 offset = strtol(str + 1, &endp, 0);
9f8d002a 314 offset = time_units((isc_stdtime_t) offset, endp, orig);
553ead32 315 val = base - offset;
7cd9d53c
MA
316 } else
317 fatal("time value %s is invalid", orig);
42848e71
BW
318
319 return ((isc_stdtime_t) val);
320}
39504d45
BW
321
322dns_rdataclass_t
323strtoclass(const char *str) {
daa73eae 324 isc_textregion_t r;
39504d45
BW
325 dns_rdataclass_t rdclass;
326 isc_result_t ret;
327
328 if (str == NULL)
329 return dns_rdataclass_in;
daa73eae 330 DE_CONST(str, r.base);
39504d45 331 r.length = strlen(str);
daa73eae 332 ret = dns_rdataclass_fromtext(&rdclass, &r);
39504d45
BW
333 if (ret != ISC_R_SUCCESS)
334 fatal("unknown class %s", str);
335 return (rdclass);
336}
8f0502e9 337
ba37674d
EH
338unsigned int
339strtodsdigest(const char *algname) {
340 if (strcasecmp(algname, "SHA1") == 0 ||
341 strcasecmp(algname, "SHA-1") == 0)
342 {
343 return (DNS_DSDIGEST_SHA1);
344 } else if (strcasecmp(algname, "SHA256") == 0 ||
345 strcasecmp(algname, "SHA-256") == 0)
346 {
347 return (DNS_DSDIGEST_SHA256);
ba37674d
EH
348 } else if (strcasecmp(algname, "SHA384") == 0 ||
349 strcasecmp(algname, "SHA-384") == 0)
350 {
351 return (DNS_DSDIGEST_SHA384);
352 } else {
353 fatal("unknown algorithm %s", algname);
354 }
355}
356
796a6c4e
TF
357static int
358cmp_dtype(const void *ap, const void *bp) {
359 int a = *(const uint8_t *)ap;
360 int b = *(const uint8_t *)bp;
361 return (a - b);
362}
363
364void
365add_dtype(unsigned int dt) {
366 unsigned i, n;
367
368 /* ensure there is space for a zero terminator */
369 n = sizeof(dtype)/sizeof(dtype[0]) - 1;
370 for (i = 0; i < n; i++) {
371 if (dtype[i] == dt) {
372 return;
373 }
374 if (dtype[i] == 0) {
375 dtype[i] = dt;
376 qsort(dtype, i+1, 1, cmp_dtype);
377 return;
378 }
379 }
380 fatal("too many -a digest type arguments");
381}
382
8f0502e9
EH
383isc_result_t
384try_dir(const char *dirname) {
385 isc_result_t result;
386 isc_dir_t d;
387
388 isc_dir_init(&d);
389 result = isc_dir_open(&d, dirname);
390 if (result == ISC_R_SUCCESS) {
391 isc_dir_close(&d);
392 }
393 return (result);
394}
c0214996
EH
395
396/*
397 * Check private key version compatibility.
398 */
399void
400check_keyversion(dst_key_t *key, char *keystr) {
401 int major, minor;
402 dst_key_getprivateformat(key, &major, &minor);
5f744ebb 403 INSIST(major <= DST_MAJOR_VERSION); /* invalid private key */
c0214996 404
5f744ebb 405 if (major < DST_MAJOR_VERSION || minor < DST_MINOR_VERSION)
c0214996
EH
406 fatal("Key %s has incompatible format version %d.%d, "
407 "use -f to force upgrade to new version.",
408 keystr, major, minor);
409 if (minor > DST_MINOR_VERSION)
410 fatal("Key %s has incompatible format version %d.%d, "
411 "use -f to force downgrade to current version.",
412 keystr, major, minor);
413}
414
415void
416set_keyversion(dst_key_t *key) {
417 int major, minor;
418 dst_key_getprivateformat(key, &major, &minor);
5f744ebb 419 INSIST(major <= DST_MAJOR_VERSION);
c0214996
EH
420
421 if (major != DST_MAJOR_VERSION || minor != DST_MINOR_VERSION)
422 dst_key_setprivateformat(key, DST_MAJOR_VERSION,
423 DST_MINOR_VERSION);
424
425 /*
426 * If the key is from a version older than 1.3, set
427 * set the creation date
428 */
429 if (major < 1 || (major == 1 && minor <= 2)) {
430 isc_stdtime_t now;
431 isc_stdtime_get(&now);
432 dst_key_settime(key, DST_TIME_CREATED, now);
433 }
434}
8a198fa7 435
994e6569 436bool
1946c596 437key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir,
994e6569 438 isc_mem_t *mctx, bool *exact)
8a198fa7
EH
439{
440 isc_result_t result;
994e6569 441 bool conflict = false;
8a198fa7
EH
442 dns_dnsseckeylist_t matchkeys;
443 dns_dnsseckey_t *key = NULL;
cb6a185c
OS
444 uint16_t id, oldid;
445 uint32_t rid, roldid;
1946c596 446 dns_secalg_t alg;
f0f71420 447 char filename[NAME_MAX];
5ac42705 448 isc_buffer_t fileb;
25cd3168 449 isc_stdtime_t now;
8a198fa7
EH
450
451 if (exact != NULL)
994e6569 452 *exact = false;
dfc015bc 453
1946c596
MA
454 id = dst_key_id(dstkey);
455 rid = dst_key_rid(dstkey);
456 alg = dst_key_alg(dstkey);
8a198fa7 457
5ac42705 458 /*
80788e72
EH
459 * For Diffie Hellman just check if there is a direct collision as
460 * they can't be revoked. Additionally dns_dnssec_findmatchingkeys
461 * only handles DNSKEY which is not used for HMAC.
5ac42705 462 */
80788e72 463 if (alg == DST_ALG_DH) {
5ac42705
MA
464 isc_buffer_init(&fileb, filename, sizeof(filename));
465 result = dst_key_buildfilename(dstkey, DST_TYPE_PRIVATE,
466 dir, &fileb);
467 if (result != ISC_R_SUCCESS)
994e6569 468 return (true);
5ac42705
MA
469 return (isc_file_exists(filename));
470 }
471
8a198fa7 472 ISC_LIST_INIT(matchkeys);
25cd3168
WK
473 isc_stdtime_get(&now);
474 result = dns_dnssec_findmatchingkeys(name, dir, now, mctx, &matchkeys);
8a198fa7 475 if (result == ISC_R_NOTFOUND)
994e6569 476 return (false);
8a198fa7
EH
477
478 while (!ISC_LIST_EMPTY(matchkeys) && !conflict) {
479 key = ISC_LIST_HEAD(matchkeys);
480 if (dst_key_alg(key->key) != alg)
481 goto next;
482
483 oldid = dst_key_id(key->key);
1946c596
MA
484 roldid = dst_key_rid(key->key);
485
486 if (oldid == rid || roldid == id || id == oldid) {
994e6569 487 conflict = true;
1946c596 488 if (id != oldid) {
8a198fa7 489 if (verbose > 1)
ca4e44eb 490 fprintf(stderr, "Key ID %d could "
8a198fa7
EH
491 "collide with %d\n",
492 id, oldid);
493 } else {
494 if (exact != NULL)
994e6569 495 *exact = true;
8a198fa7 496 if (verbose > 1)
ca4e44eb 497 fprintf(stderr, "Key ID %d exists\n",
8a198fa7 498 id);
ca4e44eb 499 }
8a198fa7
EH
500 }
501
502 next:
503 ISC_LIST_UNLINK(matchkeys, key, link);
504 dns_dnsseckey_destroy(mctx, &key);
505 }
506
ca4e44eb 507 /* Finish freeing the list */
8a198fa7
EH
508 while (!ISC_LIST_EMPTY(matchkeys)) {
509 key = ISC_LIST_HEAD(matchkeys);
510 ISC_LIST_UNLINK(matchkeys, key, link);
511 dns_dnsseckey_destroy(mctx, &key);
512 }
513
514 return (conflict);
515}
ad127d83 516
994e6569 517bool
e939674d
MA
518isoptarg(const char *arg, char **argv, void(*usage)(void)) {
519 if (!strcasecmp(isc_commandline_argument, arg)) {
520 if (argv[isc_commandline_index] == NULL) {
521 fprintf(stderr, "%s: missing argument -%c %s\n",
522 program, isc_commandline_option,
523 isc_commandline_argument);
524 usage();
525 }
526 isc_commandline_argument = argv[isc_commandline_index];
527 /* skip to next arguement */
528 isc_commandline_index++;
994e6569 529 return (true);
e939674d 530 }
994e6569 531 return (false);
e939674d 532}
358dfaee
WK
533
534#ifdef _WIN32
535void
536InitSockets(void) {
537 WORD wVersionRequested;
538 WSADATA wsaData;
539 int err;
540
541 wVersionRequested = MAKEWORD(2, 0);
542
543 err = WSAStartup( wVersionRequested, &wsaData );
544 if (err != 0) {
545 fprintf(stderr, "WSAStartup() failed: %d\n", err);
546 exit(1);
547 }
548}
549
550void
551DestroySockets(void) {
552 WSACleanup();
553}
554#endif