dnssec-signzone.c revision 649452635065426fcc08b99b351db904939a6580
/*
* Portions Copyright (C) 2004-2011 Internet Systems Consortium, Inc. ("ISC")
* Portions Copyright (C) 1999-2003 Internet Software Consortium.
*
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE
* FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Portions Copyright (C) 1995-2000 by Network Associates, Inc.
*
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE
* FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dnssec-signzone.c,v 1.271 2011/03/11 12:37:01 marka Exp $ */
/*! \file */
#include <config.h>
#include <stdlib.h>
#include <time.h>
#include <isc/commandline.h>
#include <dns/dbiterator.h>
#include <dns/fixedname.h>
#include <dns/keyvalues.h>
#include <dns/masterdump.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdataclass.h>
#include <dns/rdatasetiter.h>
#include <dns/rdatastruct.h>
#include <dns/rdatatype.h>
#include "dnssectool.h"
#ifndef PATH_MAX
#endif
const char *program = "dnssec-signzone";
int verbose;
typedef struct hashlist hashlist_t;
static int nsec_datatype = dns_rdatatype_nsec;
#define OPTOUT(x) (((x) & DNS_NSEC3FLAG_OPTOUT) != 0)
#define BUFSIZE 2048
#define MAXDSKEYS 8
#define SIGNER_EVENT_WRITE (SIGNER_EVENTCLASS + 0)
#define SOA_SERIAL_KEEP 0
#define SOA_SERIAL_INCREMENT 1
#define SOA_SERIAL_UNIXTIME 2
typedef struct signer_event sevent_t;
struct signer_event {
};
static dns_dnsseckeylist_t keylist;
static unsigned int keycount = 0;
static int cycle = -1;
static int jitter = 0;
static dns_ttl_t zone_soa_min_ttl;
static const dns_master_style_t *masterstyle;
static unsigned int nverified = 0, nverifyfailed = 0;
static int nsec3flags = 0;
static unsigned char saltbuf[255];
static size_t salt_length = 0;
static unsigned int ntasks = 0;
static dns_fixedname_t dlv_fixed;
static unsigned int serialformat = SOA_SERIAL_KEEP;
static unsigned int hash_length = 0;
if (printstats) { \
counter++; \
}
static void
#define check_dns_dbiterator_current(result) \
"dns_dbiterator_current()")
static void
isc_region_t r;
unsigned bufsize = 4096;
if (outputformat != dns_masterformat_text)
return;
if (!output_dnssec_only) {
return;
}
result == ISC_R_SUCCESS;
continue;
}
for (;;) {
if (result != ISC_R_NOSPACE)
break;
bufsize <<= 1;
}
isc_buffer_usedregion(buffer, &r);
}
}
/*%
* Sign the given RRset with given key, and add the signature record to the
* given tuple.
*/
static void
{
char keystr[DST_KEY_FORMATSIZE];
isc_buffer_t b;
else
if (result != ISC_R_SUCCESS) {
char keystr[DST_KEY_FORMATSIZE];
fatal("dnskey '%s' failed to sign data: %s",
}
if (tryverify) {
if (result == ISC_R_SUCCESS) {
} else {
}
}
&tuple);
}
static inline isc_boolean_t
}
static inline isc_boolean_t
}
static inline isc_boolean_t
}
static inline isc_boolean_t
}
/*%
* Find the key that generated an RRSIG, if it is in the key list. If
* so, return a pointer to it, otherwise return NULL.
*
* No locking is performed here, this must be done by the caller.
*/
static dns_dnsseckey_t *
return (key);
}
return (NULL);
}
/*%
* Finds the key that generated a RRSIG, if possible. First look at the keys
* that we've loaded already, and then see if there's a key on disk.
*/
static dns_dnsseckey_t *
return (key);
/*
* We did not find the key in our list. Get a write lock now, since
* we may be modifying the bits. We could do the tryupgrade() dance,
* but instead just get a write lock and check once again to see if
* it is on our list. It's possible someone else may have added it
* after all.
*/
return (key);
}
if (result != ISC_R_SUCCESS) {
return (NULL);
}
if (result == ISC_R_SUCCESS) {
} else {
}
return (key);
}
/*%
* Check to see if we expect to find a key at this name. If we see a RRSIG
* and can't find the signing key that we expect to find, we drop the rrsig.
* I'm not sure if this is completely correct, but it seems to work.
*/
static isc_boolean_t
unsigned int options = DNS_DBFIND_NOWILD;
char namestr[DNS_NAME_FORMATSIZE];
switch (result) {
case ISC_R_SUCCESS:
case DNS_R_NXDOMAIN:
case DNS_R_NXRRSET:
return (ISC_TRUE);
case DNS_R_DELEGATION:
case DNS_R_CNAME:
case DNS_R_DNAME:
return (ISC_FALSE);
}
fatal("failure looking for '%s DNSKEY' in database: %s",
/* NOTREACHED */
return (ISC_FALSE); /* removes a warning */
}
static inline isc_boolean_t
{
if (result == ISC_R_SUCCESS) {
return (ISC_TRUE);
} else {
return (ISC_FALSE);
}
}
/*%
* Signs a set. Goes through contortions to decide if each RRSIG should
* be dropped or retained, and then determines if any new SIGs need to
* be generated.
*/
static void
{
int arraysize;
int i;
char namestr[DNS_NAME_FORMATSIZE];
char typestr[TYPE_FORMATSIZE];
char sigstr[SIG_FORMATSIZE];
if (result == ISC_R_NOTFOUND) {
}
if (result != ISC_R_SUCCESS)
fatal("failed while looking for '%s RRSIG %s': %s",
if (!nosigs)
fatal("out of memory");
for (i = 0; i < arraysize; i++)
if (nosigs)
else
while (result == ISC_R_SUCCESS) {
else
/* rrsig is dropped and not replaced */
"invalid validity period\n",
sigstr);
/* rrsig is dropped and not replaced */
"private dnskey not found\n",
sigstr);
if (!expired)
} else if (issigningkey(key)) {
} else {
"ttl change" : "failed to verify");
}
} else {
"ttl change" : "failed to verify");
}
} else if (!expired) {
} else {
}
if (keep) {
&sigrdata,
&tuple);
&sigrdata,
&tuple);
}
} else {
}
if (resign) {
"resigning with dnskey");
}
}
if (result == ISC_R_NOMORE)
if (dns_rdataset_isassociated(&sigset))
{
continue;
if (!issigningkey(key))
continue;
continue;
continue;
}
"signing with dnskey");
"signing with dnskey");
}
}
}
struct hashlist {
unsigned char *hashbuf;
};
static void
l->entries = 0;
if (nodes != 0) {
l->size = 0;
} else {
l->size = 0;
}
}
static void
{
}
l->entries++;
}
static void
unsigned int hashalg, unsigned int iterations,
{
char nametext[DNS_NAME_FORMATSIZE];
unsigned int len;
size_t i;
if (verbose) {
for (i = 0 ; i < len; i++)
}
}
static int
hashlist_comp(const void *a, const void *b) {
}
static void
hashlist_sort(hashlist_t *l) {
}
static isc_boolean_t
hashlist_hasdup(hashlist_t *l) {
unsigned char *current;
/*
* Skip initial speculative wild card hashs.
*/
entries--;
}
while (entries-- > 1U) {
continue;
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static const unsigned char *
hashlist_findnext(const hashlist_t *l,
const unsigned char hash[NSEC3_MAX_HASH_LENGTH])
{
l->length, hashlist_comp);
do {
else
break;
} while (entries-- > 1);
return (next);
}
static isc_boolean_t
hashlist_exists(const hashlist_t *l,
const unsigned char hash[NSEC3_MAX_HASH_LENGTH])
{
return (ISC_TRUE);
else
return (ISC_FALSE);
}
static void
unsigned int hashalg, unsigned int iterations,
{
char namestr[DNS_NAME_FORMATSIZE];
if (result == ISC_R_NOSPACE)
return;
if (result == ISC_R_SUCCESS) {
return;
}
if (verbose) {
}
ISC_TRUE);
}
static void
{
isc_buffer_t b;
/* allow room for a trailing slash */
isc_buffer_putstr(&b, dsdir);
isc_buffer_putstr(&b, "/");
}
isc_buffer_putstr(&b, prefix);
if (isc_buffer_availablelength(&b) == 0) {
char namestr[DNS_NAME_FORMATSIZE];
}
isc_buffer_putuint8(&b, 0);
}
/*%
* Load the DS set for a child zone, if a dsset-* file can be found.
* If not, try to find a keyset-* file from an earlier version of
* dnssec-signzone, and build DS records from that.
*/
static isc_result_t
unsigned char dsbuf[DNS_DS_BUFFERSIZE];
if (result == ISC_R_SUCCESS) {
dns_rdatatype_ds, 0, 0,
if (result == ISC_R_SUCCESS) {
dns_db_detach(&db);
return (result);
}
}
dns_db_detach(&db);
}
/* No DS records found; try again, looking for DNSKEY records */
return (ISC_R_NOTFOUND);
}
if (result != ISC_R_SUCCESS) {
dns_db_detach(&db);
return (result);
}
if (result != ISC_R_SUCCESS) {
dns_db_detach(&db);
return (result);
}
result == ISC_R_SUCCESS;
{
dns_rdata_init(&ds);
}
dns_db_detach(&db);
return (result);
}
static isc_boolean_t
return (ISC_FALSE);
if (dns_rdataset_isassociated(&nsset)) {
}
}
static isc_boolean_t
return (ISC_FALSE);
if (dns_rdataset_isassociated(&dsset))
}
/*%
* Signs all records at a name.
*/
static void
char namestr[DNS_NAME_FORMATSIZE];
/*
* Determine if this is a delegation point.
*/
/*
* Now iterate through the rdatasets.
*/
while (result == ISC_R_SUCCESS) {
/* If this is a RRSIG set, skip it. */
goto skip;
/*
* If this name is a delegation point, skip all records
* except NSEC and DS sets. Otherwise check that there
* isn't a DS record.
*/
if (isdelegation) {
goto skip;
char namebuf[DNS_NAME_FORMATSIZE];
fatal("'%s': found DS RRset without NS RRset\n",
namebuf);
}
skip:
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration for name '%s' failed: %s",
if (result != ISC_R_SUCCESS)
fatal("failed to delete SIGs at node '%s': %s",
if (result != ISC_R_SUCCESS)
fatal("failed to add SIGs at node '%s': %s",
}
static inline isc_boolean_t
while (result == ISC_R_SUCCESS) {
if (!active)
else
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration failed: %s",
/*%
* The node is empty of everything but NSEC / RRSIG records.
*/
result == ISC_R_SUCCESS;
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration failed: %s",
} else {
/*
* Delete RRSIGs for types that no longer exist.
*/
result == ISC_R_SUCCESS;
/*
* Delete the NSEC chain if we are signing with
* NSEC3.
*/
if (nsec_datatype == dns_rdatatype_nsec3 &&
(type == dns_rdatatype_nsec ||
covers == dns_rdatatype_nsec)) {
covers);
continue;
}
if (type != dns_rdatatype_rrsig)
continue;
}
if (!found) {
if (result != ISC_R_NOMORE)
fatal("rdataset iteration failed: %s",
covers);
"dns_db_deleterdataset(rrsig)");
} else if (result != ISC_R_NOMORE &&
result != ISC_R_SUCCESS)
fatal("rdataset iteration failed: %s",
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration failed: %s",
}
return (active);
}
/*%
* Extracts the minimum TTL from the SOA record, and the SOA record's TTL.
*/
static void
get_soa_ttls(void) {
if (result != ISC_R_SUCCESS)
fatal("failed to find an SOA at the zone apex: %s",
}
/*%
* Increment (or set if nonzero) the SOA serial
*/
static isc_result_t
if (result != ISC_R_SUCCESS)
return result;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (serial) {
/* Set SOA serial to the value provided. */
new_serial = serial;
} else {
/* Increment SOA serial using RFC 1982 arithmetics */
if (new_serial == 0)
new_serial = 1;
}
/* If the new serial is not likely to cause a zone transfer
*
* RFC1982 section 7 defines the maximum increment to be
* (2^(32-1))-1. Using u_int32_t arithmetic, we can do a single
* comparison. (5 - 6 == (2^32)-1, not negative-one)
*/
if (new_serial == old_serial ||
"zone may not transfer\n", program);
dns_rdatatype_soa, 0);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
return (result);
}
/*%
* Delete any RRSIG records at a node.
*/
static void
return;
while (result == ISC_R_SUCCESS) {
dns_rdatatype_t covers = 0;
}
if (destroy) {
covers);
}
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration failed: %s",
}
/*%
* Set up the iterator and global state before starting the tasks.
*/
static void
presign(void) {
}
/*%
* Clean up the iterator and global state after the tasks complete.
*/
static void
postsign(void) {
}
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 void
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 (ksk_algorithms[i] != 0)
bad_algorithms[i] = 1;
return;
}
result == ISC_R_SUCCESS;
continue;
}
continue;
}
for (i = 0; i < 256; i++)
if ((ksk_algorithms[i] != 0) &&
(set_algorithms[i] == 0)) {
bad_algorithms[i] = 1;
}
}
}
static void
unsigned char *bad_algorithms)
{
while (result == ISC_R_SUCCESS) {
}
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration failed: %s",
}
/*%
* 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.
*/
static void
verifyzone(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];
#ifdef ALLOW_KSKLESS_ZONES
unsigned char self_algorithms[256];
#endif
if (disable_zone_check)
return;
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone's origin: %s",
0, 0, &rdataset, &sigrdataset);
if (result != ISC_R_SUCCESS)
fatal("cannot find DNSKEY rrset\n");
if (!dns_rdataset_isassociated(&sigrdataset))
fatal("cannot find DNSKEY RRSIGs\n");
#ifdef ALLOW_KSKLESS_ZONES
#endif
/*
* 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 {
}
mctx)) {
#ifdef ALLOW_KSKLESS_ZONES
#endif
} else {
#ifdef ALLOW_KSKLESS_ZONES
#endif
}
}
#ifdef ALLOW_KSKLESS_ZONES
if (!goodksk) {
if (!ignore_kskflag)
"self signed ZSK's for active "
"algorithm list.\n");
if (!allzsksigned)
"signed.\n");
}
#else
if (!goodksk) {
fatal("no self signed KSK's found");
}
#endif
for (i = 0; i < 256; i++) {
#ifdef ALLOW_KSKLESS_ZONES
if (ksk_algorithms[i] != 0 || zsk_algorithms[i] != 0)
#else
if (ksk_algorithms[i] != 0)
#endif
{
}
}
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 signing 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;
}
break;
}
if (result == ISC_R_NOMORE) {
} else if (result != ISC_R_SUCCESS)
fatal("iterating through the database failed: %s",
}
result == ISC_R_SUCCESS;
}
/*
* 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 (goodksk || ignore_kskflag) {
/*
* Print the success summary.
*/
for (i = 0; i < 256; i++) {
if ((ksk_algorithms[i] != 0) ||
(standby_ksk[i] != 0) ||
(revoked_zsk[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]);
}
}
}
}
/*%
* Sign the apex of the zone.
* Note the origin may not be the first node if there are out of zone
* records.
*/
static void
signapex(void) {
if (result == ISC_R_NOMORE)
else if (result != ISC_R_SUCCESS)
fatal("failure iterating database: %s",
}
/*%
* Assigns a node to a worker thread. This is protected by the master task's
* lock.
*/
static void
static unsigned int ended = 0; /* Protected by namelock. */
if (shuttingdown)
return;
if (finished) {
ended++;
}
goto unlock;
}
fatal("out of memory");
while (!found) {
/*
* The origin was handled by signapex().
*/
goto next;
}
/*
* Sort the zone data from the glue and out-of-zone data.
* For NSEC zones nodes with zone data have NSEC records.
* For NSEC3 zones the NSEC3 nodes are zone data but
* outside of the zone name space. For the rest we need
* to track the bottom of zone cuts.
* Nodes which don't need to be signed are dumped here.
*/
nsec_datatype, 0, 0,
if (dns_rdataset_isassociated(&nsec))
if (result == ISC_R_SUCCESS) {
} else if (nsec_datatype == dns_rdatatype_nsec3) {
if (!OPTOUT(nsec3flags) ||
} else
}
}
if (!found) {
}
next:
if (result == ISC_R_NOMORE) {
break;
} else if (result != ISC_R_SUCCESS)
fatal("failure iterating database: %s",
}
if (!found) {
ended++;
}
goto unlock;
}
fatal("failed to allocate event\n");
}
/*%
* Start a worker task
*/
static void
}
/*%
* Write a node to the output file, and restart the worker task.
*/
static void
}
/*%
* Sign a database node.
*/
static void
fatal("failed to allocate event\n");
}
/*%
* Update / remove the DS RRset. Preserve RRSIG(DS) if possible.
*/
static void
if (result == ISC_R_SUCCESS) {
dns_rdatatype_ds, 0);
}
if (result == ISC_R_SUCCESS) {
} else if (dns_rdataset_isassociated(&sigdsset)) {
}
}
/*
* Remove records of the given type and their signatures.
*/
static void
/*
* Delete any records of the given type at the apex.
*/
result == ISC_R_SUCCESS;
fatal("Zone contains NSEC records. Use -u "
"to update to NSEC3.");
fatal("Zone contains NSEC3 chains. Use -u "
"to update to NSEC.");
continue;
}
}
}
/*%
* Generate NSEC records for the zone and remove NSEC3/NSEC3PARAM records.
*/
static void
nsecify(void) {
isc_uint32_t nsttl = 0;
/*
* Remove any NSEC3 chains.
*/
result == ISC_R_SUCCESS;
result == ISC_R_SUCCESS;
"dns_db_deleterdataset(nsec3param/rrsig)");
}
}
while (!done) {
/*
* Skip out-of-zone records.
*/
if (result == ISC_R_NOMORE)
else
continue;
}
if (generateds)
}
while (result == ISC_R_SUCCESS) {
nextname);
if (!active) {
continue;
}
{
continue;
}
break;
}
if (result == ISC_R_NOMORE) {
} else if (result != ISC_R_SUCCESS)
fatal("iterating through the database failed: %s",
}
}
static void
unsigned int iterations)
{
isc_buffer_t b;
nsec3param.flags = 0;
&nsec3param, &b);
/*
* Delete any current NSEC3PARAM records.
*/
if (result == DNS_R_UNCHANGED)
if (result == DNS_R_UNCHANGED)
}
static void
{
unsigned char hash[NSEC3_MAX_HASH_LENGTH];
const unsigned char *nexthash;
unsigned char nsec3buffer[DNS_NSEC3_BUFFERSIZE];
char namebuf[DNS_NAME_FORMATSIZE];
salt, salt_length);
nsec3buffer, &rdata);
0, NULL);
if (result == DNS_R_UNCHANGED)
}
/*%
* Clean out NSEC3 record and RRSIG(NSEC3) that are not in the hash list.
*
* Extract the hash from the first label of 'name' then see if it
* is in hashlist. If 'name' is not in the hashlist then delete the
* any NSEC3 records which have the same parameters as the chain we
* are building.
*
* XXXMPA Should we also check that it of the form <hash>.<origin>?
*/
static void
unsigned int hashalg, unsigned int iterations,
{
/*
* Get the first label.
*/
/*
* We want just the label contents.
*/
/*
* Decode base32hex string.
*/
if (result != ISC_R_SUCCESS)
return;
/*
* Verify that the NSEC3 parameters match the current ones
* otherwise we are dealing with a different NSEC3 chain.
*/
if (result != ISC_R_SUCCESS)
return;
/*
* Delete any NSEC3 records which are not part of the current
* NSEC3 chain.
*/
result == ISC_R_SUCCESS;
continue;
&delrdataset, 0, NULL);
}
if (result != ISC_R_NOMORE)
if (!delete_rrsigs)
return;
/*
* Delete the NSEC3 RRSIGs
*/
}
static void
{
unsigned int count1 = 0;
result == ISC_R_SUCCESS;
unsigned int count2 = 0;
count1++;
result == ISC_R_SUCCESS;
count2++;
continue;
name,
}
}
}
}
static void
remove_duplicates(void) {
result == ISC_R_SUCCESS;
result == ISC_R_SUCCESS;
}
if (result != ISC_R_NOMORE)
fatal("rdatasets iteration failed.");
}
if (result != ISC_R_NOMORE)
fatal("zone iteration failed.");
}
}
/*
* Generate NSEC3 records for the zone.
*/
static void
{
int order;
isc_uint32_t nsttl = 0;
/*
* Walk the zone generating the hash names.
*/
while (!done) {
/*
* Skip out-of-zone records.
*/
if (result == ISC_R_NOMORE)
else
continue;
}
while (result == ISC_R_SUCCESS) {
nextname);
if (!active) {
continue;
}
continue;
}
if (generateds)
if (OPTOUT(nsec3flags) &&
continue;
}
}
break;
}
if (result == ISC_R_NOMORE) {
} else if (result != ISC_R_SUCCESS)
fatal("iterating through the database failed: %s",
/*
* Add hashs for empty nodes. Use closest encloser logic.
* The closest encloser either has data or is a empty
* node for another <name,nextname> span so we don't add
* it here. Empty labels on nextname are within the span.
*/
salt, salt_length);
count--;
}
}
/*
* We have all the hashes now so we can sort them.
*/
/*
* Check for duplicate hashes. If found the salt needs to
* be changed.
*/
if (hashlist_hasdup(hashlist))
fatal("Duplicate hash detected. Pick a different salt.");
/*
* Generate the nsec3 records.
*/
/*
* Clean out NSEC3 records which don't match this chain.
*/
result == ISC_R_SUCCESS;
hashlist);
}
/*
* Generate / complete the new chain.
*/
while (!done) {
/*
* Skip out-of-zone records.
*/
if (result == ISC_R_NOMORE)
else
continue;
}
while (result == ISC_R_SUCCESS) {
nextname);
if (!active) {
continue;
}
continue;
}
if (OPTOUT(nsec3flags) &&
continue;
}
}
break;
}
if (result == ISC_R_NOMORE) {
} else if (result != ISC_R_SUCCESS)
fatal("iterating through the database failed: %s",
/*
* We need to pause here to release the lock on the database.
*/
/*
* Add NSEC3's for empty nodes. Use closest encloser logic.
*/
count--;
}
}
}
/*%
* Load the zone file from disk
*/
static void
isc_buffer_t b;
int len;
isc_buffer_add(&b, len);
if (result != ISC_R_SUCCESS)
fatal("failed converting name '%s' to dns format: %s",
fatal("failed loading zone from '%s': %s",
}
/*%
* Finds all public zone keys in the zone, and attempts to load the
* private keys from disk.
*/
static void
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone's origin: %s",
/* Make note of the keys which signed the SOA, if any */
dns_rdatatype_soa, 0, 0,
if (result != ISC_R_SUCCESS)
goto cleanup;
/* Preserve the TTL of the DNSKEY RRset, if any */
dns_rdatatype_dnskey, 0, 0,
if (result != ISC_R_SUCCESS)
goto cleanup;
"with existing DNSKEY RRset TTL.\n",
keyttl);
"TTL (%d) instead.\n",
}
/* Load keys corresponding to the existing DNSKEY RRset. */
&keylist);
if (result != ISC_R_SUCCESS)
fatal("failed to load the zone keys: %s",
if (dns_rdataset_isassociated(&keysigs))
if (dns_rdataset_isassociated(&soasigs))
}
static void
int i;
for (i = 0; i < n; i++) {
if (result != ISC_R_SUCCESS)
if (!dst_key_isprivate(newkey))
fatal("cannot sign zone with non-private dnskey %s",
keyfiles[i]);
/* Skip any duplicates */
break;
}
/* We haven't seen this key before */
} else {
}
if (setksk)
}
}
static void
}
static void
char name[DNS_NAME_FORMATSIZE];
/*
* Find keys that match this zone in the key repository.
*/
if (result == ISC_R_NOTFOUND)
/*
* Update keylist with information from from the key repository.
*/
if (result != ISC_R_SUCCESS)
fatal("failed to update DNSKEY RRset at node '%s': %s",
}
static void
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone's origin: %s",
dns_rdatatype_dnskey, 0, 0, &rdataset,
NULL);
if (result != ISC_R_SUCCESS)
fatal("failed to find keys at the zone apex: %s",
while (result == ISC_R_SUCCESS) {
} else
}
if (!have_non_ksk && !ignore_kskflag) {
if (disable_zone_check)
"supply a ZSK or use '-z'.\n",
program);
else
fatal("No non-KSK DNSKEY found; "
"supply a ZSK or use '-z'.");
}
}
static void
{
unsigned char orig_salt[256];
orig_saltlen = sizeof(orig_salt);
&orig_saltlen);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (!update_chain && set_salt) {
if (salt_length != orig_saltlen ||
fatal("An NSEC3 chain exists with a different salt. "
"Use -u to update it.");
} else if (!set_salt) {
}
if (!update_chain && set_iter) {
fatal("An NSEC3 chain exists with different "
"iterations. Use -u to update it.");
} else if (!set_iter)
/*
* Find an NSEC3 record to get the current OPTOUT value.
* (This assumes all NSEC3 records agree.)
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (!update_chain && set_optout) {
fatal("An NSEC3 chain exists with%s OPTOUT. "
"Use -u -%s to %s it.",
} else if (!set_optout)
}
static void
char *filename;
char namestr[DNS_NAME_FORMATSIZE];
isc_buffer_t b;
isc_region_t r;
unsigned char dsbuf[DNS_DS_BUFFERSIZE];
unsigned char keybuf[DST_KEY_MAXSIZE];
unsigned int filenamelen;
const dns_master_style_t *style =
isc_buffer_putuint8(&namebuf, 0);
fatal("out of memory");
else
filename[0] = 0;
if (type == dns_rdatatype_dlv) {
unsigned int labels;
} else
{
continue;
} else {
}
continue;
continue;
else
}
continue;
dns_rdata_init(&ds);
isc_buffer_usedregion(&b, &r);
if (type != dns_rdatatype_dnskey) {
if (type == dns_rdatatype_dlv)
if (type == dns_rdatatype_dlv)
} else
}
dns_db_detach(&db);
}
static void
if (outputformat != dns_masterformat_text)
return;
}
static void
if (outputformat != dns_masterformat_text)
return;
}
ISC_PLATFORM_NORETURN_PRE static void
usage(void) ISC_PLATFORM_NORETURN_POST;
static void
usage(void) {
"\t\tfor the zone and determines how they are to "
"be used\n");
"dsset-* files\n");
"- absolute|offset (now - 1 hour)\n");
"- absolute|from start|from now "
"(now + 30 days)\n");
"- absolute|from start|from now "
"(matches -e)\n");
"if < interval from end ( (end-start)/4 )\n");
"(zonefile + .signed)\n");
#ifdef USE_PKCS11
"(default is \"pkcs11\")\n");
#else
#endif
"\t\twith older versions of dnssec-signzone -g\n");
exit(0);
}
static void
removetempfile(void) {
if (removefile)
}
static void
printf("Runtime in seconds: %7u.%03u\n",
(unsigned int) (runtime_ms / 1000),
(unsigned int) (runtime_ms % 1000));
if (runtime_us > 0) {
printf("Signatures per second: %7u.%03u\n",
(unsigned int) sig_ms / 1000,
(unsigned int) sig_ms % 1000);
}
}
int
int i, ch;
char *dnskey_endstr = NULL;
char *serialformatstr = NULL;
int ndskeys = 0;
char *endp;
#ifdef USE_PKCS11
const char *engine = "pkcs11";
#else
#endif
unsigned int eflags;
int tempfilelen;
isc_buffer_t b;
int len;
#define CMDLINE_FLAGS \
"3:AaCc:Dd:E:e:f:FghH:i:I:j:K:k:l:m:n:N:o:O:pPr:s:ST:tuUv:X:xz"
/*
* Process memory debugging argument first.
*/
switch (ch) {
case 'm':
break;
default:
break;
}
}
if (result != ISC_R_SUCCESS)
fatal("out of memory");
switch (ch) {
case '3':
char *sarg;
sizeof(saltbuf));
"isc_hex_decodestring(salt)");
}
break;
case 'A':
if (OPTOUT(nsec3flags))
else
break;
case 'a':
break;
case 'C':
break;
case 'c':
break;
case 'd':
fatal("DS directory must be non-empty string");
if (result != ISC_R_SUCCESS)
fatal("cannot open directory %s: %s",
break;
case 'D':
break;
case 'E':
break;
case 'e':
break;
case 'f':
break;
case 'g':
break;
case 'H':
if (*endp != '\0')
fatal("iterations must be numeric");
if (nsec3iter > 0xffffU)
fatal("iterations too big");
break;
case 'h':
usage();
break;
case 'I':
break;
case 'i':
fatal("cycle period must be numeric and "
"positive");
break;
case 'j':
fatal("jitter must be numeric and positive");
break;
case 'K':
break;
case 'k':
fatal("too many key-signing keys specified");
break;
case 'l':
isc_buffer_add(&b, len);
NULL);
break;
case 'm':
break;
case 'N':
break;
case 'n':
fatal("number of cpus must be numeric");
break;
case 'O':
break;
case 'o':
break;
case 'P':
break;
case 'p':
break;
case 'r':
break;
case 'S':
break;
case 's':
break;
case 'T':
break;
case 't':
break;
case 'U': /* Undocumented for testing only. */
break;
case 'u':
break;
case 'v':
if (*endp != '\0')
fatal("verbose level must be numeric");
break;
case 'X':
break;
case 'x':
break;
case 'z':
break;
case 'F':
/* Reserved for FIPS mode */
/* FALLTHROUGH */
case '?':
if (isc_commandline_option != '?')
usage();
break;
default:
exit(1);
}
}
if (!pseudorandom)
if (result != ISC_R_SUCCESS)
fatal("could not create hash context");
if (result != ISC_R_SUCCESS)
fatal("could not initialize dst: %s",
} else
else
if (dnskey_endstr != NULL) {
"but have identical values.\n");
} else
if (cycle == -1)
if (ntasks == 0)
directory = ".";
if (argc < 1)
usage();
argc -= 1;
argv += 1;
fatal("out of memory");
}
if (inputformatstr != NULL) {
else
}
if (outputformatstr != NULL) {
else
}
if (serialformatstr != NULL) {
else
fatal("unknown soa serial format: %s\n",
}
fatal("option -D can only be used with \"-O text\"\n");
fatal("option -D can only be used with \"-N keep\"\n");
get_soa_ttls();
if (!set_keyttl)
/*
* Check for any existing NSEC3 parameters in the zone,
* and use them as defaults if -u was not specified.
*/
else
if (IS_NSEC3) {
if (answer)
fatal("NSEC3 generation requested with "
"NSEC only DNSKEY");
}
/*
* We need to do this early on, as we start messing with the list
* of keys rather early.
*/
isc_rwlock_init(&keylist_lock, 0, 0);
/*
* Fill keylist with:
* 1) Keys listed in the DNSKEY set that have
* private keys associated, *if* no keys were
* set on the command line.
* 2) ZSKs set on the command line
* 3) KSKs set on the command line
* 4) Any keys remaining in the DNSKEY set which
* do not have private keys associated and were
* not specified on the command line.
*/
/*
* If we're doing smart signing, look in the key repository for
* key files with metadata, and merge them with the keylist
* we have now.
*/
if (smartsign)
/* Now enumerate the key list */
}
if (keycount == 0) {
if (disable_zone_check)
"or found\n", program);
else
fatal("No signing keys specified or found.");
}
if (IS_NSEC3) {
unsigned int max;
fatal("NSEC3 iterations too big for weakest DNSKEY "
"strength. Maximum iterations allowed %u.", max);
}
switch (serialformat) {
case SOA_SERIAL_INCREMENT:
setsoaserial(0);
break;
case SOA_SERIAL_UNIXTIME:
break;
case SOA_SERIAL_KEEP:
default:
/* do nothing */
break;
}
if (IS_NSEC3)
&hashlist);
else
nsecify();
if (!nokeys) {
if (make_keyset)
}
}
fatal("out of memory");
if (result != ISC_R_SUCCESS)
fatal("failed to open temporary output file: %s",
print_time(fp);
if (result != ISC_R_SUCCESS)
fatal("failed to create task manager: %s",
if (result != ISC_R_SUCCESS)
fatal("out of memory");
for (i = 0; i < (int)ntasks; i++) {
if (result != ISC_R_SUCCESS)
fatal("failed to create task: %s",
}
if (printstats)
presign();
signapex();
if (!finished) {
/*
* There is more work to do. Spread it out over multiple
* processors if possible.
*/
for (i = 0; i < (int)ntasks; i++) {
tasks[i]);
if (result != ISC_R_SUCCESS)
fatal("failed to start task: %s",
}
(void)isc_app_run();
if (!finished)
fatal("process aborted by user");
} else
for (i = 0; i < (int)ntasks; i++)
isc_task_detach(&tasks[i]);
postsign();
verifyzone();
if (outputformat != dns_masterformat_text) {
fp);
}
if (result != ISC_R_SUCCESS)
fatal("failed to rename temp file to %s: %s\n",
if (printstats)
dns_db_detach(&gdb);
while (!ISC_LIST_EMPTY(keylist)) {
}
if (free_output)
if (verbose > 10)
(void) isc_app_finish();
if (printstats) {
}
return (0);
}