dnssec.c revision 97639003b0992b5f30ce82bdcc2fcd9d621ff09c
/*
* Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1999-2003 Internet Software Consortium.
*
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC 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.c,v 1.104 2009/10/12 23:48:01 tbox Exp $
*/
/*! \file */
#include <config.h>
#include <stdlib.h>
#include <dns/fixedname.h>
#include <dns/keyvalues.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#define RETERR(x) do { \
result = (x); \
if (result != ISC_R_SUCCESS) \
goto failure; \
} while (0)
#define TYPE_SIGN 0
#define TYPE_VERIFY 1
static isc_result_t
static int
static isc_result_t
static isc_result_t
}
/*
* Make qsort happy.
*/
static int
(const dns_rdata_t *)rdata2));
}
/*
* Sort the rdataset into an array.
*/
static isc_result_t
{
int i = 0, n;
n = dns_rdataset_count(set);
return (ISC_R_NOMEMORY);
if (ret != ISC_R_SUCCESS) {
return (ret);
}
/*
* Put them in the array.
*/
do {
dns_rdata_init(&data[i]);
/*
* Sort the array.
*/
*nrdata = n;
return (ISC_R_SUCCESS);
}
{
isc_buffer_t b;
isc_region_t r;
dns_rdata_toregion(rdata, &r);
isc_buffer_add(&b, r.length);
}
static isc_result_t
isc_region_t r;
dns_rdata_toregion(sigrdata, &r);
r.length = 18;
if (ret != ISC_R_SUCCESS)
return (ret);
== ISC_R_SUCCESS);
return (dst_context_adddata(ctx, &r));
}
{
int nrdatas, i;
isc_region_t r;
unsigned int sigsize;
return (DNS_R_INVALIDTIME);
/*
* Is the key allowed to sign data?
*/
if (flags & DNS_KEYTYPE_NOAUTH)
return (DNS_R_KEYUNAUTHORIZED);
return (DNS_R_KEYUNAUTHORIZED);
if (dns_name_iswildcard(name))
if (ret != ISC_R_SUCCESS)
return (ret);
/*
* The actual contents of sig.signature are not important yet, since
* they're not used in digest_sig().
*/
return (ISC_R_NOMEMORY);
if (ret != ISC_R_SUCCESS)
goto cleanup_signature;
if (ret != ISC_R_SUCCESS)
goto cleanup_databuf;
if (ret != ISC_R_SUCCESS)
goto cleanup_databuf;
/*
* Digest the SIG rdata.
*/
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
NULL) == ISC_R_SUCCESS);
/*
* Create an envelope for each rdata: <name|type|class|ttl>.
*/
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
isc_buffer_usedregion(&envbuf, &r);
for (i = 0; i < nrdatas; i++) {
/*
* Skip duplicates.
*/
continue;
/*
* Digest the envelope.
*/
if (ret != ISC_R_SUCCESS)
goto cleanup_array;
/*
* Digest the length of the rdata.
*/
if (ret != ISC_R_SUCCESS)
goto cleanup_array;
/*
* Digest the rdata.
*/
if (ret != ISC_R_SUCCESS)
goto cleanup_array;
}
if (ret != ISC_R_SUCCESS)
goto cleanup_array;
isc_buffer_usedregion(&sigbuf, &r);
ret = ISC_R_NOSPACE;
goto cleanup_array;
}
return (ret);
}
{
isc_region_t r;
int nrdatas, i;
unsigned char data[300];
int labels = 0;
if (ret != ISC_R_SUCCESS)
return (ret);
return (DNS_R_SIGINVALID);
return (DNS_R_SIGINVALID);
if (!ignoretime) {
/*
* Is SIG temporally valid?
*/
return (DNS_R_SIGFUTURE);
return (DNS_R_SIGEXPIRED);
}
/*
* NS, SOA and DNSSKEY records are signed by their owner.
* DS records are signed by the parent.
*/
case dns_rdatatype_ns:
case dns_rdatatype_soa:
case dns_rdatatype_dnskey:
return (DNS_R_SIGINVALID);
break;
case dns_rdatatype_ds:
return (DNS_R_SIGINVALID);
/* FALLTHROUGH */
default:
return (DNS_R_SIGINVALID);
break;
}
/*
* Is the key allowed to sign data?
*/
if (flags & DNS_KEYTYPE_NOAUTH)
return (DNS_R_KEYUNAUTHORIZED);
return (DNS_R_KEYUNAUTHORIZED);
if (ret != ISC_R_SUCCESS)
goto cleanup_struct;
/*
* Digest the SIG rdata (not including the signature).
*/
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
/*
* If the name is an expanded wildcard, use the wildcard name.
*/
NULL) == ISC_R_SUCCESS);
/*
* Create an envelope for each rdata: <name|type|class|ttl>.
*/
}
else
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
isc_buffer_usedregion(&envbuf, &r);
for (i = 0; i < nrdatas; i++) {
/*
* Skip duplicates.
*/
continue;
/*
* Digest the envelope.
*/
if (ret != ISC_R_SUCCESS)
goto cleanup_array;
/*
* Digest the rdata length.
*/
/*
* Digest the rdata.
*/
if (ret != ISC_R_SUCCESS)
goto cleanup_array;
if (ret != ISC_R_SUCCESS)
goto cleanup_array;
}
if (ret == DST_R_VERIFYFAILURE)
}
return (ret);
}
{
if (result == DNS_R_FROMWILDCARD)
return (result);
}
unsigned int *nkeys)
{
unsigned int count = 0;
*nkeys = 0;
if (!is_zone_key(pubkey) ||
goto next;
/* Corrupted .key file? */
goto next;
if (result == ISC_R_FILENOTFOUND) {
count++;
goto next;
}
if (result != ISC_R_SUCCESS)
goto failure;
/* We should never get here. */
goto next;
}
count++;
next:
}
if (result != ISC_R_NOMORE)
goto failure;
if (count == 0)
else
if (result != ISC_R_SUCCESS)
while (count > 0)
return (result);
}
unsigned int *nkeys)
{
}
unsigned char data[512];
unsigned char header[DNS_MESSAGE_HEADERLEN];
unsigned int sigsize;
isc_region_t r;
if (is_response(msg))
sig.originalttl = 0;
/*
* Digest the fields of the SIG - we can cheat and use
* dns_rdata_fromstruct. Since siglen is 0, the digested data
* is identical to dns format.
*/
dns_rdatatype_sig /* SIG(0) */,
isc_buffer_usedregion(&databuf, &r);
/*
* If this is a response, digest the query.
*/
if (is_response(msg))
/*
* Digest the header.
*/
isc_buffer_usedregion(&headerbuf, &r);
/*
* Digest the remainder of the message.
*/
goto failure;
}
dns_rdatatype_sig /* SIG(0) */,
return (ISC_R_SUCCESS);
if (signeedsfree)
return (result);
}
{
unsigned char header[DNS_MESSAGE_HEADERLEN];
if (is_response(msg)) {
return (DNS_R_UNEXPECTEDTSIG);
}
goto failure;
}
goto failure;
}
goto failure;
}
goto failure;
}
goto failure;
}
/*
* Digest the SIG(0) record, except for the signature.
*/
dns_rdata_toregion(&rdata, &r);
/*
* If this is a response, digest the query.
*/
if (is_response(msg))
/*
* Extract the header.
*/
/*
* Decrement the additional field counter.
*/
/*
* Digest the modified header.
*/
/*
* Digest all non-SIG(0) records.
*/
if (result != ISC_R_SUCCESS) {
goto failure;
}
return (ISC_R_SUCCESS);
if (signeedsfree)
return (result);
}
/*%
* Does this key ('rdata') self sign the rrset ('rdataset')?
*/
{
} else {
}
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
result == ISC_R_SUCCESS;
{
if (result == ISC_R_SUCCESS) {
return (ISC_TRUE);
}
}
}
return (ISC_FALSE);
}
{
return (ISC_R_NOMEMORY);
dk->prepublish = 0;
/* KSK or ZSK? */
/* Is this an old-style key? */
return (ISC_R_SUCCESS);
}
void
}
static void
if (result == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS)
/* Metadata says publish (but possibly not activate) */
/* Metadata says activate (so we must also publish) */
}
/*
* Activation date is set (maybe in the future), but
* publication date isn't. Most likely the user wants to
* publish now and activate later.
*/
/*
* If activation date is in the future, make note of how far off
*/
}
/*
* Key has been marked inactive: we can continue publishing,
* but don't sign.
*/
}
/*
* Metadata says revoke. If the key is published,
* we *have to* sign with it per RFC5011--even if it was
* not active before.
*
* If it hasn't already been done, we should also revoke it now.
*/
if ((flags & DNS_KEYFLAG_REVOKE) == 0) {
}
}
/*
* Metadata says delete, so don't publish this key or sign with it.
*/
}
}
/*%
* Get a list of DNSSEC keys from the key repository
*/
{
char namebuf[DNS_NAME_FORMATSIZE], *p;
isc_buffer_t b;
unsigned int len;
isc_dir_init(&dir);
len = isc_buffer_usedlength(&b);
continue;
} else {
}
}
}
if (!ISC_LIST_EMPTY(list))
else
if (dir_open)
isc_dir_close(&dir);
}
return (result);
}
/*%
* Add 'newkey' to 'keylist' if it's not already there.
*
* If 'savekeys' is ISC_TRUE, then we need to preserve all
* the keys in the keyset, regardless of whether they have
* metadata indicating they should be deactivated or removed.
*/
static void
{
/* Skip duplicates */
dst_key_name(*newkey)))
break;
}
/*
* Found a match. If the old key was only public and the
* new key is private, replace the old one; otherwise
* we're done.
*/
} else if (dst_key_isprivate(*newkey)) {
}
return;
}
}
}
/*%
* Add the contents of a DNSKEY rdataset 'keyset' to 'keylist'.
*/
{
result == ISC_R_SUCCESS;
if (!is_zone_key(pubkey) ||
continue;
/* Corrupted .key file? */
continue;
if (public) {
continue;
}
if (result == ISC_R_FILENOTFOUND) {
continue;
}
/* We should never get here. */
continue;
}
}
if (result == ISC_R_NOMORE)
else if (result != ISC_R_SUCCESS)
goto failure;
goto success;
/*
* Mark all keys which signed the DNSKEY set, for future reference.
*/
result == ISC_R_SUCCESS;
break;
}
}
}
if (result == ISC_R_NOMORE)
if (dns_rdataset_isassociated(&keys))
if (dns_rdataset_isassociated(&sigs))
return (result);
}
static isc_result_t
unsigned char data[DST_KEY_MAXSIZE];
isc_buffer_t b;
isc_region_t r;
if (result != ISC_R_SUCCESS)
return (result);
isc_buffer_usedregion(&b, &r);
dns_rdatatype_dnskey, &r);
return (ISC_R_SUCCESS);
}
static isc_result_t
void (*report)(const char *, ...))
{
char alg[80];
report("Fetching %s %d/%s from key %s\n",
char keystr[DST_KEY_FORMATSIZE];
report("Key %s: Delaying activation to match the DNSKEY TTL.",
}
/* publish key */
return (result);
}
static isc_result_t
void (*report)(const char *, ...))
{
char alg[80];
report("Removing %s key %d/%s from DNSKEY RRset.\n",
&tuple));
return (result);
}
/*
* Update 'keys' with information from 'newkeys'.
*
* If 'removed' is not NULL, any keys that are being removed from
* the zone will be added to the list for post-removal processing.
*/
void (*report)(const char *, ...))
{
ISC_TRUE)) {
break;
}
}
/* No match found in keys; add the new key. */
}
continue;
}
/* Match found: remove or update it as needed */
if (key1->hint_remove) {
"expired", report));
else
} else if (key_revoked &&
/*
* A previously valid key has been revoked.
* We need to remove the old version and pull
* in the new one.
*/
"revoked", report));
else
/*
* XXX: The revoke flag is only defined for trust
* anchors. Setting the flag on a non-KSK is legal,
* but not defined in any RFC. It seems reasonable
* to treat it the same as a KSK: keep it in the
* zone, sign the DNSKEY set with it, but not
* sign other records with it.
*/
continue;
} else {
/*
* If a key was specified on the command line,
* not in the zone, it can be imported into the
* zone now.
*/
}
}
/* Free any leftover keys in newkeys */
while (!ISC_LIST_EMPTY(*newkeys)) {
}
return (result);
}