dnssec-signzone.c revision 56ae0ccb872f39eba0754eb5f1ad76bcff579449
/*
* Portions Copyright (C) 1999-2001 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 INTERNET SOFTWARE CONSORTIUM AND
* NETWORK ASSOCIATES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE CONSORTIUM OR NETWORK
* ASSOCIATES 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.141 2001/09/18 21:43:19 bwelling Exp $ */
#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
typedef struct signer_key_struct signer_key_t;
struct signer_key_struct {
unsigned int position;
};
#define SIGNER_EVENT_WRITE (SIGNER_EVENTCLASS + 0)
typedef struct signer_event sevent_t;
struct signer_event {
};
static unsigned int keycount = 0;
static int cycle = -1;
static const dns_master_style_t *masterstyle;
static unsigned int nverified = 0, nverifyfailed = 0;
static const char *directory;
static dns_name_t *lastzonecut;
static unsigned int ntasks = 0;
if (printstats) { \
counter++; \
}
static void
static inline void
if (bit != 0)
else
}
static signer_key_t *
fatal("out of memory");
return (key);
}
static void
{
if (result != ISC_R_SUCCESS) {
char keystr[KEY_FORMATSIZE];
fatal("key '%s' failed to sign data: %s",
}
if (tryverify) {
if (result == ISC_R_SUCCESS) {
} else {
}
}
}
static inline isc_boolean_t
}
static inline isc_boolean_t
}
/*
* Finds the key that generated a SIG, 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 SIG
* and can't find the signing key that we expect to find, we drop the sig.
* 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 KEY' 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 SIG 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 SIG %s': %s",
if (!nosigs)
fatal("out of memory");
for (i = 0; i < arraysize; i++)
if (nosigs)
else
while (result == ISC_R_SUCCESS) {
/* sig is dropped and not replaced */
"invalid validity period\n",
sigstr);
{
/* sig is dropped and not replaced */
"private key 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) {
} 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;
}
}
/* Determine if a KEY set contains a null key */
static isc_boolean_t
result == ISC_R_SUCCESS;
{
if (result != ISC_R_SUCCESS)
fatal("could not convert KEY into internal format");
if (dst_key_isnullkey(key)) {
dst_key_free(&key);
return (ISC_TRUE);
}
dst_key_free(&key);
}
if (result != ISC_R_NOMORE)
fatal("failure looking for null keys");
return (ISC_FALSE);
}
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);
}
/*
* Looks for signatures of the zone keys by the parent, and imports them
* if found.
*/
static void
return;
if (result != ISC_R_SUCCESS)
goto failure;
if (result != ISC_R_SUCCESS)
goto failure;
if (!dns_rdataset_isassociated(&newset) ||
goto failure;
goto failure;
}
for (;
result == ISC_R_SUCCESS;
{
break;
}
if (result != ISC_R_SUCCESS)
break;
}
if (result != ISC_R_NOMORE)
goto failure;
while (result == ISC_R_SUCCESS) {
}
if (result == ISC_R_NOMORE)
if (dns_rdataset_isassociated(&newset))
if (dns_rdataset_isassociated(&sigset))
if (result != ISC_R_SUCCESS)
fatal("zone signedkey file is invalid or does not match zone");
}
/*
* Looks for our signatures of child keys. If present, inform the caller.
*/
static isc_boolean_t
return (ISC_FALSE);
if (result != ISC_R_SUCCESS)
goto failure;
if (result != ISC_R_SUCCESS)
goto failure;
if (!dns_rdataset_isassociated(&set) ||
goto failure;
if (result != ISC_R_SUCCESS)
goto failure;
char namestr[DNS_NAME_FORMATSIZE];
namestr);
goto failure;
}
if (result == ISC_R_SUCCESS) {
break;
} else {
char namestr[DNS_NAME_FORMATSIZE];
vbprintf(1,
"verifying SIG in %s signedkey file: %s\n",
}
}
if (dns_rdataset_isassociated(&set))
if (dns_rdataset_isassociated(&sigset))
return (found);
}
/*
* There probably should be a dns_nxt_setbit, but it can get complicated if
* the length of the bit set needs to be increased. In this case, since the
* NXT bit is set and both SIG and KEY are less than NXT, the easy way works.
*/
static void
}
static void
{
unsigned char keydata[4];
isc_buffer_t b;
char namestr[DNS_NAME_FORMATSIZE];
dns_rdatatype_key, &key, &b);
if (result != ISC_R_SUCCESS)
fatal("failed to build null key");
}
static void
static int warned = 0;
if (warned++ != 0)
return;
"handle wildcards in secure zones:\n",
program);
"not generated by the server\n");
"not required by the resolver\n");
}
/*
* Signs all records at a name. This mostly just signs each set individually,
* but also adds the SIG bit to any NXTs generated earlier, deals with
*/
static void
if (dns_name_iswildcard(name)) {
char namestr[DNS_NAME_FORMATSIZE];
}
/*
* If this is not the origin, determine if it's a delegation point.
*/
if (!atorigin) {
dns_rdatatype_ns, 0, 0, &nsset,
NULL);
/* Is this a delegation point? */
if (result == ISC_R_SUCCESS) {
}
}
/*
* If this is a delegation point, determine if we need to generate
* a null key.
*/
if (isdelegation) {
dns_rdatatype_key, 0, 0, &keyset,
NULL);
char namestr[DNS_NAME_FORMATSIZE];
if (hasnullkey(&keyset)) {
fatal("%s has both a signedkey file and "
"null keys in the zone. Aborting.",
namestr);
}
}
else if (result == ISC_R_SUCCESS) {
if (hasnullkey(&keyset))
} else if (childkey) {
char namestr[DNS_NAME_FORMATSIZE];
}
if (neednullkey)
}
/*
* Now iterate through the rdatasets.
*/
while (result == ISC_R_SUCCESS) {
/* If this is a SIG set, skip it. */
goto skip;
/*
* If this is a KEY set at the apex, look for a signedkey file.
*/
goto skip;
}
/*
* If this name is a delegation point, skip all records
* except an NXT set a KEY set containing a null key.
*/
if (isdelegation) {
hasnullkey(&rdataset))))
goto skip;
}
if (!nokeys)
if (neednullkey)
}
skip:
}
if (result != ISC_R_NOMORE) {
char namestr[DNS_NAME_FORMATSIZE];
fatal("rdataset iteration for name '%s' failed: %s",
}
if (result != ISC_R_SUCCESS) {
char namestr[DNS_NAME_FORMATSIZE];
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) {
/*
* Make sure there is no NXT record for this node.
*/
dns_rdatatype_nxt, 0);
if (result == DNS_R_UNCHANGED)
}
return (active);
}
static inline isc_result_t
do {
if (result == ISC_R_SUCCESS) {
if (!active) {
}
}
return (result);
}
static inline isc_result_t
{
do {
if (result == ISC_R_SUCCESS) {
return (ISC_R_SUCCESS);
masterstyle, fp);
}
} while (result == ISC_R_SUCCESS);
return (result);
}
/*
* Extracts the TTL from the SOA.
*/
static dns_ttl_t
soattl(void) {
if (result != ISC_R_SUCCESS) {
char namestr[DNS_NAME_FORMATSIZE];
fatal("failed to find '%s SOA' in the zone: %s",
}
return (ttl);
}
/*
* Delete any SIG records at a node.
*/
static void
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) {
lastzonecut = NULL;
}
/*
* Clean up the iterator and global state after the tasks complete.
*/
static void
postsign(void) {
if (lastzonecut != NULL) {
}
}
/*
* Find the next name to nxtify & sign
*/
static isc_result_t
if (shuttingdown || finished) {
goto out;
}
if (result != ISC_R_SUCCESS)
fatal("failed to iterate through the zone");
}
&rdsiter);
while (result == ISC_R_SUCCESS) {
break;
}
}
fatal("rdataset iteration failed: %s",
if (result == ISC_R_SUCCESS) {
if (lastzonecut != NULL)
else {
sizeof(dns_name_t));
if (lastzonecut == NULL)
fatal("out of memory");
}
}
}
if (result == ISC_R_SUCCESS)
if (result == ISC_R_NOMORE) {
} else if (result != ISC_R_SUCCESS)
fatal("iterating through the database failed: %s",
out:
return (result);
}
/*
* Assigns a node to a worker thread. This is protected by the master task's
* lock.
*/
static void
fatal("out of memory");
if (result == ISC_R_NOMORE) {
}
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++;
masterstyle, fp);
}
/*
* Sign and nxtify a database node.
*/
static void
fatal("failed to allocate event\n");
}
/*
* 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
}
static void
}
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)
}
int
int i, ch;
char *endp;
unsigned int eflags;
int tempfilelen;
if (result != ISC_R_SUCCESS)
fatal("out of memory");
"c:s:e:i:v:o:f:ahpr:td:n:"))
!= -1) {
switch (ch) {
case 'c':
break;
case 's':
break;
case 'e':
break;
case 'i':
fatal("cycle period must be numeric and "
"positive");
break;
case 'p':
break;
case 'r':
break;
case 'v':
if (*endp != '\0')
fatal("verbose level must be numeric");
break;
case 'o':
break;
case 'f':
break;
case 'a':
break;
case 't':
break;
case 'd':
break;
case 'n':
fatal("number of cpus must be numeric");
break;
case 'h':
default:
usage();
}
}
if (!pseudorandom)
if (result != ISC_R_SUCCESS)
fatal("could not initialize dst");
else
else
if (cycle == -1)
if (ntasks == 0)
ntasks = isc_os_ncpus();
if (result != ISC_R_SUCCESS)
} else
if (argc < 1)
usage();
argc -= 1;
argv += 1;
fatal("out of memory");
}
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 key %s",
argv[i]);
break;
}
}
} else
}
}
if (ISC_LIST_EMPTY(keylist)) {
program);
}
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 (result != ISC_R_SUCCESS)
fatal("failed to start task: %s",
}
if (printstats)
presign();
(void)isc_app_run();
if (!finished)
fatal("process aborted by user");
for (i = 0; i < (int)ntasks; i++)
isc_task_detach(&tasks[i]);
postsign();
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) {
printf("Signatures generated: %10d\n",
nsigned);
printf("Signatures retained: %10d\n",
printf("Signatures dropped: %10d\n",
ndropped);
printf("Signatures successfully verified: %10d\n",
printf("Signatures unsuccessfully verified: %10d\n",
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);
}
}
return (0);
}