dnssec.c revision 811acf52b825f9fb4889cad7b84b581a2d4776f9
/*
* Copyright (C) 2004-2015 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.
*/
/*! \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
}
static inline void
if (dns_dnssec_stats != NULL)
}
/*
* 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);
if (downcase) {
NULL) == ISC_R_SUCCESS);
} else
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);
/*
* Downcase signer.
*/
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;
}
char namebuf[DNS_NAME_FORMATSIZE];
"successfully validated after lower casing "
"signer '%s'", namebuf);
} else if (ret == ISC_R_SUCCESS)
goto again;
}
if (ret == DST_R_VERIFYFAILURE)
if (ret != ISC_R_SUCCESS)
}
return (ret);
}
{
if (result == DNS_R_FROMWILDCARD)
return (result);
}
/* Is this an old-style key? */
/*
* Smart signing started with key format 1.3; prior to that, all
* keys are assumed active
*/
return (ISC_TRUE);
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)
return (ISC_FALSE);
return (ISC_TRUE);
return (ISC_TRUE);
return (ISC_FALSE);
}
unsigned int *nkeys)
{
unsigned int count = 0;
*nkeys = 0;
if (!is_zone_key(pubkey) ||
goto next;
/* Corrupted .key file? */
goto next;
/*
* If the key was revoked and the private file
* doesn't exist, maybe it was revoked internally
* by named. Try loading the unrevoked version.
*/
if (result == ISC_R_FILENOTFOUND) {
if ((flags & DNS_KEYFLAG_REVOKE) != 0) {
flags & ~DNS_KEYFLAG_REVOKE);
if (result == ISC_R_SUCCESS &&
ISC_FALSE)) {
}
}
}
if (result != ISC_R_SUCCESS) {
char filename[ISC_DIR_NAMEMAX];
&buf);
if (result2 != ISC_R_SUCCESS) {
char namebuf[DNS_NAME_FORMATSIZE];
char algbuf[DNS_SECALG_FORMATSIZE];
"key file for %s/%s/%d",
}
"dns_dnssec_findzonekeys2: error "
"reading %s: %s",
}
count++;
goto next;
}
if (result != ISC_R_SUCCESS)
goto failure;
/*
* If a key is marked inactive, skip it
*/
count++;
goto next;
}
/*
* Whatever the key's default TTL may have
* been, the rdataset TTL takes priority.
*/
/* 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 {
}
ignoretime, mctx));
}
{
return (ISC_FALSE);
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? */
/* Smart signing started with key format 1.3 */
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) */
/* Only publish if publish time has already passed. */
}
/*
* 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];
isc_buffer_t b;
unsigned int len, i;
isc_dir_init(&dir);
len = isc_buffer_usedlength(&b);
directory = ".";
continue;
break;
continue;
break;
continue;
if (result != ISC_R_SUCCESS) {
"dns_dnssec_findmatchingkeys: "
"error reading key file %s: %s",
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 isc_result_t
{
/* 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
* leave it. But either way, mark the key as having
* been found in the zone.
*/
} else if (dst_key_isprivate(*newkey)) {
}
return (ISC_R_SUCCESS);
}
if (result != ISC_R_SUCCESS)
return (result);
}
return (ISC_R_SUCCESS);
}
/*%
* for future reference.
*/
static isc_result_t
result == ISC_R_SUCCESS;
break;
}
}
}
if (result == ISC_R_NOMORE)
if (dns_rdataset_isassociated(&sigs))
return (result);
}
/*%
* Add the contents of a DNSKEY rdataset 'keyset' to 'keylist'.
*/
{
result == ISC_R_SUCCESS;
if (!is_zone_key(pubkey) ||
goto skip;
/* Corrupted .key file? */
goto skip;
if (publickey) {
goto skip;
}
/*
* If the key was revoked and the private file
* doesn't exist, maybe it was revoked internally
* by named. Try loading the unrevoked version.
*/
if (result == ISC_R_FILENOTFOUND) {
if ((flags & DNS_KEYFLAG_REVOKE) != 0) {
flags & ~DNS_KEYFLAG_REVOKE);
if (result == ISC_R_SUCCESS &&
ISC_FALSE)) {
}
}
}
if (result != ISC_R_SUCCESS) {
char filename[ISC_DIR_NAMEMAX];
&buf);
if (result2 != ISC_R_SUCCESS) {
char namebuf[DNS_NAME_FORMATSIZE];
char algbuf[DNS_SECALG_FORMATSIZE];
"key file for %s/%s/%d",
}
"dns_dnssec_keylistfromrdataset: error "
"reading %s: %s",
}
goto skip;
}
/* This should never happen. */
goto skip;
/*
* Whatever the key's default TTL may have
* been, the rdataset TTL takes priority.
*/
skip:
}
if (result != ISC_R_NOMORE)
if (dns_rdataset_isassociated(&keys))
return (result);
}
static isc_result_t
{
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 *, ...))
{
unsigned char buf[DST_KEY_MAXSIZE];
char alg[80];
report("Fetching %s %d/%s from key %s.",
char keystr[DST_KEY_FORMATSIZE];
report("Key %s: Delaying activation to match the DNSKEY TTL.\n",
}
/* publish key */
return (result);
}
static isc_result_t
void (*report)(const char *, ...))
{
unsigned char buf[DST_KEY_MAXSIZE];
char alg[80];
report("Removing %s key %d/%s from DNSKEY RRset.",
&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 *, ...))
{
/*
* First, look through the existing key list to find keys
* supplied from the command line which are not in the zone.
* Update the zone to include them.
*
* Also, if there are keys published in the zone already,
* use their TTL for all subsequent published keys.
*/
}
}
}
/*
* If there were no existing keys, use the smallest nonzero
* TTL of the keys found in the repository.
*/
if (thisttl != 0 &&
}
if (shortest != 0)
}
/*
* Second, scan the list of newly found keys looking for matches
* with known keys, and update accordingly.
*/
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 {
}
}
/* Free any leftover keys in newkeys */
while (!ISC_LIST_EMPTY(*newkeys)) {
}
return (result);
}