dnssec-signzone.c revision 5c3e730c227b611c6e547ccd9fc14d66c2423502
/*
* Copyright (C) 1999, 2000 Internet Software Consortium.
*
* 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 DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM 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.
*/
#include <config.h>
#include <stdlib.h>
#include <isc/commandline.h>
#include <dns/dbiterator.h>
#include <dns/keyvalues.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
#include <dns/rdatastruct.h>
#include <dns/rdatatype.h>
#define PROGRAM "dnssec-signzone"
/*#define USE_ZONESTATUS*/
#define BUFSIZE 2048
typedef struct signer_key_struct signer_key_t;
typedef struct signer_array_struct signer_array_t;
struct signer_key_struct {
};
struct signer_array_struct {
};
static int cycle = -1;
static int verbose;
static void
exit(1);
}
static inline void
if (result != ISC_R_SUCCESS) {
exit(1);
}
}
static void
return;
}
/* Not thread-safe! */
static char *
isc_buffer_t b;
isc_region_t r;
static char data[1025];
isc_buffer_usedregion(&b, &r);
return (char *) r.base;
}
/* Not thread-safe! */
static char *
isc_buffer_t b;
isc_region_t r;
static char data[10];
isc_buffer_usedregion(&b, &r);
return (char *) r.base;
}
/*
* Not thread-safe!
*/
static char *
isc_buffer_t b;
isc_region_t r;
static char data[10];
isc_buffer_usedregion(&b, &r);
return ((char *)r.base);
}
static inline void
if (bit != 0)
else
}
static void
{
if (result != ISC_R_SUCCESS)
fatal("key '%s/%s/%d' failed to sign data: %s",
if (tryverify) {
if (result == ISC_R_SUCCESS)
else
}
}
static inline isc_boolean_t
}
static inline isc_boolean_t
char origin[1024];
isc_buffer_t b;
}
/*
* 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 *
char *keyname;
return key;
}
if (result != ISC_R_SUCCESS)
return (NULL);
fatal("out of memory");
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;
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;
default:
fatal("failure looking for '%s KEY' in database: %s",
return ISC_FALSE; /* removes a warning */
}
}
static inline isc_boolean_t
{
}
#define allocbufferandrdata \
isc_buffer_t b; \
fatal("out of memory"); \
/*
* 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 i;
for (i = 0; i < 256; i++)
if (result == ISC_R_NOTFOUND) {
}
if (result != ISC_R_SUCCESS)
fatal("failed while looking for '%s SIG %s': %s",
if (!nosigs) {
while (result == ISC_R_SUCCESS) {
/* sig is dropped and not replaced */
"invalid validity period\n",
}
{
/* sig is dropped and not replaced */
"private key not found\n",
}
"key not found\n",
if (!expired)
}
else if (issigningkey(key)) {
if (!expired &&
{
vbprintf(2,
"\tsig by %s/%s/%d retained\n",
}
else {
vbprintf(2,
"\tsig by %s/%s/%d dropped - "
"%s\n",
expired ? "expired" :
"failed to verify");
}
}
if (!expired &&
{
vbprintf(2,
"\tsig by %s/%s/%d retained\n",
}
else {
vbprintf(2,
"\tsig by %s/%s/%d "
"dropped - %s\n",
expired ? "expired" :
"failed to verify");
}
}
else if (!expired) {
}
else {
}
if (keep) {
&sig, &b);
}
else if (resign) {
}
}
if (result == ISC_R_NOMORE)
}
for (i = 0; i < 256; i++)
if (wassignedby[i] != 0) {
break;
}
{
}
}
else
0, NULL);
if (result == DNS_R_UNCHANGED)
}
else if (!nosigs) {
#if 0
/*
* If this is compiled in, running a signed set through the
* signer with no private keys causes DNS_R_BADDB to occur
* later. This is bad.
*/
if (result == ISC_R_NOTFOUND)
#endif
fatal("File is currently signed but no private keys were "
"found. This won't work.");
}
}
}
}
#ifndef USE_ZONESTATUS
/* Determine if a KEY set contains a null key */
static isc_boolean_t
while (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);
}
if (result != ISC_R_NOMORE)
fatal("failure looking for null keys");
return (ISC_FALSE);
}
#endif
/*
* Looks for signatures of the zone keys by the parent, and imports them
* if found.
*/
static void
{
unsigned char filename[256];
isc_buffer_t b;
isc_region_t r;
isc_buffer_usedregion(&b, &r);
if (result != ISC_R_SUCCESS)
goto failure;
if (result != ISC_R_SUCCESS)
goto failure;
if (result != ISC_R_SUCCESS)
goto failure;
goto failure;
for (;
result == ISC_R_SUCCESS;
{
break;
}
if (result != ISC_R_SUCCESS)
break;
}
if (result != ISC_R_NOMORE)
goto failure;
}
/*
* Looks for our signatures of child keys. If present, inform the caller,
* who will set the zone status (KEY) bit in the NXT record.
*/
static isc_boolean_t
unsigned char filename[256];
isc_buffer_t b;
isc_region_t r;
isc_buffer_usedregion(&b, &r);
if (result != ISC_R_SUCCESS)
goto failure;
if (result != ISC_R_SUCCESS)
goto failure;
if (result != ISC_R_SUCCESS)
goto failure;
if (!dns_rdataset_isassociated(&set) ||
goto disfail;
if (result != ISC_R_SUCCESS)
goto disfail;
goto disfail;
if (result == ISC_R_SUCCESS) {
break;
}
}
if (dns_rdataset_isassociated(&set))
if (dns_rdataset_isassociated(&sigset))
return (found);
}
/*
* 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
{
static int warnwild = 0;
if (dns_name_iswildcard(name)) {
if (warnwild++ == 0) {
"handle wildcards in secure zones:\n", PROGRAM);
"not generated by the server\n");
"not required by the resolver\n");
}
}
if (!atorigin) {
dns_rdatatype_ns, 0, 0, &nsset,
NULL);
/* Is this a delegation point? */
if (result == ISC_R_SUCCESS) {
}
}
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, unless we're using null keys, in
* which case we need to check for a null key and add one
* if it's not present.
*/
if (isdelegation) {
case dns_rdatatype_nxt:
break;
#ifndef USE_ZONESTATUS
case dns_rdatatype_key:
if (hasnullkey(&rdataset))
break;
goto skip;
#endif
default:
goto skip;
}
}
/*
* There probably should be a dns_nxtsetbit, but it can get
* complicated if we need to extend the length of the
* bit set. In this case, since the NXT bit is set and
* SIG < NXT and KEY < NXT, the easy way works.
*/
unsigned char *nxt_bits;
isc_region_t r, r2;
unsigned char keydata[4];
isc_buffer_t b;
dns_rdata_toregion(&rdata, &r);
dns_name_fromregion(&nxtname, &r);
#ifdef USE_ZONESTATUS
if (isdelegation && childkey) {
"setting KEY bit in NXT\n",
}
#else
if (isdelegation && !childkey) {
0, 0, &keyset,
NULL);
if (result == ISC_R_SUCCESS &&
hasnullkey(&keyset))
goto alreadyhavenullkey;
if (result == ISC_R_NOTFOUND)
if (result != ISC_R_SUCCESS)
fatal("failure looking for null key "
if (dns_rdataset_isassociated(&keyset))
"adding null key\n",
0, 0,
if (result != ISC_R_SUCCESS)
fatal("failed to generate null key");
isc_buffer_usedregion(&b, &r);
dns_rdatatype_key, &r);
link);
keyrdatalist.covers = 0;
result =
&keyset);
"dns_rdatalist_tordataset");
NULL);
;
}
#endif
}
skip:
}
if (result != ISC_R_NOMORE)
fatal("rdataset iteration for name '%s' failed: %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);
}
} while (result == ISC_R_SUCCESS);
return (result);
}
/*
* Generates NXTs and SIGs for each non-glue name in the zone.
*/
static void
if (result != ISC_R_SUCCESS)
fatal("failed to find '%s SOA' in the zone: %s",
lastcut);
while (result == ISC_R_SUCCESS) {
if (!atorigin) {
0, &rdsiter);
while (result == ISC_R_SUCCESS) {
break;
}
}
fatal("rdataset iteration failed: %s",
if (result == ISC_R_SUCCESS) {
else {
sizeof(dns_name_t));
fatal("out of memory");
}
}
}
if (result == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS)
else if (result == ISC_R_NOMORE)
else {
fatal("iterating through the database failed: %s",
}
}
if (result != ISC_R_NOMORE)
fatal("iterating through the database failed: %s",
}
}
static void
isc_buffer_t b, b2;
unsigned char namedata[1024];
int len;
isc_buffer_add(&b, len);
if (result != ISC_R_SUCCESS)
fatal("failed converting name '%s' to dns format: %s",
if (result != ISC_R_SUCCESS)
fatal("failed loading zone from '%s': %s",
}
static void
}
/*
* 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++) {
fatal("out of memory");
}
}
static isc_stdtime_t
char *endp = "";
if (str[0] == '+') {
}
}
else {
if (result != ISC_R_SUCCESS)
}
if (*endp != '\0')
return ((isc_stdtime_t) val);
}
static void
usage(void) {
"(now + 30 days)\n");
"if < cycle from end ( (end-start)/4 )\n");
"(zonefile + .signed)\n");
"(if currently valid)\n");
exit(0);
}
static void
== ISC_R_SUCCESS);
/*
* Set up a channel similar to default_stderr except:
* - the logging level is passed in
* - the logging level is printed
* - no time stamp is printed
*/
}
int
int i, ch;
char *endp;
int loglevel;
if (result != ISC_R_SUCCESS)
fatal("out of memory");
!= -1) {
switch (ch) {
case 's':
fatal("out of memory");
break;
case 'e':
fatal("out of memory");
break;
case 'c':
if (*endp != '\0')
fatal("cycle period must be numeric");
break;
case 'v':
if (*endp != '\0')
fatal("verbose level must be numeric");
break;
case 'o':
fatal("out of memory");
break;
case 'f':
fatal("out of memory");
break;
case 'a':
break;
case 'h':
default:
usage();
}
}
}
else
}
else
if (cycle == -1) {
}
switch (verbose) {
case 0:
/*
* We want to see warnings about things like out-of-zone
* data in the master file even when not verbose.
*/
break;
case 1:
break;
default:
break;
}
if (argc < 1)
usage();
fatal("out of memory");
argc -= 1;
argv += 1;
fatal("out of memory");
}
fatal("out of memory");
}
if (argc == 0) {
}
}
else {
for (i = 0; i < argc; i++) {
int alg;
isc_buffer_t b;
if (result != ISC_R_SUCCESS)
usage();
dst_key_name(dkey)) == 0)
{
if (!dst_key_isprivate(dkey))
fatal("cannot sign zone with "
"non-private key "
"'%s/%s/%d'",
dst_key_id(dkey));
break;
}
}
if (result != ISC_R_SUCCESS)
fatal("failed to load key '%s/%s/%d' "
"from disk: %s", namestr,
fatal("out of memory");
}
}
}
/*
* Should we update the SOA serial?
*/
if (result != ISC_R_SUCCESS)
fatal("failed to write new database to '%s': %s",
dns_db_detach(&db);
}
/* isc_mem_stats(mctx, stdout);*/
return (0);
}