+2813. [bug] Better handling of unreadable DNSSEC key files.
+ [RT #20710]
+
+2812. [bug] Make sure updates can't result in a zone with
+ NSEC-only keys and NSEC3 records. [RT 20748]
+
2811. [cleanup] Add "rndc sign" to list of commands in rndc usage
output. [RT #20733]
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: update.c,v 1.176 2009/12/04 21:09:32 marka Exp $ */
+/* $Id: update.c,v 1.177 2009/12/18 22:16:49 each Exp $ */
#include <config.h>
check_dnssec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
dns_dbversion_t *ver, dns_diff_t *diff)
{
- dns_diff_t temp_diff;
- dns_diffop_t op;
- dns_difftuple_t *tuple, *newtuple = NULL, *next;
- isc_boolean_t flag;
+ dns_difftuple_t *tuple;
+ isc_boolean_t nseconly = ISC_FALSE, nsec3 = ISC_FALSE;
isc_result_t result;
unsigned int iterations = 0, max;
dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
- dns_diff_init(diff->mctx, &temp_diff);
+ /* Scan the tuples for an NSEC-only DNSKEY or an NSEC3PARAM */
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ if (tuple->op != DNS_DIFFOP_ADD)
+ continue;
- CHECK(dns_nsec_nseconly(db, ver, &flag));
+ if (tuple->rdata.type == dns_rdatatype_dnskey) {
+ isc_uint8_t alg;
+ alg = tuple->rdata.data[3];
+ if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
+ alg == DST_ALG_DSA || alg == DST_ALG_ECC) {
+ nseconly = ISC_TRUE;
+ break;
+ }
+ } else if (tuple->rdata.type == dns_rdatatype_nsec3param) {
+ nsec3 = ISC_TRUE;
+ break;
+ }
+ }
+
+ /* Check existing DB for NSEC-only DNSKEY */
+ if (!nseconly)
+ CHECK(dns_nsec_nseconly(db, ver, &nseconly));
- if (flag)
+ /* Check existing DB for NSEC3 */
+ if (!nsec3)
CHECK(dns_nsec3_activex(db, ver, ISC_FALSE,
- privatetype, &flag));
- if (flag) {
- update_log(client, zone, ISC_LOG_WARNING,
+ privatetype, &nsec3));
+
+ /* Refuse to allow NSEC3 with NSEC-only keys */
+ if (nseconly && nsec3) {
+ update_log(client, zone, ISC_LOG_ERROR,
"NSEC only DNSKEYs and NSEC3 chains not allowed");
- } else {
- CHECK(get_iterations(db, ver, privatetype, &iterations));
- CHECK(dns_nsec3_maxiterations(db, ver, client->mctx, &max));
- if (max != 0 && iterations > max) {
- flag = ISC_TRUE;
- update_log(client, zone, ISC_LOG_WARNING,
- "too many NSEC3 iterations (%u) for "
- "weakest DNSKEY (%u)", iterations, max);
- }
- }
- if (flag) {
- for (tuple = ISC_LIST_HEAD(diff->tuples);
- tuple != NULL;
- tuple = next) {
- next = ISC_LIST_NEXT(tuple, link);
- if (tuple->rdata.type != dns_rdatatype_dnskey &&
- tuple->rdata.type != dns_rdatatype_nsec3param)
- continue;
- op = (tuple->op == DNS_DIFFOP_DEL) ?
- DNS_DIFFOP_ADD : DNS_DIFFOP_DEL;
- CHECK(dns_difftuple_create(temp_diff.mctx, op,
- &tuple->name, tuple->ttl,
- &tuple->rdata, &newtuple));
- CHECK(do_one_tuple(&newtuple, db, ver, &temp_diff));
- INSIST(newtuple == NULL);
- }
- for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
- tuple != NULL;
- tuple = ISC_LIST_HEAD(temp_diff.tuples)) {
- ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
- dns_diff_appendminimal(diff, &tuple);
- }
+ result = DNS_R_REFUSED;
+ goto failure;
}
+ /* Verify NSEC3 params */
+ CHECK(get_iterations(db, ver, privatetype, &iterations));
+ CHECK(dns_nsec3_maxiterations(db, ver, client->mctx, &max));
+ if (max != 0 && iterations > max) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "too many NSEC3 iterations (%u) for "
+ "weakest DNSKEY (%u)", iterations, max);
+ result = DNS_R_REFUSED;
+ goto failure;
+ }
failure:
- dns_diff_clear(&temp_diff);
return (result);
}
*/
/*
- * $Id: dnssec.c,v 1.115 2009/11/24 23:48:12 tbox Exp $
+ * $Id: dnssec.c,v 1.116 2009/12/18 22:16:49 each Exp $
*/
/*! \file */
#include <dns/dnssec.h>
#include <dns/fixedname.h>
#include <dns/keyvalues.h>
+#include <dns/log.h>
#include <dns/message.h>
#include <dns/rdata.h>
#include <dns/rdatalist.h>
}
}
- if (result == ISC_R_FILENOTFOUND) {
+ if (result != ISC_R_SUCCESS) {
+ char keybuf[DNS_NAME_FORMATSIZE];
+ char algbuf[DNS_SECALG_FORMATSIZE];
+ dns_name_format(dst_key_name(pubkey), keybuf,
+ sizeof(keybuf));
+ dns_secalg_format(dst_key_alg(pubkey), algbuf,
+ sizeof(algbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING,
+ "dns_dnssec_findzonekeys2: error "
+ "reading private key file %s/%s/%d: %s",
+ keybuf, algbuf, dst_key_id(pubkey),
+ isc_result_totext(result));
+ }
+
+ if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) {
keys[count] = pubkey;
pubkey = NULL;
count++;
continue;
dstkey = NULL;
- RETERR(dst_key_fromnamedfile(dir.entry.name, directory,
- DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
- mctx, &dstkey));
+ result = dst_key_fromnamedfile(dir.entry.name,
+ directory,
+ DST_TYPE_PUBLIC |
+ DST_TYPE_PRIVATE,
+ mctx, &dstkey);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_DNSSEC,
+ ISC_LOG_WARNING,
+ "dns_dnssec_findmatchingkeys: "
+ "error reading key file %s: %s",
+ dir.entry.name,
+ isc_result_totext(result));
+ continue;
+ }
RETERR(dns_dnsseckey_create(mctx, &dstkey, &key));
key->source = dns_keysource_repository;
dst_key_alg(pubkey),
DST_TYPE_PUBLIC|DST_TYPE_PRIVATE,
directory, mctx, &privkey);
- if (result == ISC_R_FILENOTFOUND) {
+
+ if (result != ISC_R_SUCCESS) {
+ char keybuf[DNS_NAME_FORMATSIZE];
+ char algbuf[DNS_SECALG_FORMATSIZE];
+ dns_name_format(dst_key_name(pubkey), keybuf,
+ sizeof(keybuf));
+ dns_secalg_format(dst_key_alg(pubkey), algbuf,
+ sizeof(algbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING,
+ "dns_dnssec_keylistfromrdataset: error "
+ "reading private key file %s/%s/%d: %s",
+ keybuf, algbuf, dst_key_id(pubkey),
+ isc_result_totext(result));
+ }
+
+ if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) {
addkey(keylist, &pubkey, savekeys, mctx);
goto skip;
}
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: log.h,v 1.44 2009/01/17 23:47:43 tbox Exp $ */
+/* $Id: log.h,v 1.45 2009/12/18 22:16:49 each Exp $ */
/*! \file dns/log.h
* \author Principal Authors: DCL */
#define DNS_LOGMODULE_HINTS (&dns_modules[24])
#define DNS_LOGMODULE_ACACHE (&dns_modules[25])
#define DNS_LOGMODULE_DLZ (&dns_modules[26])
+#define DNS_LOGMODULE_DNSSEC (&dns_modules[27])
ISC_LANG_BEGINDECLS
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: log.c,v 1.45 2007/06/18 23:47:40 tbox Exp $ */
+/* $Id: log.c,v 1.46 2009/12/18 22:16:49 each Exp $ */
/*! \file */
{ "dns/hints", 0 },
{ "dns/acache", 0 },
{ "dns/dlz", 0 },
+ { "dns/dnssec", 0 },
{ NULL, 0 }
};
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: zone.c,v 1.541 2009/12/11 01:06:03 each Exp $ */
+/* $Id: zone.c,v 1.542 2009/12/18 22:16:49 each Exp $ */
/*! \file */
dns_rdataset_init(&rdataset);
dns_fixedname_init(&fixed);
- result = dns_db_getsigningtime(zone->db, &rdataset,
- dns_fixedname_name(&fixed));
+ result = dns_db_getsigningtime(zone->db, &rdataset,
+ dns_fixedname_name(&fixed));
if (result != ISC_R_SUCCESS) {
isc_time_settoepoch(&zone->resigntime);
return;
dst_key_free(&zone_keys[i]);
}
+/*
+ * Prevent the zone entering a inconsistent state where
+ * NSEC only DNSKEYs are present with NSEC3 chains.
+ * See update.c:check_dnssec()
+ */
+static isc_boolean_t
+dnskey_sane(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_difftuple_t *tuple;
+ isc_boolean_t nseconly = ISC_FALSE, nsec3 = ISC_FALSE;
+ dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
+
+ /* Scan the tuples for an NSEC-only DNSKEY */
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ isc_uint8_t alg;
+ if (tuple->rdata.type != dns_rdatatype_dnskey ||
+ tuple->op != DNS_DIFFOP_ADD)
+ continue;
+
+ alg = tuple->rdata.data[3];
+ if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
+ alg == DST_ALG_DSA || alg == DST_ALG_ECC) {
+ nseconly = ISC_TRUE;
+ break;
+ }
+ }
+
+ /* Check existing DB for NSEC-only DNSKEY */
+ if (!nseconly)
+ CHECK(dns_nsec_nseconly(db, ver, &nseconly));
+
+ /* Check existing DB for NSEC3 */
+ if (!nsec3)
+ CHECK(dns_nsec3_activex(db, ver, ISC_FALSE,
+ privatetype, &nsec3));
+
+ /* Refuse to allow NSEC3 with NSEC-only keys */
+ if (nseconly && nsec3) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "NSEC only DNSKEYs and NSEC3 chains not allowed");
+ goto failure;
+ }
+
+ return (ISC_TRUE);
+
+ failure:
+ return (ISC_FALSE);
+}
+
static isc_result_t
zone_rekey(dns_zone_t *zone) {
isc_result_t result;
isc_boolean_t check_ksk;
check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
- CHECK(dns_dnssec_updatekeys(&dnskeys, &keys, &rmkeys,
- &zone->origin, ttl, &diff,
- ISC_TF(!check_ksk), mctx, logmsg));
- if (!ISC_LIST_EMPTY(diff.tuples)) {
+ result = dns_dnssec_updatekeys(&dnskeys, &keys, &rmkeys,
+ &zone->origin, ttl, &diff,
+ ISC_TF(!check_ksk),
+ mctx, logmsg);
+
+ /* Keys couldn't be updated for some reason; try again later. */
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_rekey:"
+ "couldn't update zone keys: %s",
+ isc_result_totext(result));
+ zone->refreshkeytime.seconds += HOUR;
+ goto failure;
+ }
+
+ if (!ISC_LIST_EMPTY(diff.tuples) &&
+ dnskey_sane(zone, db, ver, &diff)) {
commit = ISC_TRUE;
dns_diff_apply(&diff, db, ver);
sign_dnskey(zone, db, ver, &diff);