From 28479307225582ad0b2e11441d85fcf5169551d0 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 8 Oct 2009 23:13:07 +0000 Subject: [PATCH] 2708. [func] Insecure to secure and NSEC3 parameter changes via update are now fully supported and no longer require defines to enable. We now no longer overload the NSEC3PARAM flag field, nor the NSEC OPT bit at the apex. Secure to insecure changes are controlled by by the named.conf option 'secure-to-insecure'. Warning: If you had previously enabled support by adding defines at compile time to BIND 9.6 you should ensure that all changes that are in progress have completed prior to upgrading to BIND 9.7. BIND 9.7 is not backwards compatible. --- CHANGES | 13 + NSEC3-NOTES | 42 +- README | 5 + bin/named/config.c | 3 +- bin/named/named.conf.docbook | 5 +- bin/named/update.c | 442 +++++++++++-------- bin/named/zoneconf.c | 8 +- doc/arm/Bv9ARM-book.xml | 25 +- lib/bind9/check.c | 15 +- lib/dns/Makefile.in | 4 +- lib/dns/db.c | 6 +- lib/dns/include/dns/Makefile.in | 18 +- lib/dns/include/dns/nsec3.h | 50 ++- lib/dns/include/dns/rdata.h | 20 +- lib/dns/include/dns/zone.h | 3 +- lib/dns/nsec3.c | 301 +++++++++++-- lib/dns/rbtdb.c | 38 +- lib/dns/rdata.c | 19 +- lib/dns/sdb.c | 4 +- lib/dns/win32/libdns.def | 12 +- lib/dns/win32/libdns.dsp | 8 + lib/dns/win32/libdns.mak | 25 ++ lib/dns/zone.c | 759 ++++++++++++++++++++------------ lib/isccfg/namedconf.c | 3 +- 24 files changed, 1248 insertions(+), 580 deletions(-) diff --git a/CHANGES b/CHANGES index cf41bef0b9..6b350d3b3f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,16 @@ +2708. [func] Insecure to secure and NSEC3 parameter changes via + update are now fully supported and no longer require + defines to enable. We now no longer overload the + NSEC3PARAM flag field, nor the NSEC OPT bit at the + apex. Secure to insecure changes are controlled by + by the named.conf option 'secure-to-insecure'. + + Warning: If you had previously enabled support by + adding defines at compile time to BIND 9.6 you should + ensure that all changes that are in progress have + completed prior to upgrading to BIND 9.7. BIND 9.7 + is not backwards compatible. + 2707. [func] dnssec-keyfromlabel no longer require engine name to be specified in the label if there is a default engine or the -E option has been used. Also, it diff --git a/NSEC3-NOTES b/NSEC3-NOTES index 98f25be292..e35ff4a5d7 100644 --- a/NSEC3-NOTES +++ b/NSEC3-NOTES @@ -32,18 +32,36 @@ generated as part of the initial signing process. While the update request will complete almost immediately the zone will not be completely signed until named has had time to walk the -zone and generate the NSEC and RRSIG records. Initially the NSEC -record at the zone apex will have the OPT bit set. When the NSEC -chain is complete the OPT bit will be cleared. Additionally when -the zone is fully signed the private type (default TYPE65534) records -will have a non zero value for the final octet. +zone and generate the NSEC and RRSIG records. The NSEC record at the +apex will be added last to signal that there is a complete NSEC chain. +Additionally when the zone is fully signed the private type (default +TYPE65534) records will have a non zero value for the final octet for +those record with a none zero initial octet. + +The private type record format: +If the first octet is non-zero then the record indicates that the zone needs +to be signed with the key matching the record or that all signatures that +match the record should be removed. -The private type record has 5 octets. algorithm (octet 1) key id in network order (octet 2 and 3) removal flag (octet 4) complete flag (octet 5) +Only records with the complete flag set can be removed via nsupdate. +Attempts to remove other private type records will be silently ignored. + +If the first octet is zero (this is a reserved algorithm number +that should never appear in a DNSKEY record) then the record indicates +changes to the NSEC3 chains are in progress. The rest of the record +contains a NSEC3PARAM record. The flag field tells what operation +to perform based on the flag bits. + + 0x01 OPTOUT + 0x80 CREATE + 0x40 REMOVE + 0x20 NONSEC + If you wish to go straight to a secure zone using NSEC3 you should also add a NSECPARAM record to the update request with the flags field set to indicate whether the NSEC3 chain will have the OPTOUT @@ -56,10 +74,11 @@ bit set or not. > update add example.net NSEC3PARAM 1 1 100 1234567890 > send -Again the update request will complete almost immediately however the -NSEC3PARAM record will have additional flag bits set indicating that the -NSEC3 chain is under construction. When the NSEC3 chain is complete the -flags field will be set to zero. +Again the update request will complete almost immediately however +the record won't show up or be deleted until named has had a chance +to build/remove the relevent chain. A private type record will be +created to record the operatation and will be removed once the +operation completes. While the initial signing and NSEC/NSEC3 chain generation is happening other updates are possible. @@ -109,7 +128,8 @@ NSEC chain will be generated before the NSEC3 chain is removed. To do this remove all the DNSKEY records. Any NSEC or NSEC3 chains will be removed as well as associated NSEC3PARAM records. This will -take place after the update requests completes. +take place after the update requests completes. This requires +secure-to-insecure to be set in named.conf. Periodic re-signing. diff --git a/README b/README index 21ce41ce27..526f1df317 100644 --- a/README +++ b/README @@ -73,6 +73,11 @@ BIND 9.7.0 - Improved PKCS#11 support, including Keyper support and explicit OpenSSL engine selection (see README.pkcs11 for additional details). + Warning: If you had built BIND 9.6 with any of ALLOW_NSEC3PARAM_UPDATE, + ALLOW_SECURE_TO_INSECURE or ALLOW_INSECURE_TO_SECURE defined then + you should ensure that all changes that are in progress have completed + prior to upgrading to BIND 9.7. BIND 9.7 is not backwards compatible. + BIND 9.6.0 BIND 9.6.0 includes a number of changes from BIND 9.5 and earlier diff --git a/bin/named/config.c b/bin/named/config.c index 3d72c5573d..5b8abd6d9d 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: config.c,v 1.101 2009/09/01 07:14:25 each Exp $ */ +/* $Id: config.c,v 1.102 2009/10/08 23:13:05 marka Exp $ */ /*! \file */ @@ -185,6 +185,7 @@ options {\n\ max-refresh-time 2419200; /* 4 weeks */\n\ min-refresh-time 300;\n\ multi-master no;\n\ + secure-to-insecure no;\n\ sig-validity-interval 30; /* days */\n\ sig-signing-nodes 100;\n\ sig-signing-signatures 10;\n\ diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook index a4a8044d04..ddf3ee4996 100644 --- a/bin/named/named.conf.docbook +++ b/bin/named/named.conf.docbook @@ -17,7 +17,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + Aug 13, 2004 @@ -340,6 +340,7 @@ options { try-tcp-refresh boolean; zero-no-soa-ttl boolean; zero-no-soa-ttl-cache boolean; + secure-to-insecure boolean; nsec3-test-zone boolean; // testing only @@ -499,6 +500,7 @@ view string optional_class key-directory quoted_string; zero-no-soa-ttl boolean; zero-no-soa-ttl-cache boolean; + secure-to-insecure boolean; allow-v6-synthesis { address_match_element; ... }; // obsolete fetch-glue boolean; // obsolete @@ -533,6 +535,7 @@ zone string optional_class ixfr-from-differences boolean; journal quoted_string; zero-no-soa-ttl boolean; + secure-to-insecure boolean; allow-query { address_match_element; ... }; allow-query-on { address_match_element; ... }; diff --git a/bin/named/update.c b/bin/named/update.c index ea61500e5c..b6b288e4f9 100644 --- a/bin/named/update.c +++ b/bin/named/update.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.c,v 1.159 2009/08/17 07:18:41 marka Exp $ */ +/* $Id: update.c,v 1.160 2009/10/08 23:13:05 marka Exp $ */ #include @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -1211,8 +1212,8 @@ replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { * Replace records added in this UPDATE request. */ if (db_rr->data[0] == update_rr->data[0] && - db_rr->data[1] & DNS_NSEC3FLAG_UPDATE && - update_rr->data[1] & DNS_NSEC3FLAG_UPDATE && + (db_rr->data[1] & DNS_NSEC3FLAG_UPDATE) != 0 && + (update_rr->data[1] & DNS_NSEC3FLAG_UPDATE) != 0 && memcmp(db_rr->data+2, update_rr->data+2, update_rr->length - 2) == 0) return (ISC_TRUE); @@ -1717,35 +1718,6 @@ next_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, return (result); } -static isc_boolean_t -has_opt_bit(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node) { - isc_result_t result; - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdataset_t rdataset; - isc_boolean_t has_bit = ISC_FALSE; - - dns_rdataset_init(&rdataset); - CHECK(dns_db_findrdataset(db, node, version, dns_rdatatype_nsec, - dns_rdatatype_none, 0, &rdataset, NULL)); - CHECK(dns_rdataset_first(&rdataset)); - dns_rdataset_current(&rdataset, &rdata); - has_bit = dns_nsec_typepresent(&rdata, dns_rdatatype_opt); - failure: - if (dns_rdataset_isassociated(&rdataset)) - dns_rdataset_disassociate(&rdataset); - return (has_bit); -} - -static void -set_bit(unsigned char *array, unsigned int index) { - unsigned int shift, bit; - - shift = 7 - (index % 8); - bit = 1 << shift; - - array[index / 8] |= bit; -} - /*% * Add a NSEC record for "name", recording the change in "diff". * The existing NSEC is removed. @@ -1777,24 +1749,6 @@ add_nsec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); dns_rdata_init(&rdata); CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata)); - /* - * Preserve the status of the OPT bit in the origin's NSEC record. - */ - if (dns_name_equal(dns_db_origin(db), name) && - has_opt_bit(db, ver, node)) - { - isc_region_t region; - dns_name_t next; - - dns_name_init(&next, NULL); - dns_rdata_toregion(&rdata, ®ion); - dns_name_fromregion(&next, ®ion); - isc_region_consume(®ion, next.length); - INSIST(region.length > (2 + dns_rdatatype_opt / 8) && - region.base[0] == 0 && - region.base[1] > dns_rdatatype_opt / 8); - set_bit(region.base + 2, dns_rdatatype_opt); - } dns_db_detachnode(db, &node); /* @@ -2129,7 +2083,7 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, dns_diff_t sig_diff; dns_diff_t nsec_diff; dns_diff_t nsec_mindiff; - isc_boolean_t flag; + isc_boolean_t flag, build_nsec, build_nsec3; dst_key_t *zone_keys[MAXZONEKEYS]; unsigned int nkeys = 0; unsigned int i; @@ -2142,6 +2096,7 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, isc_boolean_t check_ksk; isc_boolean_t unsecure; isc_boolean_t cut; + dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); dns_diff_init(client->mctx, &diffnames); dns_diff_init(client->mctx, &affected); @@ -2288,12 +2243,11 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, "removed any orphaned NSEC records"); /* - * If we don't have a NSEC record at the origin then we need to - * update the NSEC3 records. + * See if we need to build NSEC or NSEC3 chains. */ - CHECK(rrset_exists(db, newver, dns_db_origin(db), dns_rdatatype_nsec, - 0, &flag)); - if (!flag) + CHECK(dns_private_chains(db, newver, privatetype, &build_nsec, + &build_nsec3)); + if (!build_nsec) goto update_nsec3; update_log(client, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC chain"); @@ -2397,13 +2351,18 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, dns_rdatatype_any, 0, NULL, diff)); } else { /* - * This name is not obscured. It should have a NSEC. + * This name is not obscured. It should have a NSEC + * unless it is the at the origin, in which case it + * should already exist. */ - CHECK(rrset_exists(db, newver, name, - dns_rdatatype_nsec, 0, &flag)); - if (! flag) - CHECK(add_placeholder_nsec(db, newver, name, - diff)); + if (!dns_name_equal(name, dns_db_origin(db))) { + CHECK(dns_private_chains(db, newver, + privatetype, &flag, + NULL)); + if (flag) + CHECK(add_placeholder_nsec(db, newver, + name, diff)); + } CHECK(add_exposed_sigs(client, zone, db, newver, name, cut, diff, zone_keys, nkeys, inception, expire, check_ksk)); @@ -2490,13 +2449,7 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); - /* - * Check if we have any active NSEC3 chains by looking for a - * NSEC3PARAM RRset. - */ - CHECK(rrset_exists(db, newver, dns_db_origin(db), - dns_rdatatype_nsec3param, 0, &flag)); - if (!flag) { + if (!build_nsec3) { update_log(client, zone, ISC_LOG_DEBUG(3), "no NSEC3 chains to rebuild"); goto failure; @@ -2520,6 +2473,7 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, isc_boolean_t ns_existed, dname_existed; isc_boolean_t ns_exists, dname_exists; + isc_boolean_t exists, existed; if (t->rdata.type == dns_rdatatype_nsec || t->rdata.type == dns_rdatatype_rrsig) { @@ -2538,7 +2492,9 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, CHECK(rrset_exists(db, newver, name, dns_rdatatype_dname, 0, &dname_exists)); - if ((ns_exists || dname_exists) == (ns_existed || dname_existed)) + exists = ns_exists || dname_exists; + existed = ns_existed || dname_existed; + if (exists == existed) goto nextname; /* * There was a delegation change. Mark all subdomains @@ -2562,14 +2518,15 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, if (!flag) { CHECK(delete_if(rrsig_p, db, newver, name, dns_rdatatype_any, 0, NULL, diff)); - CHECK(dns_nsec3_delnsec3s(db, newver, name, - &nsec_diff)); + CHECK(dns_nsec3_delnsec3sx(db, newver, name, + privatetype, &nsec_diff)); } else { CHECK(add_exposed_sigs(client, zone, db, newver, name, cut, diff, zone_keys, nkeys, inception, expire, check_ksk)); - CHECK(dns_nsec3_addnsec3s(db, newver, name, nsecttl, - unsecure, &nsec_diff)); + CHECK(dns_nsec3_addnsec3sx(db, newver, name, nsecttl, + unsecure, privatetype, + &nsec_diff)); } } @@ -2960,7 +2917,9 @@ rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, } static isc_result_t -get_iterations(dns_db_t *db, dns_dbversion_t *ver, unsigned int *iterationsp) { +get_iterations(dns_db_t *db, dns_dbversion_t *ver, dns_rdatatype_t privatetype, + unsigned int *iterationsp) +{ dns_dbnode_t *node = NULL; dns_rdata_nsec3param_t nsec3param; dns_rdataset_t rdataset; @@ -2974,9 +2933,8 @@ get_iterations(dns_db_t *db, dns_dbversion_t *ver, unsigned int *iterationsp) { return (result); result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 0, (isc_stdtime_t) 0, &rdataset, NULL); - dns_db_detachnode(db, &node); if (result == ISC_R_NOTFOUND) - goto success; + goto try_private; if (result != ISC_R_SUCCESS) goto failure; @@ -2994,11 +2952,46 @@ get_iterations(dns_db_t *db, dns_dbversion_t *ver, unsigned int *iterationsp) { if (result != ISC_R_NOMORE) goto failure; + dns_rdataset_disassociate(&rdataset); + + try_private: + if (privatetype == 0) + goto success; + + result = dns_db_findrdataset(db, node, ver, privatetype, + 0, (isc_stdtime_t) 0, &rdataset, NULL); + if (result == ISC_R_NOTFOUND) + goto success; + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + dns_rdata_t private = DNS_RDATA_INIT; + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(&rdataset, &rdata); + if (!dns_nsec3param_fromprivate(&private, &rdata, + buf, sizeof(buf))) + continue; + CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); + if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) + continue; + if (nsec3param.iterations > iterations) + iterations = nsec3param.iterations; + } + if (result != ISC_R_NOMORE) + goto failure; + success: *iterationsp = iterations; result = ISC_R_SUCCESS; failure: + if (node != NULL) + dns_db_detachnode(db, &node); if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); return (result); @@ -3018,18 +3011,19 @@ check_dnssec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, isc_boolean_t flag; isc_result_t result; unsigned int iterations = 0, max; + dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); dns_diff_init(diff->mctx, &temp_diff); CHECK(dns_nsec_nseconly(db, ver, &flag)); if (flag) - CHECK(dns_nsec3_active(db, ver, ISC_FALSE, &flag)); + CHECK(dns_nsec3_activex(db, ver, ISC_FALSE, privatetype, &flag)); if (flag) { update_log(client, zone, ISC_LOG_WARNING, "NSEC only DNSKEYs and NSEC3 chains not allowed"); } else { - CHECK(get_iterations(db, ver, &iterations)); + CHECK(get_iterations(db, ver, privatetype, &iterations)); CHECK(dns_nsec3_maxiterations(db, ver, client->mctx, &max)); if (iterations > max) { flag = ISC_TRUE; @@ -3068,21 +3062,22 @@ check_dnssec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, return (result); } -#ifdef ALLOW_NSEC3PARAM_UPDATE /* * Delay NSEC3PARAM changes as they need to be applied to the whole zone. */ static isc_result_t add_nsec3param_records(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, - dns_name_t *name, dns_dbversion_t *ver, dns_diff_t *diff) + dns_dbversion_t *ver, dns_diff_t *diff) { isc_result_t result = ISC_R_SUCCESS; dns_difftuple_t *tuple, *newtuple = NULL, *next; dns_rdata_t rdata = DNS_RDATA_INIT; - unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE + 1]; dns_diff_t temp_diff; dns_diffop_t op; isc_boolean_t flag; + dns_name_t *name = dns_zone_getorigin(zone); + dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);; update_log(client, zone, ISC_LOG_DEBUG(3), "checking for NSEC3PARAM changes"); @@ -3140,12 +3135,10 @@ add_nsec3param_records(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, /* * See if we already have a CREATE request in progress. */ - dns_rdata_clone(&tuple->rdata, &rdata); - INSIST(rdata.length <= sizeof(buf)); - memcpy(buf, rdata.data, rdata.length); - buf[1] |= DNS_NSEC3FLAG_CREATE; - buf[1] &= ~DNS_NSEC3FLAG_UPDATE; - rdata.data = buf; + dns_nsec3param_toprivate(&tuple->rdata, &rdata, + privatetype, buf, sizeof(buf)); + buf[2] |= DNS_NSEC3FLAG_CREATE; + buf[2] &= ~DNS_NSEC3FLAG_UPDATE; CHECK(rr_exists(db, ver, name, &rdata, &flag)); @@ -3157,6 +3150,7 @@ add_nsec3param_records(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, &newtuple)); CHECK(do_one_tuple(&newtuple, db, ver, diff)); } + /* * Remove the temporary add record. */ @@ -3199,11 +3193,9 @@ add_nsec3param_records(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, /* * See if we already have a REMOVE request in progress. */ - dns_rdata_clone(&tuple->rdata, &rdata); - INSIST(rdata.length <= sizeof(buf)); - memcpy(buf, rdata.data, rdata.length); - buf[1] |= DNS_NSEC3FLAG_REMOVE; - rdata.data = buf; + dns_nsec3param_toprivate(&tuple->rdata, &rdata, + privatetype, buf, sizeof(buf)); + buf[2] |= DNS_NSEC3FLAG_REMOVE; CHECK(rr_exists(db, ver, name, &rdata, &flag)); @@ -3227,15 +3219,74 @@ add_nsec3param_records(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, dns_diff_clear(&temp_diff); return (result); } -#endif + +static isc_result_t +rollback_private(dns_db_t *db, dns_rdatatype_t privatetype, + dns_dbversion_t *ver, dns_diff_t *diff) +{ + dns_diff_t temp_diff; + dns_diffop_t op; + dns_difftuple_t *tuple, *newtuple = NULL, *next; + dns_name_t *name = dns_db_origin(db); + isc_mem_t *mctx = diff->mctx; + isc_result_t result; + + if (privatetype == 0) + return (ISC_R_SUCCESS); + + dns_diff_init(mctx, &temp_diff); + + /* + * Extract the changes to be rolled back. + */ + for (tuple = ISC_LIST_HEAD(diff->tuples); + tuple != NULL; + tuple = next) { + + next = ISC_LIST_NEXT(tuple, link); + + if (tuple->rdata.type != privatetype || + !dns_name_equal(name, &tuple->name)) + continue; + + /* + * Allow records which indicate that a zone has been + * signed with a DNSKEY to be be removed. + */ + if (tuple->op == DNS_DIFFOP_DEL && + tuple->rdata.length == 5 && + tuple->rdata.data[0] != 0 && + tuple->rdata.data[4] != 0) + continue; + + ISC_LIST_UNLINK(diff->tuples, tuple, link); + ISC_LIST_PREPEND(temp_diff.tuples, tuple, link); + } + + /* + * Rollback the changes. + */ + while ((tuple = ISC_LIST_HEAD(temp_diff.tuples)) != NULL) { + op = (tuple->op == DNS_DIFFOP_DEL) ? + DNS_DIFFOP_ADD : DNS_DIFFOP_DEL; + CHECK(dns_difftuple_create(mctx, op, name, tuple->ttl, + &tuple->rdata, &newtuple)); + CHECK(do_one_tuple(&newtuple, db, ver, &temp_diff)); + } + result = ISC_R_SUCCESS; + + failure: + dns_diff_clear(&temp_diff); + return (result); +} /* * Add records to cause the delayed signing of the zone by added DNSKEY * to remove the RRSIG records generated by a deleted DNSKEY. */ static isc_result_t -add_signing_records(dns_db_t *db, dns_name_t *name, dns_dbversion_t *ver, - dns_rdatatype_t privatetype, dns_diff_t *diff) +add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype, + dns_dbversion_t *ver, dns_diff_t *diff) { dns_difftuple_t *tuple, *newtuple = NULL; dns_rdata_dnskey_t dnskey; @@ -3245,6 +3296,7 @@ add_signing_records(dns_db_t *db, dns_name_t *name, dns_dbversion_t *ver, isc_result_t result = ISC_R_SUCCESS; isc_uint16_t keyid; unsigned char buf[5]; + dns_name_t *name = dns_db_origin(db); for (tuple = ISC_LIST_HEAD(diff->tuples); tuple != NULL; @@ -3259,6 +3311,7 @@ add_signing_records(dns_db_t *db, dns_name_t *name, dns_dbversion_t *ver, continue; dns_rdata_toregion(&tuple->rdata, &r); + keyid = dst_region_computeid(&r, dnskey.algorithm); buf[0] = dnskey.algorithm; @@ -3295,13 +3348,12 @@ add_signing_records(dns_db_t *db, dns_name_t *name, dns_dbversion_t *ver, return (result); } -#ifdef ALLOW_NSEC3PARAM_UPDATE /* * Mark all NSEC3 chains for deletion without creating a NSEC chain as * a side effect of deleting the last chain. */ static isc_result_t -delete_chains(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, +delete_chains(dns_db_t *db, dns_dbversion_t *ver, dns_zone_t *zone, dns_diff_t *diff) { dns_dbnode_t *node = NULL; @@ -3311,7 +3363,9 @@ delete_chains(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, dns_rdataset_t rdataset; isc_boolean_t flag; isc_result_t result = ISC_R_SUCCESS; - unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE + 1]; + dns_name_t *origin = dns_zone_getorigin(zone); + dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); dns_name_init(&next, NULL); dns_rdataset_init(&rdataset); @@ -3325,6 +3379,47 @@ delete_chains(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, */ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 0, (isc_stdtime_t) 0, &rdataset, NULL); + if (result == ISC_R_NOTFOUND) + goto try_private; + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdata_t private = DNS_RDATA_INIT; + + dns_rdataset_current(&rdataset, &rdata); + + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, origin, + rdataset.ttl, &rdata, &tuple)); + CHECK(do_one_tuple(&tuple, db, ver, diff)); + INSIST(tuple == NULL); + + dns_nsec3param_toprivate(&rdata, &private, privatetype, + buf, sizeof(buf)); + buf[2] = DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC; + + CHECK(rr_exists(db, ver, origin, &rdata, &flag)); + + if (!flag) { + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, + origin, 0, &rdata, &tuple)); + CHECK(do_one_tuple(&tuple, db, ver, diff)); + INSIST(tuple == NULL); + } + dns_rdata_reset(&rdata); + } + if (result != ISC_R_NOMORE) + goto failure; + + dns_rdataset_disassociate(&rdataset); + + try_private: + if (privatetype == 0) + goto success; + result = dns_db_findrdataset(db, node, ver, privatetype, 0, + (isc_stdtime_t) 0, &rdataset, NULL); if (result == ISC_R_NOTFOUND) goto success; if (result != ISC_R_SUCCESS) @@ -3337,18 +3432,18 @@ delete_chains(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, INSIST(rdata.length <= sizeof(buf)); memcpy(buf, rdata.data, rdata.length); - if (buf[1] == (DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC)) { + if (buf[0] != 0 || + buf[2] == (DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC)) { dns_rdata_reset(&rdata); continue; } - CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, - origin, 0, &rdata, &tuple)); + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, origin, + 0, &rdata, &tuple)); CHECK(do_one_tuple(&tuple, db, ver, diff)); INSIST(tuple == NULL); - buf[1] = DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC; - rdata.data = buf; + buf[2] = DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC; CHECK(rr_exists(db, ver, origin, &rdata, &flag)); @@ -3371,7 +3466,20 @@ delete_chains(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, dns_db_detachnode(db, &node); return (result); } -#endif + +static isc_boolean_t +isdnssec(dns_db_t *db, dns_dbversion_t *ver, dns_rdatatype_t privatetype) { + isc_result_t result; + isc_boolean_t build_nsec, build_nsec3; + + if (dns_db_issecure(db)) + return (ISC_TRUE); + + result = dns_private_chains(db, ver, privatetype, + &build_nsec, &build_nsec3); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + return (build_nsec || build_nsec3); +} static void update_action(isc_task_t *task, isc_event_t *event) { @@ -3398,12 +3506,9 @@ update_action(isc_task_t *task, isc_event_t *event) { isc_boolean_t deleted_zsk; dns_difftuple_t *tuple; dns_rdata_dnskey_t dnskey; -#ifdef ALLOW_NSEC3PARAM_UPDATE unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; -#endif -#if !defined(ALLOW_SECURE_TO_INSECURE) || !defined(ALLOW_INSECURE_TO_SECURE) isc_boolean_t had_dnskey; -#endif + dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); INSIST(event->ev_type == DNS_EVENT_UPDATE); @@ -3600,27 +3705,26 @@ update_action(isc_task_t *task, isc_event_t *event) { update_class); FAIL(DNS_R_FORMERR); } + /* * draft-ietf-dnsind-simple-secure-update-01 says * "Unlike traditional dynamic update, the client * is forbidden from updating NSEC records." */ - if (dns_db_issecure(db)) { - if (rdata.type == dns_rdatatype_nsec3) { - FAILC(DNS_R_REFUSED, - "explicit NSEC3 updates are not allowed " - "in secure zones"); - } else if (rdata.type == dns_rdatatype_nsec) { - FAILC(DNS_R_REFUSED, - "explicit NSEC updates are not allowed " - "in secure zones"); - } else if (rdata.type == dns_rdatatype_rrsig && - !dns_name_equal(name, zonename)) { - FAILC(DNS_R_REFUSED, - "explicit RRSIG updates are currently " - "not supported in secure zones except " - "at the apex"); - } + if (rdata.type == dns_rdatatype_nsec3) { + FAILC(DNS_R_REFUSED, + "explicit NSEC3 updates are not allowed " + "in secure zones"); + } else if (rdata.type == dns_rdatatype_nsec) { + FAILC(DNS_R_REFUSED, + "explicit NSEC updates are not allowed " + "in secure zones"); + } else if (rdata.type == dns_rdatatype_rrsig && + !dns_name_equal(name, zonename)) { + FAILC(DNS_R_REFUSED, + "explicit RRSIG updates are currently " + "not supported in secure zones except " + "at the apex"); } if (ssutable != NULL) { @@ -3755,7 +3859,14 @@ update_action(isc_task_t *task, isc_event_t *event) { soa_serial_changed = ISC_TRUE; } -#ifdef ALLOW_NSEC3PARAM_UPDATE + if (rdata.type == privatetype) { + update_log(client, zone, LOGLEVEL_PROTOCOL, + "attempt to add a private type " + "(%u) record rejected internal " + "use only", privatetype); + continue; + } + if (rdata.type == dns_rdatatype_nsec3param) { /* * Ignore attempts to add NSEC3PARAM records @@ -3771,7 +3882,7 @@ update_action(isc_task_t *task, isc_event_t *event) { } /* - * Set the NSEC3CHAIN creation flag. + * NSEC3CHAIN creation flag. */ INSIST(rdata.length <= sizeof(buf)); memcpy(buf, rdata.data, rdata.length); @@ -3782,14 +3893,6 @@ update_action(isc_task_t *task, isc_event_t *event) { */ ttl = 0; } -#else - if (rdata.type == dns_rdatatype_nsec3param) { - update_log(client, zone, LOGLEVEL_PROTOCOL, - "attempt to add NSEC3PARAM " - "record ignored"); - continue; - }; -#endif if ((options & DNS_ZONEOPT_CHECKWILDCARD) != 0 && dns_name_internalwildcard(name)) { @@ -3866,13 +3969,6 @@ update_action(isc_task_t *task, isc_event_t *event) { dns_rdatatype_any, 0, &rdata, &diff)); } -#ifndef ALLOW_NSEC3PARAM_UPDATE - } else if (rdata.type == dns_rdatatype_nsec3param) { - update_log(client, zone, LOGLEVEL_PROTOCOL, - "attempt to delete a NSEC3PARAM " - "records ignored"); - continue; -#endif } else if (dns_name_equal(name, zonename) && (rdata.type == dns_rdatatype_soa || rdata.type == dns_rdatatype_ns)) { @@ -3976,37 +4072,28 @@ update_action(isc_task_t *task, isc_event_t *event) { CHECK(rrset_exists(db, ver, zonename, dns_rdatatype_dnskey, 0, &has_dnskey)); -#if !defined(ALLOW_SECURE_TO_INSECURE) || !defined(ALLOW_INSECURE_TO_SECURE) - CHECK(rrset_exists(db, oldver, zonename, dns_rdatatype_dnskey, - 0, &had_dnskey)); +#define ALLOW_SECURE_TO_INSECURE(zone) \ + ((dns_zone_getoptions(zone) & DNS_ZONEOPT_SECURETOINSECURE) != 0) -#ifndef ALLOW_SECURE_TO_INSECURE - if (had_dnskey && !has_dnskey) { - update_log(client, zone, LOGLEVEL_PROTOCOL, - "update rejected: all DNSKEY records " - "removed"); - result = DNS_R_REFUSED; - goto failure; + if (!ALLOW_SECURE_TO_INSECURE(zone)) { + CHECK(rrset_exists(db, oldver, zonename, + dns_rdatatype_dnskey, 0, + &had_dnskey)); + if (had_dnskey && !has_dnskey) { + update_log(client, zone, LOGLEVEL_PROTOCOL, + "update rejected: all DNSKEY records " + "removed and 'secure-to-insecure' " + "not set"); + result = DNS_R_REFUSED; + goto failure; + } } -#endif -#ifndef ALLOW_INSECURE_TO_SECURE - if (!had_dnskey && has_dnskey) { - update_log(client, zone, LOGLEVEL_PROTOCOL, - "update rejected: DNSKEY record added"); - result = DNS_R_REFUSED; - goto failure; - } -#endif -#endif - CHECK(add_signing_records(db, zonename, ver, - dns_zone_getprivatetype(zone), - &diff)); + CHECK(rollback_private(db, privatetype, ver, &diff)); -#ifdef ALLOW_NSEC3PARAM_UPDATE - CHECK(add_nsec3param_records(client, zone, db, zonename, - ver, &diff)); -#endif + CHECK(add_signing_records(db, privatetype, ver, &diff)); + + CHECK(add_nsec3param_records(client, zone, db, ver, &diff)); if (!has_dnskey) { /* @@ -4015,10 +4102,8 @@ update_action(isc_task_t *task, isc_event_t *event) { * the last signature for the DNSKEY records are * remove any NSEC chain present will also be removed. */ -#ifdef ALLOW_NSEC3PARAM_UPDATE - CHECK(delete_chains(db, ver, zonename, &diff)); -#endif - } else if (has_dnskey && dns_db_isdnssec(db)) { + CHECK(delete_chains(db, ver, zone, &diff)); + } else if (has_dnskey && isdnssec(db, ver, privatetype)) { isc_uint32_t interval; interval = dns_zone_getsigvalidityinterval(zone); result = update_signatures(client, zone, db, oldver, @@ -4109,7 +4194,6 @@ update_action(isc_task_t *task, isc_event_t *event) { } } -#ifdef ALLOW_NSEC3PARAM_UPDATE /* * Cause the zone to add/delete NSEC3 chains for the * deferred NSEC3PARAM changes. @@ -4119,13 +4203,18 @@ update_action(isc_task_t *task, isc_event_t *event) { for (tuple = ISC_LIST_HEAD(diff.tuples); tuple != NULL; tuple = ISC_LIST_NEXT(tuple, link)) { + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_nsec3param_t nsec3param; - if (tuple->rdata.type != dns_rdatatype_nsec3param || + if (tuple->rdata.type != privatetype || tuple->op != DNS_DIFFOP_ADD) continue; - dns_rdata_tostruct(&tuple->rdata, &nsec3param, NULL); + if (!dns_nsec3param_fromprivate(&tuple->rdata, &rdata, + buf, sizeof(buf))) + continue; + dns_rdata_tostruct(&rdata, &nsec3param, NULL); if (nsec3param.flags == 0) continue; @@ -4136,7 +4225,6 @@ update_action(isc_task_t *task, isc_event_t *event) { dns_result_totext(result)); } } -#endif } else { update_log(client, zone, LOGLEVEL_DEBUG, "redundant request"); dns_db_closeversion(db, &ver, ISC_TRUE); diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index f56da44899..0e58d0ad6f 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zoneconf.c,v 1.154 2009/09/01 00:22:25 jinmei Exp $ */ +/* $Id: zoneconf.c,v 1.155 2009/10/08 23:13:06 marka Exp $ */ /*% */ @@ -929,6 +929,12 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, INSIST(0); dns_zone_setoption(zone, DNS_ZONEOPT_WARNSRVCNAME, warn); dns_zone_setoption(zone, DNS_ZONEOPT_IGNORESRVCNAME, ignore); + + obj = NULL; + result = ns_config_get(maps, "secure-to-insecure", &obj); + INSIST(obj != NULL); + dns_zone_setoption(zone, DNS_ZONEOPT_SECURETOINSECURE, + cfg_obj_asboolean(obj)); } /* diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index ee4af5b9d3..7ab3bf1496 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -18,7 +18,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + BIND 9 Administrator Reference Manual @@ -4891,6 +4891,7 @@ badresp:1,adberr:0,findfail:0,valfail:0] allow-update { address_match_list }; allow-update-forwarding { address_match_list }; update-check-ksk yes_or_no; + secure-to-insecure yes_or_no ; try-tcp-refresh yes_or_no; allow-v6-synthesis { address_match_list }; blackhole { address_match_list }; @@ -6442,6 +6443,17 @@ options { + + secure-to-insecure + + + Allow a zone to transition from secure to insecure by + deleting all DNSKEY records. The default is + no. + + + + @@ -9347,6 +9359,7 @@ zone zone_name class allow-transfer { address_match_list }; allow-update-forwarding { address_match_list }; update-check-ksk yes_or_no; + secure-to-insecure yes_or_no ; try-tcp-refresh yes_or_no; also-notify { ip_addr port ip_port ; ip_addr port ip_port ; ... }; @@ -10259,6 +10272,16 @@ zone zone_name class + + secure-to-insecure + + + See the description of + secure-to-insecure in . + + + + diff --git a/lib/bind9/check.c b/lib/bind9/check.c index cb28c9f850..c808adc2b7 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: check.c,v 1.108 2009/09/02 16:10:03 each Exp $ */ +/* $Id: check.c,v 1.109 2009/10/08 23:13:06 marka Exp $ */ /*! \file */ @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -1100,6 +1101,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, { "min-retry-time", SLAVEZONE | STUBZONE }, { "max-refresh-time", SLAVEZONE | STUBZONE }, { "min-refresh-time", SLAVEZONE | STUBZONE }, + { "secure-to-insecure", MASTERZONE }, { "sig-validity-interval", MASTERZONE }, { "sig-re-signing-interval", MASTERZONE }, { "sig-signing-nodes", MASTERZONE }, @@ -1404,6 +1406,9 @@ bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) { const char *algorithm; int i; size_t len = 0; + isc_result_t result; + isc_buffer_t buf; + unsigned char secretbuf[1024]; static const algorithmtable algorithms[] = { { "hmac-md5", 128 }, { "hmac-md5.sig-alg.reg.int", 0 }, @@ -1426,6 +1431,14 @@ bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) { return (ISC_R_FAILURE); } + isc_buffer_init(&buf, secretbuf, sizeof(secretbuf)); + result = isc_base64_decodestring(cfg_obj_asstring(secretobj), &buf); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(secretobj, logctx, ISC_LOG_ERROR, + "bad secret '%s'", isc_result_totext(result)); + return (result); + } + algorithm = cfg_obj_asstring(algobj); for (i = 0; algorithms[i].name != NULL; i++) { len = strlen(algorithms[i].name); diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index d1acc2b35a..548b32ed87 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.167 2009/10/05 17:30:49 fdupont Exp $ +# $Id: Makefile.in,v 1.168 2009/10/08 23:13:06 marka Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -61,7 +61,7 @@ DNSOBJS = acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \ keydata.@O@ keytable.@O@ lib.@O@ log.@O@ lookup.@O@ \ master.@O@ masterdump.@O@ message.@O@ \ name.@O@ ncache.@O@ nsec.@O@ nsec3.@O@ order.@O@ peer.@O@ \ - portlist.@O@ \ + portlist.@O@ private.@O@ \ rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \ rdatalist.@O@ \ rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ request.@O@ \ diff --git a/lib/dns/db.c b/lib/dns/db.c index bc64bd85c7..f1ac004301 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: db.c,v 1.94 2009/09/01 00:22:26 jinmei Exp $ */ +/* $Id: db.c,v 1.95 2009/10/08 23:13:06 marka Exp $ */ /*! \file */ @@ -938,9 +938,9 @@ dns_db_getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *name) } void -dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version) +dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset, + dns_dbversion_t *version) { if (db->methods->resigned != NULL) (db->methods->resigned)(db, rdataset, version); } - diff --git a/lib/dns/include/dns/Makefile.in b/lib/dns/include/dns/Makefile.in index e9e049e298..49a9aeb1b8 100644 --- a/lib/dns/include/dns/Makefile.in +++ b/lib/dns/include/dns/Makefile.in @@ -13,7 +13,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.55 2008/11/14 23:47:33 tbox Exp $ +# $Id: Makefile.in,v 1.56 2009/10/08 23:13:07 marka Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -21,19 +21,17 @@ top_srcdir = @top_srcdir@ @BIND9_VERSION@ -HEADERS = acl.h adb.h byaddr.h cache.h callbacks.h \ - cert.h compress.h \ +HEADERS = acl.h adb.h byaddr.h cache.h callbacks.h cert.h compress.h \ db.h dbiterator.h dbtable.h diff.h dispatch.h dlz.h \ - dnssec.h ds.h events.h fixedname.h iptable.h journal.h keyflags.h \ - keytable.h keyvalues.h lib.h log.h master.h masterdump.h \ - message.h name.h ncache.h \ - nsec.h peer.h portlist.h rbt.h rcode.h \ + dnssec.h ds.h events.h fixedname.h iptable.h journal.h \ + keyflags.h keytable.h keyvalues.h lib.h log.h \ + master.h masterdump.h message.h name.h ncache.h nsec.h \ + peer.h portlist.h private.h rbt.h rcode.h \ rdata.h rdataclass.h rdatalist.h rdataset.h rdatasetiter.h \ rdataslab.h rdatatype.h request.h resolver.h result.h \ rootns.h sdb.h sdlz.h secalg.h secproto.h soa.h ssu.h \ - tcpmsg.h time.h tkey.h \ - tsig.h ttl.h types.h validator.h version.h view.h xfrin.h \ - zone.h zonekey.h zt.h + tcpmsg.h time.h tkey.h tsig.h ttl.h types.h \ + validator.h version.h view.h xfrin.h zone.h zonekey.h zt.h GENHEADERS = enumclass.h enumtype.h rdatastruct.h diff --git a/lib/dns/include/dns/nsec3.h b/lib/dns/include/dns/nsec3.h index 9b5ee9ebf1..905f6c1dd7 100644 --- a/lib/dns/include/dns/nsec3.h +++ b/lib/dns/include/dns/nsec3.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: nsec3.h,v 1.8 2009/10/06 21:20:45 each Exp $ */ +/* $Id: nsec3.h,v 1.9 2009/10/08 23:13:07 marka Exp $ */ #ifndef DNS_NSEC3_H #define DNS_NSEC3_H 1 @@ -110,6 +110,12 @@ isc_result_t dns_nsec3_addnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, dns_ttl_t nsecttl, isc_boolean_t unsecure, dns_diff_t *diff); + +isc_result_t +dns_nsec3_addnsec3sx(dns_db_t *db, dns_dbversion_t *version, + dns_name_t *name, dns_ttl_t nsecttl, + isc_boolean_t unsecure, dns_rdatatype_t private, + dns_diff_t *diff); /*%< * Add NSEC3 records for 'name', recording the change in 'diff'. * Adjust previous NSEC3 records, if any, to reflect the addition. @@ -130,6 +136,10 @@ dns_nsec3_addnsec3s(dns_db_t *db, dns_dbversion_t *version, * NSEC3PARAM record otherwise OPTOUT will be inherited from the previous * record in the chain. * + * dns_nsec3_addnsec3sx() is similar to dns_nsec3_addnsec3s() but 'private' + * specifies the type of the private rdataset to be checked in addition to + * the nsec3param rdataset at the zone apex. + * * Requires: * 'db' to be valid. * 'version' to be valid or NULL. @@ -145,6 +155,10 @@ dns_nsec3_delnsec3(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, isc_result_t dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, dns_diff_t *diff); + +isc_result_t +dns_nsec3_delnsec3sx(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, + dns_rdatatype_t private, dns_diff_t *diff); /*%< * Remove NSEC3 records for 'name', recording the change in 'diff'. * Adjust previous NSEC3 records, if any, to reflect the removal. @@ -156,6 +170,10 @@ dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, * to dns_nsec3_addnsec3s(). Unlike dns_nsec3_addnsec3s() updated NSEC3 * records have the OPTOUT flag preserved. * + * dns_nsec3_delnsec3sx() is similar to dns_nsec3_delnsec3s() but 'private' + * specifies the type of the private rdataset to be checked in addition to + * the nsec3param rdataset at the zone apex. + * * Requires: * 'db' to be valid. * 'version' to be valid or NULL. @@ -167,10 +185,19 @@ dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, isc_result_t dns_nsec3_active(dns_db_t *db, dns_dbversion_t *version, isc_boolean_t complete, isc_boolean_t *answer); + +isc_result_t +dns_nsec3_activex(dns_db_t *db, dns_dbversion_t *version, + isc_boolean_t complete, dns_rdatatype_t private, + isc_boolean_t *answer); /*%< * Check if there are any complete/to be built NSEC3 chains. * If 'complete' is ISC_TRUE only complete chains will be recognized. * + * dns_nsec3_activex() is similar to dns_nsec3_active() but 'private' + * specifies the type of the private rdataset to be checked in addition to + * the nsec3param rdataset at the zone apex. + * * Requires: * 'db' to be valid. * 'version' to be valid or NULL. @@ -191,6 +218,27 @@ dns_nsec3_maxiterations(dns_db_t *db, dns_dbversion_t *version, * 'iterationsp' to be non NULL. */ +isc_boolean_t +dns_nsec3param_fromprivate(dns_rdata_t *src, dns_rdata_t *target, + unsigned char *buf, size_t buflen); +/*%< + * Convert a private rdata to a nsec3param rdata. + * + * Return ISC_TRUE if 'src' could be successfully converted. + * + * 'buf' should be at least DNS_NSEC3PARAM_BUFFERSIZE in size. + */ + +void +dns_nsec3param_toprivate(dns_rdata_t *src, dns_rdata_t *target, + dns_rdatatype_t privatetype, + unsigned char *buf, size_t buflen); +/*%< + * Convert a nsec3param rdata to a private rdata. + * + * 'buf' should be at least src->length + 1 in size. + */ + ISC_LANG_ENDDECLS #endif /* DNS_NSEC3_H */ diff --git a/lib/dns/include/dns/rdata.h b/lib/dns/include/dns/rdata.h index 6051752cb0..d06846b9e9 100644 --- a/lib/dns/include/dns/rdata.h +++ b/lib/dns/include/dns/rdata.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdata.h,v 1.74 2009/09/01 00:22:26 jinmei Exp $ */ +/* $Id: rdata.h,v 1.75 2009/10/08 23:13:07 marka Exp $ */ #ifndef DNS_RDATA_H #define DNS_RDATA_H 1 @@ -125,9 +125,27 @@ struct dns_rdata { #define DNS_RDATA_INIT { NULL, 0, 0, 0, 0, {(void*)(-1), (void *)(-1)}} +#define DNS_RDATA_CHECKINITIALIZED +#ifdef DNS_RDATA_CHECKINITIALIZED +#define DNS_RDATA_INITIALIZED(rdata) \ + ((rdata)->data == NULL && (rdata)->length == 0 && \ + (rdata)->rdclass == 0 && (rdata)->type == 0 && (rdata)->flags == 0 && \ + !ISC_LINK_LINKED((rdata), link)) +#else +#ifdef ISC_LIST_CHECKINIT +#define DNS_RDATA_INITIALIZED(rdata) \ + (!ISC_LINK_LINKED((rdata), link)) +#else +#define DNS_RDATA_INITIALIZED(rdata) ISC_TRUE +#endif +#endif + #define DNS_RDATA_UPDATE 0x0001 /*%< update pseudo record. */ #define DNS_RDATA_OFFLINE 0x0002 /*%< RRSIG has a offline key. */ +#define DNS_RDATA_VALIDFLAGS(rdata) \ + (((rdata)->flags & ~(DNS_RDATA_UPDATE|DNS_RDATA_OFFLINE)) == 0) + /* * Flags affecting rdata formatting style. Flags 0xFFFF0000 * are used by masterfile-level formatting and defined elsewhere. diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 9be1aabb0f..6a1f8b0f33 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.h,v 1.167 2009/10/05 19:39:20 each Exp $ */ +/* $Id: zone.h,v 1.168 2009/10/08 23:13:07 marka Exp $ */ #ifndef DNS_ZONE_H #define DNS_ZONE_H 1 @@ -71,6 +71,7 @@ typedef enum { #define DNS_ZONEOPT_TRYTCPREFRESH 0x01000000U /*%< try tcp refresh on udp failure */ #define DNS_ZONEOPT_NOTIFYTOSOA 0x02000000U /*%< Notify the SOA MNAME */ #define DNS_ZONEOPT_NSEC3TESTZONE 0x04000000U /*%< nsec3-test-zone */ +#define DNS_ZONEOPT_SECURETOINSECURE 0x08000000U /*%< secure-to-insecure */ #ifndef NOMINUM_PUBLIC /* diff --git a/lib/dns/nsec3.c b/lib/dns/nsec3.c index d3209b1017..b087cf0857 100644 --- a/lib/dns/nsec3.c +++ b/lib/dns/nsec3.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: nsec3.c,v 1.8 2009/06/04 02:56:47 tbox Exp $ */ +/* $Id: nsec3.c,v 1.9 2009/10/08 23:13:06 marka Exp $ */ #include @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -457,7 +458,6 @@ delete(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, return (result); } -#ifndef RFC5155_STRICT static isc_boolean_t better_param(dns_rdataset_t *nsec3paramset, dns_rdata_t *param) { dns_rdataset_t rdataset; @@ -472,7 +472,17 @@ better_param(dns_rdataset_t *nsec3paramset, dns_rdata_t *param) { result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) { dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdataset_current(&rdataset, &rdata); + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + + if (rdataset.type != dns_rdatatype_nsec3param) { + dns_rdata_t tmprdata = DNS_RDATA_INIT; + dns_rdataset_current(&rdataset, &tmprdata); + if (!dns_nsec3param_fromprivate(&tmprdata, &rdata, + buf, sizeof(buf))) + continue; + } else + dns_rdataset_current(&rdataset, &rdata); + if (rdata.length != param->length) continue; if (rdata.data[0] != param->data[0] || @@ -490,7 +500,6 @@ better_param(dns_rdataset_t *nsec3paramset, dns_rdata_t *param) { dns_rdataset_disassociate(&rdataset); return (ISC_FALSE); } -#endif static isc_result_t find_nsec3(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *rdataset, @@ -913,18 +922,10 @@ dns_nsec3_addnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(&rdataset, &rdata); - dns_rdata_tostruct(&rdata, &nsec3param, NULL); + CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); -#ifdef RFC5155_STRICT if (nsec3param.flags != 0) continue; -#else - if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) - continue; - if (better_param(&rdataset, &rdata)) - continue; -#endif - /* * We have a active chain. Update it. */ @@ -943,6 +944,158 @@ dns_nsec3_addnsec3s(dns_db_t *db, dns_dbversion_t *version, return (result); } +isc_boolean_t +dns_nsec3param_fromprivate(dns_rdata_t *src, dns_rdata_t *target, + unsigned char *buf, size_t buflen) +{ + dns_decompress_t dctx; + isc_result_t result; + isc_buffer_t buf1; + isc_buffer_t buf2; + + /* + * Algorithm 0 (reserved by RFC 4034) is used to identify + * NSEC3PARAM records from DNSKEY pointers. + */ + if (src->length < 1 || src->data[0] != 0) + return (ISC_FALSE); + + isc_buffer_init(&buf1, src->data + 1, src->length - 1); + isc_buffer_add(&buf1, src->length - 1); + isc_buffer_setactive(&buf1, src->length - 1); + isc_buffer_init(&buf2, buf, buflen); + dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE); + result = dns_rdata_fromwire(target, src->rdclass, + dns_rdatatype_nsec3param, + &buf1, &dctx, 0, &buf2); + dns_decompress_invalidate(&dctx); + + return (ISC_TF(result == ISC_R_SUCCESS)); +} + +void +dns_nsec3param_toprivate(dns_rdata_t *src, dns_rdata_t *target, + dns_rdatatype_t privatetype, + unsigned char *buf, size_t buflen) +{ + REQUIRE(buflen >= src->length + 1); + + REQUIRE(DNS_RDATA_INITIALIZED(target)); + + memcpy(buf + 1, src->data, src->length); + buf[0] = 0; + target->data = buf; + target->length = src->length + 1; + target->type = privatetype; + target->rdclass = src->rdclass; + target->flags = 0; + ISC_LINK_INIT(target, link); +} + +isc_result_t +dns_nsec3_addnsec3sx(dns_db_t *db, dns_dbversion_t *version, + dns_name_t *name, dns_ttl_t nsecttl, + isc_boolean_t unsecure, dns_rdatatype_t type, + dns_diff_t *diff) +{ + dns_dbnode_t *node = NULL; + dns_rdata_nsec3param_t nsec3param; + dns_rdataset_t rdataset; + dns_rdataset_t prdataset; + isc_result_t result; + + dns_rdataset_init(&rdataset); + dns_rdataset_init(&prdataset); + + /* + * Find the NSEC3 parameters for this zone. + */ + result = dns_db_getoriginnode(db, &node); + if (result != ISC_R_SUCCESS) + return (result); + + result = dns_db_findrdataset(db, node, version, type, 0, 0, + &prdataset, NULL); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto failure; + + result = dns_db_findrdataset(db, node, version, + dns_rdatatype_nsec3param, 0, 0, + &rdataset, NULL); + if (result == ISC_R_NOTFOUND) + goto try_private; + if (result != ISC_R_SUCCESS) + goto failure; + + /* + * Update each active NSEC3 chain. + */ + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(&rdataset, &rdata); + CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); + + if (nsec3param.flags != 0) + continue; + + /* + * We have a active chain. Update it. + */ + CHECK(dns_nsec3_addnsec3(db, version, name, &nsec3param, + nsecttl, unsecure, diff)); + } + if (result != ISC_R_NOMORE) + goto failure; + + dns_rdataset_disassociate(&rdataset); + + try_private: + if (!dns_rdataset_isassociated(&prdataset)) + goto success; + /* + * Update each active NSEC3 chain. + */ + for (result = dns_rdataset_first(&prdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&prdataset)) { + dns_rdata_t rdata1 = DNS_RDATA_INIT; + dns_rdata_t rdata2 = DNS_RDATA_INIT; + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + + dns_rdataset_current(&prdataset, &rdata1); + if (!dns_nsec3param_fromprivate(&rdata1, &rdata2, + buf, sizeof(buf))) + continue; + CHECK(dns_rdata_tostruct(&rdata2, &nsec3param, NULL)); + + if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) + continue; + if (better_param(&prdataset, &rdata2)) + continue; + + /* + * We have a active chain. Update it. + */ + CHECK(dns_nsec3_addnsec3(db, version, name, &nsec3param, + nsecttl, unsecure, diff)); + } + if (result == ISC_R_NOMORE) + success: + result = ISC_R_SUCCESS; + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (dns_rdataset_isassociated(&prdataset)) + dns_rdataset_disassociate(&prdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + + return (result); +} + /*% * Determine whether any NSEC3 records that were associated with * 'name' should be deleted or if they should continue to exist. @@ -1241,6 +1394,13 @@ dns_nsec3_delnsec3(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, isc_result_t dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, dns_diff_t *diff) +{ + return (dns_nsec3_delnsec3sx(db, version, name, 0, diff)); +} + +isc_result_t +dns_nsec3_delnsec3sx(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, + dns_rdatatype_t type, dns_diff_t *diff) { dns_dbnode_t *node = NULL; dns_rdata_nsec3param_t nsec3param; @@ -1259,11 +1419,10 @@ dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec3param, 0, 0, &rdataset, NULL); - dns_db_detachnode(db, &node); if (result == ISC_R_NOTFOUND) - return (ISC_R_SUCCESS); + goto try_private; if (result != ISC_R_SUCCESS) - return (result); + goto failure; /* * Update each active NSEC3 chain. @@ -1274,17 +1433,46 @@ dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(&rdataset, &rdata); - dns_rdata_tostruct(&rdata, &nsec3param, NULL); + CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); -#ifdef RFC5155_STRICT if (nsec3param.flags != 0) continue; -#else + /* + * We have a active chain. Update it. + */ + CHECK(dns_nsec3_delnsec3(db, version, name, &nsec3param, diff)); + } + + try_private: + if (type == 0) + goto success; + result = dns_db_findrdataset(db, node, version, type, 0, 0, + &rdataset, NULL); + if (result == ISC_R_NOTFOUND) + goto success; + if (result != ISC_R_SUCCESS) + goto failure; + + /* + * Update each NSEC3 chain being built. + */ + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdata_t rdata1 = DNS_RDATA_INIT; + dns_rdata_t rdata2 = DNS_RDATA_INIT; + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + + dns_rdataset_current(&rdataset, &rdata1); + if (!dns_nsec3param_fromprivate(&rdata1, &rdata2, + buf, sizeof(buf))) + continue; + CHECK(dns_rdata_tostruct(&rdata2, &nsec3param, NULL)); + if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) continue; - if (better_param(&rdataset, &rdata)) + if (better_param(&rdataset, &rdata2)) continue; -#endif /* * We have a active chain. Update it. @@ -1292,6 +1480,7 @@ dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, CHECK(dns_nsec3_delnsec3(db, version, name, &nsec3param, diff)); } if (result == ISC_R_NOMORE) + success: result = ISC_R_SUCCESS; failure: @@ -1306,6 +1495,14 @@ dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, isc_result_t dns_nsec3_active(dns_db_t *db, dns_dbversion_t *version, isc_boolean_t complete, isc_boolean_t *answer) +{ + return (dns_nsec3_activex(db, version, complete, 0, answer)); +} + +isc_result_t +dns_nsec3_activex(dns_db_t *db, dns_dbversion_t *version, + isc_boolean_t complete, dns_rdatatype_t type, + isc_boolean_t *answer) { dns_dbnode_t *node = NULL; dns_rdataset_t rdataset; @@ -1323,14 +1520,14 @@ dns_nsec3_active(dns_db_t *db, dns_dbversion_t *version, result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec3param, 0, 0, &rdataset, NULL); - dns_db_detachnode(db, &node); - if (result == ISC_R_NOTFOUND) { - *answer = ISC_FALSE; - return (ISC_R_SUCCESS); - } - if (result != ISC_R_SUCCESS) + if (result == ISC_R_NOTFOUND) + goto try_private; + + if (result != ISC_R_SUCCESS) { + dns_db_detachnode(db, &node); return (result); + } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) { @@ -1340,17 +1537,61 @@ dns_nsec3_active(dns_db_t *db, dns_dbversion_t *version, result = dns_rdata_tostruct(&rdata, &nsec3param, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if ((nsec3param.flags) == 0 || - (!complete && CREATE(nsec3param.flags))) + if (nsec3param.flags == 0) break; } dns_rdataset_disassociate(&rdataset); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { + dns_db_detachnode(db, &node); *answer = ISC_TRUE; + return (ISC_R_SUCCESS); + } + if (result == ISC_R_NOMORE) + *answer = ISC_FALSE; + + try_private: + if (type == 0 || complete) { + *answer = ISC_FALSE; + return (ISC_R_SUCCESS); + } + result = dns_db_findrdataset(db, node, version, type, 0, 0, + &rdataset, NULL); + + dns_db_detachnode(db, &node); + if (result == ISC_R_NOTFOUND) { + *answer = ISC_FALSE; + return (ISC_R_SUCCESS); + } + if (result != ISC_R_SUCCESS) + return (result); + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdata_t rdata1 = DNS_RDATA_INIT; + dns_rdata_t rdata2 = DNS_RDATA_INIT; + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + + dns_rdataset_current(&rdataset, &rdata1); + if (!dns_nsec3param_fromprivate(&rdata1, &rdata2, + buf, sizeof(buf))) + continue; + result = dns_rdata_tostruct(&rdata2, &nsec3param, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + if (!complete && CREATE(nsec3param.flags)) + break; + } + dns_rdataset_disassociate(&rdataset); + if (result == ISC_R_SUCCESS) { + *answer = ISC_TRUE; + result = ISC_R_SUCCESS; + } if (result == ISC_R_NOMORE) { *answer = ISC_FALSE; result = ISC_R_SUCCESS; } + return (result); } diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index fa1d921e32..0fbbac2f6b 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbtdb.c,v 1.282 2009/10/06 21:20:45 each Exp $ */ +/* $Id: rbtdb.c,v 1.283 2009/10/08 23:13:06 marka Exp $ */ /*! \file */ @@ -613,8 +613,7 @@ static void free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event); static void overmem(dns_db_t *db, isc_boolean_t overmem); #ifdef BIND9 -static void setnsec3parameters(dns_db_t *db, rbtdb_version_t *version, - isc_boolean_t *nsec3createflag); +static void setnsec3parameters(dns_db_t *db, rbtdb_version_t *version); #endif /*% @@ -1925,11 +1924,8 @@ iszonesecure(dns_db_t *db, rbtdb_version_t *version, dns_dbnode_t *origin) { #else dns_rdataset_t keyset; dns_rdataset_t nsecset, signsecset; - dns_rdata_t rdata = DNS_RDATA_INIT; isc_boolean_t haszonekey = ISC_FALSE; isc_boolean_t hasnsec = ISC_FALSE; - isc_boolean_t hasoptbit = ISC_FALSE; - isc_boolean_t nsec3createflag = ISC_FALSE; isc_result_t result; dns_rdataset_init(&keyset); @@ -1961,29 +1957,18 @@ iszonesecure(dns_db_t *db, rbtdb_version_t *version, dns_dbnode_t *origin) { if (result == ISC_R_SUCCESS) { if (dns_rdataset_isassociated(&signsecset)) { hasnsec = ISC_TRUE; - result = dns_rdataset_first(&nsecset); - if (result == ISC_R_SUCCESS) { - dns_rdataset_current(&nsecset, &rdata); - hasoptbit = dns_nsec_typepresent(&rdata, - dns_rdatatype_opt); - } dns_rdataset_disassociate(&signsecset); } dns_rdataset_disassociate(&nsecset); } - setnsec3parameters(db, version, &nsec3createflag); + setnsec3parameters(db, version); /* * Do we have a valid NSEC/NSEC3 chain? */ - if (version->havensec3 || (hasnsec && !hasoptbit)) + if (version->havensec3 || hasnsec) version->secure = dns_db_secure; - /* - * Do we have a NSEC/NSEC3 chain under creation? - */ - else if (hasoptbit || nsec3createflag) - version->secure = dns_db_partial; else version->secure = dns_db_insecure; #endif @@ -1995,9 +1980,7 @@ iszonesecure(dns_db_t *db, rbtdb_version_t *version, dns_dbnode_t *origin) { */ #ifdef BIND9 static void -setnsec3parameters(dns_db_t *db, rbtdb_version_t *version, - isc_boolean_t *nsec3createflag) -{ +setnsec3parameters(dns_db_t *db, rbtdb_version_t *version) { dns_rbtnode_t *node; dns_rdata_nsec3param_t nsec3param; dns_rdata_t rdata = DNS_RDATA_INIT; @@ -2028,7 +2011,7 @@ setnsec3parameters(dns_db_t *db, rbtdb_version_t *version, } while (header != NULL); if (header != NULL && - header->type == dns_rdatatype_nsec3param) { + (header->type == dns_rdatatype_nsec3param)) { /* * Find A NSEC3PARAM with a supported algorithm. */ @@ -2063,17 +2046,8 @@ setnsec3parameters(dns_db_t *db, rbtdb_version_t *version, !dns_nsec3_supportedhash(nsec3param.hash)) continue; -#ifdef RFC5155_STRICT if (nsec3param.flags != 0) continue; -#else - if ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) - != 0) - *nsec3createflag = ISC_TRUE; - if ((nsec3param.flags & ~DNS_NSEC3FLAG_OPTOUT) - != 0) - continue; -#endif memcpy(version->salt, nsec3param.salt, nsec3param.salt_length); diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c index 5e4e471a39..325980021b 100644 --- a/lib/dns/rdata.c +++ b/lib/dns/rdata.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdata.c,v 1.202 2009/09/02 23:48:02 tbox Exp $ */ +/* $Id: rdata.c,v 1.203 2009/10/08 23:13:07 marka Exp $ */ /*! \file */ @@ -276,23 +276,6 @@ dns_rdata_init(dns_rdata_t *rdata) { /* ISC_LIST_INIT(rdata->list); */ } -#if 1 -#define DNS_RDATA_INITIALIZED(rdata) \ - ((rdata)->data == NULL && (rdata)->length == 0 && \ - (rdata)->rdclass == 0 && (rdata)->type == 0 && (rdata)->flags == 0 && \ - !ISC_LINK_LINKED((rdata), link)) -#else -#ifdef ISC_LIST_CHECKINIT -#define DNS_RDATA_INITIALIZED(rdata) \ - (!ISC_LINK_LINKED((rdata), link)) -#else -#define DNS_RDATA_INITIALIZED(rdata) ISC_TRUE -#endif -#endif - -#define DNS_RDATA_VALIDFLAGS(rdata) \ - (((rdata)->flags & ~(DNS_RDATA_UPDATE|DNS_RDATA_OFFLINE)) == 0) - void dns_rdata_reset(dns_rdata_t *rdata) { diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c index 34c3455d06..52c51d9c63 100644 --- a/lib/dns/sdb.c +++ b/lib/dns/sdb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: sdb.c,v 1.70 2009/09/01 00:22:26 jinmei Exp $ */ +/* $Id: sdb.c,v 1.71 2009/10/08 23:13:07 marka Exp $ */ /*! \file */ @@ -1260,7 +1260,7 @@ static dns_dbmethods_t sdb_methods = { NULL, NULL, NULL, - NULL + NULL, }; static isc_result_t diff --git a/lib/dns/win32/libdns.def b/lib/dns/win32/libdns.def index 723a15a32f..803e4db443 100644 --- a/lib/dns/win32/libdns.def +++ b/lib/dns/win32/libdns.def @@ -189,9 +189,9 @@ dns_dlzfindzone dns_dlzregister dns_dlzstrtoargv dns_dlzunregister +dns_dnssec_findmatchingkeys dns_dnssec_findzonekeys dns_dnssec_findzonekeys2 -dns_dnssec_findmatchingkeys dns_dnssec_keyfromrdata dns_dnssec_selfsigns dns_dnssec_sign @@ -343,8 +343,8 @@ dns_name_equal dns_name_format dns_name_free dns_name_fromregion -dns_name_fromtext dns_name_fromstring +dns_name_fromtext dns_name_fromwire dns_name_fullcompare dns_name_getlabel @@ -373,16 +373,21 @@ dns_ncache_add dns_ncache_getrdataset dns_ncache_towire dns_nsec3_active +dns_nsec3_activex dns_nsec3_addnsec3 dns_nsec3_addnsec3s +dns_nsec3_addnsec3sx dns_nsec3_buildrdata dns_nsec3_delnsec3 dns_nsec3_delnsec3s +dns_nsec3_delnsec3sx dns_nsec3_hashlength dns_nsec3_hashname dns_nsec3_maxiterations dns_nsec3_supportedhash dns_nsec3_typepresent +dns_nsec3param_fromprivate +dns_nsec3param_toprivate dns_nsec_build dns_nsec_buildrdata dns_nsec_nseconly @@ -431,6 +436,7 @@ dns_peerlist_peerbyaddr dns_portlist_add dns_portlist_create dns_portlist_detach +dns_private_chains dns_rbt_addname dns_rbt_addnode dns_rbt_create @@ -574,8 +580,8 @@ dns_result_torcode dns_result_totext dns_rootns_create dns_rriterator_current -dns_rriterator_first dns_rriterator_destroy +dns_rriterator_first dns_rriterator_init dns_rriterator_next dns_rriterator_nextrrset diff --git a/lib/dns/win32/libdns.dsp b/lib/dns/win32/libdns.dsp index c1912fcf9c..f736b897c1 100644 --- a/lib/dns/win32/libdns.dsp +++ b/lib/dns/win32/libdns.dsp @@ -266,6 +266,10 @@ SOURCE=..\include\dns\portlist.h # End Source File # Begin Source File +SOURCE=..\include\dns\private.h +# End Source File +# Begin Source File + SOURCE=..\include\dns\rbt.h # End Source File # Begin Source File @@ -562,6 +566,10 @@ SOURCE=..\portlist.c # End Source File # Begin Source File +SOURCE=..\private.c +# End Source File +# Begin Source File + SOURCE=..\rbt.c # End Source File # Begin Source File diff --git a/lib/dns/win32/libdns.mak b/lib/dns/win32/libdns.mak index c18009b02e..2ae1c7cb3f 100644 --- a/lib/dns/win32/libdns.mak +++ b/lib/dns/win32/libdns.mak @@ -166,6 +166,7 @@ CLEAN : -@erase "$(INTDIR)\order.obj" -@erase "$(INTDIR)\peer.obj" -@erase "$(INTDIR)\portlist.obj" + -@erase "$(INTDIR)\private.obj" -@erase "$(INTDIR)\rbt.obj" -@erase "$(INTDIR)\rbtdb.obj" -@erase "$(INTDIR)\rbtdb64.obj" @@ -285,6 +286,7 @@ LINK32_OBJS= \ "$(INTDIR)\order.obj" \ "$(INTDIR)\peer.obj" \ "$(INTDIR)\portlist.obj" \ + "$(INTDIR)\private.obj" \ "$(INTDIR)\rbt.obj" \ "$(INTDIR)\rbtdb.obj" \ "$(INTDIR)\rbtdb64.obj" \ @@ -455,6 +457,8 @@ CLEAN : -@erase "$(INTDIR)\peer.sbr" -@erase "$(INTDIR)\portlist.obj" -@erase "$(INTDIR)\portlist.sbr" + -@erase "$(INTDIR)\private.obj" + -@erase "$(INTDIR)\private.sbr" -@erase "$(INTDIR)\rbt.obj" -@erase "$(INTDIR)\rbt.sbr" -@erase "$(INTDIR)\rbtdb.obj" @@ -606,6 +610,7 @@ BSC32_SBRS= \ "$(INTDIR)\order.sbr" \ "$(INTDIR)\peer.sbr" \ "$(INTDIR)\portlist.sbr" \ + "$(INTDIR)\private.sbr" \ "$(INTDIR)\rbt.sbr" \ "$(INTDIR)\rbtdb.sbr" \ "$(INTDIR)\rbtdb64.sbr" \ @@ -696,6 +701,7 @@ LINK32_OBJS= \ "$(INTDIR)\order.obj" \ "$(INTDIR)\peer.obj" \ "$(INTDIR)\portlist.obj" \ + "$(INTDIR)\private.obj" \ "$(INTDIR)\rbt.obj" \ "$(INTDIR)\rbtdb.obj" \ "$(INTDIR)\rbtdb64.obj" \ @@ -1375,6 +1381,25 @@ SOURCE=..\portlist.c $(CPP) $(CPP_PROJ) $(SOURCE) +!ENDIF + + +SOURCE=..\private.c + +!IF "$(CFG)" == "libdns - Win32 Release" + + +"$(INTDIR)\private.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libdns - Win32 Debug" + + +"$(INTDIR)\private.obj" "$(INTDIR)\portlist.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + !ENDIF SOURCE=..\rbt.c diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 402f866790..c7a431b680 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.509 2009/10/05 23:48:27 tbox Exp $ */ +/* $Id: zone.c,v 1.510 2009/10/08 23:13:07 marka Exp $ */ /*! \file */ @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -637,6 +638,9 @@ static isc_boolean_t dns_zonemgr_unreachable(dns_zonemgr_t *zmgr, isc_time_t *now); static isc_result_t zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, isc_uint16_t keyid, isc_boolean_t delete); +static isc_result_t delete_nsec(dns_db_t *db, dns_dbversion_t *ver, + dns_dbnode_t *node, dns_name_t *name, + dns_diff_t *diff); #define ENTER zone_debuglog(zone, me, 1, "enter") @@ -1763,11 +1767,12 @@ zone_check_mx(dns_zone_t *zone, dns_db_t *db, dns_name_t *name, dns_name_format(name, namebuf, sizeof namebuf); if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || result == DNS_R_EMPTYNAME) { + if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMXFAIL)) + level = ISC_LOG_WARNING; dns_zone_log(zone, level, "%s/MX '%s' has no address records (A or AAAA)", ownerbuf, namebuf); - /* XXX950 make fatal for 9.5.0. */ - return (ISC_TRUE); + return ((level == ISC_LOG_WARNING) ? ISC_TRUE : ISC_FALSE); } if (result == DNS_R_CNAME) { @@ -2212,15 +2217,18 @@ resume_signingwithkey(dns_zone_t *zone) { zone->privatetype, dns_rdatatype_none, 0, &rdataset, NULL); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); goto cleanup; + } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) { dns_rdataset_current(&rdataset, &rdata); - if (rdata.length != 5 || rdata.data[4] != 0) { + if (rdata.length != 5 || + rdata.data[0] == 0 || rdata.data[4] != 0) { dns_rdata_reset(&rdata); continue; } @@ -2242,7 +2250,6 @@ resume_signingwithkey(dns_zone_t *zone) { dns_db_detachnode(zone->db, &node); if (version != NULL) dns_db_closeversion(zone->db, &version, ISC_FALSE); - } static isc_result_t @@ -2251,6 +2258,9 @@ zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) { isc_result_t result; isc_time_t now; unsigned int options = 0; + char saltbuf[255*2+1]; + char flags[sizeof("REMOVE|CREATE|NONSEC|OPTOUT")]; + int i; nsec3chain = isc_mem_get(zone->mctx, sizeof *nsec3chain); if (nsec3chain == NULL) @@ -2272,6 +2282,40 @@ zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) { nsec3chain->delete_nsec = ISC_FALSE; nsec3chain->save_delete_nsec = ISC_FALSE; + if (nsec3param->flags == 0) + strlcpy(flags, "NONE", sizeof(flags)); + else { + flags[0] = '\0'; + if (nsec3param->flags & DNS_NSEC3FLAG_REMOVE) + strlcat(flags, "REMOVE", sizeof(flags)); + if (nsec3param->flags & DNS_NSEC3FLAG_CREATE) { + if (flags[0] == '\0') + strlcpy(flags, "CREATE", sizeof(flags)); + else + strlcat(flags, "|CREATE", sizeof(flags)); + } + if (nsec3param->flags & DNS_NSEC3FLAG_NONSEC) { + if (flags[0] == '\0') + strlcpy(flags, "NONSEC", sizeof(flags)); + else + strlcat(flags, "|NONSEC", sizeof(flags)); + } + if (nsec3param->flags & DNS_NSEC3FLAG_OPTOUT) { + if (flags[0] == '\0') + strlcpy(flags, "OPTOUT", sizeof(flags)); + else + strlcat(flags, "|OPTOUT", sizeof(flags)); + } + } + if (nsec3param->salt_length == 0) + strlcpy(saltbuf, "-", sizeof(saltbuf)); + else + for (i = 0; i < nsec3param->salt_length; i++) + sprintf(&saltbuf[i*2], "%02X", nsec3chain->salt[i]); + dns_zone_log(zone, ISC_LOG_INFO, + "zone_addnsec3chain(%u,%s,%u,%s)\n", + nsec3param->hash, flags, nsec3param->iterations, + saltbuf); for (current = ISC_LIST_HEAD(zone->nsec3chain); current != NULL; current = ISC_LIST_NEXT(current, link)) { @@ -2321,11 +2365,13 @@ static void resume_addnsec3chain(dns_zone_t *zone) { dns_dbnode_t *node = NULL; dns_dbversion_t *version = NULL; - dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_t rdataset; isc_result_t result; dns_rdata_nsec3param_t nsec3param; + if (zone->privatetype == 0) + return; + result = dns_db_findnode(zone->db, &zone->origin, ISC_FALSE, &node); if (result != ISC_R_SUCCESS) goto cleanup; @@ -2333,17 +2379,25 @@ resume_addnsec3chain(dns_zone_t *zone) { dns_db_currentversion(zone->db, &version); dns_rdataset_init(&rdataset); result = dns_db_findrdataset(zone->db, node, version, - dns_rdatatype_nsec3param, - dns_rdatatype_none, 0, - &rdataset, NULL); - if (result != ISC_R_SUCCESS) + zone->privatetype, dns_rdatatype_none, + 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); goto cleanup; + } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) { - dns_rdataset_current(&rdataset, &rdata); + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_t private = DNS_RDATA_INIT; + + dns_rdataset_current(&rdataset, &private); + if (!dns_nsec3param_fromprivate(&private, &rdata, buf, + sizeof(buf))) + continue; result = dns_rdata_tostruct(&rdata, &nsec3param, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); if ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0 || @@ -2355,10 +2409,8 @@ resume_addnsec3chain(dns_zone_t *zone) { dns_result_totext(result)); } } - dns_rdata_reset(&rdata); } dns_rdataset_disassociate(&rdataset); - cleanup: if (node != NULL) dns_db_detachnode(zone->db, &node); @@ -2417,10 +2469,12 @@ check_nsec3param(dns_zone_t *zone, dns_db_t *db) { dns_rdatatype_nsec3param, dns_rdatatype_none, 0, &rdataset, NULL); if (result == ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&rdataset)); result = ISC_R_SUCCESS; goto cleanup; } if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); dns_zone_log(zone, ISC_LOG_ERROR, "nsec3param lookup failure: %s", dns_result_totext(result)); @@ -3509,10 +3563,14 @@ zone_count_ns_rr(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node, dns_rdataset_init(&rdataset); result = dns_db_findrdataset(db, node, version, dns_rdatatype_ns, dns_rdatatype_none, 0, &rdataset, NULL); - if (result == ISC_R_NOTFOUND) + if (result == ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&rdataset)); goto success; - if (result != ISC_R_SUCCESS) + } + if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); goto invalidate_rdataset; + } result = dns_rdataset_first(&rdataset); while (result == ISC_R_SUCCESS) { @@ -3563,6 +3621,7 @@ zone_load_soa_rr(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa, dns_rdatatype_none, 0, &rdataset, NULL); if (result == ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&rdataset)); if (soacount != NULL) *soacount = 0; if (serial != NULL) @@ -3578,8 +3637,10 @@ zone_load_soa_rr(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, result = ISC_R_SUCCESS; goto invalidate_rdataset; } - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); goto invalidate_rdataset; + } count = 0; result = dns_rdataset_first(&rdataset); @@ -4339,10 +4400,14 @@ del_sigs(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, (isc_stdtime_t) 0, &rdataset, NULL); dns_db_detachnode(db, &node); - if (result == ISC_R_NOTFOUND) + if (result == ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&rdataset)); return (ISC_R_SUCCESS); - if (result != ISC_R_SUCCESS) + } + if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); goto failure; + } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; @@ -4451,10 +4516,14 @@ add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, result = dns_db_findrdataset(db, node, ver, type, 0, (isc_stdtime_t) 0, &rdataset, NULL); dns_db_detachnode(db, &node); - if (result == ISC_R_NOTFOUND) + if (result == ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&rdataset)); return (ISC_R_SUCCESS); - if (result != ISC_R_SUCCESS) + } + if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); goto failure; + } for (i = 0; i < nkeys; i++) { if (check_ksk && type != dns_rdatatype_dnskey && @@ -4714,16 +4783,6 @@ next_active(dns_db_t *db, dns_dbversion_t *version, dns_name_t *oldname, return (result); } -static void -set_bit(unsigned char *array, unsigned int index) { - unsigned int shift, mask; - - shift = 7 - (index % 8); - mask = 1 << shift; - - array[index / 8] |= mask; -} - static isc_boolean_t signed_with_key(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, dns_rdatatype_t type, dst_key_t *key) @@ -4736,8 +4795,10 @@ signed_with_key(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, dns_rdataset_init(&rdataset); result = dns_db_findrdataset(db, node, version, dns_rdatatype_rrsig, type, 0, &rdataset, NULL); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); return (ISC_FALSE); + } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) { @@ -4772,21 +4833,6 @@ add_nsec(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, CHECK(next_active(db, version, name, next, bottom)); CHECK(dns_nsec_buildrdata(db, version, node, next, nsecbuffer, &rdata)); - if (dns_name_equal(dns_db_origin(db), name)) { - /* - * Set the OPT bit to indicate that this is a - * partially secure zone. - */ - isc_region_t region; - - dns_rdata_toregion(&rdata, ®ion); - dns_name_fromregion(next, ®ion); - isc_region_consume(®ion, next->length); - INSIST(region.length > (2 + dns_rdatatype_opt / 8) && - region.base[0] == 0 && - region.base[1] > dns_rdatatype_opt / 8); - set_bit(region.base + 2, dns_rdatatype_opt); - } CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, ttl, &rdata)); failure: @@ -4838,7 +4884,8 @@ sign_a_node(dns_db_t *db, dns_name_t *name, dns_dbnode_t *node, seen_nsec = ISC_TRUE; else if (rdataset.type == dns_rdatatype_nsec3) seen_nsec3 = ISC_TRUE; - seen_rr = ISC_TRUE; + if (rdataset.type != dns_rdatatype_rrsig) + seen_rr = ISC_TRUE; dns_rdataset_disassociate(&rdataset); } if (result != ISC_R_NOMORE) @@ -4862,9 +4909,15 @@ sign_a_node(dns_db_t *db, dns_name_t *name, dns_dbnode_t *node, if (build_nsec && !seen_nsec3 && !seen_nsec && seen_rr) { /* Build and add NSEC. */ bottom = (seen_ns && !seen_soa) || seen_dname; - CHECK(add_nsec(db, version, name, node, minimum, bottom, diff)); - /* Count a NSEC generation as a signature generation. */ - (*signatures)--; + /* + * Build a NSEC record except at the origin. + */ + if (!dns_name_equal(name, dns_db_origin(db))) { + CHECK(add_nsec(db, version, name, node, minimum, + bottom, diff)); + /* Count a NSEC generation as a signature generation. */ + (*signatures)--; + } } result = dns_rdatasetiter_first(iterator); while (result == ISC_R_SUCCESS) { @@ -4905,63 +4958,49 @@ failure: return (result); } +/* + * If 'update_only' is set then don't create a NSEC RRset if it doesn't exist. + */ static isc_result_t updatesecure(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, - dns_ttl_t minimum, isc_boolean_t *secureupdated, dns_diff_t *diff) + dns_ttl_t minimum, isc_boolean_t update_only, + isc_boolean_t *secureupdated, dns_diff_t *diff) { isc_result_t result; - dns_rdata_t rdata = DNS_RDATA_INIT; - unsigned char nsecbuffer[DNS_NSEC_BUFFERSIZE]; dns_rdataset_t rdataset; - dns_rdata_nsec_t nsec; dns_dbnode_t *node = NULL; - /* - * Check to see if the OPT bit has already been cleared. - */ CHECK(dns_db_getoriginnode(db, &node)); - dns_rdataset_init(&rdataset); - CHECK(dns_db_findrdataset(db, node, version, dns_rdatatype_nsec, - dns_rdatatype_none, 0, &rdataset, NULL)); - CHECK(dns_rdataset_first(&rdataset)); - dns_rdataset_current(&rdataset, &rdata); - - /* - * Find the NEXT name for building the new record. - */ - CHECK(dns_rdata_tostruct(&rdata, &nsec, NULL)); - - /* - * Delete the old NSEC record. - */ - CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_DEL, name, minimum, - &rdata)); - dns_rdata_reset(&rdata); - - /* - * Add the new NSEC record. - */ - CHECK(dns_nsec_buildrdata(db, version, node, &nsec.next, nsecbuffer, - &rdata)); - CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, minimum, - &rdata)); - dns_rdata_reset(&rdata); - + if (update_only) { + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, version, + dns_rdatatype_nsec, + dns_rdatatype_none, + 0, &rdataset, NULL); + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + goto done; + } + if (result != ISC_R_SUCCESS) + goto failure; + } + CHECK(delete_nsec(db, version, node, name, diff)); + CHECK(add_nsec(db, version, name, node, minimum, ISC_FALSE, diff)); + done: if (secureupdated != NULL) *secureupdated = ISC_TRUE; failure: if (node != NULL) dns_db_detachnode(db, &node); - if (dns_rdataset_isassociated(&rdataset)) - dns_rdataset_disassociate(&rdataset); return (result); } static isc_result_t -updatesignwithkey(dns_signing_t *signing, dns_dbversion_t *version, - dns_name_t *name, dns_rdatatype_t privatetype, - dns_diff_t *diff) +updatesignwithkey(dns_zone_t *zone, dns_signing_t *signing, + dns_dbversion_t *version, dns_diff_t *diff) { isc_result_t result; dns_dbnode_t *node = NULL; @@ -4975,14 +5014,18 @@ updatesignwithkey(dns_signing_t *signing, dns_dbversion_t *version, if (result != ISC_R_SUCCESS) goto failure; - result = dns_db_findrdataset(signing->db, node, version, privatetype, - dns_rdatatype_none, 0, &rdataset, NULL); + result = dns_db_findrdataset(signing->db, node, version, + zone->privatetype, dns_rdatatype_none, + 0, &rdataset, NULL); if (result == ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&rdataset)); result = ISC_R_SUCCESS; goto failure; } - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { + INSIST(!dns_rdataset_isassociated(&rdataset)); goto failure; + } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) { @@ -4998,7 +5041,7 @@ updatesignwithkey(dns_signing_t *signing, dns_dbversion_t *version, seen_done = ISC_TRUE; else CHECK(update_one_rr(signing->db, version, diff, - DNS_DIFFOP_DEL, name, + DNS_DIFFOP_DEL, &zone->origin, rdataset.ttl, &rdata)); dns_rdata_reset(&rdata); } @@ -5013,10 +5056,10 @@ updatesignwithkey(dns_signing_t *signing, dns_dbversion_t *version, data[4] = 1; rdata.length = sizeof(data); rdata.data = data; - rdata.type = privatetype; + rdata.type = zone->privatetype; rdata.rdclass = dns_db_class(signing->db); CHECK(update_one_rr(signing->db, version, diff, DNS_DIFFOP_ADD, - name, rdataset.ttl, &rdata)); + &zone->origin, rdataset.ttl, &rdata)); } failure: if (dns_rdataset_isassociated(&rdataset)) @@ -5026,9 +5069,15 @@ updatesignwithkey(dns_signing_t *signing, dns_dbversion_t *version, return (result); } +/* + * If 'active' is set then we are not done with the chain yet so only + * delete the nsec3param record which indicates a full chain exists + * (flags == 0). + */ static isc_result_t fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain, - isc_boolean_t active, dns_diff_t *diff) + isc_boolean_t active, dns_rdatatype_t privatetype, + dns_diff_t *diff) { dns_dbnode_t *node = NULL; dns_name_t *name = dns_db_origin(db); @@ -5047,7 +5096,7 @@ fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain, result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 0, 0, &rdataset, NULL); if (result == ISC_R_NOTFOUND) - goto add; + goto try_private; if (result != ISC_R_SUCCESS) goto failure; @@ -5083,6 +5132,50 @@ fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain, if (result != ISC_R_NOMORE) goto failure; + dns_rdataset_disassociate(&rdataset); + + try_private: + + if (active) + goto add; + /* + * Delete all private records which match that in nsec3chain. + */ + result = dns_db_findrdataset(db, node, ver, privatetype, + 0, 0, &rdataset, NULL); + if (result == ISC_R_NOTFOUND) + goto add; + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdata_t private = DNS_RDATA_INIT; + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + + dns_rdataset_current(&rdataset, &private); + if (!dns_nsec3param_fromprivate(&private, &rdata, + buf, sizeof(buf))) + continue; + CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); + + if (nsec3param.hash != chain->nsec3param.hash || + nsec3param.iterations != chain->nsec3param.iterations || + nsec3param.salt_length != chain->nsec3param.salt_length || + memcmp(nsec3param.salt, chain->nsec3param.salt, + nsec3param.salt_length)) { + dns_rdata_reset(&rdata); + continue; + } + + CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, + name, rdataset.ttl, &private)); + dns_rdata_reset(&rdata); + } + if (result != ISC_R_NOMORE) + goto failure; + add: if ((chain->nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) { result = ISC_R_SUCCESS; @@ -5123,7 +5216,7 @@ delete_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node, 0, 0, &rdataset, NULL); if (result == ISC_R_NOTFOUND) return (ISC_R_SUCCESS); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) return (result); for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; @@ -5183,7 +5276,7 @@ deletematchingnsec3(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node, static isc_result_t need_nsec_chain(dns_db_t *db, dns_dbversion_t *ver, const dns_rdata_nsec3param_t *param, - isc_boolean_t *answer, isc_boolean_t *updatensec) + isc_boolean_t *answer) { dns_dbnode_t *node = NULL; dns_rdata_t rdata = DNS_RDATA_INIT; @@ -5197,29 +5290,19 @@ need_nsec_chain(dns_db_t *db, dns_dbversion_t *ver, RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, 0, 0, &rdataset, NULL); - if (result == ISC_R_NOTFOUND) - goto check_nsec3param; - - if (result != ISC_R_SUCCESS) - goto failure; - - CHECK(dns_rdataset_first(&rdataset)); - dns_rdataset_current(&rdataset, &rdata); - - if (!dns_nsec_typepresent(&rdata, dns_rdatatype_opt)) { - /* - * We have a complete NSEC chain. Signal to update - * the apex NSEC record. - */ - *updatensec = ISC_TRUE; - goto failure; + if (result == ISC_R_SUCCESS) { + dns_rdataset_disassociate(&rdataset); + dns_db_detachnode(db, &node); + return (result); + } + if (result != ISC_R_NOTFOUND) { + dns_db_detachnode(db, &node); + return (result); } - dns_rdataset_disassociate(&rdataset); - dns_rdata_reset(&rdata); - check_nsec3param: result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 0, 0, &rdataset, NULL); if (result == ISC_R_NOTFOUND) { @@ -5268,6 +5351,53 @@ need_nsec_chain(dns_db_t *db, dns_dbversion_t *ver, return (result); } +static isc_result_t +update_sigs(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *version, + dst_key_t *zone_keys[], unsigned int nkeys, dns_zone_t *zone, + isc_stdtime_t inception, isc_stdtime_t expire, isc_stdtime_t now, + isc_boolean_t check_ksk, dns_diff_t *sig_diff) +{ + dns_difftuple_t *tuple; + isc_result_t result; + + for (tuple = ISC_LIST_HEAD(diff->tuples); + tuple != NULL; + tuple = ISC_LIST_HEAD(diff->tuples)) { + result = del_sigs(zone, db, version, &tuple->name, + tuple->rdata.type, sig_diff, + zone_keys, nkeys, now); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "update_sigs:del_sigs -> %s\n", + dns_result_totext(result)); + return (result); + } + result = add_sigs(db, version, &tuple->name, + tuple->rdata.type, sig_diff, + zone_keys, nkeys, zone->mctx, inception, + expire, check_ksk); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "update_sigs:add_sigs -> %s\n", + dns_result_totext(result)); + return (result); + } + + do { + dns_difftuple_t *next = ISC_LIST_NEXT(tuple, link); + while (next != NULL && + (tuple->rdata.type != next->rdata.type || + !dns_name_equal(&tuple->name, &next->name))) + next = ISC_LIST_NEXT(next, link); + ISC_LIST_UNLINK(diff->tuples, tuple, link); + dns_diff_appendminimal(sig_diff, &tuple); + INSIST(tuple == NULL); + tuple = next; + } while (tuple != NULL); + } + return (ISC_R_SUCCESS); +} + /* * Incrementally build and sign a new NSEC3 chain using the parameters * requested. @@ -5302,9 +5432,9 @@ zone_nsec3chain(dns_zone_t *zone) { isc_boolean_t seen_soa, seen_ns, seen_dname, seen_ds; isc_boolean_t seen_nsec, seen_nsec3, seen_rr; dns_rdatasetiter_t *iterator = NULL; - dns_difftuple_t *tuple; isc_boolean_t buildnsecchain; isc_boolean_t updatensec = ISC_FALSE; + dns_rdatatype_t privatetype = zone->privatetype; dns_rdataset_init(&rdataset); dns_fixedname_init(&fixed); @@ -5486,9 +5616,17 @@ zone_nsec3chain(dns_zone_t *zone) { * Process one node. */ dns_dbiterator_pause(nsec3chain->dbiterator); - CHECK(dns_nsec3_addnsec3(db, version, name, - &nsec3chain->nsec3param, - zone->minimum, unsecure, &nsec3_diff)); + result = dns_nsec3_addnsec3(db, version, name, + &nsec3chain->nsec3param, + zone->minimum, unsecure, + &nsec3_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:" + "dns_nsec3_addnsec3 -> %s\n", + dns_result_totext(result)); + goto failure; + } + /* * Treat each call to dns_nsec3_addnsec3() as if it's cost is * two signatures. Additionally there will, in general, be @@ -5510,7 +5648,8 @@ zone_nsec3chain(dns_zone_t *zone) { if (result == ISC_R_NOMORE && nsec3chain->delete_nsec) { CHECK(fixup_nsec3param(db, version, nsec3chain, - ISC_FALSE, ¶m_diff)); + ISC_FALSE, privatetype, + ¶m_diff)); LOCK_ZONE(zone); ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain, link); @@ -5524,12 +5663,14 @@ zone_nsec3chain(dns_zone_t *zone) { CHECK(fixup_nsec3param(db, version, nsec3chain, ISC_TRUE, + privatetype, ¶m_diff)); nsec3chain->delete_nsec = ISC_TRUE; goto same_addchain; } CHECK(fixup_nsec3param(db, version, nsec3chain, - ISC_FALSE, ¶m_diff)); + ISC_FALSE, privatetype, + ¶m_diff)); LOCK_ZONE(zone); ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain, link); @@ -5590,10 +5731,22 @@ zone_nsec3chain(dns_zone_t *zone) { * of removing this NSEC3 chain. */ if (first && !updatensec && - (nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_NONSEC) == 0) - CHECK(need_nsec_chain(db, version, - &nsec3chain->nsec3param, - &buildnsecchain, &updatensec)); + (nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_NONSEC) == 0) { + result = need_nsec_chain(db, version, + &nsec3chain->nsec3param, + &buildnsecchain); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_nsec3chain:" + "need_nsec_chain -> %s\n", + dns_result_totext(result)); + goto failure; + } + } + + if (first) + dns_zone_log(zone, ISC_LOG_DEBUG(3), "zone_nsec3chain:" + "buildnsecchain = %u\n", buildnsecchain); dns_dbiterator_current(nsec3chain->dbiterator, &node, name); delegation = ISC_FALSE; @@ -5602,16 +5755,33 @@ zone_nsec3chain(dns_zone_t *zone) { /* * Delete the NSECPARAM record that matches this chain. */ - if (first) - CHECK(fixup_nsec3param(db, version, nsec3chain, - ISC_TRUE, ¶m_diff)); + if (first) { + result = fixup_nsec3param(db, version, + nsec3chain, + ISC_TRUE, privatetype, + ¶m_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_nsec3chain:" + "fixup_nsec3param -> %s\n", + dns_result_totext(result)); + goto failure; + } + } /* * Delete the NSEC3 records. */ - CHECK(deletematchingnsec3(db, version, node, name, - &nsec3chain->nsec3param, - &nsec3_diff)); + result = deletematchingnsec3(db, version, node, name, + &nsec3chain->nsec3param, + &nsec3_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_nsec3chain:" + "deletematchingnsec3 -> %s\n", + dns_result_totext(result)); + goto failure; + } goto next_removenode; } @@ -5662,7 +5832,8 @@ zone_nsec3chain(dns_zone_t *zone) { seen_nsec = ISC_TRUE; else if (rdataset.type == dns_rdatatype_nsec3) seen_nsec3 = ISC_TRUE; - seen_rr = ISC_TRUE; + if (rdataset.type != dns_rdatatype_rrsig) + seen_rr = ISC_TRUE; dns_rdataset_disassociate(&rdataset); } dns_rdatasetiter_destroy(&iterator); @@ -5672,8 +5843,12 @@ zone_nsec3chain(dns_zone_t *zone) { if ((seen_ns && !seen_soa) || seen_dname) delegation = ISC_TRUE; - CHECK(add_nsec(db, version, name, node, zone->minimum, - delegation, &nsec_diff)); + /* + * Add a NSEC record except at the origin. + */ + if (!dns_name_equal(name, dns_db_origin(db))) + CHECK(add_nsec(db, version, name, node, zone->minimum, + delegation, &nsec_diff)); next_removenode: first = ISC_FALSE; @@ -5695,8 +5870,17 @@ zone_nsec3chain(dns_zone_t *zone) { UNLOCK_ZONE(zone); ISC_LIST_APPEND(cleanup, nsec3chain, link); dns_dbiterator_pause(nsec3chain->dbiterator); - CHECK(fixup_nsec3param(db, version, nsec3chain, - ISC_FALSE, ¶m_diff)); + result = fixup_nsec3param(db, version, + nsec3chain, ISC_FALSE, + privatetype, + ¶m_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_nsec3chain:" + "fixup_nsec3param -> %s\n", + dns_result_totext(result)); + goto failure; + } goto next_removechain; } else if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -5727,108 +5911,98 @@ zone_nsec3chain(dns_zone_t *zone) { first = ISC_TRUE; } + /* + * We may need to update the NSEC/NSEC3 records for the zone apex. + */ + if (!ISC_LIST_EMPTY(param_diff.tuples)) { + isc_boolean_t rebuild_nsec = ISC_FALSE, + rebuild_nsec3 = ISC_FALSE; + result = dns_db_getoriginnode(db, &node); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + result = dns_db_allrdatasets(db, node, version, 0, &iterator); + for (result = dns_rdatasetiter_first(iterator); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iterator)) { + dns_rdatasetiter_current(iterator, &rdataset); + if (rdataset.type == dns_rdatatype_nsec) + rebuild_nsec = ISC_TRUE; + if (rdataset.type == dns_rdatatype_nsec3param) + rebuild_nsec3 = ISC_TRUE; + dns_rdataset_disassociate(&rdataset); + } + dns_rdatasetiter_destroy(&iterator); + dns_db_detachnode(db, &node); + if (rebuild_nsec) { + result = updatesecure(db, version, &zone->origin, + zone->minimum, ISC_TRUE, NULL, + &nsec_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_nsec3chain:" + "updatesecure -> %s\n", + dns_result_totext(result)); + goto failure; + } + } + if (rebuild_nsec3) { + result = dns_nsec3_addnsec3s(db, version, + dns_db_origin(db), + zone->minimum, ISC_FALSE, + &nsec3_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_nsec3chain:" + "dns_nsec3_addnsec3s -> %s\n", + dns_result_totext(result)); + goto failure; + } + } + } + /* * Add / update signatures for the NSEC3 records. */ - for (tuple = ISC_LIST_HEAD(nsec3_diff.tuples); - tuple != NULL; - tuple = ISC_LIST_HEAD(nsec3_diff.tuples)) { - /* - * We have changed the NSEC3 RRset above so we need to update - * the signatures. - */ - result = del_sigs(zone, db, version, &tuple->name, - dns_rdatatype_nsec3, &sig_diff, - zone_keys, nkeys, now); - if (result != ISC_R_SUCCESS) { - dns_zone_log(zone, ISC_LOG_ERROR, - "zone_nsec3chain:del_sigs -> %s\n", - dns_result_totext(result)); - goto failure; - } - result = add_sigs(db, version, &tuple->name, - dns_rdatatype_nsec3, &sig_diff, zone_keys, - nkeys, zone->mctx, inception, expire, - check_ksk); - if (result != ISC_R_SUCCESS) { - dns_zone_log(zone, ISC_LOG_ERROR, - "zone_nsec3chain:add_sigs -> %s\n", - dns_result_totext(result)); - goto failure; - } - - do { - dns_difftuple_t *next = ISC_LIST_NEXT(tuple, link); - while (next != NULL && - !dns_name_equal(&tuple->name, &next->name)) - next = ISC_LIST_NEXT(next, link); - ISC_LIST_UNLINK(nsec3_diff.tuples, tuple, link); - dns_diff_appendminimal(&sig_diff, &tuple); - INSIST(tuple == NULL); - tuple = next; - } while (tuple != NULL); + result = update_sigs(&nsec3_diff, db, version, zone_keys, + nkeys, zone, inception, expire, now, + check_ksk, &sig_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:" + "update_sigs -> %s\n", dns_result_totext(result)); + goto failure; } - for (tuple = ISC_LIST_HEAD(param_diff.tuples); - tuple != NULL; - tuple = ISC_LIST_HEAD(param_diff.tuples)) { - /* - * We have changed the NSEC3PARAM RRset above so we need to - * update the signatures. - */ - result = del_sigs(zone, db, version, &tuple->name, - dns_rdatatype_nsec3param, &sig_diff, - zone_keys, nkeys, now); - if (result != ISC_R_SUCCESS) { - dns_zone_log(zone, ISC_LOG_ERROR, - "zone_nsec3chain:del_sigs -> %s\n", - dns_result_totext(result)); - goto failure; - } - result = add_sigs(db, version, &tuple->name, - dns_rdatatype_nsec3param, &sig_diff, - zone_keys, nkeys, zone->mctx, inception, - expire, check_ksk); - if (result != ISC_R_SUCCESS) { - dns_zone_log(zone, ISC_LOG_ERROR, - "zone_nsec3chain:add_sigs -> %s\n", - dns_result_totext(result)); - goto failure; - } - ISC_LIST_UNLINK(param_diff.tuples, tuple, link); - dns_diff_appendminimal(&sig_diff, &tuple); - INSIST(tuple == NULL); + /* + * We have changed the NSEC3PARAM or private RRsets + * above so we need to update the signatures. + */ + result = update_sigs(¶m_diff, db, version, zone_keys, + nkeys, zone, inception, expire, now, + check_ksk, &sig_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:" + "update_sigs -> %s\n", dns_result_totext(result)); + goto failure; } - if (updatensec) - CHECK(updatesecure(db, version, &zone->origin, zone->minimum, - NULL, &nsec_diff)); + if (updatensec) { + result = updatesecure(db, version, &zone->origin, + zone->minimum, ISC_FALSE, NULL, + &nsec_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:" + "updatesecure -> %s\n", + dns_result_totext(result)); + goto failure; + } + } - for (tuple = ISC_LIST_HEAD(nsec_diff.tuples); - tuple != NULL; - tuple = ISC_LIST_HEAD(nsec_diff.tuples)) { - result = del_sigs(zone, db, version, &tuple->name, - dns_rdatatype_nsec, &sig_diff, - zone_keys, nkeys, now); - if (result != ISC_R_SUCCESS) { - dns_zone_log(zone, ISC_LOG_ERROR, - "zone_nsec3chain:del_sigs -> %s\n", - dns_result_totext(result)); - goto failure; - } - result = add_sigs(db, version, &tuple->name, - dns_rdatatype_nsec, &sig_diff, - zone_keys, nkeys, zone->mctx, inception, - expire, check_ksk); - if (result != ISC_R_SUCCESS) { - dns_zone_log(zone, ISC_LOG_ERROR, - "zone_nsec3chain:add_sigs -> %s\n", - dns_result_totext(result)); - goto failure; - } - ISC_LIST_UNLINK(nsec_diff.tuples, tuple, link); - dns_diff_appendminimal(&sig_diff, &tuple); - INSIST(tuple == NULL); + result = update_sigs(&nsec_diff, db, version, zone_keys, + nkeys, zone, inception, expire, now, + check_ksk, &sig_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:" + "update_sigs -> %s\n", dns_result_totext(result)); + goto failure; } /* @@ -5901,6 +6075,9 @@ zone_nsec3chain(dns_zone_t *zone) { set_resigntime(zone); failure: + if (result != ISC_R_SUCCESS) + dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain: %s\n", + dns_result_totext(result)); /* * On error roll back the current nsec3chain. */ @@ -5957,6 +6134,8 @@ zone_nsec3chain(dns_zone_t *zone) { for (i = 0; i < nkeys; i++) dst_key_free(&zone_keys[i]); + if (node != NULL) + dns_db_detachnode(db, &node); if (version != NULL) { dns_db_closeversion(db, &version, ISC_FALSE); dns_db_detach(&db); @@ -6066,14 +6245,18 @@ zone_sign(dns_zone_t *zone) { isc_boolean_t delegation; isc_boolean_t finishedakey = ISC_FALSE; isc_boolean_t secureupdated = ISC_FALSE; - isc_boolean_t build_nsec3 = ISC_FALSE, build_nsec = ISC_FALSE; + isc_boolean_t build_nsec = ISC_FALSE; + isc_boolean_t build_nsec3 = ISC_FALSE; isc_boolean_t first; isc_result_t result; isc_stdtime_t now, inception, soaexpire, expire, stop; isc_uint32_t jitter; - unsigned int i; + unsigned int i, j; unsigned int nkeys = 0; isc_uint32_t nodes; + isc_boolean_t was_ksk; + isc_boolean_t have_ksk; + isc_boolean_t have_nonksk; dns_rdataset_init(&rdataset); dns_fixedname_init(&fixed); @@ -6126,10 +6309,6 @@ zone_sign(dns_zone_t *zone) { expire = soaexpire - jitter % 3600; stop = now + 5; - check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK); - if (check_ksk) - check_ksk = ksk_sanity(db, version); - /* * We keep pulling nodes off each iterator in turn until * we have no more nodes to pull off or we reach the limits @@ -6139,39 +6318,14 @@ zone_sign(dns_zone_t *zone) { signatures = zone->signatures; signing = ISC_LIST_HEAD(zone->signing); first = ISC_TRUE; + /* - * See if we have a NSEC chain. + * If we have already determined that we are building a NSEC chain + * continue to do so otherwise workout which type of chain we need + * to be building if any. */ - result = dns_db_getoriginnode(db, &node); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec, - dns_rdatatype_none, 0, &rdataset, NULL); - dns_db_detachnode(db, &node); - if (result == ISC_R_SUCCESS) { - build_nsec = ISC_TRUE; - dns_rdataset_disassociate(&rdataset); - } else if (result != ISC_R_NOTFOUND) { - goto failure; - } else { - /* - * No NSEC chain present. - * See if we need to build a NSEC3 chain? - */ - result = dns_nsec3_active(db, version, ISC_TRUE, &build_nsec3); - if (result == ISC_R_SUCCESS) { - if (build_nsec3) - build_nsec3 = ISC_FALSE; - else { - result = dns_nsec3_active(db, version, - ISC_FALSE, - &build_nsec3); - if (build_nsec3) - secureupdated = ISC_TRUE; - else - build_nsec = ISC_TRUE; - } - } - } + CHECK(dns_private_chains(db, version, zone->privatetype, + &build_nsec, &build_nsec3)); while (signing != NULL && nodes-- > 0 && signatures > 0) { nextsigning = ISC_LIST_NEXT(signing, link); @@ -6193,9 +6347,42 @@ zone_sign(dns_zone_t *zone) { if (signing->db != db) goto next_signing; - is_ksk = ISC_FALSE; delegation = ISC_FALSE; + /* + * ksk_sanity() accounting for the key to be removed. + */ + + was_ksk = ISC_FALSE; + have_ksk = ISC_FALSE; + have_nonksk = ISC_FALSE; + + for (i = 0, j = 0; i < nkeys; i++) { + /* + * Find the key we want to remove. + */ + if (signing->delete && + dst_key_alg(zone_keys[i]) == signing->algorithm && + dst_key_id(zone_keys[i]) == signing->keyid) { + if ((dst_key_flags(zone_keys[j]) & + DNS_KEYFLAG_KSK) != 0) + was_ksk = ISC_TRUE; + dst_key_free(&zone_keys[i]); + } + zone_keys[j] = zone_keys[i]; + if ((dst_key_flags(zone_keys[j]) & + DNS_KEYFLAG_KSK) != 0) + have_ksk = ISC_TRUE; + else + have_nonksk = ISC_TRUE; + j++; + } + + check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK); + if (check_ksk && (!have_nonksk || !have_ksk)) + check_ksk = ISC_FALSE; + nkeys = j; + dns_dbiterator_current(signing->dbiterator, &node, name); if (signing->delete) { @@ -6203,7 +6390,6 @@ zone_sign(dns_zone_t *zone) { CHECK(del_sig(db, version, name, node, nkeys, signing->algorithm, signing->keyid, &sig_diff)); - goto next_node; } /* * On the first pass we need to check if the current node @@ -6237,15 +6423,18 @@ zone_sign(dns_zone_t *zone) { dns_dbiterator_pause(signing->dbiterator); for (i = 0; i < nkeys; i++) { /* - * Find the key we want to sign with. + * Find the keys we want to sign with. */ - if (dst_key_alg(zone_keys[i]) != signing->algorithm || - dst_key_id(zone_keys[i]) != signing->keyid || - !dst_key_isprivate(zone_keys[i])) + if (!dst_key_isprivate(zone_keys[i])) + continue; + if ((!signing->delete || was_ksk || check_ksk) && + (dst_key_alg(zone_keys[i]) != signing->algorithm || + dst_key_id(zone_keys[i]) != signing->keyid)) continue; /* * Do we do KSK processing? */ + is_ksk = ISC_FALSE; if (check_ksk && (dst_key_flags(zone_keys[i]) & DNS_KEYFLAG_KSK) != 0) is_ksk = ISC_TRUE; @@ -6282,6 +6471,7 @@ zone_sign(dns_zone_t *zone) { result = updatesecure(db, version, &zone->origin, zone->minimum, + ISC_FALSE, &secureupdated, &sig_diff); if (result != ISC_R_SUCCESS) { @@ -6292,10 +6482,8 @@ zone_sign(dns_zone_t *zone) { goto failure; } } - result = updatesignwithkey(signing, version, - &zone->origin, - zone->privatetype, - &sig_diff); + result = updatesignwithkey(zone, signing, + version, &sig_diff); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "updatesignwithkey " @@ -6303,6 +6491,7 @@ zone_sign(dns_zone_t *zone) { dns_result_totext(result)); goto failure; } + build_nsec = ISC_FALSE; goto next_signing; } else if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -6381,8 +6570,10 @@ zone_sign(dns_zone_t *zone) { /* * Have we changed anything? */ - if (ISC_LIST_HEAD(sig_diff.tuples) == NULL) + if (ISC_LIST_HEAD(sig_diff.tuples) == NULL) { + result = ISC_R_SUCCESS; goto pauseall; + } commit = ISC_TRUE; @@ -6468,7 +6659,7 @@ zone_sign(dns_zone_t *zone) { signing = ISC_LIST_HEAD(cleanup); while (signing != NULL) { ISC_LIST_UNLINK(cleanup, signing, link); - ISC_LIST_APPEND(zone->signing, signing, link); + ISC_LIST_PREPEND(zone->signing, signing, link); dns_dbiterator_first(signing->dbiterator); dns_dbiterator_pause(signing->dbiterator); signing = ISC_LIST_HEAD(cleanup); @@ -6484,6 +6675,8 @@ zone_sign(dns_zone_t *zone) { for (i = 0; i < nkeys; i++) dst_key_free(&zone_keys[i]); + INSIST(node == NULL); + if (version != NULL) { dns_db_closeversion(db, &version, ISC_FALSE); dns_db_detach(&db); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index c2f899833b..87d707c40c 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: namedconf.c,v 1.105 2009/09/02 16:10:03 each Exp $ */ +/* $Id: namedconf.c,v 1.106 2009/10/08 23:13:07 marka Exp $ */ /*! \file */ @@ -1135,6 +1135,7 @@ zone_clauses[] = { { "notify-source-v6", &cfg_type_sockaddr6wild, 0 }, { "notify-to-soa", &cfg_type_boolean, 0 }, { "nsec3-test-zone", &cfg_type_boolean, CFG_CLAUSEFLAG_TESTONLY }, + { "secure-to-insecure", &cfg_type_boolean, 0 }, { "sig-signing-nodes", &cfg_type_uint32, 0 }, { "sig-signing-signatures", &cfg_type_uint32, 0 }, { "sig-signing-type", &cfg_type_uint32, 0 },