dnssec-signzone.c revision 1415fce15f6e7f3a02d2c13911f5c85ee762134d
/*
* Portions Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
* Portions Copyright (C) 1999-2003 Internet Software Consortium.
* Portions Copyright (C) 1995-2000 by Network Associates, Inc.
*
* Permission to use, copy, modify, and distribute this software for any
* 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.201 2007/05/18 23:46:58 tbox 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/rdataset.h>
#include <dns/rdataclass.h>
#include <dns/rdatasetiter.h>
#include <dns/rdatastruct.h>
#include <dns/rdatatype.h>
#include "dnssectool.h"
const char *program = "dnssec-signzone";
int verbose;
#define BUFSIZE 2048
#define MAXDSKEYS 8
typedef struct signer_key_struct signer_key_t;
struct signer_key_struct {
unsigned int position;
};
#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 unsigned int keycount = 0;
static int cycle = -1;
static int jitter = 0;
static const dns_master_style_t *masterstyle;
static unsigned int nverified = 0, nverifyfailed = 0;
static const char *directory;
static unsigned int ntasks = 0;
static dns_fixedname_t dlv_fixed;
static unsigned int serialformat = SOA_SERIAL_KEEP;
if (printstats) { \
counter++; \
}
static void
static inline void
if (bit != 0)
else
}
static void
if (outputformat != dns_masterformat_text)
return;
masterstyle, fp);
}
static signer_key_t *
fatal("out of memory");
} else {
}
return (key);
}
static void
{
if (result != ISC_R_SUCCESS) {
char keystr[KEY_FORMATSIZE];
fatal("dnskey '%s' failed to sign data: %s",
}
if (tryverify) {
if (result == ISC_R_SUCCESS) {
} else {
}
}
}
static inline isc_boolean_t
return (key->issigningkey);
}
static inline isc_boolean_t
}
/*%
* 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 signer_key_t *
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",
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 {
expired ? "expired" :
"failed to verify");
}
{
} else {
expired ? "expired" :
"failed to verify");
}
} else if (!expired) {
} else {
}
if (keep) {
&sigrdata,
&tuple);
&sigrdata,
&tuple);
}
} else {
}
if (resign) {
isc_buffer_t b;
char keystr[KEY_FORMATSIZE];
&tuple);
}
}
if (result == ISC_R_NOMORE)
if (dns_rdataset_isassociated(&sigset))
{
isc_buffer_t b;
char keystr[KEY_FORMATSIZE];
continue;
if (!key->issigningkey)
continue;
continue;
}
}
static void
{
char filename[256];
isc_buffer_t b;
isc_buffer_putstr(&b, directory);
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);
}
/*%
* Loads the key set for a child zone, if there is one, and builds DS records.
*/
static isc_result_t
unsigned char dsbuf[DNS_DS_BUFFERSIZE];
return (ISC_R_NOTFOUND);
if (result != ISC_R_SUCCESS) {
dns_db_detach(&db);
return (DNS_R_BADDB);
}
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
unsigned int val)
{
unsigned int newlen;
int octet;
newlen = 0;
i += 2;
}
break;
if (octet < 0)
continue;
newlen += 2;
/*
* Overlapping move.
*/
}
isc_buffer_t b;
&b);
}
return (answer);
}
static isc_boolean_t
return (ISC_FALSE);
if (dns_rdataset_isassociated(&nsset)) {
}
}
/*%
* Signs all records at a name. This mostly just signs each set individually,
* but also adds the RRSIG bit to any NSECs generated earlier, deals with
*/
static void
char namestr[DNS_NAME_FORMATSIZE];
isc_uint32_t nsttl = 0;
/*
* Determine if this is a delegation point.
*/
/*
* If this is a delegation point, look for a DS set.
*/
if (isdelegation) {
if (result == ISC_R_SUCCESS) {
if (generateds) {
0);
} else
}
if (generateds) {
if (result == ISC_R_SUCCESS) {
gversion, 0,
} else if (dns_rdataset_isassociated(&sigdsset)) {
}
} else if (dns_rdataset_isassociated(&sigdsset))
}
/*
* Make sure that NSEC bits are appropriately set.
*/
dns_rdatatype_nsec, 0, 0, &rdataset,
NULL) == ISC_R_SUCCESS);
if (!nokeys)
if (changed) {
dns_rdatatype_nsec, 0, 0,
&rdataset,
NULL) == ISC_R_SUCCESS);
}
if (hasds)
else
/*
* 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",
if (!active) {
/*%
* 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;
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 TTL from the SOA.
*/
static dns_ttl_t
soattl(void) {
if (result != ISC_R_SUCCESS)
fatal("failed to find an SOA at the zone apex: %s",
return (ttl);
}
/*%
* 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
if (outputformat != dns_masterformat_text)
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) {
}
/*%
* Sign the apex of the zone.
*/
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
if (shuttingdown)
return;
if (finished) {
}
return;
}
fatal("out of memory");
while (!found) {
if (result != ISC_R_SUCCESS)
fatal("failure iterating database: %s",
dns_rdatatype_nsec, 0, 0,
if (result == ISC_R_SUCCESS)
else
if (dns_rdataset_isassociated(&nsec))
if (!found)
if (result == ISC_R_NOMORE) {
break;
} else if (result != ISC_R_SUCCESS)
fatal("failure iterating database: %s",
}
if (!found) {
}
return;
}
fatal("failed to allocate event\n");
assigned++;
}
/*%
* Start a worker task
*/
static void
}
/*%
* Write a node to the output file, and restart the worker task.
*/
static void
completed++;
}
/*%
* Sign a database node.
*/
static void
fatal("failed to allocate event\n");
}
/*%
* Generate NSEC records for the zone.
*/
static void
nsecify(void) {
while (!done) {
}
while (result == ISC_R_SUCCESS) {
nextname);
if (result != ISC_R_SUCCESS)
break;
if (!active) {
continue;
}
{
continue;
}
break;
}
if (result == ISC_R_NOMORE) {
} else if (result != ISC_R_SUCCESS)
fatal("iterating through the database failed: %s",
zonettl);
}
}
/*%
* 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
unsigned int nkeys, i;
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone's origin: %s",
if (result == ISC_R_NOTFOUND)
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone keys: %s",
for (i = 0; i < nkeys; i++) {
}
}
/*%
* Finds all public zone keys in the zone.
*/
static void
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone's origin: %s",
if (result != ISC_R_SUCCESS)
fatal("failed to find keys at the zone apex: %s",
while (result == ISC_R_SUCCESS) {
&pubkey);
if (result != ISC_R_SUCCESS)
goto next;
if (!dst_key_iszonekey(pubkey)) {
goto next;
}
next:
}
}
static void
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone's origin: %s",
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 && !ignoreksk)
"Supply non-KSK dnskey or use '-z'.\n",
program);
}
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;
break;
}
break;
}
if (type == dns_rdatatype_dlv) {
unsigned int labels;
} 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;
}
static void
usage(void) {
"(now + 30 days)\n");
"if < interval from end ( (end-start)/4 )\n");
"(zonefile + .signed)\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 *serialformatstr = NULL;
int ndskeys = 0;
char *endp;
unsigned int eflags;
int tempfilelen;
isc_buffer_t b;
int len;
if (result != ISC_R_SUCCESS)
fatal("out of memory");
"ac:d:e:f:ghi:I:j:k:l:n:N:o:O:pr:s:Stv:z"))
!= -1) {
switch (ch) {
case 'a':
break;
case 'c':
break;
case 'd':
break;
case 'e':
break;
case 'f':
break;
case 'g':
break;
case 'h':
default:
usage();
break;
case 'i':
fatal("cycle period must be numeric and "
"positive");
break;
case 'I':
break;
case 'j':
fatal("jitter must be numeric and positive");
break;
case 'l':
isc_buffer_add(&b, len);
break;
case 'k':
fatal("too many key-signing keys specified");
break;
case 'n':
fatal("number of cpus must be numeric");
break;
case 'N':
break;
case 'o':
break;
case 'O':
break;
case 'p':
break;
case 'r':
break;
case 's':
break;
case 'S':
/* This is intentionally undocumented */
/* -S: simple output style */
break;
case 't':
break;
case 'v':
if (*endp != '\0')
fatal("verbose level must be numeric");
break;
case 'z':
break;
}
}
if (!pseudorandom)
if (result != ISC_R_SUCCESS)
fatal("could not create hash context");
if (result != ISC_R_SUCCESS)
fatal("could not initialize dst");
else
else
if (cycle == -1)
if (ntasks == 0)
ntasks = isc_os_ncpus();
if (argc < 1)
usage();
argc -= 1;
argv += 1;
fatal("out of memory");
}
if (inputformatstr != NULL) {
else
}
if (outputformatstr != NULL) {
else
}
if (serialformatstr != NULL) {
else
}
if (argc == 0) {
} else {
for (i = 0; i < argc; i++) {
if (result != ISC_R_SUCCESS)
{
if (!dst_key_isprivate(dkey))
fatal("cannot sign zone with "
"non-private dnskey %s",
argv[i]);
break;
}
}
} else
}
}
for (i = 0; i < ndskeys; i++) {
if (result != ISC_R_SUCCESS)
{
/* Override key flags. */
dst_key_free(&dkey);
break;
}
}
/* Override dnskey flags. */
}
}
if (ISC_LIST_EMPTY(keylist)) {
program);
}
switch (serialformat) {
case SOA_SERIAL_INCREMENT:
setsoaserial(0);
break;
case SOA_SERIAL_UNIXTIME:
break;
case SOA_SERIAL_KEEP:
default:
/* do nothing */
break;
}
nsecify();
if (!nokeys) {
}
}
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();
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);
}