Commit 058e4418 authored by Mark Andrews's avatar Mark Andrews

3387. [func] Support for a DS digest can be disabled at

                        runtime with disable-ds-digests. [RT #21581]
parent 84c22203
3387. [func] Support for a DS digest can be disabled at
runtime with disable-ds-digests. [RT #21581]
3386. [bug] Address locking violation when generating new NSEC /
NSEC3 chains. [RT #31224]
......
......@@ -284,6 +284,7 @@ options {
max-udp-size <replaceable>integer</replaceable>;
root-delegation-only <optional> exclude { <replaceable>quoted_string</replaceable>; ... } </optional>;
disable-algorithms <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
disable-ds-digests <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
dnssec-enable <replaceable>boolean</replaceable>;
dnssec-validation <replaceable>boolean</replaceable>;
dnssec-lookaside ( <replaceable>auto</replaceable> | <replaceable>no</replaceable> | <replaceable>domain</replaceable> trust-anchor <replaceable>domain</replaceable> );
......@@ -472,6 +473,7 @@ view <replaceable>string</replaceable> <replaceable>optional_class</replaceable>
max-udp-size <replaceable>integer</replaceable>;
root-delegation-only <optional> exclude { <replaceable>quoted_string</replaceable>; ... } </optional>;
disable-algorithms <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
disable-ds-digests <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
dnssec-enable <replaceable>boolean</replaceable>;
dnssec-validation <replaceable>boolean</replaceable>;
dnssec-lookaside ( <replaceable>auto</replaceable> | <replaceable>no</replaceable> | <replaceable>domain</replaceable> trust-anchor <replaceable>domain</replaceable> );
......
......@@ -1216,6 +1216,48 @@ disable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
return (result);
}
static isc_result_t
disable_ds_digests(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
isc_result_t result;
const cfg_obj_t *digests;
const cfg_listelt_t *element;
const char *str;
dns_fixedname_t fixed;
dns_name_t *name;
isc_buffer_t b;
dns_fixedname_init(&fixed);
name = dns_fixedname_name(&fixed);
str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
isc_buffer_init(&b, str, strlen(str));
isc_buffer_add(&b, strlen(str));
CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
digests = cfg_tuple_get(disabled, "digests");
for (element = cfg_list_first(digests);
element != NULL;
element = cfg_list_next(element))
{
isc_textregion_t r;
dns_dsdigest_t digest;
DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
r.length = strlen(r.base);
/* disable_ds_digests handles numeric values. */
result = dns_dsdigest_fromtext(&digest, &r);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(cfg_listelt_value(element),
ns_g_lctx, ISC_LOG_ERROR,
"invalid algorithm");
CHECK(result);
}
CHECK(dns_resolver_disable_ds_digest(resolver, name, digest));
}
cleanup:
return (result);
}
static isc_boolean_t
on_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) {
const cfg_listelt_t *element;
......@@ -2275,6 +2317,20 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
view->resolver));
}
/*
* Set supported DS/DLV digest types.
*/
dns_resolver_reset_ds_digests(view->resolver);
disabled = NULL;
(void)ns_config_get(maps, "disable-ds-digests", &disabled);
if (disabled != NULL) {
for (element = cfg_list_first(disabled);
element != NULL;
element = cfg_list_next(element))
CHECK(disable_ds_digests(cfg_listelt_value(element),
view->resolver));
}
/*
* A global or view "forwarders" option, if present,
* creates an entry for "." in the forwarding table.
......
......@@ -56,8 +56,8 @@ VERIFY=$TOP/bin/dnssec/dnssec-verify
# v6synth
SUBDIRS="acl allow_query addzone autosign builtin cacheclean checkconf
@CHECKDS@ checknames checkzone database dlv dlvauto dlz dlzexternal
dname dns64 dnssec ecdsa forward glue gost ixfr inline limits
logfileconfig lwresd masterfile masterformat metadata notify
dname dns64 dnssec dsdigest ecdsa forward glue gost ixfr inline
limits logfileconfig lwresd masterfile masterformat metadata notify
nsupdate pending pkcs11 redirect resolver rndc rpz rrsetorder
rsabigexponent sortlist smartsign staticstub statistics stub
tkey tsig tsiggss unknown upforwd verify views xfer xferquota
......
......@@ -3595,6 +3595,7 @@ AC_CONFIG_FILES([
bin/tests/system/dlz/prereq.sh
bin/tests/system/dlzexternal/Makefile
bin/tests/system/dlzexternal/ns1/named.conf
bin/tests/system/dsdigest/prereq.sh
bin/tests/system/ecdsa/prereq.sh
bin/tests/system/filter-aaaa/Makefile
bin/tests/system/gost/prereq.sh
......
......@@ -5340,6 +5340,8 @@ badresp:1,adberr:0,findfail:0,valfail:0]
<optional> querylog <replaceable>yes_or_no</replaceable> ; </optional>
<optional> disable-algorithms <replaceable>domain</replaceable> { <replaceable>algorithm</replaceable>;
<optional> <replaceable>algorithm</replaceable>; </optional> }; </optional>
<optional> disable-ds-digests <replaceable>domain</replaceable> { <replaceable>digest_type</replaceable>;
<optional> <replaceable>digest_type</replaceable>; </optional> }; </optional>
<optional> acache-enable <replaceable>yes_or_no</replaceable> ; </optional>
<optional> acache-cleaning-interval <replaceable>number</replaceable>; </optional>
<optional> max-acache-size <replaceable>size_spec</replaceable> ; </optional>
......@@ -5871,8 +5873,33 @@ options {
specified name.
Multiple <command>disable-algorithms</command>
statements are allowed.
Only the most specific will be applied.
Only the best match <command>disable-algorithms</command>
clause will be used to determine which algorithms are used.
</para>
<para>
If all supported algorithms are disabled, the zones covered
by the <command>disable-algorithms</command> will be treated
as insecure.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>disable-ds-digests</command></term>
<listitem>
<para>
Disable the specified DS/DLV digest types at and below the
specified name.
Multiple <command>disable-ds-digests</command>
statements are allowed.
Only the best match <command>disable-ds-digests</command>
clause will be used to determine which digest types are used.
</para>
<para>
If all supported digest types are disabled, the zones covered
by the <command>disable-ds-digests</command> will be treated
as insecure.
</para>
</listitem>
</varlistentry>
......
......@@ -101,6 +101,7 @@ options {
dialup <dialuptype>;
directory <quoted_string>;
disable-algorithms <string> { <string>; ... };
disable-ds-digests <string> { <string>; ... };
disable-empty-zone <string>;
dns64 <netprefix> {
break-dnssec <boolean>;
......
......@@ -286,12 +286,61 @@ disabled_algorithms(const cfg_obj_t *disabled, isc_log_t *logctx) {
r.length = strlen(r.base);
tresult = dns_secalg_fromtext(&alg, &r);
if (tresult != ISC_R_SUCCESS) {
if (tresult != ISC_R_SUCCESS) {
cfg_obj_log(cfg_listelt_value(element), logctx,
ISC_LOG_ERROR, "invalid algorithm '%s'",
r.base);
result = tresult;
}
}
}
return (result);
}
static isc_result_t
disabled_ds_digests(const cfg_obj_t *disabled, isc_log_t *logctx) {
isc_result_t result = ISC_R_SUCCESS;
isc_result_t tresult;
const cfg_listelt_t *element;
const char *str;
isc_buffer_t b;
dns_fixedname_t fixed;
dns_name_t *name;
const cfg_obj_t *obj;
dns_fixedname_init(&fixed);
name = dns_fixedname_name(&fixed);
obj = cfg_tuple_get(disabled, "name");
str = cfg_obj_asstring(obj);
isc_buffer_init(&b, str, strlen(str));
isc_buffer_add(&b, strlen(str));
tresult = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
if (tresult != ISC_R_SUCCESS) {
cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
"bad domain name '%s'", str);
result = tresult;
}
obj = cfg_tuple_get(disabled, "digests");
for (element = cfg_list_first(obj);
element != NULL;
element = cfg_list_next(element))
{
isc_textregion_t r;
dns_dsdigest_t digest;
isc_result_t tresult;
DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
r.length = strlen(r.base);
/* works with a numeric argument too */
tresult = dns_dsdigest_fromtext(&digest, &r);
if (tresult != ISC_R_SUCCESS) {
cfg_obj_log(cfg_listelt_value(element), logctx,
ISC_LOG_ERROR, "invalid digest type '%s'",
r.base);
result = tresult;
}
}
return (result);
}
......@@ -869,6 +918,23 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx,
}
}
/*
* Set supported DS/DLV digest types.
*/
obj = NULL;
(void)cfg_map_get(options, "disable-ds-digests", &obj);
if (obj != NULL) {
for (element = cfg_list_first(obj);
element != NULL;
element = cfg_list_next(element))
{
obj = cfg_listelt_value(element);
tresult = disabled_ds_digests(obj, logctx);
if (tresult != ISC_R_SUCCESS)
result = tresult;
}
}
dns_fixedname_init(&fixed);
name = dns_fixedname_name(&fixed);
......
......@@ -67,7 +67,7 @@ dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key,
REQUIRE(key != NULL);
REQUIRE(key->type == dns_rdatatype_dnskey);
if (!dns_ds_digest_supported(digest_type))
if (!dst_ds_digest_supported(digest_type))
return (ISC_R_NOTIMPLEMENTED);
dns_fixedname_init(&fname);
......@@ -167,17 +167,3 @@ dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key,
return (dns_rdata_fromstruct(rdata, key->rdclass, dns_rdatatype_ds,
&ds, &b));
}
isc_boolean_t
dns_ds_digest_supported(unsigned int digest_type) {
#ifdef HAVE_OPENSSL_GOST
return (ISC_TF(digest_type == DNS_DSDIGEST_SHA1 ||
digest_type == DNS_DSDIGEST_SHA256 ||
digest_type == DNS_DSDIGEST_GOST ||
digest_type == DNS_DSDIGEST_SHA384));
#else
return (ISC_TF(digest_type == DNS_DSDIGEST_SHA1 ||
digest_type == DNS_DSDIGEST_SHA256 ||
digest_type == DNS_DSDIGEST_SHA384));
#endif
}
......@@ -277,6 +277,20 @@ dst_algorithm_supported(unsigned int alg) {
return (ISC_TRUE);
}
isc_boolean_t
dst_ds_digest_supported(unsigned int digest_type) {
#ifdef HAVE_OPENSSL_GOST
return (ISC_TF(digest_type == DNS_DSDIGEST_SHA1 ||
digest_type == DNS_DSDIGEST_SHA256 ||
digest_type == DNS_DSDIGEST_GOST ||
digest_type == DNS_DSDIGEST_SHA384));
#else
return (ISC_TF(digest_type == DNS_DSDIGEST_SHA1 ||
digest_type == DNS_DSDIGEST_SHA256 ||
digest_type == DNS_DSDIGEST_SHA384));
#endif
}
isc_result_t
dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp) {
dst_context_t *dctx;
......
......@@ -58,12 +58,6 @@ dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key,
* to 'buffer'.
*/
isc_boolean_t
dns_ds_digest_supported(unsigned int digest_type);
/*%<
* Is this digest algorithm supported by dns_ds_buildrdata()?
*/
ISC_LANG_ENDDECLS
#endif /* DNS_DS_H */
......@@ -456,11 +456,17 @@ dns_resolver_reset_algorithms(dns_resolver_t *resolver);
* Clear the disabled DNSSEC algorithms.
*/
void
dns_resolver_reset_ds_digests(dns_resolver_t *resolver);
/*%<
* Clear the disabled DS/DLV digest types.
*/
isc_result_t
dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name,
unsigned int alg);
/*%<
* Mark the give DNSSEC algorithm as disabled and below 'name'.
* Mark the given DNSSEC algorithm as disabled and below 'name'.
* Valid algorithms are less than 256.
*
* Returns:
......@@ -469,20 +475,37 @@ dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name,
*\li #ISC_R_NOMEMORY
*/
isc_result_t
dns_resolver_disable_ds_digest(dns_resolver_t *resolver, dns_name_t *name,
unsigned int digest_type);
/*%<
* Mark the given DS/DLV digest type as disabled and below 'name'.
* Valid types are less than 256.
*
* Returns:
*\li #ISC_R_SUCCESS
*\li #ISC_R_RANGE
*\li #ISC_R_NOMEMORY
*/
isc_boolean_t
dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name,
unsigned int alg);
/*%<
* Check if the given algorithm is supported by this resolver.
* This checks if the algorithm has been disabled via
* dns_resolver_disable_algorithm() then the underlying
* crypto libraries if not specifically disabled.
* This checks whether the algorithm has been disabled via
* dns_resolver_disable_algorithm(), then checks the underlying
* crypto libraries if it was not specifically disabled.
*/
isc_boolean_t
dns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest_type);
dns_resolver_ds_digest_supported(dns_resolver_t *resolver, dns_name_t *name,
unsigned int digest_type);
/*%<
* Is this digest type supported.
* Check if the given digest type is supported by this resolver.
* This checks whether the digest type has been disabled via
* dns_resolver_disable_ds_digest(), then checks the underlying
* crypto libraries if it was not specifically disabled.
*/
void
......
......@@ -74,6 +74,7 @@ typedef struct dns_dns64 dns_dns64_t;
typedef ISC_LIST(dns_dns64_t) dns_dns64list_t;
typedef struct dns_dnsseckey dns_dnsseckey_t;
typedef ISC_LIST(dns_dnsseckey_t) dns_dnsseckeylist_t;
typedef isc_uint8_t dns_dsdigest_t;
typedef struct dns_dumpctx dns_dumpctx_t;
typedef struct dns_fetch dns_fetch_t;
typedef struct dns_fixedname dns_fixedname_t;
......
......@@ -28,6 +28,8 @@
#include <dns/types.h>
#include <dns/name.h>
#include <dns/secalg.h>
#include <dns/ds.h>
#include <dns/dsdigest.h>
#include <dst/gssapi.h>
......@@ -167,6 +169,16 @@ dst_algorithm_supported(unsigned int alg);
* \li ISC_FALSE
*/
isc_boolean_t
dst_ds_digest_supported(unsigned int digest_type);
/*%<
* Checks that a given digest algorithm is supported by DST.
*
* Returns:
* \li ISC_TRUE
* \li ISC_FALSE
*/
isc_result_t
dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp);
/*%<
......
......@@ -32,6 +32,8 @@
#include <isc/util.h>
#include <dns/cert.h>
#include <dns/ds.h>
#include <dns/dsdigest.h>
#include <dns/keyflags.h>
#include <dns/keyvalues.h>
#include <dns/rcode.h>
......@@ -130,6 +132,15 @@
{ 1, "SHA-1", 0 }, \
{ 0, NULL, 0 }
/* RFC3658, RFC4509, RFC5933, RFC6605 */
#define DSDIGESTNAMES \
{ DNS_DSDIGEST_SHA1, "SHA-1", 0 }, \
{ DNS_DSDIGEST_SHA256, "SHA-256", 0 }, \
{ DNS_DSDIGEST_GOST, "GOST", 0 }, \
{ DNS_DSDIGEST_SHA384, "SHA-384", 0 }, \
{ 0, NULL, 0}
struct tbl {
unsigned int value;
const char *name;
......@@ -142,6 +153,7 @@ static struct tbl certs[] = { CERTNAMES };
static struct tbl secalgs[] = { SECALGNAMES };
static struct tbl secprotos[] = { SECPROTONAMES };
static struct tbl hashalgs[] = { HASHALGNAMES };
static struct tbl dsdigests[] = { DSDIGESTNAMES };
static struct keyflag {
const char *name;
......@@ -404,6 +416,34 @@ dns_keyflags_fromtext(dns_keyflags_t *flagsp, isc_textregion_t *source)
return (ISC_R_SUCCESS);
}
isc_result_t
dns_dsdigest_fromtext(dns_dsdigest_t *dsdigestp, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, dsdigests, 0xff));
*dsdigestp = value;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_dsdigest_totext(dns_dsdigest_t dsdigest, isc_buffer_t *target) {
return (dns_mnemonic_totext(dsdigest, target, dsdigests));
}
void
dns_dsdigest_format(dns_dsdigest_t typ, char *cp, unsigned int size) {
isc_buffer_t b;
isc_region_t r;
isc_result_t result;
REQUIRE(cp != NULL && size > 0);
isc_buffer_init(&b, cp, size - 1);
result = dns_dsdigest_totext(typ, &b);
isc_buffer_usedregion(&b, &r);
r.base[r.length] = 0;
if (result != ISC_R_SUCCESS)
r.base[0] = 0;
}
/*
* This uses lots of hard coded values, but how often do we actually
* add classes?
......
......@@ -35,6 +35,7 @@
#include <dns/callbacks.h>
#include <dns/cert.h>
#include <dns/compress.h>
#include <dns/dsdigest.h>
#include <dns/enumtype.h>
#include <dns/keyflags.h>
#include <dns/keyvalues.h>
......
......@@ -16,7 +16,7 @@
/* $Id$ */
/* draft-ietf-dnsext-delegation-signer-05.txt */
/* RFC3658 */
#ifndef RDATA_GENERIC_DLV_32769_C
#define RDATA_GENERIC_DLV_32769_C
......@@ -28,7 +28,6 @@
#include <dns/ds.h>
static inline isc_result_t
fromtext_dlv(ARGS_FROMTEXT) {
isc_token_t token;
......
......@@ -17,7 +17,7 @@
/* $Id$ */
/* draft-ietf-dnsext-delegation-signer-05.txt */
/* RFC3658 */
#ifndef RDATA_GENERIC_DS_43_C
#define RDATA_GENERIC_DS_43_C
......@@ -64,12 +64,10 @@ fromtext_ds(ARGS_FROMTEXT) {
/*
* Digest type.
*/
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
ISC_FALSE));
if (token.value.as_ulong > 0xffU)
RETTOK(ISC_R_RANGE);
RETERR(uint8_tobuffer(token.value.as_ulong, target));
c = (unsigned char) token.value.as_ulong;
RETTOK(dns_dsdigest_fromtext(&c, &token.value.as_textregion));
RETERR(mem_tobuffer(target, &c, 1));
/*
* Digest.
......
......@@ -404,6 +404,7 @@ struct dns_resolver {
isc_rwlock_t alglock;
#endif
dns_rbt_t * algorithms;
dns_rbt_t * digests;
#if USE_MBSLOCK
isc_rwlock_t mbslock;
#endif
......@@ -7352,6 +7353,7 @@ destroy(dns_resolver_t *res) {
isc_mem_put(res->mctx, a, sizeof(*a));
}
dns_resolver_reset_algorithms(res);
dns_resolver_reset_ds_digests(res);
destroy_badcache(res);
dns_resolver_resetmustbesecure(res);
#if USE_ALGLOCK
......@@ -7478,6 +7480,7 @@ dns_resolver_create(dns_view_t *view,
ISC_LIST_INIT(res->alternates);
res->udpsize = RECV_BUFFER_SIZE;
res->algorithms = NULL;
res->digests = NULL;
res->badcache = NULL;
res->badcount = 0;
res->badhash = 0;
......@@ -8667,11 +8670,118 @@ dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name,
return (dst_algorithm_supported(alg));
}
static void
free_digest(void *node, void *arg) {
unsigned char *digests = node;
isc_mem_t *mctx = arg;
isc_mem_put(mctx, digests, *digests);
}
void
dns_resolver_reset_ds_digests(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
#if USE_ALGLOCK
RWLOCK(&resolver->alglock, isc_rwlocktype_write);
#endif
if (resolver->digests != NULL)
dns_rbt_destroy(&resolver->digests);
#if USE_ALGLOCK
RWUNLOCK(&resolver->alglock, isc_rwlocktype_write);
#endif
}
isc_result_t
dns_resolver_disable_ds_digest(dns_resolver_t *resolver, dns_name_t *name,
unsigned int digest_type)
{
unsigned int len, mask;
unsigned char *new;
unsigned char *digests;
isc_result_t result;
dns_rbtnode_t *node = NULL;
REQUIRE(VALID_RESOLVER(resolver));
if (digest_type > 255)
return (ISC_R_RANGE);
#if USE_ALGLOCK
RWLOCK(&resolver->alglock, isc_rwlocktype_write);
#endif
if (resolver->digests == NULL) {
result = dns_rbt_create(resolver->mctx, free_digest,
resolver->mctx, &resolver->digests);
if (result != ISC_R_SUCCESS)
goto cleanup;
}
len = digest_type/8 + 2;
mask = 1 << (digest_type%8);
result = dns_rbt_addnode(resolver->digests, name, &node);
if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) {
digests = node->data;
if (digests == NULL || len > *digests) {
new = isc_mem_get(resolver->mctx, len);
if (new == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
memset(new, 0, len);
if (digests != NULL)
memcpy(new, digests, *digests);
new[len-1] |= mask;
*new = len;
node->data = new;
if (digests != NULL)
isc_mem_put(resolver->mctx, digests,