Update repub branch u/fanf2/patch to rebasing branch u/fanf2/rebasing revision v9_15_...
[ipreg/bind9.git] / bin / dnssec / dnssectool.c
1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
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/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12 /*! \file */
13
14 /*%
15 * DNSSEC Support Routines.
16 */
17
18 #include <inttypes.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21
22 #ifdef _WIN32
23 #include <Winsock2.h>
24 #endif
25
26 #include <isc/base32.h>
27 #include <isc/buffer.h>
28 #include <isc/commandline.h>
29 #include <isc/dir.h>
30 #include <isc/file.h>
31 #include <isc/heap.h>
32 #include <isc/list.h>
33 #include <isc/mem.h>
34 #include <isc/platform.h>
35 #include <isc/print.h>
36 #include <isc/string.h>
37 #include <isc/time.h>
38 #include <isc/tm.h>
39 #include <isc/util.h>
40
41 #include <dns/db.h>
42 #include <dns/dbiterator.h>
43 #include <dns/dnssec.h>
44 #include <dns/fixedname.h>
45 #include <dns/keyvalues.h>
46 #include <dns/log.h>
47 #include <dns/name.h>
48 #include <dns/nsec.h>
49 #include <dns/nsec3.h>
50 #include <dns/rdatastruct.h>
51 #include <dns/rdataclass.h>
52 #include <dns/rdataset.h>
53 #include <dns/rdatasetiter.h>
54 #include <dns/rdatatype.h>
55 #include <dns/result.h>
56 #include <dns/secalg.h>
57 #include <dns/time.h>
58
59 #include "dnssectool.h"
60
61 int verbose;
62 uint8_t dtype[8];
63
64 static fatalcallback_t *fatalcallback = NULL;
65
66 void
67 fatal(const char *format, ...) {
68 va_list args;
69
70 fprintf(stderr, "%s: fatal: ", program);
71 va_start(args, format);
72 vfprintf(stderr, format, args);
73 va_end(args);
74 fprintf(stderr, "\n");
75 if (fatalcallback != NULL)
76 (*fatalcallback)();
77 exit(1);
78 }
79
80 void
81 setfatalcallback(fatalcallback_t *callback) {
82 fatalcallback = callback;
83 }
84
85 void
86 check_result(isc_result_t result, const char *message) {
87 if (result != ISC_R_SUCCESS)
88 fatal("%s: %s", message, isc_result_totext(result));
89 }
90
91 void
92 vbprintf(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
102 void
103 version(const char *name) {
104 fprintf(stderr, "%s %s\n", name, VERSION);
105 exit(0);
106 }
107
108 void
109 sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) {
110 char namestr[DNS_NAME_FORMATSIZE];
111 char algstr[DNS_NAME_FORMATSIZE];
112
113 dns_name_format(&sig->signer, namestr, sizeof(namestr));
114 dns_secalg_format(sig->algorithm, algstr, sizeof(algstr));
115 snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid);
116 }
117
118 void
119 setup_logging(isc_mem_t *mctx, isc_log_t **logp) {
120 isc_result_t result;
121 isc_logdestination_t destination;
122 isc_logconfig_t *logconfig = NULL;
123 isc_log_t *log = NULL;
124 int level;
125
126 if (verbose < 0)
127 verbose = 0;
128 switch (verbose) {
129 case 0:
130 /*
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 }
143
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
149 RUNTIME_CHECK(isc_log_settag(logconfig, program) == ISC_R_SUCCESS);
150
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()");
167
168 RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
169 NULL, NULL) == ISC_R_SUCCESS);
170
171 *logp = log;
172 }
173
174 void
175 cleanup_logging(isc_log_t **logp) {
176 isc_log_t *log;
177
178 REQUIRE(logp != NULL);
179
180 log = *logp;
181 *logp = NULL;
182
183 if (log == NULL)
184 return;
185
186 isc_log_destroy(&log);
187 isc_log_setcontext(NULL);
188 dns_log_setcontext(NULL);
189 }
190
191 static isc_stdtime_t
192 time_units(isc_stdtime_t offset, char *suffix, const char *str) {
193 switch (suffix[0]) {
194 case 'Y': case 'y':
195 return (offset * (365 * 24 * 3600));
196 case 'M': case 'm':
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 }
208 /* NOTREACHED */
209 break;
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 }
221 /* NOTREACHED */
222 return(0); /* silence compiler warning */
223 }
224
225 static inline bool
226 isnone(const char *str) {
227 return ((strcasecmp(str, "none") == 0) ||
228 (strcasecmp(str, "never") == 0));
229 }
230
231 dns_ttl_t
232 strtottl(const char *str) {
233 const char *orig = str;
234 dns_ttl_t ttl;
235 char *endp;
236
237 if (isnone(str))
238 return ((dns_ttl_t) 0);
239
240 ttl = strtol(str, &endp, 0);
241 if (ttl == 0 && endp == str)
242 fatal("TTL must be numeric");
243 ttl = time_units(ttl, endp, orig);
244 return (ttl);
245 }
246
247 isc_stdtime_t
248 strtotime(const char *str, int64_t now, int64_t base,
249 bool *setp)
250 {
251 int64_t val, offset;
252 isc_result_t result;
253 const char *orig = str;
254 char *endp;
255 size_t n;
256 struct tm tm;
257
258 if (isnone(str)) {
259 if (setp != NULL)
260 *setp = false;
261 return ((isc_stdtime_t) 0);
262 }
263
264 if (setp != NULL)
265 *setp = true;
266
267 if ((str[0] == '0' || str[0] == '-') && str[1] == '\0')
268 return ((isc_stdtime_t) 0);
269
270 /*
271 * We accept times in the following formats:
272 * now([+-]offset)
273 * YYYYMMDD([+-]offset)
274 * YYYYMMDDhhmmss([+-]offset)
275 * Dow Mon DD HH:MM:SS YYYY
276 * [+-]offset
277 */
278 n = strspn(str, "0123456789");
279 if ((n == 8u || n == 14u) &&
280 (str[n] == '\0' || str[n] == '-' || str[n] == '+'))
281 {
282 char timestr[15];
283
284 strlcpy(timestr, str, sizeof(timestr));
285 timestr[n] = 0;
286 if (n == 8u)
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) {
295 base = now;
296 str += 3;
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);
304 }
305
306 if (str[0] == '\0')
307 return ((isc_stdtime_t) base);
308 else if (str[0] == '+') {
309 offset = strtol(str + 1, &endp, 0);
310 offset = time_units((isc_stdtime_t) offset, endp, orig);
311 val = base + offset;
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
317 fatal("time value %s is invalid", orig);
318
319 return ((isc_stdtime_t) val);
320 }
321
322 dns_rdataclass_t
323 strtoclass(const char *str) {
324 isc_textregion_t r;
325 dns_rdataclass_t rdclass;
326 isc_result_t ret;
327
328 if (str == NULL)
329 return dns_rdataclass_in;
330 DE_CONST(str, r.base);
331 r.length = strlen(str);
332 ret = dns_rdataclass_fromtext(&rdclass, &r);
333 if (ret != ISC_R_SUCCESS)
334 fatal("unknown class %s", str);
335 return (rdclass);
336 }
337
338 unsigned int
339 strtodsdigest(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);
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
357 static int
358 cmp_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
364 void
365 add_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
383 isc_result_t
384 try_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 }
395
396 /*
397 * Check private key version compatibility.
398 */
399 void
400 check_keyversion(dst_key_t *key, char *keystr) {
401 int major, minor;
402 dst_key_getprivateformat(key, &major, &minor);
403 INSIST(major <= DST_MAJOR_VERSION); /* invalid private key */
404
405 if (major < DST_MAJOR_VERSION || minor < DST_MINOR_VERSION)
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
415 void
416 set_keyversion(dst_key_t *key) {
417 int major, minor;
418 dst_key_getprivateformat(key, &major, &minor);
419 INSIST(major <= DST_MAJOR_VERSION);
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 }
435
436 bool
437 key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir,
438 isc_mem_t *mctx, bool *exact)
439 {
440 isc_result_t result;
441 bool conflict = false;
442 dns_dnsseckeylist_t matchkeys;
443 dns_dnsseckey_t *key = NULL;
444 uint16_t id, oldid;
445 uint32_t rid, roldid;
446 dns_secalg_t alg;
447 char filename[NAME_MAX];
448 isc_buffer_t fileb;
449 isc_stdtime_t now;
450
451 if (exact != NULL)
452 *exact = false;
453
454 id = dst_key_id(dstkey);
455 rid = dst_key_rid(dstkey);
456 alg = dst_key_alg(dstkey);
457
458 /*
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.
462 */
463 if (alg == DST_ALG_DH) {
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)
468 return (true);
469 return (isc_file_exists(filename));
470 }
471
472 ISC_LIST_INIT(matchkeys);
473 isc_stdtime_get(&now);
474 result = dns_dnssec_findmatchingkeys(name, dir, now, mctx, &matchkeys);
475 if (result == ISC_R_NOTFOUND)
476 return (false);
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);
484 roldid = dst_key_rid(key->key);
485
486 if (oldid == rid || roldid == id || id == oldid) {
487 conflict = true;
488 if (id != oldid) {
489 if (verbose > 1)
490 fprintf(stderr, "Key ID %d could "
491 "collide with %d\n",
492 id, oldid);
493 } else {
494 if (exact != NULL)
495 *exact = true;
496 if (verbose > 1)
497 fprintf(stderr, "Key ID %d exists\n",
498 id);
499 }
500 }
501
502 next:
503 ISC_LIST_UNLINK(matchkeys, key, link);
504 dns_dnsseckey_destroy(mctx, &key);
505 }
506
507 /* Finish freeing the list */
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 }
516
517 bool
518 isoptarg(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++;
529 return (true);
530 }
531 return (false);
532 }
533
534 #ifdef _WIN32
535 void
536 InitSockets(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
550 void
551 DestroySockets(void) {
552 WSACleanup();
553 }
554 #endif