freebsd-skq/contrib/bind9/bin/dnssec/dnssec-signzone.c
2004-09-19 01:30:24 +00:00

2103 lines
56 KiB
C

/*
* Portions Copyright (C) 2004 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.139.2.2.4.16 2004/08/28 06:25:29 marka Exp $ */
#include <config.h>
#include <stdlib.h>
#include <time.h>
#include <isc/app.h>
#include <isc/commandline.h>
#include <isc/entropy.h>
#include <isc/event.h>
#include <isc/file.h>
#include <isc/mem.h>
#include <isc/mutex.h>
#include <isc/os.h>
#include <isc/print.h>
#include <isc/serial.h>
#include <isc/stdio.h>
#include <isc/string.h>
#include <isc/task.h>
#include <isc/util.h>
#include <isc/time.h>
#include <dns/db.h>
#include <dns/dbiterator.h>
#include <dns/diff.h>
#include <dns/dnssec.h>
#include <dns/ds.h>
#include <dns/fixedname.h>
#include <dns/keyvalues.h>
#include <dns/log.h>
#include <dns/master.h>
#include <dns/masterdump.h>
#include <dns/nsec.h>
#include <dns/rdata.h>
#include <dns/rdataset.h>
#include <dns/rdataclass.h>
#include <dns/rdatasetiter.h>
#include <dns/rdatastruct.h>
#include <dns/rdatatype.h>
#include <dns/result.h>
#include <dns/time.h>
#include <dst/dst.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 {
dst_key_t *key;
isc_boolean_t issigningkey;
isc_boolean_t isdsk;
isc_boolean_t isksk;
unsigned int position;
ISC_LINK(signer_key_t) link;
};
#define SIGNER_EVENTCLASS ISC_EVENTCLASS(0x4453)
#define SIGNER_EVENT_WRITE (SIGNER_EVENTCLASS + 0)
#define SIGNER_EVENT_WORK (SIGNER_EVENTCLASS + 1)
typedef struct signer_event sevent_t;
struct signer_event {
ISC_EVENT_COMMON(sevent_t);
dns_fixedname_t *fname;
dns_dbnode_t *node;
};
static ISC_LIST(signer_key_t) keylist;
static unsigned int keycount = 0;
static isc_stdtime_t starttime = 0, endtime = 0, now;
static int cycle = -1;
static isc_boolean_t tryverify = ISC_FALSE;
static isc_boolean_t printstats = ISC_FALSE;
static isc_mem_t *mctx = NULL;
static isc_entropy_t *ectx = NULL;
static dns_ttl_t zonettl;
static FILE *fp;
static char *tempfile = NULL;
static const dns_master_style_t *masterstyle;
static unsigned int nsigned = 0, nretained = 0, ndropped = 0;
static unsigned int nverified = 0, nverifyfailed = 0;
static const char *directory;
static isc_mutex_t namelock, statslock;
static isc_taskmgr_t *taskmgr = NULL;
static dns_db_t *gdb; /* The database */
static dns_dbversion_t *gversion; /* The database version */
static dns_dbiterator_t *gdbiter; /* The database iterator */
static dns_rdataclass_t gclass; /* The class */
static dns_name_t *gorigin; /* The database origin */
static isc_task_t *master = NULL;
static unsigned int ntasks = 0;
static isc_boolean_t shuttingdown = ISC_FALSE, finished = ISC_FALSE;
static unsigned int assigned = 0, completed = 0;
static isc_boolean_t nokeys = ISC_FALSE;
static isc_boolean_t removefile = ISC_FALSE;
static isc_boolean_t generateds = ISC_FALSE;
static isc_boolean_t ignoreksk = ISC_FALSE;
static dns_name_t *dlv = NULL;
static dns_fixedname_t dlv_fixed;
static dns_master_style_t *dsstyle = NULL;
#define INCSTAT(counter) \
if (printstats) { \
LOCK(&statslock); \
counter++; \
UNLOCK(&statslock); \
}
static void
sign(isc_task_t *task, isc_event_t *event);
static inline void
set_bit(unsigned char *array, unsigned int index, unsigned int bit) {
unsigned int shift, mask;
shift = 7 - (index % 8);
mask = 1 << shift;
if (bit != 0)
array[index / 8] |= mask;
else
array[index / 8] &= (~mask & 0xFF);
}
static void
dumpnode(dns_name_t *name, dns_dbnode_t *node) {
isc_result_t result;
result = dns_master_dumpnodetostream(mctx, gdb, gversion, node, name,
masterstyle, fp);
check_result(result, "dns_master_dumpnodetostream");
}
static void
dumpdb(dns_db_t *db) {
dns_dbiterator_t *dbiter = NULL;
dns_dbnode_t *node;
dns_fixedname_t fname;
dns_name_t *name;
isc_result_t result;
dbiter = NULL;
result = dns_db_createiterator(db, ISC_FALSE, &dbiter);
check_result(result, "dns_db_createiterator()");
dns_fixedname_init(&fname);
name = dns_fixedname_name(&fname);
node = NULL;
for (result = dns_dbiterator_first(dbiter);
result == ISC_R_SUCCESS;
result = dns_dbiterator_next(dbiter))
{
result = dns_dbiterator_current(dbiter, &node, name);
check_result(result, "dns_dbiterator_current()");
dumpnode(name, node);
dns_db_detachnode(db, &node);
}
if (result != ISC_R_NOMORE)
fatal("iterating database: %s", isc_result_totext(result));
dns_dbiterator_destroy(&dbiter);
}
static signer_key_t *
newkeystruct(dst_key_t *dstkey, isc_boolean_t signwithkey) {
signer_key_t *key;
key = isc_mem_get(mctx, sizeof(signer_key_t));
if (key == NULL)
fatal("out of memory");
key->key = dstkey;
if ((dst_key_flags(dstkey) & DNS_KEYFLAG_KSK) != 0) {
key->issigningkey = signwithkey;
key->isksk = ISC_TRUE;
key->isdsk = ISC_FALSE;
} else {
key->issigningkey = signwithkey;
key->isksk = ISC_FALSE;
key->isdsk = ISC_TRUE;
}
key->position = keycount++;
ISC_LINK_INIT(key, link);
return (key);
}
static void
signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dns_rdata_t *rdata,
dst_key_t *key, isc_buffer_t *b)
{
isc_result_t result;
result = dns_dnssec_sign(name, rdataset, key, &starttime, &endtime,
mctx, b, rdata);
isc_entropy_stopcallbacksources(ectx);
if (result != ISC_R_SUCCESS) {
char keystr[KEY_FORMATSIZE];
key_format(key, keystr, sizeof(keystr));
fatal("dnskey '%s' failed to sign data: %s",
keystr, isc_result_totext(result));
}
INCSTAT(nsigned);
if (tryverify) {
result = dns_dnssec_verify(name, rdataset, key,
ISC_TRUE, mctx, rdata);
if (result == ISC_R_SUCCESS) {
vbprintf(3, "\tsignature verified\n");
INCSTAT(nverified);
} else {
vbprintf(3, "\tsignature failed to verify\n");
INCSTAT(nverifyfailed);
}
}
}
static inline isc_boolean_t
issigningkey(signer_key_t *key) {
return (key->issigningkey);
}
static inline isc_boolean_t
iszonekey(signer_key_t *key) {
return (ISC_TF(dns_name_equal(dst_key_name(key->key), gorigin) &&
dst_key_iszonekey(key->key)));
}
/*
* 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 *
keythatsigned(dns_rdata_rrsig_t *rrsig) {
isc_result_t result;
dst_key_t *pubkey = NULL, *privkey = NULL;
signer_key_t *key;
key = ISC_LIST_HEAD(keylist);
while (key != NULL) {
if (rrsig->keyid == dst_key_id(key->key) &&
rrsig->algorithm == dst_key_alg(key->key) &&
dns_name_equal(&rrsig->signer, dst_key_name(key->key)))
return key;
key = ISC_LIST_NEXT(key, link);
}
result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
rrsig->algorithm, DST_TYPE_PUBLIC,
NULL, mctx, &pubkey);
if (result != ISC_R_SUCCESS)
return (NULL);
result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
rrsig->algorithm,
DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
NULL, mctx, &privkey);
if (result == ISC_R_SUCCESS) {
dst_key_free(&pubkey);
key = newkeystruct(privkey, ISC_FALSE);
} else
key = newkeystruct(pubkey, ISC_FALSE);
ISC_LIST_APPEND(keylist, key, link);
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
expecttofindkey(dns_name_t *name) {
unsigned int options = DNS_DBFIND_NOWILD;
dns_fixedname_t fname;
isc_result_t result;
char namestr[DNS_NAME_FORMATSIZE];
dns_fixedname_init(&fname);
result = dns_db_find(gdb, name, gversion, dns_rdatatype_dnskey, options,
0, NULL, dns_fixedname_name(&fname), NULL, NULL);
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);
}
dns_name_format(name, namestr, sizeof(namestr));
fatal("failure looking for '%s DNSKEY' in database: %s",
namestr, isc_result_totext(result));
return (ISC_FALSE); /* removes a warning */
}
static inline isc_boolean_t
setverifies(dns_name_t *name, dns_rdataset_t *set, signer_key_t *key,
dns_rdata_t *rrsig)
{
isc_result_t result;
result = dns_dnssec_verify(name, set, key->key, ISC_FALSE, mctx, rrsig);
if (result == ISC_R_SUCCESS) {
INCSTAT(nverified);
return (ISC_TRUE);
} else {
INCSTAT(nverifyfailed);
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
signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name,
dns_rdataset_t *set)
{
dns_rdataset_t sigset;
dns_rdata_t sigrdata = DNS_RDATA_INIT;
dns_rdata_rrsig_t rrsig;
signer_key_t *key;
isc_result_t result;
isc_boolean_t nosigs = ISC_FALSE;
isc_boolean_t *wassignedby, *nowsignedby;
int arraysize;
dns_difftuple_t *tuple;
dns_ttl_t ttl;
int i;
char namestr[DNS_NAME_FORMATSIZE];
char typestr[TYPE_FORMATSIZE];
char sigstr[SIG_FORMATSIZE];
dns_name_format(name, namestr, sizeof(namestr));
type_format(set->type, typestr, sizeof(typestr));
ttl = ISC_MIN(set->ttl, endtime - starttime);
dns_rdataset_init(&sigset);
result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_rrsig,
set->type, 0, &sigset, NULL);
if (result == ISC_R_NOTFOUND) {
result = ISC_R_SUCCESS;
nosigs = ISC_TRUE;
}
if (result != ISC_R_SUCCESS)
fatal("failed while looking for '%s RRSIG %s': %s",
namestr, typestr, isc_result_totext(result));
vbprintf(1, "%s/%s:\n", namestr, typestr);
arraysize = keycount;
if (!nosigs)
arraysize += dns_rdataset_count(&sigset);
wassignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
nowsignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
if (wassignedby == NULL || nowsignedby == NULL)
fatal("out of memory");
for (i = 0; i < arraysize; i++)
wassignedby[i] = nowsignedby[i] = ISC_FALSE;
if (nosigs)
result = ISC_R_NOMORE;
else
result = dns_rdataset_first(&sigset);
while (result == ISC_R_SUCCESS) {
isc_boolean_t expired, future;
isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
dns_rdataset_current(&sigset, &sigrdata);
result = dns_rdata_tostruct(&sigrdata, &rrsig, NULL);
check_result(result, "dns_rdata_tostruct");
future = isc_serial_lt(now, rrsig.timesigned);
key = keythatsigned(&rrsig);
sig_format(&rrsig, sigstr, sizeof(sigstr));
if (key != NULL && issigningkey(key))
expired = isc_serial_gt(now + cycle, rrsig.timeexpire);
else
expired = isc_serial_gt(now, rrsig.timeexpire);
if (isc_serial_gt(rrsig.timesigned, rrsig.timeexpire)) {
/* rrsig is dropped and not replaced */
vbprintf(2, "\trrsig by %s dropped - "
"invalid validity period\n",
sigstr);
} else if (key == NULL && !future &&
expecttofindkey(&rrsig.signer))
{
/* rrsig is dropped and not replaced */
vbprintf(2, "\trrsig by %s dropped - "
"private dnskey not found\n",
sigstr);
} else if (key == NULL || future) {
vbprintf(2, "\trrsig by %s %s - dnskey not found\n",
expired ? "retained" : "dropped", sigstr);
if (!expired)
keep = ISC_TRUE;
} else if (issigningkey(key)) {
if (!expired && setverifies(name, set, key, &sigrdata))
{
vbprintf(2, "\trrsig by %s retained\n", sigstr);
keep = ISC_TRUE;
wassignedby[key->position] = ISC_TRUE;
nowsignedby[key->position] = ISC_TRUE;
} else {
vbprintf(2, "\trrsig by %s dropped - %s\n",
sigstr,
expired ? "expired" :
"failed to verify");
wassignedby[key->position] = ISC_TRUE;
resign = ISC_TRUE;
}
} else if (iszonekey(key)) {
if (!expired && setverifies(name, set, key, &sigrdata))
{
vbprintf(2, "\trrsig by %s retained\n", sigstr);
keep = ISC_TRUE;
wassignedby[key->position] = ISC_TRUE;
nowsignedby[key->position] = ISC_TRUE;
} else {
vbprintf(2, "\trrsig by %s dropped - %s\n",
sigstr,
expired ? "expired" :
"failed to verify");
wassignedby[key->position] = ISC_TRUE;
}
} else if (!expired) {
vbprintf(2, "\trrsig by %s retained\n", sigstr);
keep = ISC_TRUE;
} else {
vbprintf(2, "\trrsig by %s expired\n", sigstr);
}
if (keep) {
nowsignedby[key->position] = ISC_TRUE;
INCSTAT(nretained);
if (sigset.ttl != ttl) {
vbprintf(2, "\tfixing ttl %s\n", sigstr);
tuple = NULL;
result = dns_difftuple_create(mctx,
DNS_DIFFOP_DEL,
name, sigset.ttl,
&sigrdata,
&tuple);
check_result(result, "dns_difftuple_create");
dns_diff_append(del, &tuple);
result = dns_difftuple_create(mctx,
DNS_DIFFOP_ADD,
name, ttl,
&sigrdata,
&tuple);
check_result(result, "dns_difftuple_create");
dns_diff_append(add, &tuple);
}
} else {
tuple = NULL;
result = dns_difftuple_create(mctx, DNS_DIFFOP_DEL,
name, sigset.ttl,
&sigrdata, &tuple);
check_result(result, "dns_difftuple_create");
dns_diff_append(del, &tuple);
INCSTAT(ndropped);
}
if (resign) {
isc_buffer_t b;
dns_rdata_t trdata = DNS_RDATA_INIT;
unsigned char array[BUFSIZE];
char keystr[KEY_FORMATSIZE];
INSIST(!keep);
key_format(key->key, keystr, sizeof(keystr));
vbprintf(1, "\tresigning with dnskey %s\n", keystr);
isc_buffer_init(&b, array, sizeof(array));
signwithkey(name, set, &trdata, key->key, &b);
nowsignedby[key->position] = ISC_TRUE;
tuple = NULL;
result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
name, ttl, &trdata,
&tuple);
check_result(result, "dns_difftuple_create");
dns_diff_append(add, &tuple);
}
dns_rdata_reset(&sigrdata);
dns_rdata_freestruct(&rrsig);
result = dns_rdataset_next(&sigset);
}
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
check_result(result, "dns_rdataset_first/next");
if (dns_rdataset_isassociated(&sigset))
dns_rdataset_disassociate(&sigset);
for (key = ISC_LIST_HEAD(keylist);
key != NULL;
key = ISC_LIST_NEXT(key, link))
{
isc_buffer_t b;
dns_rdata_t trdata;
unsigned char array[BUFSIZE];
char keystr[KEY_FORMATSIZE];
if (nowsignedby[key->position])
continue;
if (!key->issigningkey)
continue;
if (!(ignoreksk || key->isdsk ||
(key->isksk &&
set->type == dns_rdatatype_dnskey &&
dns_name_equal(name, gorigin))))
continue;
key_format(key->key, keystr, sizeof(keystr));
vbprintf(1, "\tsigning with dnskey %s\n", keystr);
dns_rdata_init(&trdata);
isc_buffer_init(&b, array, sizeof(array));
signwithkey(name, set, &trdata, key->key, &b);
tuple = NULL;
result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name,
ttl, &trdata, &tuple);
check_result(result, "dns_difftuple_create");
dns_diff_append(add, &tuple);
}
isc_mem_put(mctx, wassignedby, arraysize * sizeof(isc_boolean_t));
isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
}
static void
opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass,
dns_db_t **dbp)
{
char filename[256];
isc_buffer_t b;
isc_result_t result;
isc_buffer_init(&b, filename, sizeof(filename));
if (directory != NULL) {
isc_buffer_putstr(&b, directory);
if (directory[strlen(directory) - 1] != '/')
isc_buffer_putstr(&b, "/");
}
isc_buffer_putstr(&b, prefix);
result = dns_name_tofilenametext(name, ISC_FALSE, &b);
check_result(result, "dns_name_tofilenametext()");
if (isc_buffer_availablelength(&b) == 0) {
char namestr[DNS_NAME_FORMATSIZE];
dns_name_format(name, namestr, sizeof(namestr));
fatal("name '%s' is too long", namestr);
}
isc_buffer_putuint8(&b, 0);
result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
rdclass, 0, NULL, dbp);
check_result(result, "dns_db_create()");
result = dns_db_load(*dbp, filename);
if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
dns_db_detach(dbp);
}
/*
* Loads the key set for a child zone, if there is one, and builds DS records.
*/
static isc_result_t
loadds(dns_name_t *name, isc_uint32_t ttl, dns_rdataset_t *dsset) {
dns_db_t *db = NULL;
dns_dbversion_t *ver = NULL;
dns_dbnode_t *node = NULL;
isc_result_t result;
dns_rdataset_t keyset;
dns_rdata_t key, ds;
unsigned char dsbuf[DNS_DS_BUFFERSIZE];
dns_diff_t diff;
dns_difftuple_t *tuple = NULL;
opendb("keyset-", name, gclass, &db);
if (db == NULL)
return (ISC_R_NOTFOUND);
result = dns_db_findnode(db, name, ISC_FALSE, &node);
if (result != ISC_R_SUCCESS) {
dns_db_detach(&db);
return (DNS_R_BADDB);
}
dns_rdataset_init(&keyset);
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, 0,
&keyset, NULL);
if (result != ISC_R_SUCCESS) {
dns_db_detachnode(db, &node);
dns_db_detach(&db);
return (result);
}
vbprintf(2, "found DNSKEY records\n");
result = dns_db_newversion(db, &ver);
check_result(result, "dns_db_newversion");
dns_diff_init(mctx, &diff);
for (result = dns_rdataset_first(&keyset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(&keyset))
{
dns_rdata_init(&key);
dns_rdata_init(&ds);
dns_rdataset_current(&keyset, &key);
result = dns_ds_buildrdata(name, &key, DNS_DSDIGEST_SHA1,
dsbuf, &ds);
check_result(result, "dns_ds_buildrdata");
result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name,
ttl, &ds, &tuple);
check_result(result, "dns_difftuple_create");
dns_diff_append(&diff, &tuple);
}
result = dns_diff_apply(&diff, db, ver);
check_result(result, "dns_diff_apply");
dns_diff_clear(&diff);
dns_db_closeversion(db, &ver, ISC_TRUE);
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ds, 0, 0,
dsset, NULL);
check_result(result, "dns_db_findrdataset");
dns_rdataset_disassociate(&keyset);
dns_db_detachnode(db, &node);
dns_db_detach(&db);
return (result);
}
static isc_boolean_t
nsec_setbit(dns_name_t *name, dns_rdataset_t *rdataset, dns_rdatatype_t type,
unsigned int val)
{
isc_result_t result;
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_nsec_t nsec;
unsigned int newlen;
unsigned char bitmap[8192 + 512];
unsigned char nsecdata[8192 + 512 + DNS_NAME_MAXWIRE];
isc_boolean_t answer = ISC_FALSE;
unsigned int i, len, window;
int octet;
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");
INSIST(nsec.len <= sizeof(bitmap));
newlen = 0;
memset(bitmap, 0, sizeof(bitmap));
for (i = 0; i < nsec.len; i += len) {
INSIST(i + 2 <= nsec.len);
window = nsec.typebits[i];
len = nsec.typebits[i+1];
i += 2;
INSIST(len > 0 && len <= 32);
INSIST(i + len <= nsec.len);
memmove(&bitmap[window * 32 + 512], &nsec.typebits[i], len);
}
set_bit(bitmap + 512, type, val);
for (window = 0; window < 256; window++) {
for (octet = 31; octet >= 0; octet--)
if (bitmap[window * 32 + 512 + octet] != 0)
break;
if (octet < 0)
continue;
bitmap[newlen] = window;
bitmap[newlen + 1] = octet + 1;
newlen += 2;
/*
* Overlapping move.
*/
memmove(&bitmap[newlen], &bitmap[window * 32 + 512], octet + 1);
newlen += octet + 1;
}
if (newlen != nsec.len ||
memcmp(nsec.typebits, bitmap, newlen) != 0) {
dns_rdata_t newrdata = DNS_RDATA_INIT;
isc_buffer_t b;
dns_diff_t diff;
dns_difftuple_t *tuple = NULL;
dns_diff_init(mctx, &diff);
result = dns_difftuple_create(mctx, DNS_DIFFOP_DEL, name,
rdataset->ttl, &rdata, &tuple);
check_result(result, "dns_difftuple_create");
dns_diff_append(&diff, &tuple);
nsec.typebits = bitmap;
nsec.len = newlen;
isc_buffer_init(&b, nsecdata, sizeof(nsecdata));
result = dns_rdata_fromstruct(&newrdata, rdata.rdclass,
dns_rdatatype_nsec, &nsec,
&b);
check_result(result, "dns_rdata_fromstruct");
result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
name, rdataset->ttl,
&newrdata, &tuple);
check_result(result, "dns_difftuple_create");
dns_diff_append(&diff, &tuple);
result = dns_diff_apply(&diff, gdb, gversion);
check_result(result, "dns_difftuple_apply");
dns_diff_clear(&diff);
answer = ISC_TRUE;
}
dns_rdata_freestruct(&nsec);
return (answer);
}
static isc_boolean_t
delegation(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, gorigin))
return (ISC_FALSE);
dns_rdataset_init(&nsset);
result = dns_db_findrdataset(gdb, node, gversion, 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));
}
/*
* 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
* parent/child KEY signatures, and handles other exceptional cases.
*/
static void
signname(dns_dbnode_t *node, dns_name_t *name) {
isc_result_t result;
dns_rdataset_t rdataset;
dns_rdatasetiter_t *rdsiter;
isc_boolean_t isdelegation = ISC_FALSE;
isc_boolean_t hasds = ISC_FALSE;
isc_boolean_t atorigin;
isc_boolean_t changed = ISC_FALSE;
dns_diff_t del, add;
char namestr[DNS_NAME_FORMATSIZE];
isc_uint32_t nsttl = 0;
dns_name_format(name, namestr, sizeof(namestr));
atorigin = dns_name_equal(name, gorigin);
/*
* Determine if this is a delegation point.
*/
if (delegation(name, node, &nsttl))
isdelegation = ISC_TRUE;
/*
* If this is a delegation point, look for a DS set.
*/
if (isdelegation) {
dns_rdataset_t dsset;
dns_rdataset_t sigdsset;
dns_rdataset_init(&dsset);
dns_rdataset_init(&sigdsset);
result = dns_db_findrdataset(gdb, node, gversion,
dns_rdatatype_ds,
0, 0, &dsset, &sigdsset);
if (result == ISC_R_SUCCESS) {
dns_rdataset_disassociate(&dsset);
if (generateds) {
result = dns_db_deleterdataset(gdb, node,
gversion,
dns_rdatatype_ds,
0);
check_result(result, "dns_db_deleterdataset");
} else
hasds = ISC_TRUE;
}
if (generateds) {
result = loadds(name, nsttl, &dsset);
if (result == ISC_R_SUCCESS) {
result = dns_db_addrdataset(gdb, node,
gversion, 0,
&dsset, 0, NULL);
check_result(result, "dns_db_addrdataset");
hasds = ISC_TRUE;
dns_rdataset_disassociate(&dsset);
if (dns_rdataset_isassociated(&sigdsset))
dns_rdataset_disassociate(&sigdsset);
} else if (dns_rdataset_isassociated(&sigdsset)) {
result = dns_db_deleterdataset(gdb, node,
gversion,
dns_rdatatype_rrsig,
dns_rdatatype_ds);
check_result(result, "dns_db_deleterdataset");
dns_rdataset_disassociate(&sigdsset);
}
} else if (dns_rdataset_isassociated(&sigdsset))
dns_rdataset_disassociate(&sigdsset);
}
/*
* Make sure that NSEC bits are appropriately set.
*/
dns_rdataset_init(&rdataset);
RUNTIME_CHECK(dns_db_findrdataset(gdb, node, gversion,
dns_rdatatype_nsec, 0, 0, &rdataset,
NULL) == ISC_R_SUCCESS);
if (!nokeys)
changed = nsec_setbit(name, &rdataset, dns_rdatatype_rrsig, 1);
if (changed) {
dns_rdataset_disassociate(&rdataset);
RUNTIME_CHECK(dns_db_findrdataset(gdb, node, gversion,
dns_rdatatype_nsec, 0, 0,
&rdataset,
NULL) == ISC_R_SUCCESS);
}
if (hasds)
(void)nsec_setbit(name, &rdataset, dns_rdatatype_ds, 1);
else
(void)nsec_setbit(name, &rdataset, dns_rdatatype_ds, 0);
dns_rdataset_disassociate(&rdataset);
/*
* Now iterate through the rdatasets.
*/
dns_diff_init(mctx, &del);
dns_diff_init(mctx, &add);
rdsiter = NULL;
result = dns_db_allrdatasets(gdb, node, gversion, 0, &rdsiter);
check_result(result, "dns_db_allrdatasets()");
result = dns_rdatasetiter_first(rdsiter);
while (result == ISC_R_SUCCESS) {
dns_rdatasetiter_current(rdsiter, &rdataset);
/* If this is a RRSIG set, skip it. */
if (rdataset.type == dns_rdatatype_rrsig)
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) {
if (rdataset.type != dns_rdatatype_nsec &&
rdataset.type != dns_rdatatype_ds)
goto skip;
} else if (rdataset.type == dns_rdatatype_ds) {
char namebuf[DNS_NAME_FORMATSIZE];
dns_name_format(name, namebuf, sizeof(namebuf));
fatal("'%s': found DS RRset without NS RRset\n",
namebuf);
}
signset(&del, &add, node, name, &rdataset);
skip:
dns_rdataset_disassociate(&rdataset);
result = dns_rdatasetiter_next(rdsiter);
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration for name '%s' failed: %s",
namestr, isc_result_totext(result));
dns_rdatasetiter_destroy(&rdsiter);
result = dns_diff_applysilently(&del, gdb, gversion);
if (result != ISC_R_SUCCESS)
fatal("failed to delete SIGs at node '%s': %s",
namestr, isc_result_totext(result));
result = dns_diff_applysilently(&add, gdb, gversion);
if (result != ISC_R_SUCCESS)
fatal("failed to add SIGs at node '%s': %s",
namestr, isc_result_totext(result));
dns_diff_clear(&del);
dns_diff_clear(&add);
}
static inline isc_boolean_t
active_node(dns_dbnode_t *node) {
dns_rdatasetiter_t *rdsiter;
isc_boolean_t active = ISC_FALSE;
isc_result_t result;
dns_rdataset_t rdataset;
dns_rdataset_init(&rdataset);
rdsiter = NULL;
result = dns_db_allrdatasets(gdb, node, gversion, 0, &rdsiter);
check_result(result, "dns_db_allrdatasets()");
result = dns_rdatasetiter_first(rdsiter);
while (result == ISC_R_SUCCESS) {
dns_rdatasetiter_current(rdsiter, &rdataset);
if (rdataset.type != dns_rdatatype_nsec &&
rdataset.type != dns_rdatatype_rrsig)
active = ISC_TRUE;
dns_rdataset_disassociate(&rdataset);
if (!active)
result = dns_rdatasetiter_next(rdsiter);
else
result = ISC_R_NOMORE;
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration failed: %s",
isc_result_totext(result));
if (!active) {
/*
* Make sure there is no NSEC / RRSIG records for
* this node.
*/
result = dns_db_deleterdataset(gdb, node, gversion,
dns_rdatatype_nsec, 0);
if (result == DNS_R_UNCHANGED)
result = ISC_R_SUCCESS;
check_result(result, "dns_db_deleterdataset(nsec)");
result = dns_rdatasetiter_first(rdsiter);
for (result = dns_rdatasetiter_first(rdsiter);
result == ISC_R_SUCCESS;
result = dns_rdatasetiter_next(rdsiter)) {
dns_rdatasetiter_current(rdsiter, &rdataset);
if (rdataset.type == dns_rdatatype_rrsig) {
dns_rdatatype_t type = rdataset.type;
dns_rdatatype_t covers = rdataset.covers;
result = dns_db_deleterdataset(gdb, node,
gversion, type,
covers);
if (result == DNS_R_UNCHANGED)
result = ISC_R_SUCCESS;
check_result(result,
"dns_db_deleterdataset(rrsig)");
}
dns_rdataset_disassociate(&rdataset);
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration failed: %s",
isc_result_totext(result));
}
dns_rdatasetiter_destroy(&rdsiter);
return (active);
}
/*
* Extracts the TTL from the SOA.
*/
static dns_ttl_t
soattl(void) {
dns_rdataset_t soaset;
dns_fixedname_t fname;
dns_name_t *name;
isc_result_t result;
dns_ttl_t ttl;
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_soa_t soa;
dns_fixedname_init(&fname);
name = dns_fixedname_name(&fname);
dns_rdataset_init(&soaset);
result = dns_db_find(gdb, gorigin, gversion, dns_rdatatype_soa,
0, 0, NULL, name, &soaset, NULL);
if (result != ISC_R_SUCCESS)
fatal("failed to find an SOA at the zone apex: %s",
isc_result_totext(result));
result = dns_rdataset_first(&soaset);
check_result(result, "dns_rdataset_first");
dns_rdataset_current(&soaset, &rdata);
result = dns_rdata_tostruct(&rdata, &soa, NULL);
check_result(result, "dns_rdata_tostruct");
ttl = soa.minimum;
dns_rdataset_disassociate(&soaset);
return (ttl);
}
/*
* Delete any RRSIG records at a node.
*/
static void
cleannode(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node) {
dns_rdatasetiter_t *rdsiter = NULL;
dns_rdataset_t set;
isc_result_t result, dresult;
dns_rdataset_init(&set);
result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
check_result(result, "dns_db_allrdatasets");
result = dns_rdatasetiter_first(rdsiter);
while (result == ISC_R_SUCCESS) {
isc_boolean_t destroy = ISC_FALSE;
dns_rdatatype_t covers = 0;
dns_rdatasetiter_current(rdsiter, &set);
if (set.type == dns_rdatatype_rrsig) {
covers = set.covers;
destroy = ISC_TRUE;
}
dns_rdataset_disassociate(&set);
result = dns_rdatasetiter_next(rdsiter);
if (destroy) {
dresult = dns_db_deleterdataset(db, node, version,
dns_rdatatype_rrsig,
covers);
check_result(dresult, "dns_db_deleterdataset");
}
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration failed: %s",
isc_result_totext(result));
dns_rdatasetiter_destroy(&rdsiter);
}
/*
* Set up the iterator and global state before starting the tasks.
*/
static void
presign(void) {
isc_result_t result;
gdbiter = NULL;
result = dns_db_createiterator(gdb, ISC_FALSE, &gdbiter);
check_result(result, "dns_db_createiterator()");
result = dns_dbiterator_first(gdbiter);
check_result(result, "dns_dbiterator_first()");
}
/*
* Clean up the iterator and global state after the tasks complete.
*/
static void
postsign(void) {
dns_dbiterator_destroy(&gdbiter);
}
/*
* Assigns a node to a worker thread. This is protected by the master task's
* lock.
*/
static void
assignwork(isc_task_t *task, isc_task_t *worker) {
dns_fixedname_t *fname;
dns_name_t *name;
dns_dbnode_t *node;
sevent_t *sevent;
dns_rdataset_t nsec;
isc_boolean_t found;
isc_result_t result;
if (shuttingdown)
return;
if (finished) {
if (assigned == completed) {
isc_task_detach(&task);
isc_app_shutdown();
}
return;
}
fname = isc_mem_get(mctx, sizeof(dns_fixedname_t));
if (fname == NULL)
fatal("out of memory");
dns_fixedname_init(fname);
name = dns_fixedname_name(fname);
node = NULL;
found = ISC_FALSE;
LOCK(&namelock);
while (!found) {
result = dns_dbiterator_current(gdbiter, &node, name);
if (result != ISC_R_SUCCESS)
fatal("failure iterating database: %s",
isc_result_totext(result));
dns_rdataset_init(&nsec);
result = dns_db_findrdataset(gdb, node, gversion,
dns_rdatatype_nsec, 0, 0,
&nsec, NULL);
if (result == ISC_R_SUCCESS)
found = ISC_TRUE;
else
dumpnode(name, node);
if (dns_rdataset_isassociated(&nsec))
dns_rdataset_disassociate(&nsec);
if (!found)
dns_db_detachnode(gdb, &node);
result = dns_dbiterator_next(gdbiter);
if (result == ISC_R_NOMORE) {
finished = ISC_TRUE;
break;
} else if (result != ISC_R_SUCCESS)
fatal("failure iterating database: %s",
isc_result_totext(result));
}
UNLOCK(&namelock);
if (!found) {
if (assigned == completed) {
isc_task_detach(&task);
isc_app_shutdown();
}
isc_mem_put(mctx, fname, sizeof(dns_fixedname_t));
return;
}
sevent = (sevent_t *)
isc_event_allocate(mctx, task, SIGNER_EVENT_WORK,
sign, NULL, sizeof(sevent_t));
if (sevent == NULL)
fatal("failed to allocate event\n");
sevent->node = node;
sevent->fname = fname;
isc_task_send(worker, ISC_EVENT_PTR(&sevent));
assigned++;
}
/*
* Start a worker task
*/
static void
startworker(isc_task_t *task, isc_event_t *event) {
isc_task_t *worker;
worker = (isc_task_t *)event->ev_arg;
assignwork(task, worker);
isc_event_free(&event);
}
/*
* Write a node to the output file, and restart the worker task.
*/
static void
writenode(isc_task_t *task, isc_event_t *event) {
isc_task_t *worker;
sevent_t *sevent = (sevent_t *)event;
completed++;
worker = (isc_task_t *)event->ev_sender;
dumpnode(dns_fixedname_name(sevent->fname), sevent->node);
cleannode(gdb, gversion, sevent->node);
dns_db_detachnode(gdb, &sevent->node);
isc_mem_put(mctx, sevent->fname, sizeof(dns_fixedname_t));
assignwork(task, worker);
isc_event_free(&event);
}
/*
* Sign a database node.
*/
static void
sign(isc_task_t *task, isc_event_t *event) {
dns_fixedname_t *fname;
dns_dbnode_t *node;
sevent_t *sevent, *wevent;
sevent = (sevent_t *)event;
node = sevent->node;
fname = sevent->fname;
isc_event_free(&event);
signname(node, dns_fixedname_name(fname));
wevent = (sevent_t *)
isc_event_allocate(mctx, task, SIGNER_EVENT_WRITE,
writenode, NULL, sizeof(sevent_t));
if (wevent == NULL)
fatal("failed to allocate event\n");
wevent->node = node;
wevent->fname = fname;
isc_task_send(master, ISC_EVENT_PTR(&wevent));
}
/*
* Generate NSEC records for the zone.
*/
static void
nsecify(void) {
dns_dbiterator_t *dbiter = NULL;
dns_dbnode_t *node = NULL, *nextnode = NULL;
dns_fixedname_t fname, fnextname, fzonecut;
dns_name_t *name, *nextname, *zonecut;
isc_boolean_t done = ISC_FALSE;
isc_result_t result;
dns_fixedname_init(&fname);
name = dns_fixedname_name(&fname);
dns_fixedname_init(&fnextname);
nextname = dns_fixedname_name(&fnextname);
dns_fixedname_init(&fzonecut);
zonecut = NULL;
result = dns_db_createiterator(gdb, ISC_FALSE, &dbiter);
check_result(result, "dns_db_createiterator()");
result = dns_dbiterator_first(dbiter);
check_result(result, "dns_dbiterator_first()");
while (!done) {
dns_dbiterator_current(dbiter, &node, name);
if (delegation(name, node, NULL)) {
zonecut = dns_fixedname_name(&fzonecut);
dns_name_copy(name, zonecut, NULL);
}
result = dns_dbiterator_next(dbiter);
nextnode = NULL;
while (result == ISC_R_SUCCESS) {
isc_boolean_t active = ISC_FALSE;
result = dns_dbiterator_current(dbiter, &nextnode,
nextname);
if (result != ISC_R_SUCCESS)
break;
active = active_node(nextnode);
if (!active) {
dns_db_detachnode(gdb, &nextnode);
result = dns_dbiterator_next(dbiter);
continue;
}
if (result != ISC_R_SUCCESS) {
dns_db_detachnode(gdb, &nextnode);
break;
}
if (!dns_name_issubdomain(nextname, gorigin) ||
(zonecut != NULL &&
dns_name_issubdomain(nextname, zonecut)))
{
dns_db_detachnode(gdb, &nextnode);
result = dns_dbiterator_next(dbiter);
continue;
}
dns_db_detachnode(gdb, &nextnode);
break;
}
if (result == ISC_R_NOMORE) {
dns_name_clone(gorigin, nextname);
done = ISC_TRUE;
} else if (result != ISC_R_SUCCESS)
fatal("iterating through the database failed: %s",
isc_result_totext(result));
result = dns_nsec_build(gdb, gversion, node, nextname,
zonettl);
check_result(result, "dns_nsec_build()");
dns_db_detachnode(gdb, &node);
}
dns_dbiterator_destroy(&dbiter);
}
/*
* Load the zone file from disk
*/
static void
loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) {
isc_buffer_t b;
int len;
dns_fixedname_t fname;
dns_name_t *name;
isc_result_t result;
len = strlen(origin);
isc_buffer_init(&b, origin, len);
isc_buffer_add(&b, len);
dns_fixedname_init(&fname);
name = dns_fixedname_name(&fname);
result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
if (result != ISC_R_SUCCESS)
fatal("failed converting name '%s' to dns format: %s",
origin, isc_result_totext(result));
result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
rdclass, 0, NULL, db);
check_result(result, "dns_db_create()");
result = dns_db_load(*db, file);
if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
fatal("failed loading zone from '%s': %s",
file, isc_result_totext(result));
}
/*
* Finds all public zone keys in the zone, and attempts to load the
* private keys from disk.
*/
static void
loadzonekeys(dns_db_t *db) {
dns_dbnode_t *node;
dns_dbversion_t *currentversion;
isc_result_t result;
dst_key_t *keys[20];
unsigned int nkeys, i;
currentversion = NULL;
dns_db_currentversion(db, &currentversion);
node = NULL;
result = dns_db_findnode(db, gorigin, ISC_FALSE, &node);
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone's origin: %s",
isc_result_totext(result));
result = dns_dnssec_findzonekeys(db, currentversion, node, gorigin,
mctx, 20, keys, &nkeys);
if (result == ISC_R_NOTFOUND)
result = ISC_R_SUCCESS;
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone keys: %s",
isc_result_totext(result));
for (i = 0; i < nkeys; i++) {
signer_key_t *key;
key = newkeystruct(keys[i], ISC_TRUE);
ISC_LIST_APPEND(keylist, key, link);
}
dns_db_detachnode(db, &node);
dns_db_closeversion(db, &currentversion, ISC_FALSE);
}
/*
* Finds all public zone keys in the zone.
*/
static void
loadzonepubkeys(dns_db_t *db) {
dns_dbversion_t *currentversion = NULL;
dns_dbnode_t *node = NULL;
dns_rdataset_t rdataset;
dns_rdata_t rdata = DNS_RDATA_INIT;
dst_key_t *pubkey;
signer_key_t *key;
isc_result_t result;
dns_db_currentversion(db, &currentversion);
result = dns_db_findnode(db, gorigin, ISC_FALSE, &node);
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone's origin: %s",
isc_result_totext(result));
dns_rdataset_init(&rdataset);
result = dns_db_findrdataset(db, node, currentversion,
dns_rdatatype_dnskey, 0, 0, &rdataset, NULL);
if (result != ISC_R_SUCCESS)
fatal("failed to find keys at the zone apex: %s",
isc_result_totext(result));
result = dns_rdataset_first(&rdataset);
check_result(result, "dns_rdataset_first");
while (result == ISC_R_SUCCESS) {
pubkey = NULL;
dns_rdata_reset(&rdata);
dns_rdataset_current(&rdataset, &rdata);
result = dns_dnssec_keyfromrdata(gorigin, &rdata, mctx,
&pubkey);
if (result != ISC_R_SUCCESS)
goto next;
if (!dst_key_iszonekey(pubkey)) {
dst_key_free(&pubkey);
goto next;
}
key = newkeystruct(pubkey, ISC_FALSE);
ISC_LIST_APPEND(keylist, key, link);
next:
result = dns_rdataset_next(&rdataset);
}
dns_rdataset_disassociate(&rdataset);
dns_db_detachnode(db, &node);
dns_db_closeversion(db, &currentversion, ISC_FALSE);
}
static void
warnifallksk(dns_db_t *db) {
dns_dbversion_t *currentversion = NULL;
dns_dbnode_t *node = NULL;
dns_rdataset_t rdataset;
dns_rdata_t rdata = DNS_RDATA_INIT;
dst_key_t *pubkey;
isc_result_t result;
dns_rdata_key_t key;
isc_boolean_t have_non_ksk = ISC_FALSE;
dns_db_currentversion(db, &currentversion);
result = dns_db_findnode(db, gorigin, ISC_FALSE, &node);
if (result != ISC_R_SUCCESS)
fatal("failed to find the zone's origin: %s",
isc_result_totext(result));
dns_rdataset_init(&rdataset);
result = dns_db_findrdataset(db, node, currentversion,
dns_rdatatype_dnskey, 0, 0, &rdataset, NULL);
if (result != ISC_R_SUCCESS)
fatal("failed to find keys at the zone apex: %s",
isc_result_totext(result));
result = dns_rdataset_first(&rdataset);
check_result(result, "dns_rdataset_first");
while (result == ISC_R_SUCCESS) {
pubkey = NULL;
dns_rdata_reset(&rdata);
dns_rdataset_current(&rdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &key, NULL);
check_result(result, "dns_rdata_tostruct");
if ((key.flags & DNS_KEYFLAG_KSK) == 0) {
have_non_ksk = ISC_TRUE;
result = ISC_R_NOMORE;
} else
result = dns_rdataset_next(&rdataset);
}
dns_rdataset_disassociate(&rdataset);
dns_db_detachnode(db, &node);
dns_db_closeversion(db, &currentversion, ISC_FALSE);
if (!have_non_ksk && !ignoreksk)
fprintf(stderr, "%s: warning: No non-KSK dnskey found. "
"Supply non-KSK dnskey or use '-z'.\n",
program);
}
static void
writeset(const char *prefix, dns_rdatatype_t type) {
char *filename;
char namestr[DNS_NAME_FORMATSIZE];
dns_db_t *db = NULL;
dns_dbversion_t *version = NULL;
dns_diff_t diff;
dns_difftuple_t *tuple = NULL;
dns_fixedname_t fixed;
dns_name_t *name;
dns_rdata_t rdata, ds;
isc_boolean_t have_ksk = ISC_FALSE;
isc_boolean_t have_non_ksk = ISC_FALSE;
isc_buffer_t b;
isc_buffer_t namebuf;
isc_region_t r;
isc_result_t result;
signer_key_t *key;
unsigned char dsbuf[DNS_DS_BUFFERSIZE];
unsigned char keybuf[DST_KEY_MAXSIZE];
unsigned int filenamelen;
const dns_master_style_t *style =
(type == dns_rdatatype_dnskey) ? masterstyle : dsstyle;
isc_buffer_init(&namebuf, namestr, sizeof(namestr));
result = dns_name_tofilenametext(gorigin, ISC_FALSE, &namebuf);
check_result(result, "dns_name_tofilenametext");
isc_buffer_putuint8(&namebuf, 0);
filenamelen = strlen(prefix) + strlen(namestr);
if (directory != NULL)
filenamelen += strlen(directory) + 1;
filename = isc_mem_get(mctx, filenamelen + 1);
if (filename == NULL)
fatal("out of memory");
if (directory != NULL)
sprintf(filename, "%s/", directory);
else
filename[0] = 0;
strcat(filename, prefix);
strcat(filename, namestr);
dns_diff_init(mctx, &diff);
for (key = ISC_LIST_HEAD(keylist);
key != NULL;
key = ISC_LIST_NEXT(key, link))
if (!key->isksk) {
have_non_ksk = ISC_TRUE;
break;
}
for (key = ISC_LIST_HEAD(keylist);
key != NULL;
key = ISC_LIST_NEXT(key, link))
if (key->isksk) {
have_ksk = ISC_TRUE;
break;
}
if (type == dns_rdatatype_dlv) {
dns_name_t tname;
unsigned int labels;
dns_name_init(&tname, NULL);
dns_fixedname_init(&fixed);
name = dns_fixedname_name(&fixed);
labels = dns_name_countlabels(gorigin);
dns_name_getlabelsequence(gorigin, 0, labels - 1, &tname);
result = dns_name_concatenate(&tname, dlv, name, NULL);
check_result(result, "dns_name_concatenate");
} else
name = gorigin;
for (key = ISC_LIST_HEAD(keylist);
key != NULL;
key = ISC_LIST_NEXT(key, link))
{
if (have_ksk && have_non_ksk && !key->isksk)
continue;
dns_rdata_init(&rdata);
dns_rdata_init(&ds);
isc_buffer_init(&b, keybuf, sizeof(keybuf));
result = dst_key_todns(key->key, &b);
check_result(result, "dst_key_todns");
isc_buffer_usedregion(&b, &r);
dns_rdata_fromregion(&rdata, gclass, dns_rdatatype_dnskey, &r);
if (type != dns_rdatatype_dnskey) {
result = dns_ds_buildrdata(gorigin, &rdata,
DNS_DSDIGEST_SHA1,
dsbuf, &ds);
check_result(result, "dns_ds_buildrdata");
if (type == dns_rdatatype_dlv)
ds.type = dns_rdatatype_dlv;
result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
name, 0, &ds, &tuple);
} else
result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
gorigin, zonettl,
&rdata, &tuple);
check_result(result, "dns_difftuple_create");
dns_diff_append(&diff, &tuple);
}
result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
gclass, 0, NULL, &db);
check_result(result, "dns_db_create");
result = dns_db_newversion(db, &version);
check_result(result, "dns_db_newversion");
result = dns_diff_apply(&diff, db, version);
check_result(result, "dns_diff_apply");
dns_diff_clear(&diff);
result = dns_master_dump(mctx, db, version, style, filename);
check_result(result, "dns_master_dump");
isc_mem_put(mctx, filename, filenamelen + 1);
dns_db_closeversion(db, &version, ISC_FALSE);
dns_db_detach(&db);
}
static void
print_time(FILE *fp) {
time_t currenttime;
currenttime = time(NULL);
fprintf(fp, "; File written on %s", ctime(&currenttime));
}
static void
print_version(FILE *fp) {
fprintf(fp, "; dnssec_signzone version " VERSION "\n");
}
static void
usage(void) {
fprintf(stderr, "Usage:\n");
fprintf(stderr, "\t%s [options] zonefile [keys]\n", program);
fprintf(stderr, "\n");
fprintf(stderr, "Version: %s\n", VERSION);
fprintf(stderr, "Options: (default value in parenthesis) \n");
fprintf(stderr, "\t-c class (IN)\n");
fprintf(stderr, "\t-d directory\n");
fprintf(stderr, "\t\tdirectory to find keyset files (.)\n");
fprintf(stderr, "\t-g:\t");
fprintf(stderr, "generate DS records from keyset files\n");
fprintf(stderr, "\t-s YYYYMMDDHHMMSS|+offset:\n");
fprintf(stderr, "\t\tRRSIG start time - absolute|offset (now - 1 hour)\n");
fprintf(stderr, "\t-e YYYYMMDDHHMMSS|+offset|\"now\"+offset]:\n");
fprintf(stderr, "\t\tRRSIG end time - absolute|from start|from now "
"(now + 30 days)\n");
fprintf(stderr, "\t-i interval:\n");
fprintf(stderr, "\t\tcycle interval - resign "
"if < interval from end ( (end-start)/4 )\n");
fprintf(stderr, "\t-v debuglevel (0)\n");
fprintf(stderr, "\t-o origin:\n");
fprintf(stderr, "\t\tzone origin (name of zonefile)\n");
fprintf(stderr, "\t-f outfile:\n");
fprintf(stderr, "\t\tfile the signed zone is written in "
"(zonefile + .signed)\n");
fprintf(stderr, "\t-r randomdev:\n");
fprintf(stderr, "\t\ta file containing random data\n");
fprintf(stderr, "\t-a:\t");
fprintf(stderr, "verify generated signatures\n");
fprintf(stderr, "\t-p:\t");
fprintf(stderr, "use pseudorandom data (faster but less secure)\n");
fprintf(stderr, "\t-t:\t");
fprintf(stderr, "print statistics\n");
fprintf(stderr, "\t-n ncpus (number of cpus present)\n");
fprintf(stderr, "\t-k key_signing_key\n");
fprintf(stderr, "\t-l lookasidezone\n");
fprintf(stderr, "\t-z:\t");
fprintf(stderr, "ignore KSK flag in DNSKEYs");
fprintf(stderr, "\n");
fprintf(stderr, "Signing Keys: ");
fprintf(stderr, "(default: all zone keys that have private keys)\n");
fprintf(stderr, "\tkeyfile (Kname+alg+tag)\n");
exit(0);
}
static void
removetempfile(void) {
if (removefile)
isc_file_remove(tempfile);
}
static void
print_stats(isc_time_t *timer_start, isc_time_t *timer_finish) {
isc_uint64_t runtime_us; /* Runtime in microseconds */
isc_uint64_t runtime_ms; /* Runtime in milliseconds */
isc_uint64_t sig_ms; /* Signatures per millisecond */
runtime_us = isc_time_microdiff(timer_finish, timer_start);
printf("Signatures generated: %10d\n", nsigned);
printf("Signatures retained: %10d\n", nretained);
printf("Signatures dropped: %10d\n", ndropped);
printf("Signatures successfully verified: %10d\n", nverified);
printf("Signatures unsuccessfully verified: %10d\n", nverifyfailed);
runtime_ms = runtime_us / 1000;
printf("Runtime in seconds: %7u.%03u\n",
(unsigned int) (runtime_ms / 1000),
(unsigned int) (runtime_ms % 1000));
if (runtime_us > 0) {
sig_ms = ((isc_uint64_t)nsigned * 1000000000) / runtime_us;
printf("Signatures per second: %7u.%03u\n",
(unsigned int) sig_ms / 1000,
(unsigned int) sig_ms % 1000);
}
}
int
main(int argc, char *argv[]) {
int i, ch;
char *startstr = NULL, *endstr = NULL, *classname = NULL;
char *origin = NULL, *file = NULL, *output = NULL;
char *dskeyfile[MAXDSKEYS];
int ndskeys = 0;
char *endp;
isc_time_t timer_start, timer_finish;
signer_key_t *key;
isc_result_t result;
isc_log_t *log = NULL;
isc_boolean_t pseudorandom = ISC_FALSE;
unsigned int eflags;
isc_boolean_t free_output = ISC_FALSE;
int tempfilelen;
dns_rdataclass_t rdclass;
dns_db_t *udb = NULL;
isc_task_t **tasks = NULL;
isc_buffer_t b;
int len;
masterstyle = &dns_master_style_explicitttl;
check_result(isc_app_start(), "isc_app_start");
result = isc_mem_create(0, 0, &mctx);
if (result != ISC_R_SUCCESS)
fatal("out of memory");
dns_result_register();
while ((ch = isc_commandline_parse(argc, argv,
"ac:d:e:f:ghi:k:l:n:o:pr:s:Stv:z"))
!= -1) {
switch (ch) {
case 'a':
tryverify = ISC_TRUE;
break;
case 'c':
classname = isc_commandline_argument;
break;
case 'd':
directory = isc_commandline_argument;
break;
case 'e':
endstr = isc_commandline_argument;
break;
case 'f':
output = isc_commandline_argument;
break;
case 'g':
generateds = ISC_TRUE;
break;
case 'h':
default:
usage();
break;
case 'i':
endp = NULL;
cycle = strtol(isc_commandline_argument, &endp, 0);
if (*endp != '\0' || cycle < 0)
fatal("cycle period must be numeric and "
"positive");
break;
case 'l':
dns_fixedname_init(&dlv_fixed);
len = strlen(isc_commandline_argument);
isc_buffer_init(&b, isc_commandline_argument, len);
isc_buffer_add(&b, len);
dns_fixedname_init(&dlv_fixed);
dlv = dns_fixedname_name(&dlv_fixed);
result = dns_name_fromtext(dlv, &b, dns_rootname,
ISC_FALSE, NULL);
check_result(result, "dns_name_fromtext(dlv)");
break;
case 'k':
if (ndskeys == MAXDSKEYS)
fatal("too many key-signing keys specified");
dskeyfile[ndskeys++] = isc_commandline_argument;
break;
case 'n':
endp = NULL;
ntasks = strtol(isc_commandline_argument, &endp, 0);
if (*endp != '\0' || ntasks > ISC_INT32_MAX)
fatal("number of cpus must be numeric");
break;
case 'o':
origin = isc_commandline_argument;
break;
case 'p':
pseudorandom = ISC_TRUE;
break;
case 'r':
setup_entropy(mctx, isc_commandline_argument, &ectx);
break;
case 's':
startstr = isc_commandline_argument;
break;
case 'S':
/* This is intentionally undocumented */
/* -S: simple output style */
masterstyle = &dns_master_style_simple;
break;
case 't':
printstats = ISC_TRUE;
break;
case 'v':
endp = NULL;
verbose = strtol(isc_commandline_argument, &endp, 0);
if (*endp != '\0')
fatal("verbose level must be numeric");
break;
case 'z':
ignoreksk = ISC_TRUE;
break;
}
}
if (ectx == NULL)
setup_entropy(mctx, NULL, &ectx);
eflags = ISC_ENTROPY_BLOCKING;
if (!pseudorandom)
eflags |= ISC_ENTROPY_GOODONLY;
result = dst_lib_init(mctx, ectx, eflags);
if (result != ISC_R_SUCCESS)
fatal("could not initialize dst");
isc_stdtime_get(&now);
if (startstr != NULL)
starttime = strtotime(startstr, now, now);
else
starttime = now - 3600; /* Allow for some clock skew. */
if (endstr != NULL)
endtime = strtotime(endstr, now, starttime);
else
endtime = starttime + (30 * 24 * 60 * 60);
if (cycle == -1)
cycle = (endtime - starttime) / 4;
if (ntasks == 0)
ntasks = isc_os_ncpus();
vbprintf(4, "using %d cpus\n", ntasks);
rdclass = strtoclass(classname);
setup_logging(verbose, mctx, &log);
argc -= isc_commandline_index;
argv += isc_commandline_index;
if (argc < 1)
usage();
file = argv[0];
argc -= 1;
argv += 1;
if (origin == NULL)
origin = file;
if (output == NULL) {
free_output = ISC_TRUE;
output = isc_mem_allocate(mctx,
strlen(file) + strlen(".signed") + 1);
if (output == NULL)
fatal("out of memory");
sprintf(output, "%s.signed", file);
}
result = dns_master_stylecreate(&dsstyle, DNS_STYLEFLAG_NO_TTL,
0, 24, 0, 0, 0, 8, mctx);
check_result(result, "dns_master_stylecreate");
gdb = NULL;
TIME_NOW(&timer_start);
loadzone(file, origin, rdclass, &gdb);
gorigin = dns_db_origin(gdb);
gclass = dns_db_class(gdb);
zonettl = soattl();
ISC_LIST_INIT(keylist);
if (argc == 0) {
loadzonekeys(gdb);
} else {
for (i = 0; i < argc; i++) {
dst_key_t *newkey = NULL;
result = dst_key_fromnamedfile(argv[i],
DST_TYPE_PUBLIC |
DST_TYPE_PRIVATE,
mctx, &newkey);
if (result != ISC_R_SUCCESS)
fatal("cannot load dnskey %s: %s", argv[i],
isc_result_totext(result));
key = ISC_LIST_HEAD(keylist);
while (key != NULL) {
dst_key_t *dkey = key->key;
if (dst_key_id(dkey) == dst_key_id(newkey) &&
dst_key_alg(dkey) == dst_key_alg(newkey) &&
dns_name_equal(dst_key_name(dkey),
dst_key_name(newkey)))
{
if (!dst_key_isprivate(dkey))
fatal("cannot sign zone with "
"non-private dnskey %s",
argv[i]);
break;
}
key = ISC_LIST_NEXT(key, link);
}
if (key == NULL) {
key = newkeystruct(newkey, ISC_TRUE);
ISC_LIST_APPEND(keylist, key, link);
} else
dst_key_free(&newkey);
}
loadzonepubkeys(gdb);
}
for (i = 0; i < ndskeys; i++) {
dst_key_t *newkey = NULL;
result = dst_key_fromnamedfile(dskeyfile[i],
DST_TYPE_PUBLIC |
DST_TYPE_PRIVATE,
mctx, &newkey);
if (result != ISC_R_SUCCESS)
fatal("cannot load dnskey %s: %s", dskeyfile[i],
isc_result_totext(result));
key = ISC_LIST_HEAD(keylist);
while (key != NULL) {
dst_key_t *dkey = key->key;
if (dst_key_id(dkey) == dst_key_id(newkey) &&
dst_key_alg(dkey) == dst_key_alg(newkey) &&
dns_name_equal(dst_key_name(dkey),
dst_key_name(newkey)))
{
/* Override key flags. */
key->issigningkey = ISC_TRUE;
key->isksk = ISC_TRUE;
key->isdsk = ISC_FALSE;
dst_key_free(&dkey);
key->key = newkey;
break;
}
key = ISC_LIST_NEXT(key, link);
}
if (key == NULL) {
/* Override dnskey flags. */
key = newkeystruct(newkey, ISC_TRUE);
key->isksk = ISC_TRUE;
key->isdsk = ISC_FALSE;
ISC_LIST_APPEND(keylist, key, link);
}
}
if (ISC_LIST_EMPTY(keylist)) {
fprintf(stderr, "%s: warning: No keys specified or found\n",
program);
nokeys = ISC_TRUE;
}
warnifallksk(gdb);
gversion = NULL;
result = dns_db_newversion(gdb, &gversion);
check_result(result, "dns_db_newversion()");
nsecify();
if (!nokeys) {
writeset("keyset-", dns_rdatatype_dnskey);
writeset("dsset-", dns_rdatatype_ds);
if (dlv != NULL) {
writeset("dlvset-", dns_rdatatype_dlv);
}
}
tempfilelen = strlen(output) + 20;
tempfile = isc_mem_get(mctx, tempfilelen);
if (tempfile == NULL)
fatal("out of memory");
result = isc_file_mktemplate(output, tempfile, tempfilelen);
check_result(result, "isc_file_mktemplate");
fp = NULL;
result = isc_file_openunique(tempfile, &fp);
if (result != ISC_R_SUCCESS)
fatal("failed to open temporary output file: %s",
isc_result_totext(result));
removefile = ISC_TRUE;
setfatalcallback(&removetempfile);
print_time(fp);
print_version(fp);
result = isc_taskmgr_create(mctx, ntasks, 0, &taskmgr);
if (result != ISC_R_SUCCESS)
fatal("failed to create task manager: %s",
isc_result_totext(result));
master = NULL;
result = isc_task_create(taskmgr, 0, &master);
if (result != ISC_R_SUCCESS)
fatal("failed to create task: %s", isc_result_totext(result));
tasks = isc_mem_get(mctx, ntasks * sizeof(isc_task_t *));
if (tasks == NULL)
fatal("out of memory");
for (i = 0; i < (int)ntasks; i++) {
tasks[i] = NULL;
result = isc_task_create(taskmgr, 0, &tasks[i]);
if (result != ISC_R_SUCCESS)
fatal("failed to create task: %s",
isc_result_totext(result));
result = isc_app_onrun(mctx, master, startworker, tasks[i]);
if (result != ISC_R_SUCCESS)
fatal("failed to start task: %s",
isc_result_totext(result));
}
RUNTIME_CHECK(isc_mutex_init(&namelock) == ISC_R_SUCCESS);
if (printstats)
RUNTIME_CHECK(isc_mutex_init(&statslock) == ISC_R_SUCCESS);
presign();
(void)isc_app_run();
if (!finished)
fatal("process aborted by user");
shuttingdown = ISC_TRUE;
for (i = 0; i < (int)ntasks; i++)
isc_task_detach(&tasks[i]);
isc_taskmgr_destroy(&taskmgr);
isc_mem_put(mctx, tasks, ntasks * sizeof(isc_task_t *));
postsign();
if (udb != NULL) {
dumpdb(udb);
dns_db_detach(&udb);
}
result = isc_stdio_close(fp);
check_result(result, "isc_stdio_close");
removefile = ISC_FALSE;
result = isc_file_rename(tempfile, output);
if (result != ISC_R_SUCCESS)
fatal("failed to rename temp file to %s: %s\n",
output, isc_result_totext(result));
DESTROYLOCK(&namelock);
if (printstats)
DESTROYLOCK(&statslock);
printf("%s\n", output);
dns_db_closeversion(gdb, &gversion, ISC_FALSE);
dns_db_detach(&gdb);
while (!ISC_LIST_EMPTY(keylist)) {
key = ISC_LIST_HEAD(keylist);
ISC_LIST_UNLINK(keylist, key, link);
dst_key_free(&key->key);
isc_mem_put(mctx, key, sizeof(signer_key_t));
}
isc_mem_put(mctx, tempfile, tempfilelen);
if (free_output)
isc_mem_free(mctx, output);
dns_master_styledestroy(&dsstyle, mctx);
cleanup_logging(&log);
dst_lib_destroy();
cleanup_entropy(&ectx);
if (verbose > 10)
isc_mem_stats(mctx, stdout);
isc_mem_destroy(&mctx);
(void) isc_app_finish();
if (printstats) {
TIME_NOW(&timer_finish);
print_stats(&timer_start, &timer_finish);
}
return (0);
}