2000-05-24 23:54:43 +00:00
|
|
|
/*
|
2018-01-19 23:45:58 +00:00
|
|
|
* Copyright (C) 2000, 2001, 2003-2005, 2007, 2009-2018 Internet Systems Consortium, Inc. ("ISC")
|
2000-08-01 01:33:37 +00:00
|
|
|
*
|
2016-06-27 14:56:38 +10:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
2000-05-24 23:54:43 +00:00
|
|
|
*/
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*! \file */
|
|
|
|
|
|
|
|
/*%
|
|
|
|
* DNSSEC Support Routines.
|
|
|
|
*/
|
2000-06-22 22:00:42 +00:00
|
|
|
|
2000-05-24 23:54:43 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2016-10-19 17:18:42 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <Winsock2.h>
|
|
|
|
#endif
|
|
|
|
|
2012-06-25 13:57:32 +10:00
|
|
|
#include <isc/base32.h>
|
2000-05-24 23:54:43 +00:00
|
|
|
#include <isc/buffer.h>
|
2015-11-05 12:09:48 +11:00
|
|
|
#include <isc/commandline.h>
|
2009-10-24 00:00:06 +00:00
|
|
|
#include <isc/dir.h>
|
2000-06-09 22:34:40 +00:00
|
|
|
#include <isc/entropy.h>
|
2016-05-05 22:27:08 +10:00
|
|
|
#include <isc/file.h>
|
2012-06-25 13:57:32 +10:00
|
|
|
#include <isc/heap.h>
|
2001-09-05 23:15:42 +00:00
|
|
|
#include <isc/list.h>
|
|
|
|
#include <isc/mem.h>
|
2017-09-12 19:05:46 -07:00
|
|
|
#include <isc/platform.h>
|
2015-11-05 12:09:48 +11:00
|
|
|
#include <isc/print.h>
|
2000-05-24 23:54:43 +00:00
|
|
|
#include <isc/string.h>
|
2000-06-22 02:47:33 +00:00
|
|
|
#include <isc/time.h>
|
2000-05-24 23:54:43 +00:00
|
|
|
#include <isc/util.h>
|
|
|
|
|
2012-06-25 13:57:32 +10:00
|
|
|
#include <dns/db.h>
|
|
|
|
#include <dns/dbiterator.h>
|
2010-01-19 20:26:07 +00:00
|
|
|
#include <dns/dnssec.h>
|
2012-06-25 13:57:32 +10:00
|
|
|
#include <dns/fixedname.h>
|
2010-01-19 20:26:07 +00:00
|
|
|
#include <dns/keyvalues.h>
|
2000-05-24 23:54:43 +00:00
|
|
|
#include <dns/log.h>
|
|
|
|
#include <dns/name.h>
|
2012-06-25 13:57:32 +10:00
|
|
|
#include <dns/nsec.h>
|
|
|
|
#include <dns/nsec3.h>
|
2000-08-11 23:07:53 +00:00
|
|
|
#include <dns/rdatastruct.h>
|
2001-09-21 00:17:01 +00:00
|
|
|
#include <dns/rdataclass.h>
|
2012-06-25 13:57:32 +10:00
|
|
|
#include <dns/rdataset.h>
|
|
|
|
#include <dns/rdatasetiter.h>
|
2000-05-24 23:54:43 +00:00
|
|
|
#include <dns/rdatatype.h>
|
|
|
|
#include <dns/result.h>
|
|
|
|
#include <dns/secalg.h>
|
2000-09-08 08:38:59 +00:00
|
|
|
#include <dns/time.h>
|
2000-05-24 23:54:43 +00:00
|
|
|
|
|
|
|
#include "dnssectool.h"
|
|
|
|
|
2012-06-25 13:57:32 +10:00
|
|
|
static isc_heap_t *expected_chains, *found_chains;
|
|
|
|
|
|
|
|
struct nsec3_chain_fixed {
|
|
|
|
isc_uint8_t hash;
|
|
|
|
isc_uint8_t salt_length;
|
|
|
|
isc_uint8_t next_length;
|
|
|
|
isc_uint16_t iterations;
|
|
|
|
/* unsigned char salt[0]; */
|
|
|
|
/* unsigned char owner[0]; */
|
|
|
|
/* unsigned char next[0]; */
|
|
|
|
};
|
|
|
|
|
2000-05-24 23:54:43 +00:00
|
|
|
extern int verbose;
|
2000-06-01 18:49:22 +00:00
|
|
|
extern const char *program;
|
2000-05-24 23:54:43 +00:00
|
|
|
|
2001-09-05 23:15:42 +00:00
|
|
|
typedef struct entropysource entropysource_t;
|
|
|
|
|
|
|
|
struct entropysource {
|
|
|
|
isc_entropysource_t *source;
|
|
|
|
isc_mem_t *mctx;
|
|
|
|
ISC_LINK(entropysource_t) link;
|
|
|
|
};
|
|
|
|
|
|
|
|
static ISC_LIST(entropysource_t) sources;
|
2001-03-31 02:12:26 +00:00
|
|
|
static fatalcallback_t *fatalcallback = NULL;
|
2000-06-22 02:47:33 +00:00
|
|
|
|
2000-05-24 23:54:43 +00:00
|
|
|
void
|
2000-06-01 18:49:22 +00:00
|
|
|
fatal(const char *format, ...) {
|
2000-05-24 23:54:43 +00:00
|
|
|
va_list args;
|
|
|
|
|
2009-06-04 02:13:37 +00:00
|
|
|
fprintf(stderr, "%s: fatal: ", program);
|
2000-05-24 23:54:43 +00:00
|
|
|
va_start(args, format);
|
|
|
|
vfprintf(stderr, format, args);
|
|
|
|
va_end(args);
|
|
|
|
fprintf(stderr, "\n");
|
2001-03-31 02:12:26 +00:00
|
|
|
if (fatalcallback != NULL)
|
|
|
|
(*fatalcallback)();
|
2000-05-24 23:54:43 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2001-03-31 02:12:26 +00:00
|
|
|
void
|
|
|
|
setfatalcallback(fatalcallback_t *callback) {
|
|
|
|
fatalcallback = callback;
|
|
|
|
}
|
|
|
|
|
2000-05-24 23:54:43 +00:00
|
|
|
void
|
2000-06-01 18:49:22 +00:00
|
|
|
check_result(isc_result_t result, const char *message) {
|
2001-11-27 22:08:58 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
fatal("%s: %s", message, isc_result_totext(result));
|
2000-05-24 23:54:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
vbprintf(int level, const char *fmt, ...) {
|
|
|
|
va_list ap;
|
|
|
|
if (level > verbose)
|
|
|
|
return;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
fprintf(stderr, "%s: ", program);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2014-06-13 06:27:43 +05:30
|
|
|
void
|
2015-01-20 13:29:18 -08:00
|
|
|
version(const char *name) {
|
|
|
|
fprintf(stderr, "%s %s\n", name, VERSION);
|
2014-06-13 06:27:43 +05:30
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2000-08-14 04:43:17 +00:00
|
|
|
void
|
|
|
|
type_format(const dns_rdatatype_t type, char *cp, unsigned int size) {
|
2000-05-24 23:54:43 +00:00
|
|
|
isc_buffer_t b;
|
|
|
|
isc_region_t r;
|
|
|
|
isc_result_t result;
|
|
|
|
|
2000-08-14 04:43:17 +00:00
|
|
|
isc_buffer_init(&b, cp, size - 1);
|
2000-05-24 23:54:43 +00:00
|
|
|
result = dns_rdatatype_totext(type, &b);
|
|
|
|
check_result(result, "dns_rdatatype_totext()");
|
|
|
|
isc_buffer_usedregion(&b, &r);
|
|
|
|
r.base[r.length] = 0;
|
|
|
|
}
|
|
|
|
|
2000-08-14 04:43:17 +00:00
|
|
|
void
|
2003-09-30 06:00:40 +00:00
|
|
|
sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) {
|
2000-08-14 04:43:17 +00:00
|
|
|
char namestr[DNS_NAME_FORMATSIZE];
|
|
|
|
char algstr[DNS_NAME_FORMATSIZE];
|
2000-08-11 23:07:53 +00:00
|
|
|
|
2001-09-19 23:08:24 +00:00
|
|
|
dns_name_format(&sig->signer, namestr, sizeof(namestr));
|
2009-10-12 20:48:12 +00:00
|
|
|
dns_secalg_format(sig->algorithm, algstr, sizeof(algstr));
|
2000-08-14 04:43:17 +00:00
|
|
|
snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid);
|
|
|
|
}
|
2000-08-11 23:07:53 +00:00
|
|
|
|
2000-05-24 23:54:43 +00:00
|
|
|
void
|
2015-01-20 13:29:18 -08:00
|
|
|
setup_logging(isc_mem_t *mctx, isc_log_t **logp) {
|
2000-05-24 23:54:43 +00:00
|
|
|
isc_result_t result;
|
|
|
|
isc_logdestination_t destination;
|
2000-11-27 22:19:37 +00:00
|
|
|
isc_logconfig_t *logconfig = NULL;
|
|
|
|
isc_log_t *log = NULL;
|
2000-05-24 23:54:43 +00:00
|
|
|
int level;
|
|
|
|
|
2005-07-01 03:28:42 +00:00
|
|
|
if (verbose < 0)
|
|
|
|
verbose = 0;
|
2000-05-24 23:54:43 +00:00
|
|
|
switch (verbose) {
|
2000-12-11 19:24:30 +00:00
|
|
|
case 0:
|
|
|
|
/*
|
2000-05-24 23:54:43 +00:00
|
|
|
* We want to see warnings about things like out-of-zone
|
|
|
|
* data in the master file even when not verbose.
|
|
|
|
*/
|
|
|
|
level = ISC_LOG_WARNING;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
level = ISC_LOG_INFO;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
level = ISC_LOG_DEBUG(verbose - 2 + 1);
|
|
|
|
break;
|
|
|
|
}
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2000-05-24 23:54:43 +00:00
|
|
|
RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
|
|
|
|
isc_log_setcontext(log);
|
|
|
|
dns_log_init(log);
|
|
|
|
dns_log_setcontext(log);
|
|
|
|
|
2000-06-01 18:49:22 +00:00
|
|
|
RUNTIME_CHECK(isc_log_settag(logconfig, program) == ISC_R_SUCCESS);
|
|
|
|
|
2000-05-24 23:54:43 +00:00
|
|
|
/*
|
|
|
|
* Set up a channel similar to default_stderr except:
|
|
|
|
* - the logging level is passed in
|
|
|
|
* - the program name and logging level are printed
|
|
|
|
* - no time stamp is printed
|
|
|
|
*/
|
|
|
|
destination.file.stream = stderr;
|
|
|
|
destination.file.name = NULL;
|
|
|
|
destination.file.versions = ISC_LOG_ROLLNEVER;
|
|
|
|
destination.file.maximum_size = 0;
|
|
|
|
result = isc_log_createchannel(logconfig, "stderr",
|
|
|
|
ISC_LOG_TOFILEDESC,
|
|
|
|
level,
|
|
|
|
&destination,
|
|
|
|
ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL);
|
|
|
|
check_result(result, "isc_log_createchannel()");
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2000-05-24 23:54:43 +00:00
|
|
|
RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
|
|
|
|
NULL, NULL) == ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
*logp = log;
|
|
|
|
}
|
|
|
|
|
2000-09-26 22:11:25 +00:00
|
|
|
void
|
|
|
|
cleanup_logging(isc_log_t **logp) {
|
|
|
|
isc_log_t *log;
|
|
|
|
|
|
|
|
REQUIRE(logp != NULL);
|
|
|
|
|
|
|
|
log = *logp;
|
|
|
|
if (log == NULL)
|
|
|
|
return;
|
|
|
|
isc_log_destroy(&log);
|
|
|
|
isc_log_setcontext(NULL);
|
|
|
|
dns_log_setcontext(NULL);
|
|
|
|
logp = NULL;
|
|
|
|
}
|
|
|
|
|
2000-06-09 22:34:40 +00:00
|
|
|
void
|
2000-06-10 01:28:11 +00:00
|
|
|
setup_entropy(isc_mem_t *mctx, const char *randomfile, isc_entropy_t **ectx) {
|
2000-06-09 22:34:40 +00:00
|
|
|
isc_result_t result;
|
2001-09-05 23:15:42 +00:00
|
|
|
isc_entropysource_t *source = NULL;
|
|
|
|
entropysource_t *elt;
|
2001-09-05 22:32:09 +00:00
|
|
|
int usekeyboard = ISC_ENTROPY_KEYBOARDMAYBE;
|
|
|
|
|
|
|
|
REQUIRE(ectx != NULL);
|
2009-06-04 02:56:47 +00:00
|
|
|
|
2001-09-05 22:32:09 +00:00
|
|
|
if (*ectx == NULL) {
|
|
|
|
result = isc_entropy_create(mctx, ectx);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
2017-09-12 19:05:46 -07:00
|
|
|
fatal("could not create entropy object: %s",
|
|
|
|
isc_result_totext(result));
|
2001-09-05 23:15:42 +00:00
|
|
|
ISC_LIST_INIT(sources);
|
2001-09-05 22:32:09 +00:00
|
|
|
}
|
2000-06-15 18:15:26 +00:00
|
|
|
|
2017-09-28 10:09:22 -07:00
|
|
|
#ifdef ISC_PLATFORM_CRYPTORANDOM
|
|
|
|
if (randomfile == NULL) {
|
|
|
|
isc_entropy_usehook(*ectx, ISC_TRUE);
|
|
|
|
}
|
|
|
|
#endif
|
2001-09-05 22:32:09 +00:00
|
|
|
if (randomfile != NULL && strcmp(randomfile, "keyboard") == 0) {
|
|
|
|
usekeyboard = ISC_ENTROPY_KEYBOARDYES;
|
|
|
|
randomfile = NULL;
|
|
|
|
}
|
2001-06-08 22:07:16 +00:00
|
|
|
|
2001-06-22 17:05:54 +00:00
|
|
|
result = isc_entropy_usebestsource(*ectx, &source, randomfile,
|
2001-09-05 22:32:09 +00:00
|
|
|
usekeyboard);
|
2001-06-08 22:07:16 +00:00
|
|
|
|
2001-06-22 17:05:54 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
fatal("could not initialize entropy source: %s",
|
|
|
|
isc_result_totext(result));
|
2001-09-05 23:15:42 +00:00
|
|
|
|
|
|
|
if (source != NULL) {
|
2001-09-19 23:08:24 +00:00
|
|
|
elt = isc_mem_get(mctx, sizeof(*elt));
|
2001-09-05 23:15:42 +00:00
|
|
|
if (elt == NULL)
|
|
|
|
fatal("out of memory");
|
|
|
|
elt->source = source;
|
|
|
|
elt->mctx = mctx;
|
|
|
|
ISC_LINK_INIT(elt, link);
|
|
|
|
ISC_LIST_APPEND(sources, elt, link);
|
|
|
|
}
|
2000-06-09 22:34:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cleanup_entropy(isc_entropy_t **ectx) {
|
2001-09-05 23:15:42 +00:00
|
|
|
entropysource_t *source;
|
|
|
|
while (!ISC_LIST_EMPTY(sources)) {
|
|
|
|
source = ISC_LIST_HEAD(sources);
|
|
|
|
ISC_LIST_UNLINK(sources, source, link);
|
|
|
|
isc_entropy_destroysource(&source->source);
|
2001-09-19 23:08:24 +00:00
|
|
|
isc_mem_put(source->mctx, source, sizeof(*source));
|
2001-09-05 23:15:42 +00:00
|
|
|
}
|
2000-06-09 22:34:40 +00:00
|
|
|
isc_entropy_detach(ectx);
|
|
|
|
}
|
2000-09-08 08:38:59 +00:00
|
|
|
|
2009-07-19 23:47:55 +00:00
|
|
|
static isc_stdtime_t
|
2009-09-02 06:29:01 +00:00
|
|
|
time_units(isc_stdtime_t offset, char *suffix, const char *str) {
|
|
|
|
switch (suffix[0]) {
|
2009-07-19 04:18:05 +00:00
|
|
|
case 'Y': case 'y':
|
|
|
|
return (offset * (365 * 24 * 3600));
|
|
|
|
case 'M': case 'm':
|
2009-09-02 06:29:01 +00:00
|
|
|
switch (suffix[1]) {
|
|
|
|
case 'O': case 'o':
|
|
|
|
return (offset * (30 * 24 * 3600));
|
|
|
|
case 'I': case 'i':
|
|
|
|
return (offset * 60);
|
|
|
|
case '\0':
|
|
|
|
fatal("'%s' ambiguous: use 'mi' for minutes "
|
|
|
|
"or 'mo' for months", str);
|
|
|
|
default:
|
|
|
|
fatal("time value %s is invalid", str);
|
|
|
|
}
|
2009-10-03 18:03:54 +00:00
|
|
|
/* NOTREACHED */
|
2009-09-02 06:29:01 +00:00
|
|
|
break;
|
2009-07-19 04:18:05 +00:00
|
|
|
case 'W': case 'w':
|
|
|
|
return (offset * (7 * 24 * 3600));
|
|
|
|
case 'D': case 'd':
|
|
|
|
return (offset * (24 * 3600));
|
|
|
|
case 'H': case 'h':
|
|
|
|
return (offset * 3600);
|
|
|
|
case 'S': case 's': case '\0':
|
|
|
|
return (offset);
|
|
|
|
default:
|
|
|
|
fatal("time value %s is invalid", str);
|
|
|
|
}
|
2009-10-03 18:03:54 +00:00
|
|
|
/* NOTREACHED */
|
2009-07-19 04:18:05 +00:00
|
|
|
return(0); /* silence compiler warning */
|
|
|
|
}
|
|
|
|
|
2014-02-06 15:59:14 -08:00
|
|
|
static inline isc_boolean_t
|
|
|
|
isnone(const char *str) {
|
|
|
|
return (ISC_TF((strcasecmp(str, "none") == 0) ||
|
|
|
|
(strcasecmp(str, "never") == 0)));
|
|
|
|
}
|
|
|
|
|
2009-09-02 06:29:01 +00:00
|
|
|
dns_ttl_t
|
|
|
|
strtottl(const char *str) {
|
|
|
|
const char *orig = str;
|
|
|
|
dns_ttl_t ttl;
|
|
|
|
char *endp;
|
|
|
|
|
2014-02-06 15:59:14 -08:00
|
|
|
if (isnone(str))
|
|
|
|
return ((dns_ttl_t) 0);
|
|
|
|
|
2009-09-02 06:29:01 +00:00
|
|
|
ttl = strtol(str, &endp, 0);
|
2009-09-02 23:48:03 +00:00
|
|
|
if (ttl == 0 && endp == str)
|
|
|
|
fatal("TTL must be numeric");
|
2009-09-02 06:29:01 +00:00
|
|
|
ttl = time_units(ttl, endp, orig);
|
|
|
|
return (ttl);
|
|
|
|
}
|
|
|
|
|
2000-09-08 08:38:59 +00:00
|
|
|
isc_stdtime_t
|
2014-02-06 15:59:14 -08:00
|
|
|
strtotime(const char *str, isc_int64_t now, isc_int64_t base,
|
|
|
|
isc_boolean_t *setp)
|
|
|
|
{
|
2000-09-08 08:38:59 +00:00
|
|
|
isc_int64_t val, offset;
|
|
|
|
isc_result_t result;
|
2009-07-19 04:18:05 +00:00
|
|
|
const char *orig = str;
|
2000-09-08 08:38:59 +00:00
|
|
|
char *endp;
|
2014-02-11 21:20:28 -08:00
|
|
|
size_t n;
|
2000-09-08 08:38:59 +00:00
|
|
|
|
2014-02-06 15:59:14 -08:00
|
|
|
if (isnone(str)) {
|
|
|
|
if (setp != NULL)
|
|
|
|
*setp = ISC_FALSE;
|
|
|
|
return ((isc_stdtime_t) 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (setp != NULL)
|
|
|
|
*setp = ISC_TRUE;
|
|
|
|
|
2009-08-13 04:13:58 +00:00
|
|
|
if ((str[0] == '0' || str[0] == '-') && str[1] == '\0')
|
2009-07-19 04:18:05 +00:00
|
|
|
return ((isc_stdtime_t) 0);
|
|
|
|
|
2013-08-15 10:48:05 +10:00
|
|
|
/*
|
|
|
|
* We accept times in the following formats:
|
|
|
|
* now([+-]offset)
|
|
|
|
* YYYYMMDD([+-]offset)
|
|
|
|
* YYYYMMDDhhmmss([+-]offset)
|
|
|
|
* [+-]offset
|
|
|
|
*/
|
|
|
|
n = strspn(str, "0123456789");
|
2014-02-13 07:48:44 +11:00
|
|
|
if ((n == 8u || n == 14u) &&
|
2013-08-15 10:48:05 +10:00
|
|
|
(str[n] == '\0' || str[n] == '-' || str[n] == '+'))
|
|
|
|
{
|
|
|
|
char timestr[15];
|
|
|
|
|
|
|
|
strlcpy(timestr, str, sizeof(timestr));
|
|
|
|
timestr[n] = 0;
|
2014-02-13 07:48:44 +11:00
|
|
|
if (n == 8u)
|
2013-08-15 10:48:05 +10:00
|
|
|
strlcat(timestr, "000000", sizeof(timestr));
|
|
|
|
result = dns_time64_fromtext(timestr, &val);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
fatal("time value %s is invalid: %s", orig,
|
|
|
|
isc_result_totext(result));
|
|
|
|
base = val;
|
|
|
|
str += n;
|
|
|
|
} else if (strncmp(str, "now", 3) == 0) {
|
2009-07-19 04:18:05 +00:00
|
|
|
base = now;
|
|
|
|
str += 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (str[0] == '\0')
|
|
|
|
return ((isc_stdtime_t) base);
|
|
|
|
else if (str[0] == '+') {
|
2000-09-08 08:38:59 +00:00
|
|
|
offset = strtol(str + 1, &endp, 0);
|
2009-09-03 00:12:23 +00:00
|
|
|
offset = time_units((isc_stdtime_t) offset, endp, orig);
|
2000-09-08 08:38:59 +00:00
|
|
|
val = base + offset;
|
2009-07-19 04:18:05 +00:00
|
|
|
} else if (str[0] == '-') {
|
|
|
|
offset = strtol(str + 1, &endp, 0);
|
2009-09-03 00:12:23 +00:00
|
|
|
offset = time_units((isc_stdtime_t) offset, endp, orig);
|
2009-07-19 04:18:05 +00:00
|
|
|
val = base - offset;
|
2013-08-16 09:44:30 +10:00
|
|
|
} else
|
|
|
|
fatal("time value %s is invalid", orig);
|
2000-09-08 08:38:59 +00:00
|
|
|
|
|
|
|
return ((isc_stdtime_t) val);
|
|
|
|
}
|
2001-09-21 00:17:01 +00:00
|
|
|
|
|
|
|
dns_rdataclass_t
|
|
|
|
strtoclass(const char *str) {
|
2004-02-03 00:59:05 +00:00
|
|
|
isc_textregion_t r;
|
2001-09-21 00:17:01 +00:00
|
|
|
dns_rdataclass_t rdclass;
|
|
|
|
isc_result_t ret;
|
|
|
|
|
|
|
|
if (str == NULL)
|
|
|
|
return dns_rdataclass_in;
|
2004-02-03 00:59:05 +00:00
|
|
|
DE_CONST(str, r.base);
|
2001-09-21 00:17:01 +00:00
|
|
|
r.length = strlen(str);
|
2004-02-03 00:59:05 +00:00
|
|
|
ret = dns_rdataclass_fromtext(&rdclass, &r);
|
2001-09-21 00:17:01 +00:00
|
|
|
if (ret != ISC_R_SUCCESS)
|
|
|
|
fatal("unknown class %s", str);
|
|
|
|
return (rdclass);
|
|
|
|
}
|
2009-10-24 00:00:06 +00:00
|
|
|
|
2017-10-05 01:04:18 -07:00
|
|
|
unsigned int
|
|
|
|
strtodsdigest(const char *algname) {
|
|
|
|
if (strcasecmp(algname, "SHA1") == 0 ||
|
|
|
|
strcasecmp(algname, "SHA-1") == 0)
|
|
|
|
{
|
|
|
|
return (DNS_DSDIGEST_SHA1);
|
|
|
|
} else if (strcasecmp(algname, "SHA256") == 0 ||
|
|
|
|
strcasecmp(algname, "SHA-256") == 0)
|
|
|
|
{
|
|
|
|
return (DNS_DSDIGEST_SHA256);
|
|
|
|
#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
|
|
|
|
} else if (strcasecmp(algname, "GOST") == 0) {
|
|
|
|
return (DNS_DSDIGEST_GOST);
|
|
|
|
#endif
|
|
|
|
} else if (strcasecmp(algname, "SHA384") == 0 ||
|
|
|
|
strcasecmp(algname, "SHA-384") == 0)
|
|
|
|
{
|
|
|
|
return (DNS_DSDIGEST_SHA384);
|
|
|
|
} else {
|
|
|
|
fatal("unknown algorithm %s", algname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-24 00:00:06 +00:00
|
|
|
isc_result_t
|
|
|
|
try_dir(const char *dirname) {
|
|
|
|
isc_result_t result;
|
|
|
|
isc_dir_t d;
|
|
|
|
|
|
|
|
isc_dir_init(&d);
|
|
|
|
result = isc_dir_open(&d, dirname);
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
isc_dir_close(&d);
|
|
|
|
}
|
|
|
|
return (result);
|
|
|
|
}
|
2009-10-26 21:18:24 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check private key version compatibility.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
check_keyversion(dst_key_t *key, char *keystr) {
|
|
|
|
int major, minor;
|
|
|
|
dst_key_getprivateformat(key, &major, &minor);
|
2009-10-26 23:47:35 +00:00
|
|
|
INSIST(major <= DST_MAJOR_VERSION); /* invalid private key */
|
2009-10-26 21:18:24 +00:00
|
|
|
|
2009-10-26 23:47:35 +00:00
|
|
|
if (major < DST_MAJOR_VERSION || minor < DST_MINOR_VERSION)
|
2009-10-26 21:18:24 +00:00
|
|
|
fatal("Key %s has incompatible format version %d.%d, "
|
|
|
|
"use -f to force upgrade to new version.",
|
|
|
|
keystr, major, minor);
|
|
|
|
if (minor > DST_MINOR_VERSION)
|
|
|
|
fatal("Key %s has incompatible format version %d.%d, "
|
|
|
|
"use -f to force downgrade to current version.",
|
|
|
|
keystr, major, minor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
set_keyversion(dst_key_t *key) {
|
|
|
|
int major, minor;
|
|
|
|
dst_key_getprivateformat(key, &major, &minor);
|
2009-10-26 23:47:35 +00:00
|
|
|
INSIST(major <= DST_MAJOR_VERSION);
|
2009-10-26 21:18:24 +00:00
|
|
|
|
|
|
|
if (major != DST_MAJOR_VERSION || minor != DST_MINOR_VERSION)
|
|
|
|
dst_key_setprivateformat(key, DST_MAJOR_VERSION,
|
|
|
|
DST_MINOR_VERSION);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the key is from a version older than 1.3, set
|
|
|
|
* set the creation date
|
|
|
|
*/
|
|
|
|
if (major < 1 || (major == 1 && minor <= 2)) {
|
|
|
|
isc_stdtime_t now;
|
|
|
|
isc_stdtime_get(&now);
|
|
|
|
dst_key_settime(key, DST_TIME_CREATED, now);
|
|
|
|
}
|
|
|
|
}
|
2010-01-19 20:26:07 +00:00
|
|
|
|
|
|
|
isc_boolean_t
|
2011-10-20 21:20:02 +00:00
|
|
|
key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir,
|
|
|
|
isc_mem_t *mctx, isc_boolean_t *exact)
|
2010-01-19 20:26:07 +00:00
|
|
|
{
|
|
|
|
isc_result_t result;
|
|
|
|
isc_boolean_t conflict = ISC_FALSE;
|
|
|
|
dns_dnsseckeylist_t matchkeys;
|
|
|
|
dns_dnsseckey_t *key = NULL;
|
2011-10-21 03:55:33 +00:00
|
|
|
isc_uint16_t id, oldid;
|
2011-10-20 21:20:02 +00:00
|
|
|
isc_uint32_t rid, roldid;
|
|
|
|
dns_secalg_t alg;
|
2016-05-05 22:27:08 +10:00
|
|
|
char filename[ISC_DIR_NAMEMAX];
|
|
|
|
isc_buffer_t fileb;
|
2010-01-19 20:26:07 +00:00
|
|
|
|
|
|
|
if (exact != NULL)
|
|
|
|
*exact = ISC_FALSE;
|
2011-10-20 23:46:51 +00:00
|
|
|
|
2011-10-20 21:20:02 +00:00
|
|
|
id = dst_key_id(dstkey);
|
|
|
|
rid = dst_key_rid(dstkey);
|
|
|
|
alg = dst_key_alg(dstkey);
|
2010-01-19 20:26:07 +00:00
|
|
|
|
2016-05-05 22:27:08 +10:00
|
|
|
/*
|
2018-01-18 17:29:28 -08:00
|
|
|
* For Diffie Hellman just check if there is a direct collision as
|
|
|
|
* they can't be revoked. Additionally dns_dnssec_findmatchingkeys
|
|
|
|
* only handles DNSKEY which is not used for HMAC.
|
2016-05-05 22:27:08 +10:00
|
|
|
*/
|
2018-01-18 17:29:28 -08:00
|
|
|
if (alg == DST_ALG_DH) {
|
2016-05-05 22:27:08 +10:00
|
|
|
isc_buffer_init(&fileb, filename, sizeof(filename));
|
|
|
|
result = dst_key_buildfilename(dstkey, DST_TYPE_PRIVATE,
|
|
|
|
dir, &fileb);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (ISC_TRUE);
|
|
|
|
return (isc_file_exists(filename));
|
|
|
|
}
|
|
|
|
|
2010-01-19 20:26:07 +00:00
|
|
|
ISC_LIST_INIT(matchkeys);
|
|
|
|
result = dns_dnssec_findmatchingkeys(name, dir, mctx, &matchkeys);
|
|
|
|
if (result == ISC_R_NOTFOUND)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
|
|
|
|
while (!ISC_LIST_EMPTY(matchkeys) && !conflict) {
|
|
|
|
key = ISC_LIST_HEAD(matchkeys);
|
|
|
|
if (dst_key_alg(key->key) != alg)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
oldid = dst_key_id(key->key);
|
2011-10-20 21:20:02 +00:00
|
|
|
roldid = dst_key_rid(key->key);
|
|
|
|
|
|
|
|
if (oldid == rid || roldid == id || id == oldid) {
|
2010-01-19 20:26:07 +00:00
|
|
|
conflict = ISC_TRUE;
|
2011-10-20 21:20:02 +00:00
|
|
|
if (id != oldid) {
|
2010-01-19 20:26:07 +00:00
|
|
|
if (verbose > 1)
|
2010-01-19 23:48:56 +00:00
|
|
|
fprintf(stderr, "Key ID %d could "
|
2010-01-19 20:26:07 +00:00
|
|
|
"collide with %d\n",
|
|
|
|
id, oldid);
|
|
|
|
} else {
|
|
|
|
if (exact != NULL)
|
|
|
|
*exact = ISC_TRUE;
|
|
|
|
if (verbose > 1)
|
2010-01-19 23:48:56 +00:00
|
|
|
fprintf(stderr, "Key ID %d exists\n",
|
2010-01-19 20:26:07 +00:00
|
|
|
id);
|
2010-01-19 23:48:56 +00:00
|
|
|
}
|
2010-01-19 20:26:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
next:
|
|
|
|
ISC_LIST_UNLINK(matchkeys, key, link);
|
|
|
|
dns_dnsseckey_destroy(mctx, &key);
|
|
|
|
}
|
|
|
|
|
2010-01-19 23:48:56 +00:00
|
|
|
/* Finish freeing the list */
|
2010-01-19 20:26:07 +00:00
|
|
|
while (!ISC_LIST_EMPTY(matchkeys)) {
|
|
|
|
key = ISC_LIST_HEAD(matchkeys);
|
|
|
|
ISC_LIST_UNLINK(matchkeys, key, link);
|
|
|
|
dns_dnsseckey_destroy(mctx, &key);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (conflict);
|
|
|
|
}
|
2012-06-25 13:57:32 +10:00
|
|
|
|
|
|
|
isc_boolean_t
|
|
|
|
is_delegation(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
|
|
|
|
dns_name_t *name, dns_dbnode_t *node, isc_uint32_t *ttlp)
|
|
|
|
{
|
|
|
|
dns_rdataset_t nsset;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
if (dns_name_equal(name, origin))
|
|
|
|
return (ISC_FALSE);
|
|
|
|
|
|
|
|
dns_rdataset_init(&nsset);
|
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_ns,
|
|
|
|
0, 0, &nsset, NULL);
|
|
|
|
if (dns_rdataset_isassociated(&nsset)) {
|
|
|
|
if (ttlp != NULL)
|
|
|
|
*ttlp = nsset.ttl;
|
|
|
|
dns_rdataset_disassociate(&nsset);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ISC_TF(result == ISC_R_SUCCESS));
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_boolean_t
|
|
|
|
goodsig(dns_name_t *origin, dns_rdata_t *sigrdata, dns_name_t *name,
|
|
|
|
dns_rdataset_t *keyrdataset, dns_rdataset_t *rdataset, isc_mem_t *mctx)
|
|
|
|
{
|
|
|
|
dns_rdata_dnskey_t key;
|
|
|
|
dns_rdata_rrsig_t sig;
|
|
|
|
dst_key_t *dstkey = NULL;
|
|
|
|
isc_result_t result;
|
|
|
|
|
2012-10-06 14:20:45 +10:00
|
|
|
result = dns_rdata_tostruct(sigrdata, &sig, NULL);
|
|
|
|
check_result(result, "dns_rdata_tostruct()");
|
2012-06-25 13:57:32 +10:00
|
|
|
|
|
|
|
for (result = dns_rdataset_first(keyrdataset);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdataset_next(keyrdataset)) {
|
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdataset_current(keyrdataset, &rdata);
|
2012-10-06 14:20:45 +10:00
|
|
|
result = dns_rdata_tostruct(&rdata, &key, NULL);
|
|
|
|
check_result(result, "dns_rdata_tostruct()");
|
2012-06-25 13:57:32 +10:00
|
|
|
result = dns_dnssec_keyfromrdata(origin, &rdata, mctx,
|
|
|
|
&dstkey);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
if (sig.algorithm != key.algorithm ||
|
|
|
|
sig.keyid != dst_key_id(dstkey) ||
|
|
|
|
!dns_name_equal(&sig.signer, origin)) {
|
|
|
|
dst_key_free(&dstkey);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
result = dns_dnssec_verify(name, rdataset, dstkey, ISC_FALSE,
|
|
|
|
mctx, sigrdata);
|
|
|
|
dst_key_free(&dstkey);
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
return(ISC_TRUE);
|
|
|
|
}
|
|
|
|
return (ISC_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
verifynsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
|
|
|
|
dns_dbnode_t *node, dns_name_t *nextname)
|
|
|
|
{
|
|
|
|
unsigned char buffer[DNS_NSEC_BUFFERSIZE];
|
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char nextbuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char found[DNS_NAME_FORMATSIZE];
|
|
|
|
dns_rdataset_t rdataset;
|
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdata_t tmprdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdata_nsec_t nsec;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
|
|
|
|
0, 0, &rdataset, NULL);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
fprintf(stderr, "Missing NSEC record for %s\n", namebuf);
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = dns_rdataset_first(&rdataset);
|
|
|
|
check_result(result, "dns_rdataset_first()");
|
|
|
|
|
|
|
|
dns_rdataset_current(&rdataset, &rdata);
|
|
|
|
result = dns_rdata_tostruct(&rdata, &nsec, NULL);
|
|
|
|
check_result(result, "dns_rdata_tostruct()");
|
|
|
|
/* Check bit next name is consistent */
|
|
|
|
if (!dns_name_equal(&nsec.next, nextname)) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
dns_name_format(nextname, nextbuf, sizeof(nextbuf));
|
|
|
|
dns_name_format(&nsec.next, found, sizeof(found));
|
2013-01-23 14:56:00 -08:00
|
|
|
fprintf(stderr, "Bad NSEC record for %s, next name "
|
2012-06-25 13:57:32 +10:00
|
|
|
"mismatch (expected:%s, found:%s)\n", namebuf,
|
|
|
|
nextbuf, found);
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
/* Check bit map is consistent */
|
|
|
|
result = dns_nsec_buildrdata(db, ver, node, nextname, buffer,
|
|
|
|
&tmprdata);
|
|
|
|
check_result(result, "dns_nsec_buildrdata()");
|
|
|
|
if (dns_rdata_compare(&rdata, &tmprdata) != 0) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
2013-01-23 14:56:00 -08:00
|
|
|
fprintf(stderr, "Bad NSEC record for %s, bit map "
|
2012-06-25 13:57:32 +10:00
|
|
|
"mismatch\n", namebuf);
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
result = dns_rdataset_next(&rdataset);
|
|
|
|
if (result != ISC_R_NOMORE) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
fprintf(stderr, "Multipe NSEC records for %s\n", namebuf);
|
|
|
|
goto failure;
|
|
|
|
|
|
|
|
}
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
failure:
|
|
|
|
if (dns_rdataset_isassociated(&rdataset))
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
return (ISC_R_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
check_no_rrsig(dns_db_t *db, dns_dbversion_t *ver, dns_rdataset_t *rdataset,
|
|
|
|
dns_name_t *name, dns_dbnode_t *node)
|
|
|
|
{
|
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char typebuf[80];
|
|
|
|
dns_rdataset_t sigrdataset;
|
|
|
|
dns_rdatasetiter_t *rdsiter = NULL;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
dns_rdataset_init(&sigrdataset);
|
|
|
|
result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
|
|
|
|
check_result(result, "dns_db_allrdatasets()");
|
|
|
|
for (result = dns_rdatasetiter_first(rdsiter);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdatasetiter_next(rdsiter)) {
|
|
|
|
dns_rdatasetiter_current(rdsiter, &sigrdataset);
|
|
|
|
if (sigrdataset.type == dns_rdatatype_rrsig &&
|
|
|
|
sigrdataset.covers == rdataset->type)
|
|
|
|
break;
|
|
|
|
dns_rdataset_disassociate(&sigrdataset);
|
|
|
|
}
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
type_format(rdataset->type, typebuf, sizeof(typebuf));
|
|
|
|
fprintf(stderr, "Warning: Found unexpected signatures for "
|
|
|
|
"%s/%s\n", namebuf, typebuf);
|
|
|
|
}
|
|
|
|
if (dns_rdataset_isassociated(&sigrdataset))
|
|
|
|
dns_rdataset_disassociate(&sigrdataset);
|
|
|
|
dns_rdatasetiter_destroy(&rdsiter);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_boolean_t
|
|
|
|
chain_compare(void *arg1, void *arg2) {
|
|
|
|
struct nsec3_chain_fixed *e1 = arg1, *e2 = arg2;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do each element in turn to get a stable sort.
|
|
|
|
*/
|
|
|
|
if (e1->hash < e2->hash)
|
|
|
|
return (ISC_TRUE);
|
|
|
|
if (e1->hash > e2->hash)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
if (e1->iterations < e2->iterations)
|
|
|
|
return (ISC_TRUE);
|
|
|
|
if (e1->iterations > e2->iterations)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
if (e1->salt_length < e2->salt_length)
|
|
|
|
return (ISC_TRUE);
|
|
|
|
if (e1->salt_length > e2->salt_length)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
if (e1->next_length < e2->next_length)
|
|
|
|
return (ISC_TRUE);
|
|
|
|
if (e1->next_length > e2->next_length)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
len = e1->salt_length + 2 * e1->next_length;
|
|
|
|
if (memcmp(e1 + 1, e2 + 1, len) < 0)
|
|
|
|
return (ISC_TRUE);
|
|
|
|
return (ISC_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_boolean_t
|
|
|
|
chain_equal(struct nsec3_chain_fixed *e1, struct nsec3_chain_fixed *e2) {
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
if (e1->hash != e2->hash)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
if (e1->iterations != e2->iterations)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
if (e1->salt_length != e2->salt_length)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
if (e1->next_length != e2->next_length)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
len = e1->salt_length + 2 * e1->next_length;
|
|
|
|
if (memcmp(e1 + 1, e2 + 1, len) != 0)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
return (ISC_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
record_nsec3(const unsigned char *rawhash, const dns_rdata_nsec3_t *nsec3,
|
|
|
|
isc_mem_t *mctx, isc_heap_t *chains)
|
|
|
|
{
|
|
|
|
struct nsec3_chain_fixed *element;
|
|
|
|
size_t len;
|
|
|
|
unsigned char *cp;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
len = sizeof(*element) + nsec3->next_length * 2 + nsec3->salt_length;
|
|
|
|
|
|
|
|
element = isc_mem_get(mctx, len);
|
|
|
|
if (element == NULL)
|
|
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
memset(element, 0, len);
|
|
|
|
element->hash = nsec3->hash;
|
|
|
|
element->salt_length = nsec3->salt_length;
|
|
|
|
element->next_length = nsec3->next_length;
|
|
|
|
element->iterations = nsec3->iterations;
|
|
|
|
cp = (unsigned char *)(element + 1);
|
2014-01-08 16:27:10 -08:00
|
|
|
memmove(cp, nsec3->salt, nsec3->salt_length);
|
2012-06-25 13:57:32 +10:00
|
|
|
cp += nsec3->salt_length;
|
2014-01-08 16:27:10 -08:00
|
|
|
memmove(cp, rawhash, nsec3->next_length);
|
2012-06-25 13:57:32 +10:00
|
|
|
cp += nsec3->next_length;
|
2014-01-08 16:27:10 -08:00
|
|
|
memmove(cp, nsec3->next, nsec3->next_length);
|
2012-06-25 13:57:32 +10:00
|
|
|
result = isc_heap_insert(chains, element);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
fprintf(stderr, "isc_heap_insert failed: %s\n",
|
|
|
|
isc_result_totext(result));
|
|
|
|
isc_mem_put(mctx, element, len);
|
|
|
|
}
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
match_nsec3(dns_name_t *name, isc_mem_t *mctx,
|
|
|
|
dns_rdata_nsec3param_t *nsec3param, dns_rdataset_t *rdataset,
|
|
|
|
unsigned char types[8192], unsigned int maxtype,
|
|
|
|
unsigned char *rawhash, size_t rhsize)
|
|
|
|
{
|
|
|
|
unsigned char cbm[8244];
|
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
dns_rdata_nsec3_t nsec3;
|
|
|
|
isc_result_t result;
|
|
|
|
unsigned int len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find matching NSEC3 record.
|
|
|
|
*/
|
|
|
|
for (result = dns_rdataset_first(rdataset);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdataset_next(rdataset)) {
|
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdataset_current(rdataset, &rdata);
|
|
|
|
result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
|
|
|
|
check_result(result, "dns_rdata_tostruct()");
|
|
|
|
if (nsec3.hash == nsec3param->hash &&
|
|
|
|
nsec3.next_length == rhsize &&
|
|
|
|
nsec3.iterations == nsec3param->iterations &&
|
|
|
|
nsec3.salt_length == nsec3param->salt_length &&
|
|
|
|
memcmp(nsec3.salt, nsec3param->salt,
|
|
|
|
nsec3param->salt_length) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
fprintf(stderr, "Missing NSEC3 record for %s\n", namebuf);
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the type list.
|
|
|
|
*/
|
|
|
|
len = dns_nsec_compressbitmap(cbm, types, maxtype);
|
|
|
|
if (nsec3.len != len || memcmp(cbm, nsec3.typebits, len) != 0) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
2013-01-23 14:56:00 -08:00
|
|
|
fprintf(stderr, "Bad NSEC3 record for %s, bit map "
|
2012-06-25 13:57:32 +10:00
|
|
|
"mismatch\n", namebuf);
|
|
|
|
return (ISC_R_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record chain.
|
|
|
|
*/
|
|
|
|
result = record_nsec3(rawhash, &nsec3, mctx, expected_chains);
|
|
|
|
check_result(result, "record_nsec3()");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure there is only one NSEC3 record with this set of
|
|
|
|
* parameters.
|
|
|
|
*/
|
|
|
|
for (result = dns_rdataset_next(rdataset);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdataset_next(rdataset)) {
|
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdataset_current(rdataset, &rdata);
|
|
|
|
result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
|
|
|
|
check_result(result, "dns_rdata_tostruct()");
|
|
|
|
if (nsec3.hash == nsec3param->hash &&
|
|
|
|
nsec3.iterations == nsec3param->iterations &&
|
|
|
|
nsec3.salt_length == nsec3param->salt_length &&
|
|
|
|
memcmp(nsec3.salt, nsec3param->salt,
|
|
|
|
nsec3.salt_length) == 0) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
fprintf(stderr, "Multiple NSEC3 records with the "
|
|
|
|
"same parameter set for %s", namebuf);
|
|
|
|
result = DNS_R_DUPLICATE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (result != ISC_R_NOMORE)
|
|
|
|
return (result);
|
|
|
|
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_boolean_t
|
|
|
|
innsec3params(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *nsec3paramset) {
|
|
|
|
dns_rdata_nsec3param_t nsec3param;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
for (result = dns_rdataset_first(nsec3paramset);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdataset_next(nsec3paramset)) {
|
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
|
|
|
|
dns_rdataset_current(nsec3paramset, &rdata);
|
|
|
|
result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
|
2012-10-06 14:20:45 +10:00
|
|
|
check_result(result, "dns_rdata_tostruct()");
|
2012-06-25 13:57:32 +10:00
|
|
|
if (nsec3param.flags == 0 &&
|
|
|
|
nsec3param.hash == nsec3->hash &&
|
|
|
|
nsec3param.iterations == nsec3->iterations &&
|
|
|
|
nsec3param.salt_length == nsec3->salt_length &&
|
|
|
|
memcmp(nsec3param.salt, nsec3->salt,
|
|
|
|
nsec3->salt_length) == 0)
|
|
|
|
return (ISC_TRUE);
|
|
|
|
}
|
|
|
|
return (ISC_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
record_found(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx,
|
|
|
|
dns_name_t *name, dns_dbnode_t *node,
|
|
|
|
dns_rdataset_t *nsec3paramset)
|
|
|
|
{
|
|
|
|
unsigned char owner[NSEC3_MAX_HASH_LENGTH];
|
|
|
|
dns_rdata_nsec3_t nsec3;
|
|
|
|
dns_rdataset_t rdataset;
|
|
|
|
dns_label_t hashlabel;
|
|
|
|
isc_buffer_t b;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
if (nsec3paramset == NULL || !dns_rdataset_isassociated(nsec3paramset))
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
|
|
|
|
0, 0, &rdataset, NULL);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
dns_name_getlabel(name, 0, &hashlabel);
|
|
|
|
isc_region_consume(&hashlabel, 1);
|
|
|
|
isc_buffer_init(&b, owner, sizeof(owner));
|
|
|
|
result = isc_base32hex_decoderegion(&hashlabel, &b);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (result = dns_rdataset_first(&rdataset);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdataset_next(&rdataset)) {
|
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdataset_current(&rdataset, &rdata);
|
|
|
|
result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
|
|
|
|
check_result(result, "dns_rdata_tostruct()");
|
|
|
|
if (nsec3.next_length != isc_buffer_usedlength(&b))
|
|
|
|
continue;
|
|
|
|
/*
|
|
|
|
* We only care about NSEC3 records that match a NSEC3PARAM
|
|
|
|
* record.
|
|
|
|
*/
|
|
|
|
if (!innsec3params(&nsec3, nsec3paramset))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record chain.
|
|
|
|
*/
|
|
|
|
result = record_nsec3(owner, &nsec3, mctx, found_chains);
|
|
|
|
check_result(result, "record_nsec3()");
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2013-01-23 14:56:00 -08:00
|
|
|
static isc_boolean_t
|
|
|
|
isoptout(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
|
|
|
|
dns_rdata_t *nsec3rdata)
|
|
|
|
{
|
|
|
|
dns_rdataset_t rdataset;
|
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdata_nsec3_t nsec3;
|
|
|
|
dns_rdata_nsec3param_t nsec3param;
|
|
|
|
dns_fixedname_t fixed;
|
|
|
|
dns_name_t *hashname;
|
|
|
|
isc_result_t result;
|
|
|
|
dns_dbnode_t *node = NULL;
|
|
|
|
unsigned char rawhash[NSEC3_MAX_HASH_LENGTH];
|
|
|
|
size_t rhsize = sizeof(rawhash);
|
|
|
|
isc_boolean_t ret;
|
|
|
|
|
|
|
|
result = dns_rdata_tostruct(nsec3rdata, &nsec3param, NULL);
|
|
|
|
check_result(result, "dns_rdata_tostruct()");
|
|
|
|
|
|
|
|
dns_fixedname_init(&fixed);
|
|
|
|
result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, origin, origin,
|
|
|
|
nsec3param.hash, nsec3param.iterations,
|
|
|
|
nsec3param.salt, nsec3param.salt_length);
|
|
|
|
check_result(result, "dns_nsec3_hashname()");
|
|
|
|
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
hashname = dns_fixedname_name(&fixed);
|
|
|
|
result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node);
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
|
|
|
|
0, 0, &rdataset, NULL);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
|
|
|
|
result = dns_rdataset_first(&rdataset);
|
|
|
|
check_result(result, "dns_rdataset_first()");
|
|
|
|
|
|
|
|
dns_rdataset_current(&rdataset, &rdata);
|
|
|
|
|
|
|
|
result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
ret = ISC_FALSE;
|
|
|
|
else
|
|
|
|
ret = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0);
|
|
|
|
|
|
|
|
if (dns_rdataset_isassociated(&rdataset))
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
if (node != NULL)
|
|
|
|
dns_db_detachnode(db, &node);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2012-06-25 13:57:32 +10:00
|
|
|
static isc_result_t
|
|
|
|
verifynsec3(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
|
|
|
|
isc_mem_t *mctx, dns_name_t *name, dns_rdata_t *rdata,
|
2013-01-23 14:56:00 -08:00
|
|
|
isc_boolean_t delegation, isc_boolean_t empty,
|
|
|
|
unsigned char types[8192], unsigned int maxtype)
|
2012-06-25 13:57:32 +10:00
|
|
|
{
|
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char hashbuf[DNS_NAME_FORMATSIZE];
|
|
|
|
dns_rdataset_t rdataset;
|
|
|
|
dns_rdata_nsec3param_t nsec3param;
|
|
|
|
dns_fixedname_t fixed;
|
|
|
|
dns_name_t *hashname;
|
|
|
|
isc_result_t result;
|
|
|
|
dns_dbnode_t *node = NULL;
|
|
|
|
unsigned char rawhash[NSEC3_MAX_HASH_LENGTH];
|
|
|
|
size_t rhsize = sizeof(rawhash);
|
2013-01-23 14:56:00 -08:00
|
|
|
isc_boolean_t optout;
|
2012-06-25 13:57:32 +10:00
|
|
|
|
|
|
|
result = dns_rdata_tostruct(rdata, &nsec3param, NULL);
|
|
|
|
check_result(result, "dns_rdata_tostruct()");
|
|
|
|
|
|
|
|
if (nsec3param.flags != 0)
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
if (!dns_nsec3_supportedhash(nsec3param.hash))
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
2013-01-23 14:56:00 -08:00
|
|
|
optout = isoptout(db, ver, origin, rdata);
|
|
|
|
|
2012-06-25 13:57:32 +10:00
|
|
|
dns_fixedname_init(&fixed);
|
|
|
|
result = dns_nsec3_hashname(&fixed, rawhash, &rhsize, name, origin,
|
2012-06-26 23:45:56 +00:00
|
|
|
nsec3param.hash, nsec3param.iterations,
|
|
|
|
nsec3param.salt, nsec3param.salt_length);
|
2012-06-25 13:57:32 +10:00
|
|
|
check_result(result, "dns_nsec3_hashname()");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't use dns_db_find() here as it works with the choosen
|
|
|
|
* nsec3 chain and we may also be called with uncommitted data
|
|
|
|
* from dnssec-signzone so the secure status of the zone may not
|
|
|
|
* be up to date.
|
|
|
|
*/
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
hashname = dns_fixedname_name(&fixed);
|
|
|
|
result = dns_db_findnsec3node(db, hashname, ISC_FALSE, &node);
|
2012-06-26 23:45:56 +00:00
|
|
|
if (result == ISC_R_SUCCESS)
|
2012-06-25 13:57:32 +10:00
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
|
|
|
|
0, 0, &rdataset, NULL);
|
|
|
|
if (result != ISC_R_SUCCESS &&
|
2013-01-23 14:56:00 -08:00
|
|
|
(!delegation || (empty && !optout) ||
|
|
|
|
(!empty && dns_nsec_isset(types, dns_rdatatype_ds))))
|
|
|
|
{
|
2012-06-25 13:57:32 +10:00
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
dns_name_format(hashname, hashbuf, sizeof(hashbuf));
|
|
|
|
fprintf(stderr, "Missing NSEC3 record for %s (%s)\n",
|
|
|
|
namebuf, hashbuf);
|
2013-01-23 14:56:00 -08:00
|
|
|
} else if (result == ISC_R_NOTFOUND &&
|
|
|
|
delegation && (!empty || optout))
|
|
|
|
{
|
|
|
|
result = ISC_R_SUCCESS;
|
2012-06-25 13:57:32 +10:00
|
|
|
} else if (result == ISC_R_SUCCESS) {
|
|
|
|
result = match_nsec3(name, mctx, &nsec3param, &rdataset,
|
|
|
|
types, maxtype, rawhash, rhsize);
|
2013-01-23 14:56:00 -08:00
|
|
|
}
|
|
|
|
|
2012-06-25 13:57:32 +10:00
|
|
|
if (dns_rdataset_isassociated(&rdataset))
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
if (node != NULL)
|
|
|
|
dns_db_detachnode(db, &node);
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
verifynsec3s(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
|
|
|
|
isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *nsec3paramset,
|
2013-01-23 14:56:00 -08:00
|
|
|
isc_boolean_t delegation, isc_boolean_t empty,
|
|
|
|
unsigned char types[8192], unsigned int maxtype)
|
2012-06-25 13:57:32 +10:00
|
|
|
{
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
for (result = dns_rdataset_first(nsec3paramset);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdataset_next(nsec3paramset)) {
|
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
|
|
|
|
dns_rdataset_current(nsec3paramset, &rdata);
|
|
|
|
result = verifynsec3(db, ver, origin, mctx, name, &rdata,
|
2013-01-23 14:56:00 -08:00
|
|
|
delegation, empty, types, maxtype);
|
2012-06-25 13:57:32 +10:00
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (result == ISC_R_NOMORE)
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
verifyset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
|
|
|
|
isc_mem_t *mctx, dns_rdataset_t *rdataset, dns_name_t *name,
|
|
|
|
dns_dbnode_t *node, dns_rdataset_t *keyrdataset,
|
|
|
|
unsigned char *act_algorithms, unsigned char *bad_algorithms)
|
|
|
|
{
|
|
|
|
unsigned char set_algorithms[256];
|
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char algbuf[80];
|
|
|
|
char typebuf[80];
|
|
|
|
dns_rdataset_t sigrdataset;
|
|
|
|
dns_rdatasetiter_t *rdsiter = NULL;
|
|
|
|
isc_result_t result;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
dns_rdataset_init(&sigrdataset);
|
|
|
|
result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
|
|
|
|
check_result(result, "dns_db_allrdatasets()");
|
|
|
|
for (result = dns_rdatasetiter_first(rdsiter);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdatasetiter_next(rdsiter)) {
|
|
|
|
dns_rdatasetiter_current(rdsiter, &sigrdataset);
|
|
|
|
if (sigrdataset.type == dns_rdatatype_rrsig &&
|
|
|
|
sigrdataset.covers == rdataset->type)
|
|
|
|
break;
|
|
|
|
dns_rdataset_disassociate(&sigrdataset);
|
|
|
|
}
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
type_format(rdataset->type, typebuf, sizeof(typebuf));
|
|
|
|
fprintf(stderr, "No signatures for %s/%s\n", namebuf, typebuf);
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
if (act_algorithms[i] != 0)
|
|
|
|
bad_algorithms[i] = 1;
|
|
|
|
dns_rdatasetiter_destroy(&rdsiter);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(set_algorithms, 0, sizeof(set_algorithms));
|
|
|
|
for (result = dns_rdataset_first(&sigrdataset);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdataset_next(&sigrdataset)) {
|
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdata_rrsig_t sig;
|
|
|
|
|
|
|
|
dns_rdataset_current(&sigrdataset, &rdata);
|
2012-10-06 14:20:45 +10:00
|
|
|
result = dns_rdata_tostruct(&rdata, &sig, NULL);
|
|
|
|
check_result(result, "dns_rdata_tostruct()");
|
2012-06-25 13:57:32 +10:00
|
|
|
if (rdataset->ttl != sig.originalttl) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
type_format(rdataset->type, typebuf, sizeof(typebuf));
|
|
|
|
fprintf(stderr, "TTL mismatch for %s %s keytag %u\n",
|
|
|
|
namebuf, typebuf, sig.keyid);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((set_algorithms[sig.algorithm] != 0) ||
|
|
|
|
(act_algorithms[sig.algorithm] == 0))
|
|
|
|
continue;
|
|
|
|
if (goodsig(origin, &rdata, name, keyrdataset, rdataset, mctx))
|
|
|
|
set_algorithms[sig.algorithm] = 1;
|
|
|
|
}
|
|
|
|
dns_rdatasetiter_destroy(&rdsiter);
|
|
|
|
if (memcmp(set_algorithms, act_algorithms, sizeof(set_algorithms))) {
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
type_format(rdataset->type, typebuf, sizeof(typebuf));
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
if ((act_algorithms[i] != 0) &&
|
|
|
|
(set_algorithms[i] == 0)) {
|
|
|
|
dns_secalg_format(i, algbuf, sizeof(algbuf));
|
|
|
|
fprintf(stderr, "No correct %s signature for "
|
|
|
|
"%s %s\n", algbuf, namebuf, typebuf);
|
|
|
|
bad_algorithms[i] = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dns_rdataset_disassociate(&sigrdataset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
verifynode(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
|
|
|
|
isc_mem_t *mctx, dns_name_t *name, dns_dbnode_t *node,
|
|
|
|
isc_boolean_t delegation, dns_rdataset_t *keyrdataset,
|
|
|
|
unsigned char *act_algorithms, unsigned char *bad_algorithms,
|
|
|
|
dns_rdataset_t *nsecset, dns_rdataset_t *nsec3paramset,
|
|
|
|
dns_name_t *nextname)
|
|
|
|
{
|
|
|
|
unsigned char types[8192];
|
|
|
|
unsigned int maxtype = 0;
|
|
|
|
dns_rdataset_t rdataset; dns_rdatasetiter_t *rdsiter = NULL;
|
|
|
|
isc_result_t result, tresult;
|
|
|
|
|
|
|
|
memset(types, 0, sizeof(types));
|
|
|
|
result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
|
|
|
|
check_result(result, "dns_db_allrdatasets()");
|
|
|
|
result = dns_rdatasetiter_first(rdsiter);
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
while (result == ISC_R_SUCCESS) {
|
|
|
|
dns_rdatasetiter_current(rdsiter, &rdataset);
|
|
|
|
/*
|
|
|
|
* If we are not at a delegation then everything should be
|
|
|
|
* signed. If we are at a delegation then only the DS set
|
|
|
|
* is signed. The NS set is not signed at a delegation but
|
|
|
|
* its existance is recorded in the bit map. Anything else
|
|
|
|
* other than NSEC and DS is not signed at a delegation.
|
|
|
|
*/
|
|
|
|
if (rdataset.type != dns_rdatatype_rrsig &&
|
|
|
|
rdataset.type != dns_rdatatype_dnskey &&
|
|
|
|
(!delegation || rdataset.type == dns_rdatatype_ds ||
|
|
|
|
rdataset.type == dns_rdatatype_nsec)) {
|
|
|
|
verifyset(db, ver, origin, mctx, &rdataset,
|
|
|
|
name, node, keyrdataset,
|
|
|
|
act_algorithms, bad_algorithms);
|
|
|
|
dns_nsec_setbit(types, rdataset.type, 1);
|
|
|
|
if (rdataset.type > maxtype)
|
|
|
|
maxtype = rdataset.type;
|
|
|
|
} else if (rdataset.type != dns_rdatatype_rrsig &&
|
|
|
|
rdataset.type != dns_rdatatype_dnskey) {
|
|
|
|
if (rdataset.type == dns_rdatatype_ns)
|
|
|
|
dns_nsec_setbit(types, rdataset.type, 1);
|
|
|
|
check_no_rrsig(db, ver, &rdataset, name, node);
|
|
|
|
} else
|
|
|
|
dns_nsec_setbit(types, rdataset.type, 1);
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
result = dns_rdatasetiter_next(rdsiter);
|
|
|
|
}
|
|
|
|
if (result != ISC_R_NOMORE)
|
|
|
|
fatal("rdataset iteration failed: %s",
|
|
|
|
isc_result_totext(result));
|
|
|
|
dns_rdatasetiter_destroy(&rdsiter);
|
|
|
|
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
|
2012-06-26 23:45:56 +00:00
|
|
|
if (nsecset != NULL && dns_rdataset_isassociated(nsecset))
|
2012-06-25 13:57:32 +10:00
|
|
|
result = verifynsec(db, ver, name, node, nextname);
|
|
|
|
|
|
|
|
if (nsec3paramset != NULL && dns_rdataset_isassociated(nsec3paramset)) {
|
|
|
|
tresult = verifynsec3s(db, ver, origin, mctx, name,
|
2013-01-23 14:56:00 -08:00
|
|
|
nsec3paramset, delegation, ISC_FALSE,
|
|
|
|
types, maxtype);
|
2012-06-25 13:57:32 +10:00
|
|
|
if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS)
|
|
|
|
result = tresult;
|
|
|
|
}
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_boolean_t
|
|
|
|
is_empty(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) {
|
|
|
|
dns_rdatasetiter_t *rdsiter = NULL;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
result = dns_db_allrdatasets(db, node, ver, 0, &rdsiter);
|
|
|
|
check_result(result, "dns_db_allrdatasets()");
|
|
|
|
result = dns_rdatasetiter_first(rdsiter);
|
|
|
|
dns_rdatasetiter_destroy(&rdsiter);
|
|
|
|
if (result == ISC_R_NOMORE)
|
|
|
|
return (ISC_TRUE);
|
|
|
|
return (ISC_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
check_no_nsec(dns_name_t *name, dns_dbnode_t *node, dns_db_t *db,
|
|
|
|
dns_dbversion_t *ver)
|
|
|
|
{
|
|
|
|
dns_rdataset_t rdataset;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
|
|
|
|
0, 0, &rdataset, NULL);
|
|
|
|
if (result != ISC_R_NOTFOUND) {
|
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
fatal("unexpected NSEC RRset at %s\n", namebuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dns_rdataset_isassociated(&rdataset))
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_boolean_t
|
|
|
|
newchain(const struct nsec3_chain_fixed *first,
|
|
|
|
const struct nsec3_chain_fixed *e)
|
|
|
|
{
|
|
|
|
if (first->hash != e->hash ||
|
|
|
|
first->iterations != e->iterations ||
|
|
|
|
first->salt_length != e->salt_length ||
|
|
|
|
first->next_length != e->next_length ||
|
|
|
|
memcmp(first + 1, e + 1, first->salt_length) != 0)
|
|
|
|
return (ISC_TRUE);
|
|
|
|
return (ISC_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_element(isc_mem_t *mctx, struct nsec3_chain_fixed *e) {
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
len = sizeof(*e) + e->salt_length + 2 * e->next_length;
|
|
|
|
isc_mem_put(mctx, e, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_boolean_t
|
|
|
|
checknext(const struct nsec3_chain_fixed *first,
|
|
|
|
const struct nsec3_chain_fixed *e)
|
|
|
|
{
|
|
|
|
char buf[512];
|
|
|
|
const unsigned char *d1 = (const unsigned char *)(first + 1);
|
|
|
|
const unsigned char *d2 = (const unsigned char *)(e + 1);
|
|
|
|
isc_buffer_t b;
|
|
|
|
isc_region_t sr;
|
|
|
|
|
|
|
|
d1 += first->salt_length + first->next_length;
|
|
|
|
d2 += e->salt_length;
|
|
|
|
|
2012-06-26 23:45:56 +00:00
|
|
|
if (memcmp(d1, d2, first->next_length) == 0)
|
2012-06-25 13:57:32 +10:00
|
|
|
return (ISC_TRUE);
|
|
|
|
|
|
|
|
DE_CONST(d1 - first->next_length, sr.base);
|
|
|
|
sr.length = first->next_length;
|
|
|
|
isc_buffer_init(&b, buf, sizeof(buf));
|
|
|
|
isc_base32hex_totext(&sr, 1, "", &b);
|
|
|
|
fprintf(stderr, "Break in NSEC3 chain at: %.*s\n",
|
|
|
|
(int) isc_buffer_usedlength(&b), buf);
|
|
|
|
|
|
|
|
DE_CONST(d1, sr.base);
|
|
|
|
sr.length = first->next_length;
|
|
|
|
isc_buffer_init(&b, buf, sizeof(buf));
|
|
|
|
isc_base32hex_totext(&sr, 1, "", &b);
|
|
|
|
fprintf(stderr, "Expected: %.*s\n", (int) isc_buffer_usedlength(&b),
|
|
|
|
buf);
|
|
|
|
|
|
|
|
DE_CONST(d2, sr.base);
|
|
|
|
sr.length = first->next_length;
|
|
|
|
isc_buffer_init(&b, buf, sizeof(buf));
|
|
|
|
isc_base32hex_totext(&sr, 1, "", &b);
|
|
|
|
fprintf(stderr, "Found: %.*s\n", (int) isc_buffer_usedlength(&b), buf);
|
|
|
|
|
|
|
|
return (ISC_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define EXPECTEDANDFOUND "Expected and found NSEC3 chains not equal\n"
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
verify_nsec3_chains(isc_mem_t *mctx) {
|
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
struct nsec3_chain_fixed *e, *f = NULL;
|
|
|
|
struct nsec3_chain_fixed *first = NULL, *prev = NULL;
|
|
|
|
|
|
|
|
while ((e = isc_heap_element(expected_chains, 1)) != NULL) {
|
|
|
|
isc_heap_delete(expected_chains, 1);
|
|
|
|
if (f == NULL)
|
|
|
|
f = isc_heap_element(found_chains, 1);
|
|
|
|
if (f != NULL) {
|
|
|
|
isc_heap_delete(found_chains, 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that they match.
|
|
|
|
*/
|
|
|
|
if (chain_equal(e, f)) {
|
|
|
|
free_element(mctx, f);
|
|
|
|
f = NULL;
|
|
|
|
} else {
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
fprintf(stderr, EXPECTEDANDFOUND);
|
|
|
|
result = ISC_R_FAILURE;
|
|
|
|
/*
|
|
|
|
* Attempt to resync found_chain.
|
|
|
|
*/
|
|
|
|
while (f != NULL && !chain_compare(e, f)) {
|
|
|
|
free_element(mctx, f);
|
|
|
|
f = isc_heap_element(found_chains, 1);
|
|
|
|
if (f != NULL)
|
|
|
|
isc_heap_delete(found_chains, 1);
|
|
|
|
if (f != NULL && chain_equal(e, f)) {
|
|
|
|
free_element(mctx, f);
|
|
|
|
f = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (result == ISC_R_SUCCESS) {
|
|
|
|
fprintf(stderr, EXPECTEDANDFOUND);
|
|
|
|
result = ISC_R_FAILURE;
|
|
|
|
}
|
|
|
|
if (first == NULL || newchain(first, e)) {
|
|
|
|
if (prev != NULL) {
|
|
|
|
if (!checknext(prev, first))
|
|
|
|
result = ISC_R_FAILURE;
|
|
|
|
if (prev != first)
|
|
|
|
free_element(mctx, prev);
|
|
|
|
}
|
|
|
|
if (first != NULL)
|
|
|
|
free_element(mctx, first);
|
|
|
|
prev = first = e;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!checknext(prev, e))
|
|
|
|
result = ISC_R_FAILURE;
|
|
|
|
if (prev != first)
|
|
|
|
free_element(mctx, prev);
|
|
|
|
prev = e;
|
|
|
|
}
|
|
|
|
if (prev != NULL) {
|
|
|
|
if (!checknext(prev, first))
|
|
|
|
result = ISC_R_FAILURE;
|
|
|
|
if (prev != first)
|
|
|
|
free_element(mctx, prev);
|
|
|
|
}
|
|
|
|
if (first != NULL)
|
|
|
|
free_element(mctx, first);
|
|
|
|
do {
|
|
|
|
if (f != NULL) {
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
fprintf(stderr, EXPECTEDANDFOUND);
|
|
|
|
result = ISC_R_FAILURE;
|
|
|
|
}
|
|
|
|
free_element(mctx, f);
|
|
|
|
}
|
|
|
|
f = isc_heap_element(found_chains, 1);
|
2012-06-26 23:45:56 +00:00
|
|
|
if (f != NULL)
|
2012-06-25 13:57:32 +10:00
|
|
|
isc_heap_delete(found_chains, 1);
|
|
|
|
} while (f != NULL);
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
verifyemptynodes(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
|
2013-01-23 14:56:00 -08:00
|
|
|
isc_mem_t *mctx, dns_name_t *name, dns_name_t *prevname,
|
|
|
|
isc_boolean_t isdelegation, dns_rdataset_t *nsec3paramset)
|
2012-06-25 13:57:32 +10:00
|
|
|
{
|
|
|
|
dns_namereln_t reln;
|
|
|
|
int order;
|
|
|
|
unsigned int labels, nlabels, i;
|
|
|
|
dns_name_t suffix;
|
|
|
|
isc_result_t result = ISC_R_SUCCESS, tresult;
|
|
|
|
|
2013-01-23 14:56:00 -08:00
|
|
|
reln = dns_name_fullcompare(prevname, name, &order, &labels);
|
2012-06-25 13:57:32 +10:00
|
|
|
if (order >= 0)
|
|
|
|
return (result);
|
|
|
|
|
2013-01-23 14:56:00 -08:00
|
|
|
nlabels = dns_name_countlabels(name);
|
2012-06-25 13:57:32 +10:00
|
|
|
|
|
|
|
if (reln == dns_namereln_commonancestor ||
|
|
|
|
reln == dns_namereln_contains) {
|
|
|
|
dns_name_init(&suffix, NULL);
|
|
|
|
for (i = labels + 1; i < nlabels; i++) {
|
2013-01-23 14:56:00 -08:00
|
|
|
dns_name_getlabelsequence(name, nlabels - i, i,
|
2012-06-25 13:57:32 +10:00
|
|
|
&suffix);
|
|
|
|
if (nsec3paramset != NULL &&
|
|
|
|
dns_rdataset_isassociated(nsec3paramset)) {
|
|
|
|
tresult = verifynsec3s(db, ver, origin, mctx,
|
|
|
|
&suffix, nsec3paramset,
|
2013-01-23 14:56:00 -08:00
|
|
|
isdelegation, ISC_TRUE,
|
|
|
|
NULL, 0);
|
2012-06-25 13:57:32 +10:00
|
|
|
if (result == ISC_R_SUCCESS &&
|
|
|
|
tresult != ISC_R_SUCCESS)
|
|
|
|
result = tresult;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*%
|
|
|
|
* Verify that certain things are sane:
|
|
|
|
*
|
|
|
|
* The apex has a DNSKEY record with at least one KSK, and at least
|
|
|
|
* one ZSK if the -x flag was not used.
|
|
|
|
*
|
|
|
|
* The DNSKEY record was signed with at least one of the KSKs in this
|
|
|
|
* set.
|
|
|
|
*
|
|
|
|
* The rest of the zone was signed with at least one of the ZSKs
|
|
|
|
* present in the DNSKEY RRSET.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
verifyzone(dns_db_t *db, dns_dbversion_t *ver,
|
|
|
|
dns_name_t *origin, isc_mem_t *mctx,
|
|
|
|
isc_boolean_t ignore_kskflag, isc_boolean_t keyset_kskonly)
|
|
|
|
{
|
|
|
|
char algbuf[80];
|
|
|
|
dns_dbiterator_t *dbiter = NULL;
|
|
|
|
dns_dbnode_t *node = NULL, *nextnode = NULL;
|
2013-01-23 14:56:00 -08:00
|
|
|
dns_fixedname_t fname, fnextname, fprevname, fzonecut;
|
|
|
|
dns_name_t *name, *nextname, *prevname, *zonecut;
|
2012-06-25 13:57:32 +10:00
|
|
|
dns_rdata_dnskey_t dnskey;
|
|
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
|
|
dns_rdataset_t keyset, soaset;
|
|
|
|
dns_rdataset_t keysigs, soasigs;
|
|
|
|
dns_rdataset_t nsecset, nsecsigs;
|
|
|
|
dns_rdataset_t nsec3paramset, nsec3paramsigs;
|
|
|
|
int i;
|
|
|
|
isc_boolean_t done = ISC_FALSE;
|
|
|
|
isc_boolean_t first = ISC_TRUE;
|
|
|
|
isc_boolean_t goodksk = ISC_FALSE;
|
|
|
|
isc_boolean_t goodzsk = ISC_FALSE;
|
|
|
|
isc_result_t result, vresult = ISC_R_UNSET;
|
|
|
|
unsigned char revoked_ksk[256];
|
|
|
|
unsigned char revoked_zsk[256];
|
|
|
|
unsigned char standby_ksk[256];
|
|
|
|
unsigned char standby_zsk[256];
|
|
|
|
unsigned char ksk_algorithms[256];
|
|
|
|
unsigned char zsk_algorithms[256];
|
|
|
|
unsigned char bad_algorithms[256];
|
|
|
|
unsigned char act_algorithms[256];
|
|
|
|
|
|
|
|
result = isc_heap_create(mctx, chain_compare, NULL, 1024,
|
|
|
|
&expected_chains);
|
|
|
|
check_result(result, "isc_heap_create()");
|
|
|
|
result = isc_heap_create(mctx, chain_compare, NULL, 1024,
|
|
|
|
&found_chains);
|
|
|
|
check_result(result, "isc_heap_create()");
|
|
|
|
|
|
|
|
result = dns_db_findnode(db, origin, ISC_FALSE, &node);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
fatal("failed to find the zone's origin: %s",
|
|
|
|
isc_result_totext(result));
|
|
|
|
|
|
|
|
dns_rdataset_init(&keyset);
|
|
|
|
dns_rdataset_init(&keysigs);
|
|
|
|
dns_rdataset_init(&soaset);
|
|
|
|
dns_rdataset_init(&soasigs);
|
|
|
|
dns_rdataset_init(&nsecset);
|
|
|
|
dns_rdataset_init(&nsecsigs);
|
|
|
|
dns_rdataset_init(&nsec3paramset);
|
|
|
|
dns_rdataset_init(&nsec3paramsigs);
|
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey,
|
|
|
|
0, 0, &keyset, &keysigs);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
fatal("Zone contains no DNSSEC keys\n");
|
|
|
|
|
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa,
|
|
|
|
0, 0, &soaset, &soasigs);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
fatal("Zone contains no SOA record\n");
|
|
|
|
|
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
|
|
|
|
0, 0, &nsecset, &nsecsigs);
|
|
|
|
if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
|
|
|
|
fatal("NSEC lookup failed\n");
|
|
|
|
|
|
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
|
|
|
|
0, 0, &nsec3paramset, &nsec3paramsigs);
|
|
|
|
if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
|
|
|
|
fatal("NSEC3PARAM lookup failed\n");
|
|
|
|
|
|
|
|
if (!dns_rdataset_isassociated(&keysigs))
|
|
|
|
fatal("DNSKEY is not signed (keys offline or inactive?)\n");
|
|
|
|
|
|
|
|
if (!dns_rdataset_isassociated(&soasigs))
|
|
|
|
fatal("SOA is not signed (keys offline or inactive?)\n");
|
|
|
|
|
|
|
|
if (dns_rdataset_isassociated(&nsecset) &&
|
|
|
|
!dns_rdataset_isassociated(&nsecsigs))
|
|
|
|
fatal("NSEC is not signed (keys offline or inactive?)\n");
|
|
|
|
|
|
|
|
if (dns_rdataset_isassociated(&nsec3paramset) &&
|
|
|
|
!dns_rdataset_isassociated(&nsec3paramsigs))
|
|
|
|
fatal("NSEC3PARAM is not signed (keys offline or inactive?)\n");
|
|
|
|
|
|
|
|
if (!dns_rdataset_isassociated(&nsecset) &&
|
|
|
|
!dns_rdataset_isassociated(&nsec3paramset))
|
|
|
|
fatal("No valid NSEC/NSEC3 chain for testing\n");
|
|
|
|
|
|
|
|
dns_db_detachnode(db, &node);
|
|
|
|
|
|
|
|
memset(revoked_ksk, 0, sizeof(revoked_ksk));
|
|
|
|
memset(revoked_zsk, 0, sizeof(revoked_zsk));
|
|
|
|
memset(standby_ksk, 0, sizeof(standby_ksk));
|
|
|
|
memset(standby_zsk, 0, sizeof(standby_zsk));
|
|
|
|
memset(ksk_algorithms, 0, sizeof(ksk_algorithms));
|
|
|
|
memset(zsk_algorithms, 0, sizeof(zsk_algorithms));
|
|
|
|
memset(bad_algorithms, 0, sizeof(bad_algorithms));
|
|
|
|
memset(act_algorithms, 0, sizeof(act_algorithms));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that the DNSKEY RR has at least one self signing KSK
|
|
|
|
* and one ZSK per algorithm in it (or, if -x was used, one
|
|
|
|
* self-signing KSK).
|
|
|
|
*/
|
|
|
|
for (result = dns_rdataset_first(&keyset);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdataset_next(&keyset)) {
|
|
|
|
dns_rdataset_current(&keyset, &rdata);
|
|
|
|
result = dns_rdata_tostruct(&rdata, &dnskey, NULL);
|
|
|
|
check_result(result, "dns_rdata_tostruct");
|
|
|
|
|
|
|
|
if ((dnskey.flags & DNS_KEYOWNER_ZONE) == 0)
|
|
|
|
;
|
|
|
|
else if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) {
|
|
|
|
if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 &&
|
|
|
|
!dns_dnssec_selfsigns(&rdata, origin, &keyset,
|
|
|
|
&keysigs, ISC_FALSE,
|
|
|
|
mctx)) {
|
|
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
char buffer[1024];
|
|
|
|
isc_buffer_t buf;
|
|
|
|
|
|
|
|
dns_name_format(origin, namebuf,
|
|
|
|
sizeof(namebuf));
|
|
|
|
isc_buffer_init(&buf, buffer, sizeof(buffer));
|
|
|
|
result = dns_rdata_totext(&rdata, NULL, &buf);
|
|
|
|
check_result(result, "dns_rdata_totext");
|
|
|
|
fatal("revoked KSK is not self signed:\n"
|
|
|
|
"%s DNSKEY %.*s", namebuf,
|
|
|
|
(int)isc_buffer_usedlength(&buf), buffer);
|
|
|
|
}
|
|
|
|
if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0 &&
|
|
|
|
revoked_ksk[dnskey.algorithm] != 255)
|
|
|
|
revoked_ksk[dnskey.algorithm]++;
|
|
|
|
else if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 &&
|
|
|
|
revoked_zsk[dnskey.algorithm] != 255)
|
|
|
|
revoked_zsk[dnskey.algorithm]++;
|
|
|
|
} else if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) {
|
|
|
|
if (dns_dnssec_selfsigns(&rdata, origin, &keyset,
|
|
|
|
&keysigs, ISC_FALSE, mctx)) {
|
|
|
|
if (ksk_algorithms[dnskey.algorithm] != 255)
|
|
|
|
ksk_algorithms[dnskey.algorithm]++;
|
|
|
|
goodksk = ISC_TRUE;
|
|
|
|
} else {
|
|
|
|
if (standby_ksk[dnskey.algorithm] != 255)
|
|
|
|
standby_ksk[dnskey.algorithm]++;
|
|
|
|
}
|
|
|
|
} else if (dns_dnssec_selfsigns(&rdata, origin, &keyset,
|
|
|
|
&keysigs, ISC_FALSE, mctx)) {
|
|
|
|
if (zsk_algorithms[dnskey.algorithm] != 255)
|
|
|
|
zsk_algorithms[dnskey.algorithm]++;
|
|
|
|
goodzsk = ISC_TRUE;
|
|
|
|
} else if (dns_dnssec_signs(&rdata, origin, &soaset,
|
|
|
|
&soasigs, ISC_FALSE, mctx)) {
|
|
|
|
if (zsk_algorithms[dnskey.algorithm] != 255)
|
|
|
|
zsk_algorithms[dnskey.algorithm]++;
|
|
|
|
} else {
|
|
|
|
if (standby_zsk[dnskey.algorithm] != 255)
|
|
|
|
standby_zsk[dnskey.algorithm]++;
|
|
|
|
}
|
|
|
|
dns_rdata_freestruct(&dnskey);
|
|
|
|
dns_rdata_reset(&rdata);
|
|
|
|
}
|
|
|
|
dns_rdataset_disassociate(&keysigs);
|
|
|
|
dns_rdataset_disassociate(&soaset);
|
|
|
|
dns_rdataset_disassociate(&soasigs);
|
|
|
|
if (dns_rdataset_isassociated(&nsecsigs))
|
|
|
|
dns_rdataset_disassociate(&nsecsigs);
|
|
|
|
if (dns_rdataset_isassociated(&nsec3paramsigs))
|
|
|
|
dns_rdataset_disassociate(&nsec3paramsigs);
|
|
|
|
|
|
|
|
if (ignore_kskflag ) {
|
|
|
|
if (!goodksk && !goodzsk)
|
|
|
|
fatal("No self-signed DNSKEY found.");
|
|
|
|
} else if (!goodksk)
|
|
|
|
fatal("No self-signed KSK DNSKEY found. Supply an active\n"
|
|
|
|
"key with the KSK flag set, or use '-P'.");
|
|
|
|
|
|
|
|
fprintf(stderr, "Verifying the zone using the following algorithms:");
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
|
|
if (ignore_kskflag)
|
|
|
|
act_algorithms[i] = (ksk_algorithms[i] != 0 ||
|
|
|
|
zsk_algorithms[i] != 0) ? 1 : 0;
|
|
|
|
else
|
|
|
|
act_algorithms[i] = ksk_algorithms[i] != 0 ? 1 : 0;
|
|
|
|
if (act_algorithms[i] != 0) {
|
|
|
|
dns_secalg_format(i, algbuf, sizeof(algbuf));
|
|
|
|
fprintf(stderr, " %s", algbuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(stderr, ".\n");
|
|
|
|
|
|
|
|
if (!ignore_kskflag && !keyset_kskonly) {
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
|
|
/*
|
|
|
|
* The counts should both be zero or both be non-zero.
|
|
|
|
* Mark the algorithm as bad if this is not met.
|
|
|
|
*/
|
|
|
|
if ((ksk_algorithms[i] != 0) ==
|
|
|
|
(zsk_algorithms[i] != 0))
|
|
|
|
continue;
|
|
|
|
dns_secalg_format(i, algbuf, sizeof(algbuf));
|
|
|
|
fprintf(stderr, "Missing %s for algorithm %s\n",
|
|
|
|
(ksk_algorithms[i] != 0)
|
|
|
|
? "ZSK"
|
|
|
|
: "self-signed KSK",
|
|
|
|
algbuf);
|
|
|
|
bad_algorithms[i] = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that all the other records were signed by keys that are
|
|
|
|
* present in the DNSKEY RRSET.
|
|
|
|
*/
|
|
|
|
|
|
|
|
dns_fixedname_init(&fname);
|
|
|
|
name = dns_fixedname_name(&fname);
|
|
|
|
dns_fixedname_init(&fnextname);
|
|
|
|
nextname = dns_fixedname_name(&fnextname);
|
2013-01-23 14:56:00 -08:00
|
|
|
dns_fixedname_init(&fprevname);
|
|
|
|
prevname = NULL;
|
2012-06-25 13:57:32 +10:00
|
|
|
dns_fixedname_init(&fzonecut);
|
|
|
|
zonecut = NULL;
|
|
|
|
|
|
|
|
result = dns_db_createiterator(db, DNS_DB_NONSEC3, &dbiter);
|
|
|
|
check_result(result, "dns_db_createiterator()");
|
|
|
|
|
|
|
|
result = dns_dbiterator_first(dbiter);
|
|
|
|
check_result(result, "dns_dbiterator_first()");
|
|
|
|
|
|
|
|
while (!done) {
|
|
|
|
isc_boolean_t isdelegation = ISC_FALSE;
|
|
|
|
|
|
|
|
result = dns_dbiterator_current(dbiter, &node, name);
|
|
|
|
check_dns_dbiterator_current(result);
|
|
|
|
if (!dns_name_issubdomain(name, origin)) {
|
|
|
|
check_no_nsec(name, node, db, ver);
|
|
|
|
dns_db_detachnode(db, &node);
|
|
|
|
result = dns_dbiterator_next(dbiter);
|
|
|
|
if (result == ISC_R_NOMORE)
|
|
|
|
done = ISC_TRUE;
|
|
|
|
else
|
|
|
|
check_result(result, "dns_dbiterator_next()");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (is_delegation(db, ver, origin, name, node, NULL)) {
|
|
|
|
zonecut = dns_fixedname_name(&fzonecut);
|
|
|
|
dns_name_copy(name, zonecut, NULL);
|
|
|
|
isdelegation = ISC_TRUE;
|
|
|
|
}
|
|
|
|
nextnode = NULL;
|
|
|
|
result = dns_dbiterator_next(dbiter);
|
|
|
|
while (result == ISC_R_SUCCESS) {
|
|
|
|
result = dns_dbiterator_current(dbiter, &nextnode,
|
|
|
|
nextname);
|
|
|
|
check_dns_dbiterator_current(result);
|
|
|
|
if (!dns_name_issubdomain(nextname, origin) ||
|
|
|
|
(zonecut != NULL &&
|
|
|
|
dns_name_issubdomain(nextname, zonecut)))
|
|
|
|
{
|
|
|
|
check_no_nsec(nextname, nextnode, db, ver);
|
|
|
|
dns_db_detachnode(db, &nextnode);
|
|
|
|
result = dns_dbiterator_next(dbiter);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (is_empty(db, ver, nextnode)) {
|
|
|
|
dns_db_detachnode(db, &nextnode);
|
|
|
|
result = dns_dbiterator_next(dbiter);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
dns_db_detachnode(db, &nextnode);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (result == ISC_R_NOMORE) {
|
|
|
|
done = ISC_TRUE;
|
|
|
|
nextname = origin;
|
|
|
|
} else if (result != ISC_R_SUCCESS)
|
|
|
|
fatal("iterating through the database failed: %s",
|
|
|
|
isc_result_totext(result));
|
|
|
|
result = verifynode(db, ver, origin, mctx, name, node,
|
|
|
|
isdelegation, &keyset, act_algorithms,
|
|
|
|
bad_algorithms, &nsecset, &nsec3paramset,
|
|
|
|
nextname);
|
|
|
|
if (vresult == ISC_R_UNSET)
|
|
|
|
vresult = ISC_R_SUCCESS;
|
|
|
|
if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS)
|
|
|
|
vresult = result;
|
2013-01-23 14:56:00 -08:00
|
|
|
if (prevname != NULL) {
|
|
|
|
result = verifyemptynodes(db, ver, origin, mctx, name,
|
|
|
|
prevname, isdelegation,
|
|
|
|
&nsec3paramset);
|
|
|
|
} else
|
|
|
|
prevname = dns_fixedname_name(&fprevname);
|
|
|
|
dns_name_copy(name, prevname, NULL);
|
2012-06-25 13:57:32 +10:00
|
|
|
if (vresult == ISC_R_SUCCESS && result != ISC_R_SUCCESS)
|
|
|
|
vresult = result;
|
|
|
|
dns_db_detachnode(db, &node);
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_dbiterator_destroy(&dbiter);
|
|
|
|
|
|
|
|
result = dns_db_createiterator(db, DNS_DB_NSEC3ONLY, &dbiter);
|
|
|
|
check_result(result, "dns_db_createiterator()");
|
|
|
|
|
|
|
|
for (result = dns_dbiterator_first(dbiter);
|
|
|
|
result == ISC_R_SUCCESS;
|
|
|
|
result = dns_dbiterator_next(dbiter) ) {
|
|
|
|
result = dns_dbiterator_current(dbiter, &node, name);
|
|
|
|
check_dns_dbiterator_current(result);
|
|
|
|
result = verifynode(db, ver, origin, mctx, name, node,
|
|
|
|
ISC_FALSE, &keyset, act_algorithms,
|
|
|
|
bad_algorithms, NULL, NULL, NULL);
|
|
|
|
check_result(result, "verifynode");
|
|
|
|
record_found(db, ver, mctx, name, node, &nsec3paramset);
|
|
|
|
dns_db_detachnode(db, &node);
|
|
|
|
}
|
|
|
|
dns_dbiterator_destroy(&dbiter);
|
|
|
|
|
|
|
|
dns_rdataset_disassociate(&keyset);
|
|
|
|
if (dns_rdataset_isassociated(&nsecset))
|
|
|
|
dns_rdataset_disassociate(&nsecset);
|
|
|
|
if (dns_rdataset_isassociated(&nsec3paramset))
|
|
|
|
dns_rdataset_disassociate(&nsec3paramset);
|
|
|
|
|
|
|
|
result = verify_nsec3_chains(mctx);
|
|
|
|
if (vresult == ISC_R_UNSET)
|
|
|
|
vresult = ISC_R_SUCCESS;
|
|
|
|
if (result != ISC_R_SUCCESS && vresult == ISC_R_SUCCESS)
|
|
|
|
vresult = result;
|
|
|
|
isc_heap_destroy(&expected_chains);
|
|
|
|
isc_heap_destroy(&found_chains);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we made it this far, we have what we consider a properly signed
|
|
|
|
* zone. Set the good flag.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
|
|
if (bad_algorithms[i] != 0) {
|
|
|
|
if (first)
|
|
|
|
fprintf(stderr, "The zone is not fully signed "
|
|
|
|
"for the following algorithms:");
|
|
|
|
dns_secalg_format(i, algbuf, sizeof(algbuf));
|
|
|
|
fprintf(stderr, " %s", algbuf);
|
|
|
|
first = ISC_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!first) {
|
|
|
|
fprintf(stderr, ".\n");
|
|
|
|
fatal("DNSSEC completeness test failed.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vresult != ISC_R_SUCCESS)
|
|
|
|
fatal("DNSSEC completeness test failed (%s).",
|
|
|
|
dns_result_totext(vresult));
|
|
|
|
|
|
|
|
if (goodksk || ignore_kskflag) {
|
|
|
|
/*
|
|
|
|
* Print the success summary.
|
|
|
|
*/
|
|
|
|
fprintf(stderr, "Zone fully signed:\n");
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
|
|
if ((ksk_algorithms[i] != 0) ||
|
|
|
|
(standby_ksk[i] != 0) ||
|
2017-09-13 09:50:51 +10:00
|
|
|
(revoked_ksk[i] != 0) ||
|
2012-06-25 13:57:32 +10:00
|
|
|
(zsk_algorithms[i] != 0) ||
|
|
|
|
(standby_zsk[i] != 0) ||
|
|
|
|
(revoked_zsk[i] != 0)) {
|
|
|
|
dns_secalg_format(i, algbuf, sizeof(algbuf));
|
|
|
|
fprintf(stderr, "Algorithm: %s: KSKs: "
|
|
|
|
"%u active, %u stand-by, %u revoked\n",
|
|
|
|
algbuf, ksk_algorithms[i],
|
|
|
|
standby_ksk[i], revoked_ksk[i]);
|
|
|
|
fprintf(stderr, "%*sZSKs: "
|
|
|
|
"%u active, %u %s, %u revoked\n",
|
|
|
|
(int) strlen(algbuf) + 13, "",
|
|
|
|
zsk_algorithms[i],
|
|
|
|
standby_zsk[i],
|
|
|
|
keyset_kskonly ? "present" : "stand-by",
|
|
|
|
revoked_zsk[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-11-05 12:09:48 +11:00
|
|
|
|
|
|
|
isc_boolean_t
|
|
|
|
isoptarg(const char *arg, char **argv, void(*usage)(void)) {
|
|
|
|
if (!strcasecmp(isc_commandline_argument, arg)) {
|
|
|
|
if (argv[isc_commandline_index] == NULL) {
|
|
|
|
fprintf(stderr, "%s: missing argument -%c %s\n",
|
|
|
|
program, isc_commandline_option,
|
|
|
|
isc_commandline_argument);
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
isc_commandline_argument = argv[isc_commandline_index];
|
|
|
|
/* skip to next arguement */
|
|
|
|
isc_commandline_index++;
|
|
|
|
return (ISC_TRUE);
|
|
|
|
}
|
|
|
|
return (ISC_FALSE);
|
|
|
|
}
|
2016-10-19 17:18:42 +02:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
void
|
|
|
|
InitSockets(void) {
|
|
|
|
WORD wVersionRequested;
|
|
|
|
WSADATA wsaData;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
wVersionRequested = MAKEWORD(2, 0);
|
|
|
|
|
|
|
|
err = WSAStartup( wVersionRequested, &wsaData );
|
|
|
|
if (err != 0) {
|
|
|
|
fprintf(stderr, "WSAStartup() failed: %d\n", err);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DestroySockets(void) {
|
|
|
|
WSACleanup();
|
|
|
|
}
|
|
|
|
#endif
|