Home | History | Annotate | Line # | Download | only in dnssec
dnssectool.c revision 1.10
      1   1.8  christos /*	$NetBSD: dnssectool.c,v 1.10 2024/09/22 00:13:56 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.1  christos #include <isc/print.h>
     36   1.9  christos #include <isc/result.h>
     37   1.1  christos #include <isc/string.h>
     38   1.1  christos #include <isc/time.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.1  christos #include <dns/keyvalues.h>
     47   1.1  christos #include <dns/log.h>
     48   1.1  christos #include <dns/name.h>
     49   1.1  christos #include <dns/nsec.h>
     50   1.1  christos #include <dns/nsec3.h>
     51   1.1  christos #include <dns/rdataclass.h>
     52   1.1  christos #include <dns/rdataset.h>
     53   1.1  christos #include <dns/rdatasetiter.h>
     54   1.4  christos #include <dns/rdatastruct.h>
     55   1.1  christos #include <dns/rdatatype.h>
     56   1.1  christos #include <dns/secalg.h>
     57   1.1  christos #include <dns/time.h>
     58   1.1  christos 
     59   1.1  christos #include "dnssectool.h"
     60   1.1  christos 
     61   1.4  christos #define KEYSTATES_NVALUES 4
     62   1.4  christos static const char *keystates[KEYSTATES_NVALUES] = {
     63   1.4  christos 	"hidden",
     64   1.4  christos 	"rumoured",
     65   1.4  christos 	"omnipresent",
     66   1.4  christos 	"unretentive",
     67   1.4  christos };
     68   1.4  christos 
     69   1.4  christos int verbose = 0;
     70   1.4  christos bool quiet = false;
     71   1.9  christos dns_dsdigest_t dtype[8];
     72   1.1  christos 
     73   1.1  christos static fatalcallback_t *fatalcallback = NULL;
     74   1.1  christos 
     75   1.1  christos void
     76   1.1  christos fatal(const char *format, ...) {
     77   1.1  christos 	va_list args;
     78   1.1  christos 
     79   1.1  christos 	fprintf(stderr, "%s: fatal: ", program);
     80   1.1  christos 	va_start(args, format);
     81   1.1  christos 	vfprintf(stderr, format, args);
     82   1.1  christos 	va_end(args);
     83   1.1  christos 	fprintf(stderr, "\n");
     84   1.4  christos 	if (fatalcallback != NULL) {
     85   1.1  christos 		(*fatalcallback)();
     86   1.4  christos 	}
     87  1.10  christos 	_exit(EXIT_FAILURE);
     88   1.1  christos }
     89   1.1  christos 
     90   1.1  christos void
     91   1.1  christos setfatalcallback(fatalcallback_t *callback) {
     92   1.1  christos 	fatalcallback = callback;
     93   1.1  christos }
     94   1.1  christos 
     95   1.1  christos void
     96   1.1  christos check_result(isc_result_t result, const char *message) {
     97   1.4  christos 	if (result != ISC_R_SUCCESS) {
     98   1.1  christos 		fatal("%s: %s", message, isc_result_totext(result));
     99   1.4  christos 	}
    100   1.1  christos }
    101   1.1  christos 
    102   1.1  christos void
    103   1.1  christos vbprintf(int level, const char *fmt, ...) {
    104   1.1  christos 	va_list ap;
    105   1.4  christos 	if (level > verbose) {
    106   1.1  christos 		return;
    107   1.4  christos 	}
    108   1.1  christos 	va_start(ap, fmt);
    109   1.1  christos 	fprintf(stderr, "%s: ", program);
    110   1.1  christos 	vfprintf(stderr, fmt, ap);
    111   1.1  christos 	va_end(ap);
    112   1.1  christos }
    113   1.1  christos 
    114   1.1  christos void
    115   1.1  christos version(const char *name) {
    116   1.9  christos 	fprintf(stderr, "%s %s\n", name, PACKAGE_VERSION);
    117  1.10  christos 	exit(EXIT_SUCCESS);
    118   1.1  christos }
    119   1.1  christos 
    120   1.1  christos void
    121   1.1  christos sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) {
    122   1.1  christos 	char namestr[DNS_NAME_FORMATSIZE];
    123   1.1  christos 	char algstr[DNS_NAME_FORMATSIZE];
    124   1.1  christos 
    125   1.1  christos 	dns_name_format(&sig->signer, namestr, sizeof(namestr));
    126   1.1  christos 	dns_secalg_format(sig->algorithm, algstr, sizeof(algstr));
    127   1.1  christos 	snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid);
    128   1.1  christos }
    129   1.1  christos 
    130   1.1  christos void
    131   1.1  christos setup_logging(isc_mem_t *mctx, isc_log_t **logp) {
    132   1.1  christos 	isc_logdestination_t destination;
    133   1.1  christos 	isc_logconfig_t *logconfig = NULL;
    134   1.1  christos 	isc_log_t *log = NULL;
    135   1.1  christos 	int level;
    136   1.1  christos 
    137   1.4  christos 	if (verbose < 0) {
    138   1.1  christos 		verbose = 0;
    139   1.4  christos 	}
    140   1.1  christos 	switch (verbose) {
    141   1.1  christos 	case 0:
    142   1.1  christos 		/*
    143   1.1  christos 		 * We want to see warnings about things like out-of-zone
    144   1.1  christos 		 * data in the master file even when not verbose.
    145   1.1  christos 		 */
    146   1.1  christos 		level = ISC_LOG_WARNING;
    147   1.1  christos 		break;
    148   1.1  christos 	case 1:
    149   1.1  christos 		level = ISC_LOG_INFO;
    150   1.1  christos 		break;
    151   1.1  christos 	default:
    152   1.1  christos 		level = ISC_LOG_DEBUG(verbose - 2 + 1);
    153   1.1  christos 		break;
    154   1.1  christos 	}
    155   1.1  christos 
    156   1.4  christos 	isc_log_create(mctx, &log, &logconfig);
    157   1.1  christos 	isc_log_setcontext(log);
    158   1.1  christos 	dns_log_init(log);
    159   1.1  christos 	dns_log_setcontext(log);
    160   1.4  christos 	isc_log_settag(logconfig, program);
    161   1.1  christos 
    162   1.1  christos 	/*
    163   1.1  christos 	 * Set up a channel similar to default_stderr except:
    164   1.1  christos 	 *  - the logging level is passed in
    165   1.1  christos 	 *  - the program name and logging level are printed
    166   1.1  christos 	 *  - no time stamp is printed
    167   1.1  christos 	 */
    168   1.1  christos 	destination.file.stream = stderr;
    169   1.1  christos 	destination.file.name = NULL;
    170   1.1  christos 	destination.file.versions = ISC_LOG_ROLLNEVER;
    171   1.1  christos 	destination.file.maximum_size = 0;
    172   1.4  christos 	isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC, level,
    173   1.4  christos 			      &destination,
    174   1.4  christos 			      ISC_LOG_PRINTTAG | ISC_LOG_PRINTLEVEL);
    175   1.1  christos 
    176   1.4  christos 	RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr", NULL, NULL) ==
    177   1.4  christos 		      ISC_R_SUCCESS);
    178   1.1  christos 
    179   1.1  christos 	*logp = log;
    180   1.1  christos }
    181   1.1  christos 
    182   1.1  christos void
    183   1.1  christos cleanup_logging(isc_log_t **logp) {
    184   1.1  christos 	isc_log_t *log;
    185   1.1  christos 
    186   1.1  christos 	REQUIRE(logp != NULL);
    187   1.1  christos 
    188   1.1  christos 	log = *logp;
    189   1.1  christos 	*logp = NULL;
    190   1.1  christos 
    191   1.4  christos 	if (log == NULL) {
    192   1.1  christos 		return;
    193   1.4  christos 	}
    194   1.1  christos 
    195   1.1  christos 	isc_log_destroy(&log);
    196   1.1  christos 	isc_log_setcontext(NULL);
    197   1.1  christos 	dns_log_setcontext(NULL);
    198   1.1  christos }
    199   1.1  christos 
    200   1.1  christos static isc_stdtime_t
    201   1.1  christos time_units(isc_stdtime_t offset, char *suffix, const char *str) {
    202   1.1  christos 	switch (suffix[0]) {
    203   1.4  christos 	case 'Y':
    204   1.4  christos 	case 'y':
    205   1.1  christos 		return (offset * (365 * 24 * 3600));
    206   1.4  christos 	case 'M':
    207   1.4  christos 	case 'm':
    208   1.1  christos 		switch (suffix[1]) {
    209   1.4  christos 		case 'O':
    210   1.4  christos 		case 'o':
    211   1.1  christos 			return (offset * (30 * 24 * 3600));
    212   1.4  christos 		case 'I':
    213   1.4  christos 		case 'i':
    214   1.1  christos 			return (offset * 60);
    215   1.4  christos 		case '\0':
    216   1.1  christos 			fatal("'%s' ambiguous: use 'mi' for minutes "
    217   1.4  christos 			      "or 'mo' for months",
    218   1.4  christos 			      str);
    219   1.4  christos 		default:
    220   1.1  christos 			fatal("time value %s is invalid", str);
    221   1.1  christos 		}
    222   1.7  christos 		UNREACHABLE();
    223   1.1  christos 		break;
    224   1.4  christos 	case 'W':
    225   1.4  christos 	case 'w':
    226   1.1  christos 		return (offset * (7 * 24 * 3600));
    227   1.4  christos 	case 'D':
    228   1.4  christos 	case 'd':
    229   1.1  christos 		return (offset * (24 * 3600));
    230   1.4  christos 	case 'H':
    231   1.4  christos 	case 'h':
    232   1.1  christos 		return (offset * 3600);
    233   1.4  christos 	case 'S':
    234   1.4  christos 	case 's':
    235   1.4  christos 	case '\0':
    236   1.1  christos 		return (offset);
    237   1.4  christos 	default:
    238   1.1  christos 		fatal("time value %s is invalid", str);
    239   1.1  christos 	}
    240   1.7  christos 	UNREACHABLE();
    241   1.4  christos 	return (0); /* silence compiler warning */
    242   1.1  christos }
    243   1.1  christos 
    244   1.7  christos static bool
    245   1.1  christos isnone(const char *str) {
    246   1.3  christos 	return ((strcasecmp(str, "none") == 0) ||
    247   1.9  christos 		(strcasecmp(str, "never") == 0) ||
    248   1.9  christos 		(strcasecmp(str, "unset") == 0));
    249   1.1  christos }
    250   1.1  christos 
    251   1.1  christos dns_ttl_t
    252   1.1  christos strtottl(const char *str) {
    253   1.1  christos 	const char *orig = str;
    254   1.1  christos 	dns_ttl_t ttl;
    255   1.1  christos 	char *endp;
    256   1.1  christos 
    257   1.4  christos 	if (isnone(str)) {
    258   1.4  christos 		return ((dns_ttl_t)0);
    259   1.4  christos 	}
    260   1.1  christos 
    261   1.1  christos 	ttl = strtol(str, &endp, 0);
    262   1.4  christos 	if (ttl == 0 && endp == str) {
    263   1.1  christos 		fatal("TTL must be numeric");
    264   1.4  christos 	}
    265   1.1  christos 	ttl = time_units(ttl, endp, orig);
    266   1.1  christos 	return (ttl);
    267   1.1  christos }
    268   1.1  christos 
    269   1.4  christos dst_key_state_t
    270   1.4  christos strtokeystate(const char *str) {
    271   1.4  christos 	if (isnone(str)) {
    272   1.4  christos 		return (DST_KEY_STATE_NA);
    273   1.4  christos 	}
    274   1.4  christos 
    275   1.4  christos 	for (int i = 0; i < KEYSTATES_NVALUES; i++) {
    276   1.4  christos 		if (keystates[i] != NULL && strcasecmp(str, keystates[i]) == 0)
    277   1.4  christos 		{
    278   1.4  christos 			return ((dst_key_state_t)i);
    279   1.4  christos 		}
    280   1.4  christos 	}
    281   1.6  christos 	fatal("unknown key state %s", str);
    282   1.4  christos }
    283   1.4  christos 
    284   1.1  christos isc_stdtime_t
    285   1.4  christos strtotime(const char *str, int64_t now, int64_t base, bool *setp) {
    286   1.3  christos 	int64_t val, offset;
    287   1.1  christos 	isc_result_t result;
    288   1.1  christos 	const char *orig = str;
    289   1.1  christos 	char *endp;
    290   1.1  christos 	size_t n;
    291   1.9  christos 	struct tm tm;
    292   1.1  christos 
    293   1.1  christos 	if (isnone(str)) {
    294   1.4  christos 		if (setp != NULL) {
    295   1.3  christos 			*setp = false;
    296   1.4  christos 		}
    297   1.4  christos 		return ((isc_stdtime_t)0);
    298   1.1  christos 	}
    299   1.1  christos 
    300   1.4  christos 	if (setp != NULL) {
    301   1.3  christos 		*setp = true;
    302   1.4  christos 	}
    303   1.1  christos 
    304   1.4  christos 	if ((str[0] == '0' || str[0] == '-') && str[1] == '\0') {
    305   1.4  christos 		return ((isc_stdtime_t)0);
    306   1.4  christos 	}
    307   1.1  christos 
    308   1.1  christos 	/*
    309   1.1  christos 	 * We accept times in the following formats:
    310   1.1  christos 	 *   now([+-]offset)
    311   1.1  christos 	 *   YYYYMMDD([+-]offset)
    312   1.1  christos 	 *   YYYYMMDDhhmmss([+-]offset)
    313   1.9  christos 	 *   Day Mon DD HH:MM:SS YYYY([+-]offset)
    314   1.9  christos 	 *   1234567890([+-]offset)
    315   1.1  christos 	 *   [+-]offset
    316   1.1  christos 	 */
    317   1.1  christos 	n = strspn(str, "0123456789");
    318   1.1  christos 	if ((n == 8u || n == 14u) &&
    319   1.8  christos 	    (str[n] == '\0' || str[n] == '-' || str[n] == '+'))
    320   1.8  christos 	{
    321   1.1  christos 		char timestr[15];
    322   1.1  christos 
    323   1.1  christos 		strlcpy(timestr, str, sizeof(timestr));
    324   1.1  christos 		timestr[n] = 0;
    325   1.4  christos 		if (n == 8u) {
    326   1.1  christos 			strlcat(timestr, "000000", sizeof(timestr));
    327   1.4  christos 		}
    328   1.1  christos 		result = dns_time64_fromtext(timestr, &val);
    329   1.4  christos 		if (result != ISC_R_SUCCESS) {
    330   1.1  christos 			fatal("time value %s is invalid: %s", orig,
    331   1.1  christos 			      isc_result_totext(result));
    332   1.4  christos 		}
    333   1.1  christos 		base = val;
    334   1.1  christos 		str += n;
    335   1.9  christos 	} else if (n == 10u &&
    336   1.9  christos 		   (str[n] == '\0' || str[n] == '-' || str[n] == '+'))
    337   1.9  christos 	{
    338   1.9  christos 		base = strtoll(str, &endp, 0);
    339   1.9  christos 		str += 10;
    340   1.1  christos 	} else if (strncmp(str, "now", 3) == 0) {
    341   1.1  christos 		base = now;
    342   1.1  christos 		str += 3;
    343   1.9  christos 	} else if (str[0] >= 'A' && str[0] <= 'Z') {
    344   1.9  christos 		/* parse ctime() format as written by `dnssec-settime -p` */
    345   1.9  christos 		endp = isc_tm_strptime(str, "%a %b %d %H:%M:%S %Y", &tm);
    346   1.9  christos 		if (endp != str + 24) {
    347   1.9  christos 			fatal("time value %s is invalid", orig);
    348   1.9  christos 		}
    349   1.9  christos 		base = mktime(&tm);
    350   1.9  christos 		str += 24;
    351   1.1  christos 	}
    352   1.1  christos 
    353   1.4  christos 	if (str[0] == '\0') {
    354   1.4  christos 		return ((isc_stdtime_t)base);
    355   1.4  christos 	} else if (str[0] == '+') {
    356   1.1  christos 		offset = strtol(str + 1, &endp, 0);
    357   1.4  christos 		offset = time_units((isc_stdtime_t)offset, endp, orig);
    358   1.1  christos 		val = base + offset;
    359   1.1  christos 	} else if (str[0] == '-') {
    360   1.1  christos 		offset = strtol(str + 1, &endp, 0);
    361   1.4  christos 		offset = time_units((isc_stdtime_t)offset, endp, orig);
    362   1.1  christos 		val = base - offset;
    363   1.4  christos 	} else {
    364   1.1  christos 		fatal("time value %s is invalid", orig);
    365   1.4  christos 	}
    366   1.1  christos 
    367   1.4  christos 	return ((isc_stdtime_t)val);
    368   1.1  christos }
    369   1.1  christos 
    370   1.1  christos dns_rdataclass_t
    371   1.1  christos strtoclass(const char *str) {
    372   1.1  christos 	isc_textregion_t r;
    373   1.1  christos 	dns_rdataclass_t rdclass;
    374   1.4  christos 	isc_result_t result;
    375   1.1  christos 
    376   1.4  christos 	if (str == NULL) {
    377   1.4  christos 		return (dns_rdataclass_in);
    378   1.4  christos 	}
    379   1.1  christos 	DE_CONST(str, r.base);
    380   1.1  christos 	r.length = strlen(str);
    381   1.4  christos 	result = dns_rdataclass_fromtext(&rdclass, &r);
    382   1.4  christos 	if (result != ISC_R_SUCCESS) {
    383   1.1  christos 		fatal("unknown class %s", str);
    384   1.4  christos 	}
    385   1.1  christos 	return (rdclass);
    386   1.1  christos }
    387   1.1  christos 
    388   1.1  christos unsigned int
    389   1.4  christos strtodsdigest(const char *str) {
    390   1.4  christos 	isc_textregion_t r;
    391   1.4  christos 	dns_dsdigest_t alg;
    392   1.4  christos 	isc_result_t result;
    393   1.4  christos 
    394   1.4  christos 	DE_CONST(str, r.base);
    395   1.4  christos 	r.length = strlen(str);
    396   1.4  christos 	result = dns_dsdigest_fromtext(&alg, &r);
    397   1.4  christos 	if (result != ISC_R_SUCCESS) {
    398   1.4  christos 		fatal("unknown DS algorithm %s", str);
    399   1.4  christos 	}
    400   1.4  christos 	return (alg);
    401   1.4  christos }
    402   1.4  christos 
    403   1.4  christos static int
    404   1.4  christos cmp_dtype(const void *ap, const void *bp) {
    405   1.4  christos 	int a = *(const uint8_t *)ap;
    406   1.4  christos 	int b = *(const uint8_t *)bp;
    407   1.4  christos 	return (a - b);
    408   1.4  christos }
    409   1.4  christos 
    410   1.4  christos void
    411   1.4  christos add_dtype(unsigned int dt) {
    412   1.4  christos 	unsigned i, n;
    413   1.4  christos 
    414   1.4  christos 	/* ensure there is space for a zero terminator */
    415   1.4  christos 	n = sizeof(dtype) / sizeof(dtype[0]) - 1;
    416   1.4  christos 	for (i = 0; i < n; i++) {
    417   1.4  christos 		if (dtype[i] == dt) {
    418   1.4  christos 			return;
    419   1.4  christos 		}
    420   1.4  christos 		if (dtype[i] == 0) {
    421   1.4  christos 			dtype[i] = dt;
    422   1.4  christos 			qsort(dtype, i + 1, 1, cmp_dtype);
    423   1.4  christos 			return;
    424   1.4  christos 		}
    425   1.1  christos 	}
    426   1.4  christos 	fatal("too many -a digest type arguments");
    427   1.1  christos }
    428   1.1  christos 
    429   1.1  christos isc_result_t
    430   1.1  christos try_dir(const char *dirname) {
    431   1.1  christos 	isc_result_t result;
    432   1.1  christos 	isc_dir_t d;
    433   1.1  christos 
    434   1.1  christos 	isc_dir_init(&d);
    435   1.1  christos 	result = isc_dir_open(&d, dirname);
    436   1.1  christos 	if (result == ISC_R_SUCCESS) {
    437   1.1  christos 		isc_dir_close(&d);
    438   1.1  christos 	}
    439   1.1  christos 	return (result);
    440   1.1  christos }
    441   1.1  christos 
    442   1.1  christos /*
    443   1.1  christos  * Check private key version compatibility.
    444   1.1  christos  */
    445   1.1  christos void
    446   1.1  christos check_keyversion(dst_key_t *key, char *keystr) {
    447   1.1  christos 	int major, minor;
    448   1.1  christos 	dst_key_getprivateformat(key, &major, &minor);
    449   1.1  christos 	INSIST(major <= DST_MAJOR_VERSION); /* invalid private key */
    450   1.1  christos 
    451   1.4  christos 	if (major < DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) {
    452   1.1  christos 		fatal("Key %s has incompatible format version %d.%d, "
    453   1.1  christos 		      "use -f to force upgrade to new version.",
    454   1.1  christos 		      keystr, major, minor);
    455   1.4  christos 	}
    456   1.4  christos 	if (minor > DST_MINOR_VERSION) {
    457   1.1  christos 		fatal("Key %s has incompatible format version %d.%d, "
    458   1.1  christos 		      "use -f to force downgrade to current version.",
    459   1.1  christos 		      keystr, major, minor);
    460   1.4  christos 	}
    461   1.1  christos }
    462   1.1  christos 
    463   1.1  christos void
    464   1.1  christos set_keyversion(dst_key_t *key) {
    465   1.1  christos 	int major, minor;
    466   1.1  christos 	dst_key_getprivateformat(key, &major, &minor);
    467   1.1  christos 	INSIST(major <= DST_MAJOR_VERSION);
    468   1.1  christos 
    469   1.4  christos 	if (major != DST_MAJOR_VERSION || minor != DST_MINOR_VERSION) {
    470   1.1  christos 		dst_key_setprivateformat(key, DST_MAJOR_VERSION,
    471   1.1  christos 					 DST_MINOR_VERSION);
    472   1.4  christos 	}
    473   1.1  christos 
    474   1.1  christos 	/*
    475   1.1  christos 	 * If the key is from a version older than 1.3, set
    476   1.1  christos 	 * set the creation date
    477   1.1  christos 	 */
    478   1.1  christos 	if (major < 1 || (major == 1 && minor <= 2)) {
    479   1.1  christos 		isc_stdtime_t now;
    480   1.1  christos 		isc_stdtime_get(&now);
    481   1.1  christos 		dst_key_settime(key, DST_TIME_CREATED, now);
    482   1.1  christos 	}
    483   1.1  christos }
    484   1.1  christos 
    485   1.3  christos bool
    486   1.1  christos key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir,
    487   1.4  christos 	      isc_mem_t *mctx, bool *exact) {
    488   1.1  christos 	isc_result_t result;
    489   1.3  christos 	bool conflict = false;
    490   1.1  christos 	dns_dnsseckeylist_t matchkeys;
    491   1.1  christos 	dns_dnsseckey_t *key = NULL;
    492   1.3  christos 	uint16_t id, oldid;
    493   1.3  christos 	uint32_t rid, roldid;
    494   1.1  christos 	dns_secalg_t alg;
    495   1.3  christos 	char filename[NAME_MAX];
    496   1.1  christos 	isc_buffer_t fileb;
    497   1.3  christos 	isc_stdtime_t now;
    498   1.1  christos 
    499   1.4  christos 	if (exact != NULL) {
    500   1.3  christos 		*exact = false;
    501   1.4  christos 	}
    502   1.1  christos 
    503   1.1  christos 	id = dst_key_id(dstkey);
    504   1.1  christos 	rid = dst_key_rid(dstkey);
    505   1.1  christos 	alg = dst_key_alg(dstkey);
    506   1.1  christos 
    507   1.1  christos 	/*
    508   1.3  christos 	 * For Diffie Hellman just check if there is a direct collision as
    509   1.3  christos 	 * they can't be revoked.  Additionally dns_dnssec_findmatchingkeys
    510   1.3  christos 	 * only handles DNSKEY which is not used for HMAC.
    511   1.1  christos 	 */
    512   1.3  christos 	if (alg == DST_ALG_DH) {
    513   1.1  christos 		isc_buffer_init(&fileb, filename, sizeof(filename));
    514   1.4  christos 		result = dst_key_buildfilename(dstkey, DST_TYPE_PRIVATE, dir,
    515   1.4  christos 					       &fileb);
    516   1.4  christos 		if (result != ISC_R_SUCCESS) {
    517   1.3  christos 			return (true);
    518   1.4  christos 		}
    519   1.1  christos 		return (isc_file_exists(filename));
    520   1.1  christos 	}
    521   1.1  christos 
    522   1.1  christos 	ISC_LIST_INIT(matchkeys);
    523   1.3  christos 	isc_stdtime_get(&now);
    524   1.3  christos 	result = dns_dnssec_findmatchingkeys(name, dir, now, mctx, &matchkeys);
    525   1.4  christos 	if (result == ISC_R_NOTFOUND) {
    526   1.3  christos 		return (false);
    527   1.4  christos 	}
    528   1.1  christos 
    529   1.1  christos 	while (!ISC_LIST_EMPTY(matchkeys) && !conflict) {
    530   1.1  christos 		key = ISC_LIST_HEAD(matchkeys);
    531   1.4  christos 		if (dst_key_alg(key->key) != alg) {
    532   1.1  christos 			goto next;
    533   1.4  christos 		}
    534   1.1  christos 
    535   1.1  christos 		oldid = dst_key_id(key->key);
    536   1.1  christos 		roldid = dst_key_rid(key->key);
    537   1.1  christos 
    538   1.1  christos 		if (oldid == rid || roldid == id || id == oldid) {
    539   1.3  christos 			conflict = true;
    540   1.1  christos 			if (id != oldid) {
    541   1.4  christos 				if (verbose > 1) {
    542   1.4  christos 					fprintf(stderr,
    543   1.4  christos 						"Key ID %d could "
    544   1.1  christos 						"collide with %d\n",
    545   1.1  christos 						id, oldid);
    546   1.4  christos 				}
    547   1.1  christos 			} else {
    548   1.4  christos 				if (exact != NULL) {
    549   1.3  christos 					*exact = true;
    550   1.4  christos 				}
    551   1.4  christos 				if (verbose > 1) {
    552   1.1  christos 					fprintf(stderr, "Key ID %d exists\n",
    553   1.1  christos 						id);
    554   1.4  christos 				}
    555   1.1  christos 			}
    556   1.1  christos 		}
    557   1.1  christos 
    558   1.4  christos 	next:
    559   1.1  christos 		ISC_LIST_UNLINK(matchkeys, key, link);
    560   1.1  christos 		dns_dnsseckey_destroy(mctx, &key);
    561   1.1  christos 	}
    562   1.1  christos 
    563   1.1  christos 	/* Finish freeing the list */
    564   1.1  christos 	while (!ISC_LIST_EMPTY(matchkeys)) {
    565   1.1  christos 		key = ISC_LIST_HEAD(matchkeys);
    566   1.1  christos 		ISC_LIST_UNLINK(matchkeys, key, link);
    567   1.1  christos 		dns_dnsseckey_destroy(mctx, &key);
    568   1.1  christos 	}
    569   1.1  christos 
    570   1.1  christos 	return (conflict);
    571   1.1  christos }
    572   1.1  christos 
    573   1.3  christos bool
    574   1.4  christos isoptarg(const char *arg, char **argv, void (*usage)(void)) {
    575   1.1  christos 	if (!strcasecmp(isc_commandline_argument, arg)) {
    576   1.1  christos 		if (argv[isc_commandline_index] == NULL) {
    577   1.1  christos 			fprintf(stderr, "%s: missing argument -%c %s\n",
    578   1.1  christos 				program, isc_commandline_option,
    579   1.1  christos 				isc_commandline_argument);
    580   1.1  christos 			usage();
    581   1.1  christos 		}
    582   1.1  christos 		isc_commandline_argument = argv[isc_commandline_index];
    583   1.4  christos 		/* skip to next argument */
    584   1.1  christos 		isc_commandline_index++;
    585   1.3  christos 		return (true);
    586   1.1  christos 	}
    587   1.3  christos 	return (false);
    588   1.1  christos }
    589