Change nsec3param salt config to saltlen

Upon request from Mark, change the configuration of salt to salt
length.

Introduce a new function 'dns_zone_checknsec3aram' that can be used
upon reconfiguration to check if the existing NSEC3 parameters are
in sync with the configuration. If a salt is used that matches the
configured salt length, don't change the NSEC3 parameters.
This commit is contained in:
Matthijs Mekking 2020-10-23 15:02:19 +02:00
parent 00c5dabea3
commit 6f97bb6b1f
21 changed files with 204 additions and 114 deletions

View file

@ -66,8 +66,8 @@ DNSSEC-POLICY
keys { ( csk | ksk | zsk ) [ ( key-directory ) ] lifetime
duration_or_unlimited algorithm string [ integer ]; ... };
max-zone-ttl duration;
nsec3param [ iterations integer ] [ optout boolean ] [ salt
string ];
nsec3param [ iterations integer ] [ optout boolean ] [
salt-length integer ];
parent-ds-ttl duration;
parent-propagation-delay duration;
publish-safety duration;

View file

@ -29,6 +29,7 @@
#include <dns/log.h>
#include <dns/masterdump.h>
#include <dns/name.h>
#include <dns/nsec3.h>
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
@ -1561,15 +1562,32 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
bool sigvalinsecs;
if (kasp != NULL) {
unsigned char saltbuf[255];
unsigned char *salt;
DE_CONST("-", salt);
if (dns_kasp_nsec3(kasp)) {
result = dns_zone_setnsec3param(
result = dns_zone_checknsec3param(
zone, 1, dns_kasp_nsec3flags(kasp),
dns_kasp_nsec3iter(kasp),
dns_kasp_nsec3saltlen(kasp),
dns_kasp_nsec3salt(kasp), true);
dns_kasp_nsec3saltlen(kasp), NULL);
if (result != ISC_R_SUCCESS) {
if (dns_kasp_nsec3saltlen(kasp) > 0) {
RETERR(dns_nsec3_generate_salt(
saltbuf,
dns_kasp_nsec3saltlen(
kasp)));
salt = saltbuf;
}
result = dns_zone_setnsec3param(
zone, 1,
dns_kasp_nsec3flags(kasp),
dns_kasp_nsec3iter(kasp),
dns_kasp_nsec3saltlen(kasp),
salt, true);
}
} else {
unsigned char *salt;
DE_CONST("-", salt);
result = dns_zone_setnsec3param(zone, 0, 0, 0,
0, salt, true);
}

View file

@ -22,7 +22,7 @@ dnssec-policy "test" {
csk key-directory lifetime unlimited algorithm rsasha256 2048;
};
max-zone-ttl 86400;
nsec3param iterations 5 optout no salt "deadbeef";
nsec3param iterations 5 optout no salt-length 8;
parent-ds-ttl 7200;
parent-propagation-delay PT1H;
publish-safety PT3600S;

View file

@ -508,14 +508,6 @@ grep "update-check-ksk: cannot be configured if dnssec-policy is also set" < che
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "checking named-checkconf kasp nsec3 salt errors ($n)"
ret=0
$CHECKCONF kasp-bad-nsec3-salt.conf > checkconf.out$n 2>&1 && ret=1
grep "dnssec-policy: bad nsec3 salt pepper" < checkconf.out$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "checking named-checkconf kasp nsec3 iterations errors ($n)"
ret=0

View file

@ -25,7 +25,7 @@ dnssec-policy "optout" {
};
dnssec-policy "nsec3-other" {
nsec3param iterations 11 optout yes salt "deadbeef";
nsec3param iterations 11 optout yes salt-length 0;
};
options {

View file

@ -25,7 +25,7 @@ dnssec-policy "optout" {
};
dnssec-policy "nsec3-other" {
nsec3param iterations 11 optout yes salt "deadbeef";
nsec3param iterations 11 optout yes salt-length 0;
};
options {

View file

@ -38,11 +38,14 @@ set_zone_policy() {
ZONE=$1
POLICY=$2
}
# Set expected NSEC3 parameters: flags ($1), iterations ($2), and salt ($3).
# Set expected NSEC3 parameters: flags ($1), iterations ($2), and
# salt length ($3).
set_nsec3param() {
FLAGS=$1
ITERATIONS=$2
SALT=$3
SALTLEN=$3
SALT=""
test "$SALTLEN" = "0" && SALT="-"
}
# The apex NSEC3PARAM record indicates that it is signed.
@ -167,7 +170,7 @@ dnssec_verify
# Zone: nsec3.kasp.
set_zone_policy "nsec3.kasp" "nsec3"
set_nsec3param "0" "5" "-"
set_nsec3param "0" "5" "8"
echo_i "initial check zone ${ZONE}"
check_nsec3
dnssec_verify
@ -192,14 +195,14 @@ dnssec_verify
# Zone: nsec3-from-optout.kasp.
set_zone_policy "nsec3-from-optout.kasp" "optout"
set_nsec3param "1" "5" "-"
set_nsec3param "1" "5" "8"
echo_i "initial check zone ${ZONE}"
check_nsec3
dnssec_verify
# Zone: nsec3-other.kasp.
set_zone_policy "nsec3-other.kasp" "nsec3-other"
set_nsec3param "1" "11" "DEADBEEF"
set_nsec3param "1" "11" "0"
echo_i "initial check zone ${ZONE}"
check_nsec3
dnssec_verify
@ -213,7 +216,7 @@ rndc_reconfig ns3 10.53.0.3
# Zone: nsec-to-nsec3.kasp. (reconfigured)
set_zone_policy "nsec-to-nsec3.kasp" "nsec3"
set_nsec3param "0" "5" "-"
set_nsec3param "0" "5" "8"
echo_i "check zone ${ZONE} after reconfig"
check_nsec3
dnssec_verify
@ -226,7 +229,7 @@ dnssec_verify
# Zone: nsec3-change.kasp. (reconfigured)
set_zone_policy "nsec3-change.kasp" "nsec3-other"
set_nsec3param "1" "11" "DEADBEEF"
set_nsec3param "1" "11" "0"
echo_i "check zone ${ZONE} after reconfig"
check_nsec3
dnssec_verify
@ -238,25 +241,28 @@ check_nsec
dnssec_verify
# Zone: nsec3-to-optout.kasp. (reconfigured)
set_zone_policy "nsec3-to-optout.kasp" "optout"
set_nsec3param "1" "5" "-"
echo_i "check zone ${ZONE} after reconfig"
check_nsec3
dnssec_verify
# DISABLED:
# There is a bug in the nsec3param building code that thinks when the
# optout bit is changed, the chain already exists. [GL #2216]
#set_zone_policy "nsec3-to-optout.kasp" "optout"
#set_nsec3param "1" "5" "8"
#echo_i "check zone ${ZONE} after reconfig"
#check_nsec3
#dnssec_verify
# Zone: nsec3-from-optout.kasp. (reconfigured)
# DISABLED:
# There is a bug in the nsec3param building code that thinks when the
# optout bit is removed, the chain already exists. [GL #2216]
# optout bit is changed, the chain already exists. [GL #2216]
#set_zone_policy "nsec3-from-optout.kasp" "nsec3"
#set_nsec3param "0" "5" "-"
#set_nsec3param "0" "5" "8"
#echo_i "check zone ${ZONE} after reconfig"
#check_nsec3
#dnssec_verify
# Zone: nsec3-other.kasp. (same)
set_zone_policy "nsec3-other.kasp" "nsec3-other"
set_nsec3param "1" "11" "DEADBEEF"
set_nsec3param "1" "11" "0"
echo_i "check zone ${ZONE} after reconfig"
check_nsec3
dnssec_verify

View file

@ -4958,14 +4958,15 @@ The following options can be specified in a ``dnssec-policy`` statement:
``nsec3param``
Use NSEC3 instead of NSEC, and optionally set the NSEC3 parameters.
Here is an example (for illustration purposes only) of
a ``nsec3`` configuration:
Here is an example of an ``nsec3`` configuration:
::
nsec3param ttl 0 iterations 5 optout no salt "-";
nsec3param iterations 5 optout no salt-length 8;
The default is to use NSEC.
The default is to use NSEC. The ``iterations``, ``optout``
and ``salt-length`` parts are optional, but if not set, the
values in the example above are the default NSEC3 parameters.
``zone-propagation-delay``
This is the expected propagation delay from the time when a zone

View file

@ -105,8 +105,8 @@ dnssec\-policy string {
keys { ( csk | ksk | zsk ) [ ( key\-directory ) ] lifetime
duration_or_unlimited algorithm string [ integer ]; ... };
max\-zone\-ttl duration;
nsec3param [ iterations integer ] [ optout boolean ] [ salt
string ];
nsec3param [ iterations integer ] [ optout boolean ] [
salt\-length integer ];
parent\-ds\-ttl duration;
parent\-propagation\-delay duration;
publish\-safety duration;

View file

@ -5,8 +5,8 @@
keys { ( csk | ksk | zsk ) [ ( key-directory ) ] lifetime
<duration_or_unlimited> algorithm <string> [ <integer> ]; ... };
max-zone-ttl <duration>;
nsec3param [ iterations <integer> ] [ optout <boolean> ] [ salt
<string> ];
nsec3param [ iterations <integer> ] [ optout <boolean> ] [
salt-length <integer> ];
parent-ds-ttl <duration>;
parent-propagation-delay <duration>;
publish-safety <duration>;

View file

@ -26,8 +26,8 @@ dnssec-policy <string> {
keys { ( csk | ksk | zsk ) [ ( key-directory ) ] lifetime
<duration_or_unlimited> algorithm <string> [ <integer> ]; ... };
max-zone-ttl <duration>;
nsec3param [ iterations <integer> ] [ optout <boolean> ] [ salt
<string> ];
nsec3param [ iterations <integer> ] [ optout <boolean> ] [
salt-length <integer> ];
parent-ds-ttl <duration>;
parent-propagation-delay <duration>;
parent-registration-delay <duration>; // obsolete

View file

@ -26,8 +26,8 @@ dnssec-policy <string> {
keys { ( csk | ksk | zsk ) [ ( key-directory ) ] lifetime
<duration_or_unlimited> algorithm <string> [ <integer> ]; ... };
max-zone-ttl <duration>;
nsec3param [ iterations <integer> ] [ optout <boolean> ] [ salt
<string> ];
nsec3param [ iterations <integer> ] [ optout <boolean> ] [
salt-length <integer> ];
parent-ds-ttl <duration>;
parent-propagation-delay <duration>;
publish-safety <duration>;

View file

@ -51,11 +51,10 @@ struct dns_kasp_key {
};
struct dns_kasp_nsec3param {
unsigned char salt[255];
uint8_t saltlen;
uint8_t algorithm;
uint8_t iterations;
bool optout;
uint8_t saltlen;
uint8_t algorithm;
uint8_t iterations;
bool optout;
};
/* Stores a DNSSEC policy */
@ -661,18 +660,6 @@ dns_kasp_nsec3saltlen(dns_kasp_t *kasp);
*
*/
unsigned char *
dns_kasp_nsec3salt(dns_kasp_t *kasp);
/*%<
* The NSEC3 salt used.
*
* Requires:
*
*\li 'kasp' is a valid, frozen kasp.
*\li 'kasp->nsec3' is true.
*
*/
void
dns_kasp_setnsec3(dns_kasp_t *kasp, bool nsec3);
/*%<
@ -684,9 +671,9 @@ dns_kasp_setnsec3(dns_kasp_t *kasp, bool nsec3);
*
*/
isc_result_t
void
dns_kasp_setnsec3param(dns_kasp_t *kasp, uint8_t iter, bool optout,
const char *salt);
uint8_t saltlen);
/*%<
* Set the desired NSEC3 parameters.
*
@ -695,11 +682,6 @@ dns_kasp_setnsec3param(dns_kasp_t *kasp, uint8_t iter, bool optout,
*\li 'kasp' is a valid, unfrozen kasp.
*\li 'kasp->nsec3' is true.
*
* Returns:
*
*\li ISC_R_SUCCESS, if NSEC3 parameters are set.
*\li Error, if isc_hex_decodestring() fails.
*
*/
ISC_LANG_ENDDECLS

View file

@ -159,7 +159,7 @@
#define DNS_R_TOOMANYKEYS (ISC_RESULTCLASS_DNS + 121)
#define DNS_R_KEYNOTACTIVE (ISC_RESULTCLASS_DNS + 122)
#define DNS_R_NSEC3ITERRANGE (ISC_RESULTCLASS_DNS + 123)
#define DNS_R_NSEC3BADSALT (ISC_RESULTCLASS_DNS + 124)
#define DNS_R_NSEC3SALTRANGE (ISC_RESULTCLASS_DNS + 124)
#define DNS_R_NSEC3BADALG (ISC_RESULTCLASS_DNS + 125)
#define DNS_R_NRESULTS 126 /*%< Number of results */

View file

@ -2378,6 +2378,23 @@ dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw);
isc_result_t
dns_zone_keydone(dns_zone_t *zone, const char *data);
isc_result_t
dns_zone_checknsec3param(dns_zone_t *zone, uint8_t hash, uint8_t flags,
uint16_t iter, uint8_t saltlen, unsigned char *salt);
/*%
* Check if the NSEC3 parameters for the zone match the requested parameters.
*
* If 'salt' is NULL, a match is found if the salt has the requested length,
* otherwise the NSEC3 salt must match the requested salt value too.
*
* Requires:
* \li 'zone' to be valid.
*
* Returns:
* \li ISC_R_SUCCESS, if a match is found.
* \li Error, if no match is found, or if the db lookup failed.
*/
isc_result_t
dns_zone_setnsec3param(dns_zone_t *zone, uint8_t hash, uint8_t flags,
uint16_t iter, uint8_t saltlen, unsigned char *salt,

View file

@ -479,15 +479,6 @@ dns_kasp_nsec3saltlen(dns_kasp_t *kasp) {
return (kasp->nsec3param.saltlen);
}
unsigned char *
dns_kasp_nsec3salt(dns_kasp_t *kasp) {
REQUIRE(kasp != NULL);
REQUIRE(kasp->frozen);
REQUIRE(kasp->nsec3);
return kasp->nsec3param.salt;
}
bool
dns_kasp_nsec3(dns_kasp_t *kasp) {
REQUIRE(kasp != NULL);
@ -504,27 +495,14 @@ dns_kasp_setnsec3(dns_kasp_t *kasp, bool nsec3) {
kasp->nsec3 = nsec3;
}
isc_result_t
void
dns_kasp_setnsec3param(dns_kasp_t *kasp, uint8_t iter, bool optout,
const char *salt) {
isc_buffer_t buf;
isc_result_t ret = ISC_R_SUCCESS;
uint8_t saltlen) {
REQUIRE(kasp != NULL);
REQUIRE(!kasp->frozen);
REQUIRE(kasp->nsec3);
kasp->nsec3param.iterations = iter;
kasp->nsec3param.optout = optout;
kasp->nsec3param.saltlen = 0;
if (salt != NULL && strcmp(salt, "-") != 0) {
isc_buffer_init(&buf, kasp->nsec3param.salt,
sizeof(kasp->nsec3param.salt));
ret = isc_hex_decodestring(salt, &buf);
if (ret == ISC_R_SUCCESS) {
kasp->nsec3param.saltlen = isc_buffer_usedlength(&buf);
}
}
return (ret);
kasp->nsec3param.saltlen = saltlen;
}

View file

@ -171,7 +171,7 @@ static const char *text[DNS_R_NRESULTS] = {
"key is not actively signing", /*%< 122 DNS_R_KEYNOTACTIVE */
"NSEC3 iterations out of range", /*%< 123 DNS_R_NSEC3ITERRANGE */
"bad NSEC3 salt", /*%< 124 DNS_R_NSEC3BADSALT */
"NSEC3 salt length too high", /*%< 124 DNS_R_NSEC3SALTRANGE */
"cannot use NSEC3 with key algorithm", /*%< 125 DNS_R_NSEC3BADALG */
};
@ -304,7 +304,7 @@ static const char *ids[DNS_R_NRESULTS] = {
"DNS_R_TOOMANYKEYS",
"DNS_R_KEYNOTACTIVE",
"DNS_R_NSEC3ITERRANGE",
"DNS_R_NSEC3BADSALT",
"DNS_R_NSEC3SALTRANGE",
"DNS_R_NSEC3BADALG",
};

View file

@ -439,7 +439,6 @@ dns_kasp_keys
dns_kasp_nsec3
dns_kasp_nsec3flags
dns_kasp_nsec3iter
dns_kasp_nsec3salt
dns_kasp_nsec3saltlen
dns_kasp_parentpropagationdelay
dns_kasp_publishsafety
@ -1165,6 +1164,7 @@ dns_zone_catz_enable
dns_zone_catz_enable_db
dns_zone_cdscheck
dns_zone_checknames
dns_zone_checknsec3param
dns_zone_clearforwardacl
dns_zone_clearnotifyacl
dns_zone_clearqueryacl

View file

@ -21052,6 +21052,100 @@ failure:
INSIST(newver == NULL);
}
/*
* Check if zone has NSEC3PARAM (and thus a chain) with the right parameters.
*/
isc_result_t
dns_zone_checknsec3param(dns_zone_t *zone, uint8_t hash, uint8_t flags,
uint16_t iter, uint8_t saltlen, unsigned char *salt) {
isc_result_t result = ISC_R_UNEXPECTED;
dns_dbnode_t *node = NULL;
dns_db_t *db = NULL;
dns_dbversion_t *version = NULL;
dns_rdataset_t rdataset;
dns_rdata_nsec3param_t nsec3param;
dns_rdata_t rdata = DNS_RDATA_INIT;
REQUIRE(DNS_ZONE_VALID(zone));
UNUSED(flags);
dns_rdataset_init(&rdataset);
ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
if (zone->db != NULL) {
dns_db_attach(zone->db, &db);
}
ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
if (db == NULL) {
goto cleanup;
}
result = dns_db_findnode(db, &zone->origin, false, &node);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_ERROR,
"nsec3param lookup failure: %s",
dns_result_totext(result));
goto cleanup;
}
dns_db_currentversion(db, &version);
result = dns_db_findrdataset(db, node, version,
dns_rdatatype_nsec3param,
dns_rdatatype_none, 0, &rdataset, NULL);
if (result != ISC_R_SUCCESS) {
INSIST(!dns_rdataset_isassociated(&rdataset));
if (result != ISC_R_NOTFOUND) {
dns_zone_log(zone, ISC_LOG_ERROR,
"nsec3param lookup failure: %s",
dns_result_totext(result));
}
goto cleanup;
}
for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS;
result = dns_rdataset_next(&rdataset))
{
dns_rdataset_current(&rdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
INSIST(result == ISC_R_SUCCESS);
dns_rdata_reset(&rdata);
if (nsec3param.hash != hash) {
continue;
}
if (nsec3param.iterations != iter) {
continue;
}
if (nsec3param.salt_length != saltlen) {
continue;
}
if (salt != NULL) {
if (memcmp(nsec3param.salt, salt, saltlen) != 0) {
continue;
}
}
/* Found a match. */
result = ISC_R_SUCCESS;
goto cleanup;
}
INSIST(result != ISC_R_SUCCESS);
cleanup:
if (dns_rdataset_isassociated(&rdataset)) {
dns_rdataset_disassociate(&rdataset);
}
if (node != NULL) {
dns_db_detachnode(db, &node);
}
if (version != NULL) {
dns_db_closeversion(db, &version, false);
}
if (db != NULL) {
dns_db_detach(&db);
}
return (result);
}
/*
* Called when an "rndc signing -nsec3param ..." command is received.
*

View file

@ -29,7 +29,8 @@
#include <isccfg/kaspconf.h>
#include <isccfg/namedconf.h>
#define DEFAULT_NSEC3PARAM_ITER 5
#define DEFAULT_NSEC3PARAM_ITER 5
#define DEFAULT_NSEC3PARAM_SALTLEN 8
/*
* Utility function for getting a configuration option.
@ -171,8 +172,8 @@ cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
dns_kasp_key_t *kkey;
unsigned int min_keysize = 4096;
const cfg_obj_t *obj = NULL;
const char *salt = NULL;
uint32_t iter = DEFAULT_NSEC3PARAM_ITER;
uint32_t saltlen = DEFAULT_NSEC3PARAM_SALTLEN;
uint32_t badalg = 0;
bool optout = false;
isc_result_t ret = ISC_R_SUCCESS;
@ -236,17 +237,19 @@ cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
}
/* Salt */
obj = cfg_tuple_get(config, "salt");
if (cfg_obj_isstring(obj)) {
salt = cfg_obj_asstring(obj);
obj = cfg_tuple_get(config, "salt-length");
if (cfg_obj_isuint32(obj)) {
saltlen = cfg_obj_asuint32(obj);
}
if (saltlen > 0xff) {
cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
"dnssec-policy: nsec3 salt length %u too high",
saltlen);
return (DNS_R_NSEC3SALTRANGE);
}
ret = dns_kasp_setnsec3param(kasp, iter, optout, salt);
if (ret != ISC_R_SUCCESS) {
cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
"dnssec-policy: bad nsec3 salt %s", salt);
}
return (ret);
dns_kasp_setnsec3param(kasp, iter, optout, saltlen);
return (ISC_R_SUCCESS);
}
isc_result_t

View file

@ -587,17 +587,16 @@ static cfg_type_t cfg_type_nsec3optout = {
&cfg_rep_boolean, &nsec3optout_kw
};
static keyword_type_t nsec3salt_kw = { "salt", &cfg_type_sstring };
static keyword_type_t nsec3salt_kw = { "salt-length", &cfg_type_uint32 };
static cfg_type_t cfg_type_nsec3salt = {
"salt", parse_optional_keyvalue,
print_keyvalue, doc_optional_keyvalue,
&cfg_rep_string, &nsec3salt_kw
"salt-length", parse_optional_keyvalue, print_keyvalue,
doc_optional_keyvalue, &cfg_rep_uint32, &nsec3salt_kw
};
static cfg_tuplefielddef_t nsec3param_fields[] = {
{ "iterations", &cfg_type_nsec3iter, 0 },
{ "optout", &cfg_type_nsec3optout, 0 },
{ "salt", &cfg_type_nsec3salt, 0 },
{ "salt-length", &cfg_type_nsec3salt, 0 },
{ NULL, NULL, 0 }
};