dnssectool.c revision c76e8412f4ff4f5945157410312df2a8950f942d
/*
* Copyright (C) 2000, 2001, 2003-2005, 2007, 2009-2016 Internet Systems Consortium, Inc. ("ISC")
*
* 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/.
*/
/*! \file */
/*%
* DNSSEC Support Routines.
*/
#include <config.h>
#include <stdlib.h>
#ifdef _WIN32
#include <Winsock2.h>
#endif
#include <isc/commandline.h>
#include <dns/dbiterator.h>
#include <dns/fixedname.h>
#include <dns/keyvalues.h>
#include <dns/rdatastruct.h>
#include <dns/rdataclass.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
#include <dns/rdatatype.h>
#include "dnssectool.h"
struct nsec3_chain_fixed {
/* unsigned char salt[0]; */
/* unsigned char owner[0]; */
/* unsigned char next[0]; */
};
extern int verbose;
extern const char *program;
typedef struct entropysource entropysource_t;
struct entropysource {
};
void
if (fatalcallback != NULL)
(*fatalcallback)();
exit(1);
}
void
}
void
if (result != ISC_R_SUCCESS)
}
void
return;
}
void
exit(0);
}
void
isc_buffer_t b;
isc_region_t r;
isc_buffer_usedregion(&b, &r);
}
void
char namestr[DNS_NAME_FORMATSIZE];
char algstr[DNS_NAME_FORMATSIZE];
}
void
int level;
if (verbose < 0)
verbose = 0;
switch (verbose) {
case 0:
/*
* We want to see warnings about things like out-of-zone
* data in the master file even when not verbose.
*/
break;
case 1:
break;
default:
break;
}
/*
* 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
*/
}
void
return;
}
void
if (result != ISC_R_SUCCESS)
fatal("could not create entropy object");
}
randomfile = NULL;
}
if (result != ISC_R_SUCCESS)
fatal("could not initialize entropy source: %s",
fatal("out of memory");
}
}
void
while (!ISC_LIST_EMPTY(sources)) {
}
}
static isc_stdtime_t
switch (suffix[0]) {
case 'Y': case 'y':
case 'M': case 'm':
switch (suffix[1]) {
case 'O': case 'o':
case 'I': case 'i':
return (offset * 60);
case '\0':
fatal("'%s' ambiguous: use 'mi' for minutes "
"or 'mo' for months", str);
default:
}
/* NOTREACHED */
break;
case 'W': case 'w':
case 'D': case 'd':
case 'H': case 'h':
return (offset * 3600);
case 'S': case 's': case '\0':
return (offset);
default:
}
/* NOTREACHED */
return(0); /* silence compiler warning */
}
static inline isc_boolean_t
}
char *endp;
return ((dns_ttl_t) 0);
fatal("TTL must be numeric");
return (ttl);
}
{
char *endp;
size_t n;
return ((isc_stdtime_t) 0);
}
return ((isc_stdtime_t) 0);
/*
* We accept times in the following formats:
* now([+-]offset)
* YYYYMMDD([+-]offset)
* YYYYMMDDhhmmss([+-]offset)
* [+-]offset
*/
if ((n == 8u || n == 14u) &&
{
char timestr[15];
timestr[n] = 0;
if (n == 8u)
if (result != ISC_R_SUCCESS)
str += n;
str += 3;
}
if (str[0] == '\0')
return ((isc_stdtime_t) base);
else if (str[0] == '+') {
} else if (str[0] == '-') {
} else
return ((isc_stdtime_t) val);
}
strtoclass(const char *str) {
return dns_rdataclass_in;
if (ret != ISC_R_SUCCESS)
return (rdclass);
}
isc_dir_t d;
isc_dir_init(&d);
if (result == ISC_R_SUCCESS) {
isc_dir_close(&d);
}
return (result);
}
/*
* Check private key version compatibility.
*/
void
fatal("Key %s has incompatible format version %d.%d, "
"use -f to force upgrade to new version.",
if (minor > DST_MINOR_VERSION)
fatal("Key %s has incompatible format version %d.%d, "
"use -f to force downgrade to current version.",
}
void
/*
* If the key is from a version older than 1.3, set
* set the creation date
*/
}
}
{
char filename[ISC_DIR_NAMEMAX];
/*
* For HMAC and 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.
*/
switch (alg) {
case DST_ALG_HMACMD5:
case DST_ALG_HMACSHA1:
case DST_ALG_HMACSHA224:
case DST_ALG_HMACSHA256:
case DST_ALG_HMACSHA384:
case DST_ALG_HMACSHA512:
case DST_ALG_DH:
if (result != ISC_R_SUCCESS)
return (ISC_TRUE);
return (isc_file_exists(filename));
}
if (result == ISC_R_NOTFOUND)
return (ISC_FALSE);
goto next;
if (verbose > 1)
"collide with %d\n",
} else {
if (verbose > 1)
id);
}
}
next:
}
/* Finish freeing the list */
while (!ISC_LIST_EMPTY(matchkeys)) {
}
return (conflict);
}
{
return (ISC_FALSE);
if (dns_rdataset_isassociated(&nsset)) {
}
}
static isc_boolean_t
{
result == ISC_R_SUCCESS;
&dstkey);
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
continue;
}
if (result == ISC_R_SUCCESS)
return(ISC_TRUE);
}
return (ISC_FALSE);
}
static isc_result_t
{
unsigned char buffer[DNS_NSEC_BUFFERSIZE];
char namebuf[DNS_NAME_FORMATSIZE];
char nextbuf[DNS_NAME_FORMATSIZE];
char found[DNS_NAME_FORMATSIZE];
if (result != ISC_R_SUCCESS) {
goto failure;
}
/* Check bit next name is consistent */
"mismatch (expected:%s, found:%s)\n", namebuf,
goto failure;
}
/* Check bit map is consistent */
&tmprdata);
"mismatch\n", namebuf);
goto failure;
}
if (result != ISC_R_NOMORE) {
goto failure;
}
return (ISC_R_SUCCESS);
return (ISC_R_FAILURE);
}
static void
{
char namebuf[DNS_NAME_FORMATSIZE];
char typebuf[80];
result == ISC_R_SUCCESS;
break;
}
if (result == ISC_R_SUCCESS) {
}
}
static isc_boolean_t
/*
* Do each element in turn to get a stable sort.
*/
return (ISC_TRUE);
return (ISC_FALSE);
return (ISC_TRUE);
return (ISC_FALSE);
return (ISC_TRUE);
return (ISC_FALSE);
return (ISC_TRUE);
return (ISC_FALSE);
return (ISC_TRUE);
return (ISC_FALSE);
}
static isc_boolean_t
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_TRUE);
}
static isc_result_t
{
struct nsec3_chain_fixed *element;
unsigned char *cp;
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS) {
}
return (result);
}
static isc_result_t
{
unsigned char cbm[8244];
char namebuf[DNS_NAME_FORMATSIZE];
unsigned int len;
/*
* Find matching NSEC3 record.
*/
result == ISC_R_SUCCESS;
nsec3param->salt_length) == 0)
break;
}
if (result != ISC_R_SUCCESS) {
return (result);
}
/*
* Check the type list.
*/
"mismatch\n", namebuf);
return (ISC_R_FAILURE);
}
/*
* Record chain.
*/
/*
* Make sure there is only one NSEC3 record with this set of
* parameters.
*/
result == ISC_R_SUCCESS;
nsec3.salt_length) == 0) {
"same parameter set for %s", namebuf);
break;
}
}
if (result != ISC_R_NOMORE)
return (result);
return (result);
}
static isc_boolean_t
result == ISC_R_SUCCESS;
if (nsec3param.flags == 0 &&
nsec3->salt_length) == 0)
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static isc_result_t
{
unsigned char owner[NSEC3_MAX_HASH_LENGTH];
isc_buffer_t b;
return (ISC_R_SUCCESS);
if (result != ISC_R_SUCCESS)
return (ISC_R_SUCCESS);
if (result != ISC_R_SUCCESS)
goto cleanup;
result == ISC_R_SUCCESS;
continue;
/*
* We only care about NSEC3 records that match a NSEC3PARAM
* record.
*/
continue;
/*
* Record chain.
*/
}
return (ISC_R_SUCCESS);
}
static isc_boolean_t
{
unsigned char rawhash[NSEC3_MAX_HASH_LENGTH];
if (result == ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
if (result != ISC_R_SUCCESS)
else
return (ret);
}
static isc_result_t
{
char namebuf[DNS_NAME_FORMATSIZE];
char hashbuf[DNS_NAME_FORMATSIZE];
unsigned char rawhash[NSEC3_MAX_HASH_LENGTH];
if (nsec3param.flags != 0)
return (ISC_R_SUCCESS);
return (ISC_R_SUCCESS);
/*
* 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.
*/
if (result == ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS &&
{
} else if (result == ISC_R_NOTFOUND &&
{
} else if (result == ISC_R_SUCCESS) {
}
return (result);
}
static isc_result_t
{
result == ISC_R_SUCCESS;
if (result != ISC_R_SUCCESS)
break;
}
if (result == ISC_R_NOMORE)
return (result);
}
static void
unsigned char *act_algorithms, unsigned char *bad_algorithms)
{
unsigned char set_algorithms[256];
char namebuf[DNS_NAME_FORMATSIZE];
char algbuf[80];
char typebuf[80];
int i;
result == ISC_R_SUCCESS;
break;
}
if (result != ISC_R_SUCCESS) {
for (i = 0; i < 256; i++)
if (act_algorithms[i] != 0)
bad_algorithms[i] = 1;
return;
}
result == ISC_R_SUCCESS;
continue;
}
continue;
}
for (i = 0; i < 256; i++)
if ((act_algorithms[i] != 0) &&
(set_algorithms[i] == 0)) {
bad_algorithms[i] = 1;
}
}
}
static isc_result_t
unsigned char *act_algorithms, unsigned char *bad_algorithms,
{
unsigned char types[8192];
unsigned int maxtype = 0;
while (result == ISC_R_SUCCESS) {
/*
* 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.
*/
} else
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration failed: %s",
}
return (result);
}
static isc_boolean_t
if (result == ISC_R_NOMORE)
return (ISC_TRUE);
return (ISC_FALSE);
}
static void
{
if (result != ISC_R_NOTFOUND) {
char namebuf[DNS_NAME_FORMATSIZE];
}
}
static isc_boolean_t
const struct nsec3_chain_fixed *e)
{
return (ISC_TRUE);
return (ISC_FALSE);
}
static void
}
static isc_boolean_t
const struct nsec3_chain_fixed *e)
{
char buf[512];
const unsigned char *d2 = (const unsigned char *)(e + 1);
isc_buffer_t b;
d2 += e->salt_length;
return (ISC_TRUE);
(int) isc_buffer_usedlength(&b), buf);
buf);
return (ISC_FALSE);
}
#define EXPECTEDANDFOUND "Expected and found NSEC3 chains not equal\n"
static isc_result_t
struct nsec3_chain_fixed *e, *f = NULL;
if (f == NULL)
if (f != NULL) {
/*
* Check that they match.
*/
if (chain_equal(e, f)) {
free_element(mctx, f);
f = NULL;
} else {
if (result == ISC_R_SUCCESS)
/*
* Attempt to resync found_chain.
*/
while (f != NULL && !chain_compare(e, f)) {
free_element(mctx, f);
if (f != NULL)
if (f != NULL && chain_equal(e, f)) {
free_element(mctx, f);
f = NULL;
break;
}
}
}
} else if (result == ISC_R_SUCCESS) {
}
}
continue;
}
prev = e;
}
}
do {
if (f != NULL) {
if (result == ISC_R_SUCCESS) {
}
free_element(mctx, f);
}
if (f != NULL)
} while (f != NULL);
return (result);
}
static isc_result_t
{
int order;
if (order >= 0)
return (result);
if (reln == dns_namereln_commonancestor ||
reln == dns_namereln_contains) {
&suffix);
if (nsec3paramset != NULL &&
NULL, 0);
if (result == ISC_R_SUCCESS &&
tresult != ISC_R_SUCCESS)
}
}
}
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
{
char algbuf[80];
int i;
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];
&found_chains);
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone's origin: %s",
if (result != ISC_R_SUCCESS)
fatal("Zone contains no DNSSEC keys\n");
if (result != ISC_R_SUCCESS)
fatal("Zone contains no SOA record\n");
fatal("NSEC lookup failed\n");
0, 0, &nsec3paramset, &nsec3paramsigs);
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) &&
fatal("NSEC is not signed (keys offline or inactive?)\n");
if (dns_rdataset_isassociated(&nsec3paramset) &&
fatal("NSEC3PARAM is not signed (keys offline or inactive?)\n");
if (!dns_rdataset_isassociated(&nsecset) &&
/*
* 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).
*/
result == ISC_R_SUCCESS;
;
mctx)) {
char namebuf[DNS_NAME_FORMATSIZE];
char buffer[1024];
sizeof(namebuf));
fatal("revoked KSK is not self signed:\n"
"%s DNSKEY %.*s", namebuf,
}
} else {
}
} else {
}
}
if (ignore_kskflag ) {
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'.");
for (i = 0; i < 256; i++) {
if (ignore_kskflag)
act_algorithms[i] = (ksk_algorithms[i] != 0 ||
zsk_algorithms[i] != 0) ? 1 : 0;
else
if (act_algorithms[i] != 0) {
}
}
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;
(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.
*/
while (!done) {
if (result == ISC_R_NOMORE)
else
continue;
}
}
while (result == ISC_R_SUCCESS) {
nextname);
{
continue;
}
continue;
}
break;
}
if (result == ISC_R_NOMORE) {
} else if (result != ISC_R_SUCCESS)
fatal("iterating through the database failed: %s",
nextname);
if (vresult == ISC_R_UNSET)
} else
}
result == ISC_R_SUCCESS;
}
if (dns_rdataset_isassociated(&nsecset))
if (vresult == ISC_R_UNSET)
/*
* 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)
"for the following algorithms:");
}
}
if (!first) {
fatal("DNSSEC completeness test failed.");
}
if (vresult != ISC_R_SUCCESS)
fatal("DNSSEC completeness test failed (%s).",
if (goodksk || ignore_kskflag) {
/*
* Print the success summary.
*/
for (i = 0; i < 256; i++) {
if ((ksk_algorithms[i] != 0) ||
(standby_ksk[i] != 0) ||
(revoked_ksk[i] != 0) ||
(zsk_algorithms[i] != 0) ||
(standby_zsk[i] != 0) ||
(revoked_zsk[i] != 0)) {
"%u active, %u stand-by, %u revoked\n",
algbuf, ksk_algorithms[i],
standby_ksk[i], revoked_ksk[i]);
"%u active, %u %s, %u revoked\n",
zsk_algorithms[i],
standby_zsk[i],
revoked_zsk[i]);
}
}
}
}
usage();
}
/* skip to next arguement */
return (ISC_TRUE);
}
return (ISC_FALSE);
}
#ifdef _WIN32
void
InitSockets(void) {
int err;
if (err != 0) {
exit(1);
}
}
void
DestroySockets(void) {
WSACleanup();
}
#endif