nsec3.c revision 0c27b3fe77ac1d5094ba3521e8142d9e7973133f
/*
* Copyright (C) 2006, 2008-2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* $Id$ */
#include <config.h>
#include <isc/iterated_hash.h>
#include <dns/compress.h>
#include <dns/dbiterator.h>
#include <dns/fixedname.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
#include <dns/rdatastruct.h>
#define CHECK(x) do { \
result = (x); \
if (result != ISC_R_SUCCESS) \
goto failure; \
} while (0)
#define OPTOUT(x) (((x) & DNS_NSEC3FLAG_OPTOUT) != 0)
#define CREATE(x) (((x) & DNS_NSEC3FLAG_CREATE) != 0)
#define INITIAL(x) (((x) & DNS_NSEC3FLAG_INITIAL) != 0)
#define REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 0)
unsigned int flags, unsigned int iterations,
{
isc_region_t r;
unsigned int i;
unsigned int max_type;
unsigned char *p;
switch (hashalg) {
case dns_hash_sha1:
break;
}
p = buffer;
*p++ = hashalg;
*p++ = flags;
*p++ = iterations >> 8;
*p++ = iterations;
*p++ = (unsigned char)salt_length;
p += salt_length;
*p++ = (unsigned char)hash_length;
p += hash_length;
/*
* Use the end of the space for a raw bitmap leaving enough
* space for the window identifiers and length octets.
*/
max_type = 0;
goto collapse_bitmap;
if (result != ISC_R_SUCCESS)
return (result);
result == ISC_R_SUCCESS;
{
/*
* Work out if we need to set the RRSIG bit for
* this node. We set the RRSIG bit if either of
* the following conditions are met:
* 1) We have a SOA or DS then we need to set
* the RRSIG bit as both always will be signed.
* 2) We set the RRSIG bit if we don't have
* a NS record but do have other data.
*/
else
}
}
if (dns_rdatatype_rrsig > max_type)
}
/*
* At zone cuts, deny the existence of glue in the parent zone.
*/
for (i = 0; i <= max_type; i++) {
if (dns_nsec_isset(bm, i) &&
dns_nsec_setbit(bm, i, 0);
}
}
if (result != ISC_R_NOMORE)
return (result);
return (ISC_R_SUCCESS);
}
/* This should never fail */
i += 2;
break;
continue;
type % 256));
break;
}
return (present);
}
unsigned char rethash[NSEC3_MAX_HASH_LENGTH],
{
unsigned char hash[NSEC3_MAX_HASH_LENGTH];
unsigned char nametext[DNS_NAME_FORMATSIZE];
/* hash the node name */
salt, (int)saltlength,
if (len == 0U)
return (DNS_R_BADALG);
if (hash_length != NULL)
*hash_length = len;
/* convert the hash to base32hex non-padded */
/* convert the hex to a domain name */
}
unsigned int
switch (hash) {
case dns_hash_sha1:
return(ISC_SHA1_DIGESTLENGTH);
}
return (0);
}
switch (hash) {
case dns_hash_sha1:
return (ISC_TRUE);
}
return (ISC_FALSE);
}
/*%
* Update a single RR in version 'ver' of 'db' and log the
* update in 'diff'.
*
* Ensures:
* \li '*tuple' == NULL. Either the tuple is freed, or its
* ownership has been transferred to the diff.
*/
static isc_result_t
{
/*
* Create a singleton diff.
*/
/*
* Apply it to the database.
*/
if (result != ISC_R_SUCCESS) {
return (result);
}
/*
* Merge it into the current pending journal entry.
*/
/*
* Do not clear temp_diff.
*/
return (ISC_R_SUCCESS);
}
/*%
* Set '*exists' to true iff the given name exists, to false otherwise.
*/
static isc_result_t
{
if (result == ISC_R_NOTFOUND) {
return (ISC_R_SUCCESS);
}
if (result != ISC_R_SUCCESS)
return (result);
(isc_stdtime_t) 0, &iter);
if (result != ISC_R_SUCCESS)
goto cleanup_node;
if (result == ISC_R_SUCCESS) {
} else if (result == ISC_R_NOMORE) {
} else
return (result);
}
static isc_boolean_t
const dns_rdata_nsec3param_t *nsec3param)
{
return (ISC_TRUE);
return (ISC_FALSE);
}
/*%
* Delete NSEC3 records at "name" which match "param", recording the
* change in "diff".
*/
static isc_result_t
{
if (result == ISC_R_NOTFOUND)
return (ISC_R_SUCCESS);
if (result != ISC_R_SUCCESS)
return (result);
if (result == ISC_R_NOTFOUND) {
goto cleanup_node;
}
if (result != ISC_R_SUCCESS)
goto cleanup_node;
result == ISC_R_SUCCESS;
{
continue;
if (result != ISC_R_SUCCESS)
goto failure;
if (result != ISC_R_SUCCESS)
goto failure;
}
if (result != ISC_R_NOMORE)
goto failure;
return (result);
}
static isc_boolean_t
return (ISC_TRUE);
result == ISC_R_SUCCESS;
unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
continue;
} else
continue;
continue;
return (ISC_TRUE);
}
}
return (ISC_FALSE);
}
static isc_result_t
const dns_rdata_nsec3param_t *nsec3param)
{
result == ISC_R_SUCCESS;
break;
}
return (result);
}
{
int pass;
unsigned char *old_next;
unsigned char *salt;
unsigned char nexthash[NSEC3_MAX_HASH_LENGTH];
unsigned char nsec3buf[DNS_NSEC3_BUFFERSIZE];
unsigned int iterations;
unsigned int labels;
unsigned int old_length;
unsigned int salt_length;
/*
* Chain parameters.
*/
/*
* Default flags for a new chain.
*/
/*
* If this is the first NSEC3 in the chain nexthash will
* remain pointing to itself.
*/
next_length = sizeof(nexthash);
salt, salt_length));
/*
* Create the node if it doesn't exist and hold
* a reference to it until we have added the NSEC3.
*/
/*
* Seek the iterator to the 'newnode'.
*/
/*
* If we updating a existing NSEC3 then find its
* next field.
*/
if (result == ISC_R_SUCCESS) {
if (result == ISC_R_SUCCESS) {
/*
* If the NSEC3 is not for a unsecure delegation then
* we are just updating it. If it is for a unsecure
* delegation then we need find out if we need to
* remove the NSEC3 record or not by examining the
* previous NSEC3 record.
*/
if (!unsecure)
goto addnsec3;
nsec3param, diff);
goto failure;
} else
} else {
if (result != ISC_R_NOMORE)
goto failure;
}
}
/*
* Find the previous NSEC3 (if any) and update it if required.
*/
pass = 0;
do {
if (result == ISC_R_NOMORE) {
pass++;
}
(isc_stdtime_t) 0, &rdataset,
NULL);
if (result != ISC_R_SUCCESS)
continue;
if (result == ISC_R_NOMORE) {
continue;
}
if (result != ISC_R_SUCCESS)
goto failure;
if (maybe_remove_unsecure) {
/*
* If we have OPTOUT set in the previous NSEC3 record
* we actually need to delete the NSEC3 record.
* Otherwise we just need to replace the NSEC3 record.
*/
nsec3param, diff);
goto failure;
}
goto addnsec3;
} else {
/*
* Is this is a unsecure delegation we are adding?
* If so no change is required.
*/
goto failure;
}
}
/*
* Delete the old previous NSEC3.
*/
/*
* Fixup the previous NSEC3.
*/
&buffer));
break;
} while (pass < 2);
/*
* Create the NSEC3 RDATA.
*/
/*
* Delete the old NSEC3 and record the change.
*/
/*
* Add the new NSEC3 and record the change.
*/
/*
* Add missing NSEC3 records for empty nodes
*/
do {
break;
if (exists)
break;
salt, salt_length));
/*
* Create the node if it doesn't exist and hold
* a reference to it until we have added the NSEC3
* or we discover we don't need to add make a change.
*/
(isc_stdtime_t) 0, &rdataset,
NULL);
if (result == ISC_R_SUCCESS) {
if (result == ISC_R_SUCCESS) {
break;
}
if (result != ISC_R_NOMORE)
goto failure;
}
/*
* Find the previous NSEC3 and update it.
*/
pass = 0;
do {
if (result == ISC_R_NOMORE) {
pass++;
}
(isc_stdtime_t) 0,
if (result != ISC_R_SUCCESS)
continue;
if (result == ISC_R_NOMORE) {
continue;
}
if (result != ISC_R_SUCCESS)
goto failure;
/*
* Delete the old previous NSEC3.
*/
/*
* Fixup the previous NSEC3.
*/
sizeof(nsec3buf));
&buffer));
&tuple));
break;
} while (pass < 2);
/*
* Create the NSEC3 RDATA for the empty node.
*/
&rdata));
/*
* Delete the old NSEC3 and record the change.
*/
/*
* Add the new NSEC3 and record the change.
*/
} while (1);
/* result cannot be ISC_R_NOMORE here */
return (result);
}
/*%
* Add NSEC3 records for "name", recording the change in "diff".
* The existing NSEC3 records are removed.
*/
{
/*
* Find the NSEC3 parameters for this zone.
*/
if (result != ISC_R_SUCCESS)
return (result);
dns_rdatatype_nsec3param, 0, 0,
if (result == ISC_R_NOTFOUND)
return (ISC_R_SUCCESS);
if (result != ISC_R_SUCCESS)
return (result);
/*
* Update each active NSEC3 chain.
*/
result == ISC_R_SUCCESS;
if (nsec3param.flags != 0)
continue;
/*
* We have a active chain. Update it.
*/
}
if (result == ISC_R_NOMORE)
return (result);
}
{
/*
* Algorithm 0 (reserved by RFC 4034) is used to identify
* NSEC3PARAM records from DNSKEY pointers.
*/
return (ISC_FALSE);
}
void
{
buf[0] = 0;
}
static isc_result_t
{
else
if (result == ISC_R_NOTFOUND) {
goto failure;
}
result == ISC_R_SUCCESS;
break;
}
if (result == ISC_R_SUCCESS) {
} else if (result == ISC_R_NOMORE) {
}
return (result);
}
{
if (result != ISC_R_SUCCESS)
return (result);
/*
* Cause all NSEC3 chains to be deleted.
*/
if (result == ISC_R_NOTFOUND)
goto try_private;
if (result != ISC_R_SUCCESS)
goto failure;
result == ISC_R_SUCCESS;
if (nonsec)
if (!flag) {
&tuple));
}
}
if (result != ISC_R_NOMORE)
goto failure;
if (privatetype == 0)
goto success;
if (result == ISC_R_NOTFOUND)
goto success;
if (result != ISC_R_SUCCESS)
goto failure;
result == ISC_R_SUCCESS;
/*
* Private NSEC3 record length >= 6.
* <0(1), hash(1), flags(1), iterations(2), saltlen(1)>
*/
continue;
if (nonsec)
if (!flag) {
}
}
if (result != ISC_R_NOMORE)
goto failure;
return (result);
}
{
/*
* Find the NSEC3 parameters for this zone.
*/
if (result != ISC_R_SUCCESS)
return (result);
goto failure;
dns_rdatatype_nsec3param, 0, 0,
if (result == ISC_R_NOTFOUND)
goto try_private;
if (result != ISC_R_SUCCESS)
goto failure;
/*
* Update each active NSEC3 chain.
*/
result == ISC_R_SUCCESS;
if (nsec3param.flags != 0)
continue;
/*
* We have a active chain. Update it.
*/
}
if (result != ISC_R_NOMORE)
goto failure;
if (!dns_rdataset_isassociated(&prdataset))
goto success;
/*
* Update each active NSEC3 chain.
*/
result == ISC_R_SUCCESS;
unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
continue;
continue;
continue;
/*
* We have a active chain. Update it.
*/
}
if (result == ISC_R_NOMORE)
return (result);
}
/*%
* Determine whether any NSEC3 records that were associated with
* 'name' should be deleted or if they should continue to exist.
* ISC_TRUE indicates they should be deleted.
* ISC_FALSE indicates they should be retained.
*/
static isc_result_t
{
(isc_stdtime_t) 0, NULL,
result == DNS_R_ZONECUT) {
return (ISC_R_SUCCESS);
}
return (ISC_R_SUCCESS);
}
/*
* Silence compiler.
*/
return (result);
}
{
int pass;
unsigned char *salt;
unsigned char nexthash[NSEC3_MAX_HASH_LENGTH];
unsigned char nsec3buf[DNS_NSEC3_BUFFERSIZE];
unsigned int iterations;
unsigned int labels;
unsigned int salt_length;
/*
* Chain parameters.
*/
/*
* If this is the first NSEC3 in the chain nexthash will
* remain pointing to itself.
*/
next_length = sizeof(nexthash);
salt, salt_length));
if (result == ISC_R_NOTFOUND)
goto success;
if (result != ISC_R_SUCCESS)
goto failure;
if (result == ISC_R_NOTFOUND)
goto success;
if (result != ISC_R_SUCCESS)
goto failure;
/*
* If we find a existing NSEC3 for this chain then save the
* next field.
*/
if (result == ISC_R_SUCCESS) {
}
if (result == ISC_R_NOMORE)
goto success;
if (result != ISC_R_SUCCESS)
goto failure;
/*
* Find the previous NSEC3 and update it.
*/
pass = 0;
do {
if (result == ISC_R_NOMORE) {
pass++;
}
(isc_stdtime_t) 0, &rdataset,
NULL);
if (result != ISC_R_SUCCESS)
continue;
if (result == ISC_R_NOMORE) {
continue;
}
if (result != ISC_R_SUCCESS)
goto failure;
/*
* Delete the old previous NSEC3.
*/
/*
* Fixup the previous NSEC3.
*/
&buffer));
break;
} while (pass < 2);
/*
* Delete the old NSEC3 and record the change.
*/
/*
* Delete NSEC3 records for now non active nodes.
*/
do {
break;
if (!yesno)
break;
salt, salt_length));
if (result == ISC_R_NOTFOUND)
goto success;
if (result != ISC_R_SUCCESS)
goto failure;
(isc_stdtime_t) 0, &rdataset,
NULL);
if (result == ISC_R_NOTFOUND)
goto success;
if (result != ISC_R_SUCCESS)
goto failure;
if (result == ISC_R_SUCCESS) {
}
if (result == ISC_R_NOMORE)
goto success;
if (result != ISC_R_SUCCESS)
goto failure;
pass = 0;
do {
if (result == ISC_R_NOMORE) {
pass++;
}
(isc_stdtime_t) 0,
if (result != ISC_R_SUCCESS)
continue;
if (result == ISC_R_NOMORE) {
continue;
}
if (result != ISC_R_SUCCESS)
goto failure;
/*
* Delete the old previous NSEC3.
*/
/*
* Fixup the previous NSEC3.
*/
sizeof(nsec3buf));
&buffer));
&tuple));
break;
} while (pass < 2);
/*
* Delete the old NSEC3 and record the change.
*/
} while (1);
return (result);
}
{
}
{
/*
* Find the NSEC3 parameters for this zone.
*/
if (result != ISC_R_SUCCESS)
return (result);
dns_rdatatype_nsec3param, 0, 0,
if (result == ISC_R_NOTFOUND)
goto try_private;
if (result != ISC_R_SUCCESS)
goto failure;
/*
* Update each active NSEC3 chain.
*/
result == ISC_R_SUCCESS;
if (nsec3param.flags != 0)
continue;
/*
* We have a active chain. Update it.
*/
}
if (privatetype == 0)
goto success;
if (result == ISC_R_NOTFOUND)
goto success;
if (result != ISC_R_SUCCESS)
goto failure;
/*
* Update each NSEC3 chain being built.
*/
result == ISC_R_SUCCESS;
unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
continue;
continue;
continue;
/*
* We have a active chain. Update it.
*/
}
if (result == ISC_R_NOMORE)
return (result);
}
{
}
{
if (result != ISC_R_SUCCESS)
return (result);
dns_rdatatype_nsec3param, 0, 0,
if (result == ISC_R_NOTFOUND)
goto try_private;
if (result != ISC_R_SUCCESS) {
return (result);
}
result == ISC_R_SUCCESS;
if (nsec3param.flags == 0)
break;
}
if (result == ISC_R_SUCCESS) {
return (ISC_R_SUCCESS);
}
if (result == ISC_R_NOMORE)
if (privatetype == 0 || complete) {
return (ISC_R_SUCCESS);
}
if (result == ISC_R_NOTFOUND) {
return (ISC_R_SUCCESS);
}
if (result != ISC_R_SUCCESS)
return (result);
result == ISC_R_SUCCESS;
unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
continue;
break;
}
if (result == ISC_R_SUCCESS) {
}
if (result == ISC_R_NOMORE) {
}
return (result);
}
{
if (result != ISC_R_SUCCESS)
return (result);
if (result == ISC_R_NOTFOUND) {
*iterationsp = 0;
return (ISC_R_SUCCESS);
}
if (result != ISC_R_SUCCESS)
goto failure;
result == ISC_R_SUCCESS;
dst_key_free(&key);
}
if (result != ISC_R_NOMORE)
goto failure;
if (minbits <= 1024)
*iterationsp = 150;
else if (minbits <= 2048)
*iterationsp = 500;
else
*iterationsp = 2500;
return (result);
}
{
char namebuf[DNS_NAME_FORMATSIZE];
int order;
int scope;
unsigned char hash[NSEC3_MAX_HASH_LENGTH];
unsigned char owner[NSEC3_MAX_HASH_LENGTH];
unsigned int length;
unsigned int qlabels;
unsigned int zlabels;
if (result != ISC_R_SUCCESS) {
return (result);
}
if (result != ISC_R_SUCCESS)
return (result);
/*
* NSEC3 records must have two or more labels to be valid.
*/
if (zlabels < 2)
return (ISC_R_IGNORE);
/*
* Strip off the NSEC3 hash to get the zone.
*/
zlabels--;
/*
* If not below the zone name we can ignore this record.
*/
return (ISC_R_IGNORE);
/*
* Is this zone the same or deeper than the current zone?
*/
if (dns_name_countlabels(zonename) == 0 ||
return (ISC_R_IGNORE);
/*
* Are we only looking for the most enclosing zone?
*/
return (ISC_R_SUCCESS);
/*
* Only set unknown once we are sure that this NSEC3 is from
* the deepest covering zone.
*/
return (ISC_R_IGNORE);
}
/*
* Recover the hash from the first label.
*/
if (result != ISC_R_SUCCESS)
return (result);
/*
* The hash lengths should match. If not ignore the record.
*/
return (ISC_R_IGNORE);
/*
* Work out what this NSEC3 covers.
* Inside (<0) or outside (>=0).
*/
/*
* Prepare to compute all the hashes.
*/
/*
* The computed hash length should match.
*/
"ignoring NSEC bad length %u vs %u",
return (ISC_R_IGNORE);
}
/*
* The hashes are the same.
*/
if (!atparent) {
/*
* This NSEC3 record is from somewhere
* higher in the DNS, and at the
* parent of a delegation. It can not
* be legitimately used here.
*/
"ignoring parent NSEC3");
return (ISC_R_IGNORE);
}
/*
* This NSEC3 record is from the child.
* It can not be legitimately used here.
*/
"ignoring child NSEC3");
return (ISC_R_IGNORE);
}
if (type == dns_rdatatype_cname ||
type == dns_rdatatype_nxt ||
type == dns_rdatatype_nsec ||
type == dns_rdatatype_key ||
"NSEC3 proves name exists (owner) "
"data=%d", *data);
return (ISC_R_SUCCESS);
}
"NSEC3 proves CNAME exists");
return (ISC_R_IGNORE);
}
if (order == 0 &&
{
/*
* This NSEC3 record is from somewhere higher in
* the DNS, and at the parent of a delegation.
* It can not be legitimately used here.
*/
"ignoring parent NSEC3");
return (ISC_R_IGNORE);
}
/*
* Potential closest encloser.
*/
if (order == 0) {
(dns_name_countlabels(closest) == 0 ||
{
sizeof(namebuf));
"NSEC3 indicates potential closest "
"encloser: '%s'", namebuf);
*setclosest = ISC_TRUE;
}
"NSEC3 at super-domain %s", namebuf);
return (answer);
}
/*
* Find if the name does not exist.
*
* We continue as we need to find the name closest to the
* closest encloser that doesn't exist.
*
* We also need to continue to ensure that we are not
* proving the non-existence of a record in a sub-zone.
* If that would be the case we will return ISC_R_IGNORE
* above.
*/
{
"name does not exist: '%s'", namebuf);
(dns_name_countlabels(nearest) == 0 ||
*setnearest = ISC_TRUE;
}
"NSEC3 indicates optout");
else
"NSEC3 indicates secure range");
*optout =
}
}
qlabels--;
if (qlabels > 0)
}
return (answer);
}