From: Mark Andrews Date: Tue, 30 Aug 2011 05:16:15 +0000 (+0000) Subject: 3147. [func] Initial inline signing support. [RT #23657] X-Git-Tag: v9.9.0a2~1^2~81 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9198ab377b1cbf07d6d0c6eec25296c135bd66bd;p=thirdparty%2Fbind9.git 3147. [func] Initial inline signing support. [RT #23657] --- diff --git a/CHANGES b/CHANGES index 00e1411ac97..d90e7a15c7d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +3147. [func] Initial inline signing support. [RT #23657] + --- 9.9.0a1 released --- 3146. [test] Fixed gcc4.6.0 errors in ATF. [RT #25598] diff --git a/bin/named/config.c b/bin/named/config.c index 5d5fc43e208..2a3dcf02221 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: config.c,v 1.119 2011/07/01 02:25:47 marka Exp $ */ +/* $Id: config.c,v 1.120 2011/08/30 05:16:10 marka Exp $ */ /*! \file */ @@ -199,6 +199,7 @@ options {\n\ sig-signing-nodes 100;\n\ sig-signing-signatures 10;\n\ sig-signing-type 65534;\n\ + inline-signing no;\n\ zone-statistics false;\n\ max-journal-size unlimited;\n\ ixfr-from-differences false;\n\ @@ -294,7 +295,8 @@ ns_checknames_get(const cfg_obj_t **maps, const char *which, if (maps[i] == NULL) return (ISC_R_NOTFOUND); checknames = NULL; - if (cfg_map_get(maps[i], "check-names", &checknames) == ISC_R_SUCCESS) { + if (cfg_map_get(maps[i], "check-names", + &checknames) == ISC_R_SUCCESS) { /* * Zone map entry is not a list. */ @@ -307,7 +309,8 @@ ns_checknames_get(const cfg_obj_t **maps, const char *which, element = cfg_list_next(element)) { value = cfg_listelt_value(element); type = cfg_tuple_get(value, "type"); - if (strcasecmp(cfg_obj_asstring(type), which) == 0) { + if (strcasecmp(cfg_obj_asstring(type), + which) == 0) { *obj = cfg_tuple_get(value, "mode"); return (ISC_R_SUCCESS); } diff --git a/bin/named/include/named/zoneconf.h b/bin/named/include/named/zoneconf.h index ebaad684ae7..95ed2a64dfd 100644 --- a/bin/named/include/named/zoneconf.h +++ b/bin/named/include/named/zoneconf.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zoneconf.h,v 1.28 2010/12/20 23:47:20 tbox Exp $ */ +/* $Id: zoneconf.h,v 1.29 2011/08/30 05:16:11 marka Exp $ */ #ifndef NS_ZONECONF_H #define NS_ZONECONF_H 1 @@ -33,7 +33,7 @@ ISC_LANG_BEGINDECLS isc_result_t ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac, - dns_zone_t *zone); + dns_zone_t *zone, dns_zone_t *raw); /*%< * Configure or reconfigure a zone according to the named.conf * data in 'cctx' and 'czone'. diff --git a/bin/named/server.c b/bin/named/server.c index bef3f8c95c0..9361c97f38b 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.c,v 1.616 2011/08/02 20:36:11 each Exp $ */ +/* $Id: server.c,v 1.617 2011/08/30 05:16:10 marka Exp $ */ /*! \file */ @@ -3225,6 +3225,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, { dns_view_t *pview = NULL; /* Production view */ dns_zone_t *zone = NULL; /* New or reused zone */ + dns_zone_t *raw = NULL; /* New or reused raw zone */ dns_zone_t *dupzone = NULL; const cfg_obj_t *options = NULL; const cfg_obj_t *zoptions = NULL; @@ -3232,6 +3233,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, const cfg_obj_t *forwarders = NULL; const cfg_obj_t *forwardtype = NULL; const cfg_obj_t *only = NULL; + const cfg_obj_t *signing = NULL; isc_result_t result; isc_result_t tresult; isc_buffer_t buffer; @@ -3378,7 +3380,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, dns_zone_setstats(zone, ns_g_server->zonestats); } CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, - zone)); + zone, NULL)); dns_zone_attach(zone, &view->redirect); goto cleanup; } @@ -3469,10 +3471,30 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, */ dns_zone_setadded(zone, added); + signing = NULL; + if ((strcasecmp(ztypestr, "master") == 0 || + strcasecmp(ztypestr, "slave") == 0) && + cfg_map_get(zoptions, "inline-signing", &signing) == ISC_R_SUCCESS && + cfg_obj_asboolean(signing)) + { + dns_zone_getraw(zone, &raw); + if (raw == NULL) { + CHECK(dns_zone_create(&raw, mctx)); + CHECK(dns_zone_setorigin(raw, origin)); + dns_zone_setview(raw, view); + if (view->acache != NULL) + dns_zone_setacache(raw, view->acache); + CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, + raw)); + dns_zone_setstats(raw, ns_g_server->zonestats); + dns_zone_link(zone, raw); + } + } + /* * Configure the zone. */ - CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone)); + CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone, raw)); /* * Add the zone to its view in the new view list. @@ -3482,6 +3504,8 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, cleanup: if (zone != NULL) dns_zone_detach(&zone); + if (raw != NULL) + dns_zone_detach(&raw); if (pview != NULL) dns_view_detach(&pview); diff --git a/bin/named/update.c b/bin/named/update.c index b2dec79bc7d..46e3403ba56 100644 --- a/bin/named/update.c +++ b/bin/named/update.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.c,v 1.195 2011/07/01 02:25:47 marka Exp $ */ +/* $Id: update.c,v 1.196 2011/08/30 05:16:11 marka Exp $ */ #include @@ -269,6 +269,11 @@ update_log(ns_client_t *client, dns_zone_t *zone, namebuf, classbuf, message); } +static void +update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) { + update_log(arg, zone, level, "%s", message); +} + /*% * Increment updated-related statistics counters. */ @@ -721,45 +726,6 @@ rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, RETURN_EXISTENCE_FLAG; } -/*% - * Set '*visible' to true if the RRset exists and is part of the - * visible zone. Otherwise '*visible' is set to false unless a - * error occurs. - */ -static isc_result_t -rrset_visible(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, - dns_rdatatype_t type, isc_boolean_t *visible) -{ - isc_result_t result; - dns_fixedname_t fixed; - - dns_fixedname_init(&fixed); - result = dns_db_find(db, name, ver, type, DNS_DBFIND_NOWILD, - (isc_stdtime_t) 0, NULL, - dns_fixedname_name(&fixed), NULL, NULL); - switch (result) { - case ISC_R_SUCCESS: - *visible = ISC_TRUE; - break; - /* - * Glue, obscured, deleted or replaced records. - */ - case DNS_R_DELEGATION: - case DNS_R_DNAME: - case DNS_R_CNAME: - case DNS_R_NXDOMAIN: - case DNS_R_NXRRSET: - case DNS_R_EMPTYNAME: - case DNS_R_COVERINGNSEC: - *visible = ISC_FALSE; - result = ISC_R_SUCCESS; - break; - default: - break; - } - return (result); -} - /*% * Helper function for cname_incompatible_rrset_exists. */ @@ -1174,16 +1140,6 @@ true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { return (ISC_TRUE); } -/*% - * Return true if the record is a RRSIG. - */ -static isc_boolean_t -rrsig_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { - UNUSED(update_rr); - return ((db_rr->type == dns_rdatatype_rrsig) ? - ISC_TRUE : ISC_FALSE); -} - /*% * Return true iff the two RRs have identical rdata. */ @@ -1278,1350 +1234,225 @@ delete_if_action(void *data, rr_t *rr) { result = update_one_rr(ctx->db, ctx->ver, ctx->diff, DNS_DIFFOP_DEL, ctx->name, rr->ttl, &rr->rdata); - return (result); - } else { - return (ISC_R_SUCCESS); - } -} - -/*% - * Conditionally delete RRs. Apply 'predicate' to the RRs - * specified by 'db', 'ver', 'name', and 'type' (which can - * be dns_rdatatype_any to match any type). Delete those - * RRs for which the predicate returns true, and log the - * deletions in 'diff'. - */ -static isc_result_t -delete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver, - dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers, - dns_rdata_t *update_rr, dns_diff_t *diff) -{ - conditional_delete_ctx_t ctx; - ctx.predicate = predicate; - ctx.db = db; - ctx.ver = ver; - ctx.diff = diff; - ctx.name = name; - ctx.update_rr = update_rr; - return (foreach_rr(db, ver, name, type, covers, - delete_if_action, &ctx)); -} - -/**************************************************************************/ -/*% - * Prepare an RR for the addition of the new RR 'ctx->update_rr', - * with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting - * the RRs if it is replaced by the new RR or has a conflicting TTL. - * The necessary changes are appended to ctx->del_diff and ctx->add_diff; - * we need to do all deletions before any additions so that we don't run - * into transient states with conflicting TTLs. - */ - -typedef struct { - dns_db_t *db; - dns_dbversion_t *ver; - dns_diff_t *diff; - dns_name_t *name; - dns_rdata_t *update_rr; - dns_ttl_t update_rr_ttl; - isc_boolean_t ignore_add; - dns_diff_t del_diff; - dns_diff_t add_diff; -} add_rr_prepare_ctx_t; - -static isc_result_t -add_rr_prepare_action(void *data, rr_t *rr) { - isc_result_t result = ISC_R_SUCCESS; - add_rr_prepare_ctx_t *ctx = data; - dns_difftuple_t *tuple = NULL; - isc_boolean_t equal; - - /* - * If the update RR is a "duplicate" of the update RR, - * the update should be silently ignored. - */ - equal = ISC_TF(dns_rdata_casecompare(&rr->rdata, ctx->update_rr) == 0); - if (equal && rr->ttl == ctx->update_rr_ttl) { - ctx->ignore_add = ISC_TRUE; - return (ISC_R_SUCCESS); - } - - /* - * If this RR is "equal" to the update RR, it should - * be deleted before the update RR is added. - */ - if (replaces_p(ctx->update_rr, &rr->rdata)) { - CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL, - ctx->name, rr->ttl, &rr->rdata, - &tuple)); - dns_diff_append(&ctx->del_diff, &tuple); - return (ISC_R_SUCCESS); - } - - /* - * If this RR differs in TTL from the update RR, - * its TTL must be adjusted. - */ - if (rr->ttl != ctx->update_rr_ttl) { - CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL, - ctx->name, rr->ttl, &rr->rdata, - &tuple)); - dns_diff_append(&ctx->del_diff, &tuple); - if (!equal) { - CHECK(dns_difftuple_create(ctx->add_diff.mctx, - DNS_DIFFOP_ADD, ctx->name, - ctx->update_rr_ttl, - &rr->rdata, &tuple)); - dns_diff_append(&ctx->add_diff, &tuple); - } - } - failure: - return (result); -} - -/**************************************************************************/ -/* - * Miscellaneous subroutines. - */ - -/*% - * Extract a single update RR from 'section' of dynamic update message - * 'msg', with consistency checking. - * - * Stores the owner name, rdata, and TTL of the update RR at 'name', - * 'rdata', and 'ttl', respectively. - */ -static void -get_current_rr(dns_message_t *msg, dns_section_t section, - dns_rdataclass_t zoneclass, dns_name_t **name, - dns_rdata_t *rdata, dns_rdatatype_t *covers, - dns_ttl_t *ttl, dns_rdataclass_t *update_class) -{ - dns_rdataset_t *rdataset; - isc_result_t result; - dns_message_currentname(msg, section, name); - rdataset = ISC_LIST_HEAD((*name)->list); - INSIST(rdataset != NULL); - INSIST(ISC_LIST_NEXT(rdataset, link) == NULL); - *covers = rdataset->covers; - *ttl = rdataset->ttl; - result = dns_rdataset_first(rdataset); - INSIST(result == ISC_R_SUCCESS); - dns_rdataset_current(rdataset, rdata); - INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE); - *update_class = rdata->rdclass; - rdata->rdclass = zoneclass; -} - -/*% - * Increment the SOA serial number of database 'db', version 'ver'. - * Replace the SOA record in the database, and log the - * change in 'diff'. - */ - - /* - * XXXRTH Failures in this routine will be worth logging, when - * we have a logging system. Failure to find the zonename - * or the SOA rdataset warrant at least an UNEXPECTED_ERROR(). - */ - -static isc_result_t -update_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, - isc_mem_t *mctx, dns_updatemethod_t method) -{ - dns_difftuple_t *deltuple = NULL; - dns_difftuple_t *addtuple = NULL; - isc_uint32_t serial; - isc_result_t result; - - CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple)); - CHECK(dns_difftuple_copy(deltuple, &addtuple)); - addtuple->op = DNS_DIFFOP_ADD; - - serial = dns_soa_getserial(&addtuple->rdata); - serial = dns_update_soaserial(serial, method); - dns_soa_setserial(serial, &addtuple->rdata); - CHECK(do_one_tuple(&deltuple, db, ver, diff)); - CHECK(do_one_tuple(&addtuple, db, ver, diff)); - result = ISC_R_SUCCESS; - - failure: - if (addtuple != NULL) - dns_difftuple_free(&addtuple); - if (deltuple != NULL) - dns_difftuple_free(&deltuple); - return (result); -} - -/*% - * Check that the new SOA record at 'update_rdata' does not - * illegally cause the SOA serial number to decrease or stay - * unchanged relative to the existing SOA in 'db'. - * - * Sets '*ok' to ISC_TRUE if the update is legal, ISC_FALSE if not. - * - * William King points out that RFC2136 is inconsistent about - * the case where the serial number stays unchanged: - * - * section 3.4.2.2 requires a server to ignore a SOA update request - * if the serial number on the update SOA is less_than_or_equal to - * the zone SOA serial. - * - * section 3.6 requires a server to ignore a SOA update request if - * the serial is less_than the zone SOA serial. - * - * Paul says 3.4.2.2 is correct. - * - */ -static isc_result_t -check_soa_increment(dns_db_t *db, dns_dbversion_t *ver, - dns_rdata_t *update_rdata, isc_boolean_t *ok) -{ - isc_uint32_t db_serial; - isc_uint32_t update_serial; - isc_result_t result; - - update_serial = dns_soa_getserial(update_rdata); - - result = dns_db_getsoaserial(db, ver, &db_serial); - if (result != ISC_R_SUCCESS) - return (result); - - if (DNS_SERIAL_GE(db_serial, update_serial)) { - *ok = ISC_FALSE; - } else { - *ok = ISC_TRUE; - } - - return (ISC_R_SUCCESS); - -} - -/**************************************************************************/ -/* - * Incremental updating of NSECs and RRSIGs. - */ - -#define MAXZONEKEYS 32 /*%< Maximum number of zone keys supported. */ - -/*% - * We abuse the dns_diff_t type to represent a set of domain names - * affected by the update. - */ -static isc_result_t -namelist_append_name(dns_diff_t *list, dns_name_t *name) { - isc_result_t result; - dns_difftuple_t *tuple = NULL; - static dns_rdata_t dummy_rdata = DNS_RDATA_INIT; - - CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0, - &dummy_rdata, &tuple)); - dns_diff_append(list, &tuple); - failure: - return (result); -} - -static isc_result_t -namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected) -{ - isc_result_t result; - dns_fixedname_t fixedname; - dns_name_t *child; - dns_dbiterator_t *dbit = NULL; - - dns_fixedname_init(&fixedname); - child = dns_fixedname_name(&fixedname); - - CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit)); - - for (result = dns_dbiterator_seek(dbit, name); - result == ISC_R_SUCCESS; - result = dns_dbiterator_next(dbit)) - { - dns_dbnode_t *node = NULL; - CHECK(dns_dbiterator_current(dbit, &node, child)); - dns_db_detachnode(db, &node); - if (! dns_name_issubdomain(child, name)) - break; - CHECK(namelist_append_name(affected, child)); - } - if (result == ISC_R_NOMORE) - result = ISC_R_SUCCESS; - failure: - if (dbit != NULL) - dns_dbiterator_destroy(&dbit); - return (result); -} - - - -/*% - * Helper function for non_nsec_rrset_exists(). - */ -static isc_result_t -is_non_nsec_action(void *data, dns_rdataset_t *rrset) { - UNUSED(data); - if (!(rrset->type == dns_rdatatype_nsec || - rrset->type == dns_rdatatype_nsec3 || - (rrset->type == dns_rdatatype_rrsig && - (rrset->covers == dns_rdatatype_nsec || - rrset->covers == dns_rdatatype_nsec3)))) - return (ISC_R_EXISTS); - return (ISC_R_SUCCESS); -} - -/*% - * Check whether there is an rrset other than a NSEC or RRSIG NSEC, - * i.e., anything that justifies the continued existence of a name - * after a secure update. - * - * If such an rrset exists, set '*exists' to ISC_TRUE. - * Otherwise, set it to ISC_FALSE. - */ -static isc_result_t -non_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver, - dns_name_t *name, isc_boolean_t *exists) -{ - isc_result_t result; - result = foreach_rrset(db, ver, name, is_non_nsec_action, NULL); - RETURN_EXISTENCE_FLAG; -} - -/*% - * A comparison function for sorting dns_diff_t:s by name. - */ -static int -name_order(const void *av, const void *bv) { - dns_difftuple_t const * const *ap = av; - dns_difftuple_t const * const *bp = bv; - dns_difftuple_t const *a = *ap; - dns_difftuple_t const *b = *bp; - return (dns_name_compare(&a->name, &b->name)); -} - -static isc_result_t -uniqify_name_list(dns_diff_t *list) { - isc_result_t result; - dns_difftuple_t *p, *q; - - CHECK(dns_diff_sort(list, name_order)); - - p = ISC_LIST_HEAD(list->tuples); - while (p != NULL) { - do { - q = ISC_LIST_NEXT(p, link); - if (q == NULL || ! dns_name_equal(&p->name, &q->name)) - break; - ISC_LIST_UNLINK(list->tuples, q, link); - dns_difftuple_free(&q); - } while (1); - p = ISC_LIST_NEXT(p, link); - } - failure: - return (result); -} - -static isc_result_t -is_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, - isc_boolean_t *flag, isc_boolean_t *cut, isc_boolean_t *unsecure) -{ - isc_result_t result; - dns_fixedname_t foundname; - dns_fixedname_init(&foundname); - result = dns_db_find(db, name, ver, dns_rdatatype_any, - DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD, - (isc_stdtime_t) 0, NULL, - dns_fixedname_name(&foundname), - NULL, NULL); - if (result == ISC_R_SUCCESS || result == DNS_R_EMPTYNAME) { - *flag = ISC_TRUE; - *cut = ISC_FALSE; - if (unsecure != NULL) - *unsecure = ISC_FALSE; - return (ISC_R_SUCCESS); - } else if (result == DNS_R_ZONECUT) { - *flag = ISC_TRUE; - *cut = ISC_TRUE; - if (unsecure != NULL) { - /* - * We are at the zonecut. Check to see if there - * is a DS RRset. - */ - if (dns_db_find(db, name, ver, dns_rdatatype_ds, 0, - (isc_stdtime_t) 0, NULL, - dns_fixedname_name(&foundname), - NULL, NULL) == DNS_R_NXRRSET) - *unsecure = ISC_TRUE; - else - *unsecure = ISC_FALSE; - } - return (ISC_R_SUCCESS); - } else if (result == DNS_R_GLUE || result == DNS_R_DNAME || - result == DNS_R_DELEGATION || result == DNS_R_NXDOMAIN) { - *flag = ISC_FALSE; - *cut = ISC_FALSE; - if (unsecure != NULL) - *unsecure = ISC_FALSE; - return (ISC_R_SUCCESS); - } else { - /* - * Silence compiler. - */ - *flag = ISC_FALSE; - *cut = ISC_FALSE; - if (unsecure != NULL) - *unsecure = ISC_FALSE; - return (result); - } -} - -/*% - * Find the next/previous name that has a NSEC record. - * In other words, skip empty database nodes and names that - * have had their NSECs removed because they are obscured by - * a zone cut. - */ -static isc_result_t -next_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, - dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname, - isc_boolean_t forward) -{ - isc_result_t result; - dns_dbiterator_t *dbit = NULL; - isc_boolean_t has_nsec = ISC_FALSE; - unsigned int wraps = 0; - isc_boolean_t secure = dns_db_issecure(db); - - CHECK(dns_db_createiterator(db, 0, &dbit)); - - CHECK(dns_dbiterator_seek(dbit, oldname)); - do { - dns_dbnode_t *node = NULL; - - if (forward) - result = dns_dbiterator_next(dbit); - else - result = dns_dbiterator_prev(dbit); - if (result == ISC_R_NOMORE) { - /* - * Wrap around. - */ - if (forward) - CHECK(dns_dbiterator_first(dbit)); - else - CHECK(dns_dbiterator_last(dbit)); - wraps++; - if (wraps == 2) { - update_log(client, zone, ISC_LOG_ERROR, - "secure zone with no NSECs"); - result = DNS_R_BADZONE; - goto failure; - } - } - CHECK(dns_dbiterator_current(dbit, &node, newname)); - dns_db_detachnode(db, &node); - - /* - * The iterator may hold the tree lock, and - * rrset_exists() calls dns_db_findnode() which - * may try to reacquire it. To avoid deadlock - * we must pause the iterator first. - */ - CHECK(dns_dbiterator_pause(dbit)); - if (secure) { - CHECK(rrset_exists(db, ver, newname, - dns_rdatatype_nsec, 0, &has_nsec)); - } else { - dns_fixedname_t ffound; - dns_name_t *found; - dns_fixedname_init(&ffound); - found = dns_fixedname_name(&ffound); - result = dns_db_find(db, newname, ver, - dns_rdatatype_soa, - DNS_DBFIND_NOWILD, 0, NULL, found, - NULL, NULL); - if (result == ISC_R_SUCCESS || - result == DNS_R_EMPTYNAME || - result == DNS_R_NXRRSET || - result == DNS_R_CNAME || - (result == DNS_R_DELEGATION && - dns_name_equal(newname, found))) { - has_nsec = ISC_TRUE; - result = ISC_R_SUCCESS; - } else if (result != DNS_R_NXDOMAIN) - break; - } - } while (! has_nsec); - failure: - if (dbit != NULL) - dns_dbiterator_destroy(&dbit); - - return (result); -} - -/*% - * Add a NSEC record for "name", recording the change in "diff". - * The existing NSEC is removed. - */ -static isc_result_t -add_nsec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, - dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl, - dns_diff_t *diff) -{ - isc_result_t result; - dns_dbnode_t *node = NULL; - unsigned char buffer[DNS_NSEC_BUFFERSIZE]; - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_difftuple_t *tuple = NULL; - dns_fixedname_t fixedname; - dns_name_t *target; - - dns_fixedname_init(&fixedname); - target = dns_fixedname_name(&fixedname); - - /* - * Find the successor name, aka NSEC target. - */ - CHECK(next_active(client, zone, db, ver, name, target, ISC_TRUE)); - - /* - * Create the NSEC RDATA. - */ - CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); - dns_rdata_init(&rdata); - CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata)); - dns_db_detachnode(db, &node); - - /* - * Delete the old NSEC and record the change. - */ - CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0, - NULL, diff)); - /* - * Add the new NSEC and record the change. - */ - CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, - nsecttl, &rdata, &tuple)); - CHECK(do_one_tuple(&tuple, db, ver, diff)); - INSIST(tuple == NULL); - - failure: - if (node != NULL) - dns_db_detachnode(db, &node); - return (result); -} - -/*% - * Add a placeholder NSEC record for "name", recording the change in "diff". - */ -static isc_result_t -add_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, - dns_diff_t *diff) -{ - isc_result_t result; - dns_difftuple_t *tuple = NULL; - isc_region_t r; - unsigned char data[1] = { 0 }; /* The root domain, no bits. */ - dns_rdata_t rdata = DNS_RDATA_INIT; - - r.base = data; - r.length = sizeof(data); - dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r); - CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0, - &rdata, &tuple)); - CHECK(do_one_tuple(&tuple, db, ver, diff)); - failure: - return (result); -} - -static isc_result_t -find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, - isc_mem_t *mctx, unsigned int maxkeys, - dst_key_t **keys, unsigned int *nkeys) -{ - isc_result_t result; - dns_dbnode_t *node = NULL; - const char *directory = dns_zone_getkeydirectory(zone); - CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); - CHECK(dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db), - directory, mctx, maxkeys, keys, nkeys)); - failure: - if (node != NULL) - dns_db_detachnode(db, &node); - return (result); -} - -/*% - * Add RRSIG records for an RRset, recording the change in "diff". - */ -static isc_result_t -add_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, - dns_dbversion_t *ver, dns_name_t *name, dns_rdatatype_t type, - dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys, - isc_stdtime_t inception, isc_stdtime_t expire, - isc_boolean_t check_ksk, isc_boolean_t keyset_kskonly) -{ - isc_result_t result; - dns_dbnode_t *node = NULL; - dns_rdataset_t rdataset; - dns_rdata_t sig_rdata = DNS_RDATA_INIT; - isc_buffer_t buffer; - unsigned char data[1024]; /* XXX */ - unsigned int i, j; - isc_boolean_t added_sig = ISC_FALSE; - isc_mem_t *mctx = client->mctx; - - dns_rdataset_init(&rdataset); - isc_buffer_init(&buffer, data, sizeof(data)); - - /* Get the rdataset to sign. */ - if (type == dns_rdatatype_nsec3) - CHECK(dns_db_findnsec3node(db, name, ISC_FALSE, &node)); - else - CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); - CHECK(dns_db_findrdataset(db, node, ver, type, 0, - (isc_stdtime_t) 0, &rdataset, NULL)); - dns_db_detachnode(db, &node); - -#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0) -#define KSK(x) ((dst_key_flags(x) & DNS_KEYFLAG_KSK) != 0) -#define ALG(x) dst_key_alg(x) - - /* - * If we are honoring KSK flags then we need to check that we - * have both KSK and non-KSK keys that are not revoked per - * algorithm. - */ - for (i = 0; i < nkeys; i++) { - isc_boolean_t both = ISC_FALSE; - - if (!dst_key_isprivate(keys[i])) - continue; - - if (check_ksk && !REVOKE(keys[i])) { - isc_boolean_t have_ksk, have_nonksk; - if (KSK(keys[i])) { - have_ksk = ISC_TRUE; - have_nonksk = ISC_FALSE; - } else { - have_ksk = ISC_FALSE; - have_nonksk = ISC_TRUE; - } - for (j = 0; j < nkeys; j++) { - if (j == i || ALG(keys[i]) != ALG(keys[j])) - continue; - if (REVOKE(keys[j])) - continue; - if (KSK(keys[j])) - have_ksk = ISC_TRUE; - else - have_nonksk = ISC_TRUE; - both = have_ksk && have_nonksk; - if (both) - break; - } - } - - if (both) { - if (type == dns_rdatatype_dnskey) { - if (!KSK(keys[i]) && keyset_kskonly) - continue; - } else if (KSK(keys[i])) - continue; - } else if (REVOKE(keys[i]) && type != dns_rdatatype_dnskey) - continue; - - /* Calculate the signature, creating a RRSIG RDATA. */ - CHECK(dns_dnssec_sign(name, &rdataset, keys[i], - &inception, &expire, - mctx, &buffer, &sig_rdata)); - - /* Update the database and journal with the RRSIG. */ - /* XXX inefficient - will cause dataset merging */ - CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN, name, - rdataset.ttl, &sig_rdata)); - dns_rdata_reset(&sig_rdata); - isc_buffer_init(&buffer, data, sizeof(data)); - added_sig = ISC_TRUE; - } - if (!added_sig) { - update_log(client, zone, ISC_LOG_ERROR, - "found no active private keys, " - "unable to generate any signatures"); - result = ISC_R_NOTFOUND; - } - - failure: - if (dns_rdataset_isassociated(&rdataset)) - dns_rdataset_disassociate(&rdataset); - if (node != NULL) - dns_db_detachnode(db, &node); - return (result); -} - -/* - * Delete expired RRsigs and any RRsigs we are about to re-sign. - * See also zone.c:del_sigs(). - */ -static isc_result_t -del_keysigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, - dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys) -{ - isc_result_t result; - dns_dbnode_t *node = NULL; - dns_rdataset_t rdataset; - dns_rdata_t rdata = DNS_RDATA_INIT; - unsigned int i; - dns_rdata_rrsig_t rrsig; - isc_boolean_t found; - - dns_rdataset_init(&rdataset); - - result = dns_db_findnode(db, name, ISC_FALSE, &node); - if (result == ISC_R_NOTFOUND) - return (ISC_R_SUCCESS); - if (result != ISC_R_SUCCESS) - goto failure; - result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig, - dns_rdatatype_dnskey, (isc_stdtime_t) 0, - &rdataset, NULL); - dns_db_detachnode(db, &node); - - if (result == ISC_R_NOTFOUND) - return (ISC_R_SUCCESS); - if (result != ISC_R_SUCCESS) - goto failure; - - for (result = dns_rdataset_first(&rdataset); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(&rdataset)) { - dns_rdataset_current(&rdataset, &rdata); - result = dns_rdata_tostruct(&rdata, &rrsig, NULL); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - found = ISC_FALSE; - for (i = 0; i < nkeys; i++) { - if (rrsig.keyid == dst_key_id(keys[i])) { - found = ISC_TRUE; - if (!dst_key_isprivate(keys[i])) { - /* - * The re-signing code in zone.c - * will mark this as offline. - * Just skip the record for now. - */ - break; - } - result = update_one_rr(db, ver, diff, - DNS_DIFFOP_DEL, name, - rdataset.ttl, &rdata); - break; - } - } - /* - * If there is not a matching DNSKEY then delete the RRSIG. - */ - if (!found) - result = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, - name, rdataset.ttl, &rdata); - dns_rdata_reset(&rdata); - if (result != ISC_R_SUCCESS) - break; - } - dns_rdataset_disassociate(&rdataset); - if (result == ISC_R_NOMORE) - result = ISC_R_SUCCESS; -failure: - if (node != NULL) - dns_db_detachnode(db, &node); - return (result); -} - -static isc_result_t -add_exposed_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, - dns_dbversion_t *ver, dns_name_t *name, isc_boolean_t cut, - dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys, - isc_stdtime_t inception, isc_stdtime_t expire, - isc_boolean_t check_ksk, isc_boolean_t keyset_kskonly) -{ - isc_result_t result; - dns_dbnode_t *node; - dns_rdatasetiter_t *iter; - - node = NULL; - result = dns_db_findnode(db, name, ISC_FALSE, &node); - if (result == ISC_R_NOTFOUND) - return (ISC_R_SUCCESS); - if (result != ISC_R_SUCCESS) - return (result); - - iter = NULL; - result = dns_db_allrdatasets(db, node, ver, - (isc_stdtime_t) 0, &iter); - if (result != ISC_R_SUCCESS) - goto cleanup_node; - - for (result = dns_rdatasetiter_first(iter); - result == ISC_R_SUCCESS; - result = dns_rdatasetiter_next(iter)) - { - dns_rdataset_t rdataset; - dns_rdatatype_t type; - isc_boolean_t flag; - - dns_rdataset_init(&rdataset); - dns_rdatasetiter_current(iter, &rdataset); - type = rdataset.type; - dns_rdataset_disassociate(&rdataset); - - /* - * We don't need to sign unsigned NSEC records at the cut - * as they are handled elsewhere. - */ - if ((type == dns_rdatatype_rrsig) || - (cut && type != dns_rdatatype_ds)) - continue; - result = rrset_exists(db, ver, name, dns_rdatatype_rrsig, - type, &flag); - if (result != ISC_R_SUCCESS) - goto cleanup_iterator; - if (flag) - continue;; - result = add_sigs(client, zone, db, ver, name, type, diff, - keys, nkeys, inception, expire, - check_ksk, keyset_kskonly); - if (result != ISC_R_SUCCESS) - goto cleanup_iterator; - } - if (result == ISC_R_NOMORE) - result = ISC_R_SUCCESS; - - cleanup_iterator: - dns_rdatasetiter_destroy(&iter); - - cleanup_node: - dns_db_detachnode(db, &node); - - return (result); -} - -/*% - * Update RRSIG, NSEC and NSEC3 records affected by an update. The original - * update, including the SOA serial update but excluding the RRSIG & NSEC - * changes, is in "diff" and has already been applied to "newver" of "db". - * The database version prior to the update is "oldver". - * - * The necessary RRSIG, NSEC and NSEC3 changes will be applied to "newver" - * and added (as a minimal diff) to "diff". - * - * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds. - */ -static isc_result_t -update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, - dns_dbversion_t *oldver, dns_dbversion_t *newver, - dns_diff_t *diff, isc_uint32_t sigvalidityinterval) -{ - isc_result_t result; - dns_difftuple_t *t; - dns_diff_t diffnames; - dns_diff_t affected; - dns_diff_t sig_diff; - dns_diff_t nsec_diff; - dns_diff_t nsec_mindiff; - isc_boolean_t flag, build_nsec, build_nsec3; - dst_key_t *zone_keys[MAXZONEKEYS]; - unsigned int nkeys = 0; - unsigned int i; - isc_stdtime_t now, inception, expire; - dns_ttl_t nsecttl; - dns_rdata_soa_t soa; - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdataset_t rdataset; - dns_dbnode_t *node = NULL; - isc_boolean_t check_ksk, keyset_kskonly; - 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); - - dns_diff_init(client->mctx, &sig_diff); - sig_diff.resign = dns_zone_getsigresigninginterval(zone); - dns_diff_init(client->mctx, &nsec_diff); - dns_diff_init(client->mctx, &nsec_mindiff); - - result = find_zone_keys(zone, db, newver, client->mctx, - MAXZONEKEYS, zone_keys, &nkeys); - if (result != ISC_R_SUCCESS) { - update_log(client, zone, ISC_LOG_ERROR, - "could not get zone keys for secure dynamic update"); - goto failure; - } - - isc_stdtime_get(&now); - inception = now - 3600; /* Allow for some clock skew. */ - expire = now + sigvalidityinterval; - - /* - * Do we look at the KSK flag on the DNSKEY to determining which - * keys sign which RRsets? First check the zone option then - * check the keys flags to make sure at least one has a ksk set - * and one doesn't. - */ - check_ksk = ISC_TF((dns_zone_getoptions(zone) & - DNS_ZONEOPT_UPDATECHECKKSK) != 0); - keyset_kskonly = ISC_TF((dns_zone_getoptions(zone) & - DNS_ZONEOPT_DNSKEYKSKONLY) != 0); - - /* - * Get the NSEC/NSEC3 TTL from the SOA MINIMUM field. - */ - CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); - dns_rdataset_init(&rdataset); - CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa, 0, - (isc_stdtime_t) 0, &rdataset, NULL)); - CHECK(dns_rdataset_first(&rdataset)); - dns_rdataset_current(&rdataset, &rdata); - CHECK(dns_rdata_tostruct(&rdata, &soa, NULL)); - nsecttl = soa.minimum; - dns_rdataset_disassociate(&rdataset); - dns_db_detachnode(db, &node); - - /* - * Find all RRsets directly affected by the update, and - * update their RRSIGs. Also build a list of names affected - * by the update in "diffnames". - */ - CHECK(dns_diff_sort(diff, temp_order)); - - t = ISC_LIST_HEAD(diff->tuples); - while (t != NULL) { - dns_name_t *name = &t->name; - /* Now "name" is a new, unique name affected by the update. */ - - CHECK(namelist_append_name(&diffnames, name)); - - while (t != NULL && dns_name_equal(&t->name, name)) { - dns_rdatatype_t type; - type = t->rdata.type; - - /* - * Now "name" and "type" denote a new unique RRset - * affected by the update. - */ - - /* Don't sign RRSIGs. */ - if (type == dns_rdatatype_rrsig) - goto skip; - - /* - * Delete all old RRSIGs covering this type, since they - * are all invalid when the signed RRset has changed. - * We may not be able to recreate all of them - tough. - * Special case changes to the zone's DNSKEY records - * to support offline KSKs. - */ - if (type == dns_rdatatype_dnskey) - del_keysigs(db, newver, name, &sig_diff, - zone_keys, nkeys); - else - CHECK(delete_if(true_p, db, newver, name, - dns_rdatatype_rrsig, type, - NULL, &sig_diff)); - - /* - * If this RRset is still visible after the update, - * add a new signature for it. - */ - CHECK(rrset_visible(db, newver, name, type, &flag)); - if (flag) { - CHECK(add_sigs(client, zone, db, newver, name, - type, &sig_diff, zone_keys, - nkeys, inception, expire, - check_ksk, keyset_kskonly)); - } - skip: - /* Skip any other updates to the same RRset. */ - while (t != NULL && - dns_name_equal(&t->name, name) && - t->rdata.type == type) - { - t = ISC_LIST_NEXT(t, link); - } - } - } - update_log(client, zone, ISC_LOG_DEBUG(3), "updated data signatures"); - - /* Remove orphaned NSECs and RRSIG NSECs. */ - for (t = ISC_LIST_HEAD(diffnames.tuples); - t != NULL; - t = ISC_LIST_NEXT(t, link)) - { - CHECK(non_nsec_rrset_exists(db, newver, &t->name, &flag)); - if (! flag) { - CHECK(delete_if(true_p, db, newver, &t->name, - dns_rdatatype_any, 0, - NULL, &sig_diff)); - } - } - update_log(client, zone, ISC_LOG_DEBUG(3), - "removed any orphaned NSEC records"); - - /* - * See if we need to build NSEC or NSEC3 chains. - */ - 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"); - - /* - * When a name is created or deleted, its predecessor needs to - * have its NSEC updated. - */ - for (t = ISC_LIST_HEAD(diffnames.tuples); - t != NULL; - t = ISC_LIST_NEXT(t, link)) - { - isc_boolean_t existed, exists; - dns_fixedname_t fixedname; - dns_name_t *prevname; - - dns_fixedname_init(&fixedname); - prevname = dns_fixedname_name(&fixedname); - - CHECK(name_exists(db, oldver, &t->name, &existed)); - CHECK(name_exists(db, newver, &t->name, &exists)); - if (exists == existed) - continue; - - /* - * Find the predecessor. - * When names become obscured or unobscured in this update - * transaction, we may find the wrong predecessor because - * the NSECs have not yet been updated to reflect the delegation - * change. This should not matter because in this case, - * the correct predecessor is either the delegation node or - * a newly unobscured node, and those nodes are on the - * "affected" list in any case. - */ - CHECK(next_active(client, zone, db, newver, - &t->name, prevname, ISC_FALSE)); - CHECK(namelist_append_name(&affected, prevname)); - } - - /* - * Find names potentially affected by delegation changes - * (obscured by adding an NS or DNAME, or unobscured by - * removing one). - */ - for (t = ISC_LIST_HEAD(diffnames.tuples); - t != NULL; - t = ISC_LIST_NEXT(t, link)) - { - isc_boolean_t ns_existed, dname_existed; - isc_boolean_t ns_exists, dname_exists; - - CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_ns, 0, - &ns_existed)); - CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_dname, 0, - &dname_existed)); - CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0, - &ns_exists)); - CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_dname, 0, - &dname_exists)); - if ((ns_exists || dname_exists) == (ns_existed || dname_existed)) - continue; - /* - * There was a delegation change. Mark all subdomains - * of t->name as potentially needing a NSEC update. - */ - CHECK(namelist_append_subdomain(db, &t->name, &affected)); + return (result); + } else { + return (ISC_R_SUCCESS); } +} - ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link); - INSIST(ISC_LIST_EMPTY(diffnames.tuples)); +/*% + * Conditionally delete RRs. Apply 'predicate' to the RRs + * specified by 'db', 'ver', 'name', and 'type' (which can + * be dns_rdatatype_any to match any type). Delete those + * RRs for which the predicate returns true, and log the + * deletions in 'diff'. + */ +static isc_result_t +delete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers, + dns_rdata_t *update_rr, dns_diff_t *diff) +{ + conditional_delete_ctx_t ctx; + ctx.predicate = predicate; + ctx.db = db; + ctx.ver = ver; + ctx.diff = diff; + ctx.name = name; + ctx.update_rr = update_rr; + return (foreach_rr(db, ver, name, type, covers, + delete_if_action, &ctx)); +} - CHECK(uniqify_name_list(&affected)); +/**************************************************************************/ +/*% + * Prepare an RR for the addition of the new RR 'ctx->update_rr', + * with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting + * the RRs if it is replaced by the new RR or has a conflicting TTL. + * The necessary changes are appended to ctx->del_diff and ctx->add_diff; + * we need to do all deletions before any additions so that we don't run + * into transient states with conflicting TTLs. + */ - /* - * Determine which names should have NSECs, and delete/create - * NSECs to make it so. We don't know the final NSEC targets yet, - * so we just create placeholder NSECs with arbitrary contents - * to indicate that their respective owner names should be part of - * the NSEC chain. - */ - for (t = ISC_LIST_HEAD(affected.tuples); - t != NULL; - t = ISC_LIST_NEXT(t, link)) - { - isc_boolean_t exists; - dns_name_t *name = &t->name; +typedef struct { + dns_db_t *db; + dns_dbversion_t *ver; + dns_diff_t *diff; + dns_name_t *name; + dns_rdata_t *update_rr; + dns_ttl_t update_rr_ttl; + isc_boolean_t ignore_add; + dns_diff_t del_diff; + dns_diff_t add_diff; +} add_rr_prepare_ctx_t; - CHECK(name_exists(db, newver, name, &exists)); - if (! exists) - continue; - CHECK(is_active(db, newver, name, &flag, &cut, NULL)); - if (!flag) { - /* - * This name is obscured. Delete any - * existing NSEC record. - */ - CHECK(delete_if(true_p, db, newver, name, - dns_rdatatype_nsec, 0, - NULL, &nsec_diff)); - CHECK(delete_if(rrsig_p, db, newver, name, - dns_rdatatype_any, 0, NULL, diff)); - } else { - /* - * This name is not obscured. It needs to have a - * NSEC unless it is the at the origin, in which - * case it should already exist if there is a complete - * NSEC chain and if there isn't a complete NSEC chain - * we don't want to add one as that would signal that - * there is a complete NSEC chain. - */ - if (!dns_name_equal(name, dns_db_origin(db))) { - CHECK(rrset_exists(db, newver, name, - dns_rdatatype_nsec, 0, - &flag)); - if (!flag) - CHECK(add_placeholder_nsec(db, newver, - name, diff)); - } - CHECK(add_exposed_sigs(client, zone, db, newver, name, - cut, &sig_diff, zone_keys, nkeys, - inception, expire, check_ksk, - keyset_kskonly)); - } - } +static isc_result_t +add_rr_prepare_action(void *data, rr_t *rr) { + isc_result_t result = ISC_R_SUCCESS; + add_rr_prepare_ctx_t *ctx = data; + dns_difftuple_t *tuple = NULL; + isc_boolean_t equal; /* - * Now we know which names are part of the NSEC chain. - * Make them all point at their correct targets. + * If the update RR is a "duplicate" of the update RR, + * the update should be silently ignored. */ - for (t = ISC_LIST_HEAD(affected.tuples); - t != NULL; - t = ISC_LIST_NEXT(t, link)) - { - CHECK(rrset_exists(db, newver, &t->name, - dns_rdatatype_nsec, 0, &flag)); - if (flag) { - /* - * There is a NSEC, but we don't know if it is correct. - * Delete it and create a correct one to be sure. - * If the update was unnecessary, the diff minimization - * will take care of eliminating it from the journal, - * IXFRs, etc. - * - * The RRSIG bit should always be set in the NSECs - * we generate, because they will all get RRSIG NSECs. - * (XXX what if the zone keys are missing?). - * Because the RRSIG NSECs have not necessarily been - * created yet, the correctness of the bit mask relies - * on the assumption that NSECs are only created if - * there is other data, and if there is other data, - * there are other RRSIGs. - */ - CHECK(add_nsec(client, zone, db, newver, &t->name, - nsecttl, &nsec_diff)); - } + equal = ISC_TF(dns_rdata_casecompare(&rr->rdata, ctx->update_rr) == 0); + if (equal && rr->ttl == ctx->update_rr_ttl) { + ctx->ignore_add = ISC_TRUE; + return (ISC_R_SUCCESS); } /* - * Minimize the set of NSEC updates so that we don't - * have to regenerate the RRSIG NSECs for NSECs that were - * replaced with identical ones. + * If this RR is "equal" to the update RR, it should + * be deleted before the update RR is added. */ - while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) { - ISC_LIST_UNLINK(nsec_diff.tuples, t, link); - dns_diff_appendminimal(&nsec_mindiff, &t); + if (replaces_p(ctx->update_rr, &rr->rdata)) { + CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL, + ctx->name, rr->ttl, &rr->rdata, + &tuple)); + dns_diff_append(&ctx->del_diff, &tuple); + return (ISC_R_SUCCESS); } - update_log(client, zone, ISC_LOG_DEBUG(3), - "signing rebuilt NSEC chain"); - - /* Update RRSIG NSECs. */ - for (t = ISC_LIST_HEAD(nsec_mindiff.tuples); - t != NULL; - t = ISC_LIST_NEXT(t, link)) - { - if (t->op == DNS_DIFFOP_DEL) { - CHECK(delete_if(true_p, db, newver, &t->name, - dns_rdatatype_rrsig, dns_rdatatype_nsec, - NULL, &sig_diff)); - } else if (t->op == DNS_DIFFOP_ADD) { - CHECK(add_sigs(client, zone, db, newver, &t->name, - dns_rdatatype_nsec, &sig_diff, - zone_keys, nkeys, inception, expire, - check_ksk, keyset_kskonly)); - } else { - INSIST(0); + /* + * If this RR differs in TTL from the update RR, + * its TTL must be adjusted. + */ + if (rr->ttl != ctx->update_rr_ttl) { + CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL, + ctx->name, rr->ttl, &rr->rdata, + &tuple)); + dns_diff_append(&ctx->del_diff, &tuple); + if (!equal) { + CHECK(dns_difftuple_create(ctx->add_diff.mctx, + DNS_DIFFOP_ADD, ctx->name, + ctx->update_rr_ttl, + &rr->rdata, &tuple)); + dns_diff_append(&ctx->add_diff, &tuple); } } + failure: + return (result); +} - update_nsec3: - - /* Record our changes for the journal. */ - while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) { - ISC_LIST_UNLINK(sig_diff.tuples, t, link); - dns_diff_appendminimal(diff, &t); - } - while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) { - ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link); - dns_diff_appendminimal(diff, &t); - } - - INSIST(ISC_LIST_EMPTY(sig_diff.tuples)); - INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); - INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); - - if (!build_nsec3) { - update_log(client, zone, ISC_LOG_DEBUG(3), - "no NSEC3 chains to rebuild"); - goto failure; - } - - update_log(client, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC3 chains"); +/**************************************************************************/ +/* + * Miscellaneous subroutines. + */ - dns_diff_clear(&diffnames); - dns_diff_clear(&affected); +/*% + * Extract a single update RR from 'section' of dynamic update message + * 'msg', with consistency checking. + * + * Stores the owner name, rdata, and TTL of the update RR at 'name', + * 'rdata', and 'ttl', respectively. + */ +static void +get_current_rr(dns_message_t *msg, dns_section_t section, + dns_rdataclass_t zoneclass, dns_name_t **name, + dns_rdata_t *rdata, dns_rdatatype_t *covers, + dns_ttl_t *ttl, dns_rdataclass_t *update_class) +{ + dns_rdataset_t *rdataset; + isc_result_t result; + dns_message_currentname(msg, section, name); + rdataset = ISC_LIST_HEAD((*name)->list); + INSIST(rdataset != NULL); + INSIST(ISC_LIST_NEXT(rdataset, link) == NULL); + *covers = rdataset->covers; + *ttl = rdataset->ttl; + result = dns_rdataset_first(rdataset); + INSIST(result == ISC_R_SUCCESS); + dns_rdataset_current(rdataset, rdata); + INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE); + *update_class = rdata->rdclass; + rdata->rdclass = zoneclass; +} - CHECK(dns_diff_sort(diff, temp_order)); +/*% + * Increment the SOA serial number of database 'db', version 'ver'. + * Replace the SOA record in the database, and log the + * change in 'diff'. + */ /* - * Find names potentially affected by delegation changes - * (obscured by adding an NS or DNAME, or unobscured by - * removing one). + * XXXRTH Failures in this routine will be worth logging, when + * we have a logging system. Failure to find the zonename + * or the SOA rdataset warrant at least an UNEXPECTED_ERROR(). */ - t = ISC_LIST_HEAD(diff->tuples); - while (t != NULL) { - dns_name_t *name = &t->name; - - 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) { - t = ISC_LIST_NEXT(t, link); - continue; - } - - CHECK(namelist_append_name(&affected, name)); - - CHECK(rrset_exists(db, oldver, name, dns_rdatatype_ns, 0, - &ns_existed)); - CHECK(rrset_exists(db, oldver, name, dns_rdatatype_dname, 0, - &dname_existed)); - CHECK(rrset_exists(db, newver, name, dns_rdatatype_ns, 0, - &ns_exists)); - CHECK(rrset_exists(db, newver, name, dns_rdatatype_dname, 0, - &dname_exists)); - - exists = ns_exists || dname_exists; - existed = ns_existed || dname_existed; - if (exists == existed) - goto nextname; - /* - * There was a delegation change. Mark all subdomains - * of t->name as potentially needing a NSEC3 update. - */ - CHECK(namelist_append_subdomain(db, name, &affected)); - - nextname: - while (t != NULL && dns_name_equal(&t->name, name)) - t = ISC_LIST_NEXT(t, link); - } +static isc_result_t +update_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, + isc_mem_t *mctx, dns_updatemethod_t method) +{ + dns_difftuple_t *deltuple = NULL; + dns_difftuple_t *addtuple = NULL; + isc_uint32_t serial; + isc_result_t result; - for (t = ISC_LIST_HEAD(affected.tuples); - t != NULL; - t = ISC_LIST_NEXT(t, link)) { - dns_name_t *name = &t->name; + CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple)); + CHECK(dns_difftuple_copy(deltuple, &addtuple)); + addtuple->op = DNS_DIFFOP_ADD; - unsecure = ISC_FALSE; /* Silence compiler warning. */ - CHECK(is_active(db, newver, name, &flag, &cut, &unsecure)); + serial = dns_soa_getserial(&addtuple->rdata); + serial = dns_update_soaserial(serial, method); + dns_soa_setserial(serial, &addtuple->rdata); + CHECK(do_one_tuple(&deltuple, db, ver, diff)); + CHECK(do_one_tuple(&addtuple, db, ver, diff)); + result = ISC_R_SUCCESS; - if (!flag) { - CHECK(delete_if(rrsig_p, db, newver, name, - dns_rdatatype_any, 0, NULL, diff)); - CHECK(dns_nsec3_delnsec3sx(db, newver, name, - privatetype, &nsec_diff)); - } else { - CHECK(add_exposed_sigs(client, zone, db, newver, name, - cut, &sig_diff, zone_keys, nkeys, - inception, expire, check_ksk, - keyset_kskonly)); - CHECK(dns_nsec3_addnsec3sx(db, newver, name, nsecttl, - unsecure, privatetype, - &nsec_diff)); - } - } + failure: + if (addtuple != NULL) + dns_difftuple_free(&addtuple); + if (deltuple != NULL) + dns_difftuple_free(&deltuple); + return (result); +} - /* - * Minimize the set of NSEC3 updates so that we don't - * have to regenerate the RRSIG NSEC3s for NSEC3s that were - * replaced with identical ones. - */ - while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) { - ISC_LIST_UNLINK(nsec_diff.tuples, t, link); - dns_diff_appendminimal(&nsec_mindiff, &t); - } +/*% + * Check that the new SOA record at 'update_rdata' does not + * illegally cause the SOA serial number to decrease or stay + * unchanged relative to the existing SOA in 'db'. + * + * Sets '*ok' to ISC_TRUE if the update is legal, ISC_FALSE if not. + * + * William King points out that RFC2136 is inconsistent about + * the case where the serial number stays unchanged: + * + * section 3.4.2.2 requires a server to ignore a SOA update request + * if the serial number on the update SOA is less_than_or_equal to + * the zone SOA serial. + * + * section 3.6 requires a server to ignore a SOA update request if + * the serial is less_than the zone SOA serial. + * + * Paul says 3.4.2.2 is correct. + * + */ +static isc_result_t +check_soa_increment(dns_db_t *db, dns_dbversion_t *ver, + dns_rdata_t *update_rdata, isc_boolean_t *ok) +{ + isc_uint32_t db_serial; + isc_uint32_t update_serial; + isc_result_t result; - update_log(client, zone, ISC_LOG_DEBUG(3), - "signing rebuilt NSEC3 chain"); + update_serial = dns_soa_getserial(update_rdata); - /* Update RRSIG NSEC3s. */ - for (t = ISC_LIST_HEAD(nsec_mindiff.tuples); - t != NULL; - t = ISC_LIST_NEXT(t, link)) - { - if (t->op == DNS_DIFFOP_DEL) { - CHECK(delete_if(true_p, db, newver, &t->name, - dns_rdatatype_rrsig, - dns_rdatatype_nsec3, - NULL, &sig_diff)); - } else if (t->op == DNS_DIFFOP_ADD) { - CHECK(add_sigs(client, zone, db, newver, &t->name, - dns_rdatatype_nsec3, - &sig_diff, zone_keys, nkeys, - inception, expire, check_ksk, - keyset_kskonly)); - } else { - INSIST(0); - } - } + result = dns_db_getsoaserial(db, ver, &db_serial); + if (result != ISC_R_SUCCESS) + return (result); - /* Record our changes for the journal. */ - while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) { - ISC_LIST_UNLINK(sig_diff.tuples, t, link); - dns_diff_appendminimal(diff, &t); - } - while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) { - ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link); - dns_diff_appendminimal(diff, &t); + if (DNS_SERIAL_GE(db_serial, update_serial)) { + *ok = ISC_FALSE; + } else { + *ok = ISC_TRUE; } - INSIST(ISC_LIST_EMPTY(sig_diff.tuples)); - INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); - INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); - - failure: - dns_diff_clear(&sig_diff); - dns_diff_clear(&nsec_diff); - dns_diff_clear(&nsec_mindiff); - - dns_diff_clear(&affected); - dns_diff_clear(&diffnames); - - for (i = 0; i < nkeys; i++) - dst_key_free(&zone_keys[i]); + return (ISC_R_SUCCESS); - return (result); } - /**************************************************************************/ /*% * The actual update code in all its glory. We try to follow @@ -2684,7 +1515,7 @@ ns_update_start(ns_client_t *client, isc_result_t sigresult) { isc_result_t result; dns_name_t *zonename; dns_rdataset_t *zone_rdataset; - dns_zone_t *zone = NULL; + dns_zone_t *zone = NULL, *raw = NULL; /* * Interpret the zone section. @@ -2718,6 +1549,17 @@ ns_update_start(ns_client_t *client, isc_result_t sigresult) { if (result != ISC_R_SUCCESS) FAILC(DNS_R_NOTAUTH, "not authoritative for update zone"); + /* + * If there is a raw (unsigned) zone associated with this + * zone then it processes the UPDATE request. + */ + dns_zone_getraw(zone, &raw); + if (raw != NULL) { + dns_zone_detach(&zone); + dns_zone_attach(raw, &zone); + dns_zone_detach(&raw); + } + switch(dns_zone_gettype(zone)) { case dns_zone_master: case dns_zone_dlz: @@ -4229,9 +3071,14 @@ update_action(isc_task_t *task, isc_event_t *event) { &diff)); } else if (has_dnskey && isdnssec(db, ver, privatetype)) { isc_uint32_t interval; + dns_update_log_t log; + interval = dns_zone_getsigvalidityinterval(zone); - result = update_signatures(client, zone, db, oldver, - ver, &diff, interval); + log.func = update_log_cb; + log.arg = client; + result = dns_update_signatures(&log, zone, db, oldver, + ver, &diff, interval); + if (result != ISC_R_SUCCESS) { update_log(client, zone, ISC_LOG_ERROR, diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 32f6825fa19..6c12e4fb4b4 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zoneconf.c,v 1.178 2011/07/01 02:25:47 marka Exp $ */ +/* $Id: zoneconf.c,v 1.179 2011/08/30 05:16:11 marka Exp $ */ /*% */ @@ -786,7 +786,7 @@ checknames(dns_zonetype_t ztype, const cfg_obj_t **maps, isc_result_t ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac, - dns_zone_t *zone) + dns_zone_t *zone, dns_zone_t *raw) { isc_result_t result; const char *zname; @@ -820,6 +820,7 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, isc_stats_t *zoneqrystats; isc_boolean_t zonestats_on; int seconds; + dns_zone_t *mayberaw = (raw != NULL) ? raw : zone; i = 0; if (zconfig != NULL) { @@ -851,9 +852,16 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, RETERR(ns_config_getclass(cfg_tuple_get(zconfig, "class"), vclass, &zclass)); dns_zone_setclass(zone, zclass); + if (raw != NULL) + dns_zone_setclass(raw, zclass); ztype = zonetype_fromconfig(zoptions); - dns_zone_settype(zone, ztype); + if (raw != NULL) { + dns_zone_settype(raw, ztype); + dns_zone_settype(zone, dns_zone_master); + } else + dns_zone_settype(zone, ztype); + obj = NULL; result = cfg_map_get(zoptions, "database", &obj); @@ -914,18 +922,40 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, else INSIST(0); } - RETERR(dns_zone_setfile2(zone, filename, masterformat)); + + if (raw != NULL) { +#define SIGNED ".signed" + size_t signedlen = strlen(filename) + sizeof(SIGNED); + char *signedname; + + RETERR(dns_zone_setfile2(raw, filename, masterformat)); + signedname = isc_mem_get(mctx, signedlen); + if (signedname == NULL) + return (ISC_R_NOMEMORY); + + (void)snprintf(signedname, signedlen, "%s" SIGNED, filename); + result = dns_zone_setfile2(zone, signedname, + dns_masterformat_raw); + isc_mem_put(mctx, signedname, signedlen); + if (result != ISC_R_SUCCESS) + return (result); + } else + RETERR(dns_zone_setfile2(zone, filename, masterformat)); obj = NULL; result = cfg_map_get(zoptions, "journal", &obj); if (result == ISC_R_SUCCESS) - RETERR(dns_zone_setjournal(zone, cfg_obj_asstring(obj))); + RETERR(dns_zone_setjournal(mayberaw, cfg_obj_asstring(obj))); + /* + * Notify messages are processed by the raw zone if it exists. + */ if (ztype == dns_zone_slave) RETERR(configure_zone_acl(zconfig, vconfig, config, - allow_notify, ac, zone, + allow_notify, ac, mayberaw, dns_zone_setnotifyacl, dns_zone_clearnotifyacl)); + /* * XXXAG This probably does not make sense for stubs. */ @@ -955,6 +985,8 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, else INSIST(0); } + if (raw != NULL) + dns_zone_setdialup(raw, dialup); dns_zone_setdialup(zone, dialup); obj = NULL; @@ -994,6 +1026,8 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, else INSIST(0); } + if (raw != NULL) + dns_zone_setnotifytype(raw, dns_notifytype_no); dns_zone_setnotifytype(zone, notifytype); obj = NULL; @@ -1052,6 +1086,8 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, obj = NULL; result = ns_config_get(maps, "max-journal-size", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (raw != NULL) + dns_zone_setjournalsize(raw, -1); dns_zone_setjournalsize(zone, -1); if (cfg_obj_isstring(obj)) { const char *str = cfg_obj_asstring(obj); @@ -1071,6 +1107,8 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, } journal_size = (isc_uint32_t)value; } + if (raw != NULL) + dns_zone_setjournalsize(raw, journal_size); dns_zone_setjournalsize(zone, journal_size); obj = NULL; @@ -1086,7 +1124,14 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, ixfrdiff = ISC_TRUE; else ixfrdiff = ISC_FALSE; - dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS, ixfrdiff); + if (raw != NULL) { + dns_zone_setoption(raw, DNS_ZONEOPT_IXFRFROMDIFFS, + ISC_TRUE); + dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS, + ISC_FALSE); + } else + dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS, + ixfrdiff); checknames(ztype, maps, &obj); INSIST(obj != NULL); @@ -1099,8 +1144,21 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, fail = check = ISC_FALSE; } else INSIST(0); - dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, check); - dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL, fail); + if (raw != NULL) { + dns_zone_setoption(raw, DNS_ZONEOPT_CHECKNAMES, + check); + dns_zone_setoption(raw, DNS_ZONEOPT_CHECKNAMESFAIL, + fail); + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, + ISC_FALSE); + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL, + ISC_FALSE); + } else { + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, + check); + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL, + fail); + } obj = NULL; result = ns_config_get(maps, "notify-delay", &obj); @@ -1159,11 +1217,11 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, dns_acl_t *updateacl; RETERR(configure_zone_acl(zconfig, vconfig, config, - allow_update, ac, zone, + allow_update, ac, mayberaw, dns_zone_setupdateacl, dns_zone_clearupdateacl)); - updateacl = dns_zone_getupdateacl(zone); + updateacl = dns_zone_getupdateacl(mayberaw); if (updateacl != NULL && dns_acl_isinsecure(updateacl)) isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, @@ -1171,7 +1229,12 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, "address, which is insecure", zname); - RETERR(configure_zone_ssutable(zoptions, zone, zname)); + RETERR(configure_zone_ssutable(zoptions, mayberaw, zname)); + } + + if (ztype == dns_zone_master || raw != NULL) { + isc_boolean_t allow = ISC_FALSE, maint = ISC_FALSE; + isc_boolean_t create = ISC_FALSE; obj = NULL; result = ns_config_get(maps, "sig-validity-interval", &obj); @@ -1236,10 +1299,28 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, INSIST(result == ISC_R_SUCCESS && obj != NULL); RETERR(dns_zone_setrefreshkeyinterval(zone, cfg_obj_asuint32(obj))); - } else if (ztype == dns_zone_slave) { + + obj = NULL; + result = cfg_map_get(zoptions, "auto-dnssec", &obj); + if (result == ISC_R_SUCCESS) { + const char *arg = cfg_obj_asstring(obj); + if (strcasecmp(arg, "allow") == 0) + allow = ISC_TRUE; + else if (strcasecmp(arg, "maintain") == 0) + allow = maint = ISC_TRUE; + else if (strcasecmp(arg, "off") == 0) + ; + else + INSIST(0); + dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, allow); + dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, maint); + } + } + + if (ztype == dns_zone_slave) { RETERR(configure_zone_acl(zconfig, vconfig, config, - allow_update_forwarding, ac, zone, - dns_zone_setforwardacl, + allow_update_forwarding, ac, + mayberaw, dns_zone_setforwardacl, dns_zone_clearforwardacl)); } @@ -1255,7 +1336,7 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, check = cfg_obj_asboolean(obj); else check = ISC_FALSE; - dns_zone_setoption(zone, DNS_ZONEOPT_CHECKWILDCARD, check); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKWILDCARD, check); obj = NULL; result = ns_config_get(maps, "check-dup-records", &obj); @@ -1269,8 +1350,8 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, fail = check = ISC_FALSE; } else INSIST(0); - dns_zone_setoption(zone, DNS_ZONEOPT_CHECKDUPRR, check); - dns_zone_setoption(zone, DNS_ZONEOPT_CHECKDUPRRFAIL, fail); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKDUPRR, check); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKDUPRRFAIL, fail); obj = NULL; result = ns_config_get(maps, "check-mx", &obj); @@ -1284,13 +1365,13 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, fail = check = ISC_FALSE; } else INSIST(0); - dns_zone_setoption(zone, DNS_ZONEOPT_CHECKMX, check); - dns_zone_setoption(zone, DNS_ZONEOPT_CHECKMXFAIL, fail); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKMX, check); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKMXFAIL, fail); obj = NULL; result = ns_config_get(maps, "check-integrity", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - dns_zone_setoption(zone, DNS_ZONEOPT_CHECKINTEGRITY, + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKINTEGRITY, cfg_obj_asboolean(obj)); obj = NULL; @@ -1305,8 +1386,8 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, warn = ignore = ISC_TRUE; } else INSIST(0); - dns_zone_setoption(zone, DNS_ZONEOPT_WARNMXCNAME, warn); - dns_zone_setoption(zone, DNS_ZONEOPT_IGNOREMXCNAME, ignore); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_WARNMXCNAME, warn); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_IGNOREMXCNAME, ignore); obj = NULL; result = ns_config_get(maps, "check-srv-cname", &obj); @@ -1320,31 +1401,16 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, warn = ignore = ISC_TRUE; } else INSIST(0); - dns_zone_setoption(zone, DNS_ZONEOPT_WARNSRVCNAME, warn); - dns_zone_setoption(zone, DNS_ZONEOPT_IGNORESRVCNAME, ignore); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_WARNSRVCNAME, warn); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_IGNORESRVCNAME, + ignore); obj = NULL; result = ns_config_get(maps, "dnssec-secure-to-insecure", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - dns_zone_setoption(zone, DNS_ZONEOPT_SECURETOINSECURE, + dns_zone_setoption(mayberaw, DNS_ZONEOPT_SECURETOINSECURE, cfg_obj_asboolean(obj)); - obj = NULL; - result = cfg_map_get(zoptions, "auto-dnssec", &obj); - if (result == ISC_R_SUCCESS) { - const char *arg = cfg_obj_asstring(obj); - if (strcasecmp(arg, "allow") == 0) - allow = ISC_TRUE; - else if (strcasecmp(arg, "maintain") == 0) - allow = maint = ISC_TRUE; - else if (strcasecmp(arg, "off") == 0) - ; - else - INSIST(0); - dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, allow); - dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, maint); - } - obj = NULL; result = cfg_map_get(zoptions, "dnssec-update-mode", &obj); if (result == ISC_R_SUCCESS) { @@ -1385,12 +1451,12 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, RETERR(ns_config_getipandkeylist(config, obj, mctx, &addrs, &keynames, &count)); - result = dns_zone_setmasterswithkeys(zone, addrs, + result = dns_zone_setmasterswithkeys(mayberaw, addrs, keynames, count); ns_config_putipandkeylist(mctx, &addrs, &keynames, count); - } else - result = dns_zone_setmasters(zone, NULL, 0); + } else + result = dns_zone_setmasters(mayberaw, NULL, 0); RETERR(result); multi = ISC_FALSE; @@ -1400,59 +1466,63 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, INSIST(result == ISC_R_SUCCESS && obj != NULL); multi = cfg_obj_asboolean(obj); } - dns_zone_setoption(zone, DNS_ZONEOPT_MULTIMASTER, multi); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_MULTIMASTER, multi); obj = NULL; result = ns_config_get(maps, "max-transfer-time-in", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - dns_zone_setmaxxfrin(zone, cfg_obj_asuint32(obj) * 60); + dns_zone_setmaxxfrin(mayberaw, cfg_obj_asuint32(obj) * 60); obj = NULL; result = ns_config_get(maps, "max-transfer-idle-in", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - dns_zone_setidlein(zone, cfg_obj_asuint32(obj) * 60); + dns_zone_setidlein(mayberaw, cfg_obj_asuint32(obj) * 60); obj = NULL; result = ns_config_get(maps, "max-refresh-time", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - dns_zone_setmaxrefreshtime(zone, cfg_obj_asuint32(obj)); + dns_zone_setmaxrefreshtime(mayberaw, cfg_obj_asuint32(obj)); obj = NULL; result = ns_config_get(maps, "min-refresh-time", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - dns_zone_setminrefreshtime(zone, cfg_obj_asuint32(obj)); + dns_zone_setminrefreshtime(mayberaw, cfg_obj_asuint32(obj)); obj = NULL; result = ns_config_get(maps, "max-retry-time", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - dns_zone_setmaxretrytime(zone, cfg_obj_asuint32(obj)); + dns_zone_setmaxretrytime(mayberaw, cfg_obj_asuint32(obj)); obj = NULL; result = ns_config_get(maps, "min-retry-time", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - dns_zone_setminretrytime(zone, cfg_obj_asuint32(obj)); + dns_zone_setminretrytime(mayberaw, cfg_obj_asuint32(obj)); obj = NULL; result = ns_config_get(maps, "transfer-source", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - RETERR(dns_zone_setxfrsource4(zone, cfg_obj_assockaddr(obj))); + RETERR(dns_zone_setxfrsource4(mayberaw, + cfg_obj_assockaddr(obj))); ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj)); obj = NULL; result = ns_config_get(maps, "transfer-source-v6", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - RETERR(dns_zone_setxfrsource6(zone, cfg_obj_assockaddr(obj))); + RETERR(dns_zone_setxfrsource6(mayberaw, + cfg_obj_assockaddr(obj))); ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj)); obj = NULL; result = ns_config_get(maps, "alt-transfer-source", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - RETERR(dns_zone_setaltxfrsource4(zone, cfg_obj_assockaddr(obj))); + RETERR(dns_zone_setaltxfrsource4(mayberaw, + cfg_obj_assockaddr(obj))); obj = NULL; result = ns_config_get(maps, "alt-transfer-source-v6", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); - RETERR(dns_zone_setaltxfrsource6(zone, cfg_obj_assockaddr(obj))); + RETERR(dns_zone_setaltxfrsource6(mayberaw, + cfg_obj_assockaddr(obj))); obj = NULL; (void)ns_config_get(maps, "use-alt-transfer-source", &obj); @@ -1468,11 +1538,11 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, alt = ISC_FALSE; } else alt = cfg_obj_asboolean(obj); - dns_zone_setoption(zone, DNS_ZONEOPT_USEALTXFRSRC, alt); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_USEALTXFRSRC, alt); obj = NULL; (void)ns_config_get(maps, "try-tcp-refresh", &obj); - dns_zone_setoption(zone, DNS_ZONEOPT_TRYTCPREFRESH, + dns_zone_setoption(mayberaw, DNS_ZONEOPT_TRYTCPREFRESH, cfg_obj_asboolean(obj)); break; @@ -1540,7 +1610,18 @@ ns_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig) { if (!((cfilename == NULL && zfilename == NULL) || (cfilename != NULL && zfilename != NULL && strcmp(cfilename, zfilename) == 0))) - return (ISC_FALSE); + return (ISC_FALSE); + + obj = NULL; + (void)cfg_map_get(zoptions, "signing", &obj); + if (obj == NULL || !cfg_obj_asboolean(obj)) { + dns_zone_t *raw = NULL; + dns_zone_getraw(zone, &raw); + if (raw != NULL) { + dns_zone_detach(&raw); + return (ISC_FALSE); + } + } return (ISC_TRUE); } diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index 51d9b8ad43f..0478efaec2d 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -15,7 +15,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: conf.sh.in,v 1.68 2011/08/09 02:24:28 marka Exp $ +# $Id: conf.sh.in,v 1.69 2011/08/30 05:16:11 marka Exp $ # # Common configuration data for system tests, to be sourced into @@ -54,7 +54,7 @@ JOURNALPRINT=$TOP/bin/tools/named-journalprint # v6synth SUBDIRS="acl allow_query addzone autosign builtin cacheclean checkconf checknames checkzone database dlv dlvauto dlz dlzexternal - dname dns64 dnssec forward glue gost ixfr limits + dname dns64 dnssec forward glue gost ixfr inline limits logfileconfig lwresd masterfile masterformat metadata notify nsupdate pending pkcs11 redirect resolver rndc rpz rrsetorder sortlist smartsign staticstub stub tkey tsig tsiggss unknown diff --git a/bin/tests/system/inline/clean.sh b/bin/tests/system/inline/clean.sh new file mode 100644 index 00000000000..0d87e303292 --- /dev/null +++ b/bin/tests/system/inline/clean.sh @@ -0,0 +1,11 @@ +rm -f */named.memstats +rm -f */named.run +rm -f */trusted.conf +rm -f ns2/bits.db +rm -f ns3/K* +rm -f ns3/bits.bk +rm -f ns3/bits.bk.jnl +rm -f ns3/bits.bk.signed +rm -f ns3/bits.bk.signed.jnl +rm -f ns4/noixfr.db +rm -f random.data diff --git a/bin/tests/system/inline/ns1/named.conf b/bin/tests/system/inline/ns1/named.conf new file mode 100644 index 00000000000..55caaad805a --- /dev/null +++ b/bin/tests/system/inline/ns1/named.conf @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: named.conf,v 1.1 2011/08/30 05:16:13 marka Exp $ */ + +// NS1 + +controls { /* empty */ }; + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify yes; + dnssec-enable yes; + dnssec-validation yes; +}; + +zone "." { + type master; + file "root.db.signed"; +}; + +// include "trusted.conf"; diff --git a/bin/tests/system/inline/ns2/bits.db.in b/bin/tests/system/inline/ns2/bits.db.in new file mode 100644 index 00000000000..d62fbf1d42a --- /dev/null +++ b/bin/tests/system/inline/ns2/bits.db.in @@ -0,0 +1,135 @@ +; Copyright (C) 2004, 2007-2011 Internet Systems Consortium, Inc. ("ISC") +; Copyright (C) 2000-2002 Internet Software Consortium. +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +; $Id: bits.db.in,v 1.1 2011/08/30 05:16:13 marka Exp $ + +$TTL 300 ; 5 minutes +@ IN SOA ns2 . ( + 2000042407 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns3 +ns2 A 10.53.0.2 +ns3 A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 + +; Used for testing ANY queries +foo TXT "testing" +foo A 10.0.1.0 + +bad-cname CNAME a +bad-dname DNAME @ + +; Used for testing CNAME queries +cname1 CNAME cname1-target +cname1-target TXT "testing cname" + +cname2 CNAME cname2-target +cname2-target TXT "testing cname" + +; Used for testing DNAME queries +dname1 DNAME dname1-target +foo.dname1-target TXT "testing dname" + +dname2 DNAME dname2-target +foo.dname2-target TXT "testing dname" + +; A secure subdomain +secure NS ns.secure +ns.secure A 10.53.0.3 + +; An insecure subdomain +insecure NS ns.insecure +ns.insecure A 10.53.0.3 + +; A secure subdomain we're going to inject bogus data into +bogus NS ns.bogus +ns.bogus A 10.53.0.3 + +; A dynamic secure subdomain +dynamic NS dynamic +dynamic A 10.53.0.3 + +; A insecure subdomain +mustbesecure NS ns.mustbesecure +ns.mustbesecure A 10.53.0.3 + +; A rfc2535 signed zone w/ CNAME +rfc2535 NS ns.rfc2535 +ns.rfc2535 A 10.53.0.3 + +z A 10.0.0.26 + +keyless NS ns.keyless +ns.keyless A 10.53.0.3 + +nsec3 NS ns.nsec3 +ns.nsec3 A 10.53.0.3 + +optout NS ns.optout +ns.optout A 10.53.0.3 + +nsec3-unknown NS ns.nsec3-unknown +ns.nsec3-unknown A 10.53.0.3 + +optout-unknown NS ns.optout-unknown +ns.optout-unknown A 10.53.0.3 + +multiple NS ns.multiple +ns.multiple A 10.53.0.3 + +*.wild A 10.0.0.27 + +rsasha256 NS ns.rsasha256 +ns.rsasha256 A 10.53.0.3 + +rsasha512 NS ns.rsasha512 +ns.rsasha512 A 10.53.0.3 + +kskonly NS ns.kskonly +ns.kskonly A 10.53.0.3 + +update-nsec3 NS ns.update-nsec3 +ns.update-nsec3 A 10.53.0.3 + +auto-nsec NS ns.auto-nsec +ns.auto-nsec A 10.53.0.3 + +auto-nsec3 NS ns.auto-nsec3 +ns.auto-nsec3 A 10.53.0.3 + + +below-cname CNAME some.where.else. + +insecure.below-cname NS ns.insecure.below-cname +ns.insecure.below-cname A 10.53.0.3 + +secure.below-cname NS ns.secure.below-cname +ns.secure.below-cname A 10.53.0.3 + +ttlpatch NS ns.ttlpatch +ns.ttlpatch A 10.53.0.3 + +split-dnssec NS ns.split-dnssec +ns.split-dnssec A 10.53.0.3 + +split-smart NS ns.split-smart +ns.split-smart A 10.53.0.3 diff --git a/bin/tests/system/inline/ns2/named.conf b/bin/tests/system/inline/ns2/named.conf new file mode 100644 index 00000000000..67122de1746 --- /dev/null +++ b/bin/tests/system/inline/ns2/named.conf @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: named.conf,v 1.1 2011/08/30 05:16:13 marka Exp $ */ + +// NS2 + +controls { /* empty */ }; + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + recursion no; + notify yes; + notify-delay 0; +}; + +zone "bits" { + type master; + file "bits.db"; + allow-update { any; }; +}; diff --git a/bin/tests/system/inline/ns3/named.conf b/bin/tests/system/inline/ns3/named.conf new file mode 100644 index 00000000000..2cca211fad8 --- /dev/null +++ b/bin/tests/system/inline/ns3/named.conf @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: named.conf,v 1.1 2011/08/30 05:16:13 marka Exp $ */ + +// NS2 + +controls { /* empty */ }; + +options { + query-source address 10.53.0.3; + notify-source 10.53.0.3; + transfer-source 10.53.0.3; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + recursion no; + notify yes; + try-tcp-refresh no; + notify-delay 0; +}; + +zone "bits" { + type slave; + masters { 10.53.0.2; }; + inline-signing yes; + auto-dnssec maintain; + allow-update-forwarding { any; }; + file "bits.bk"; +}; + +server 10.53.0.4 { request-ixfr no; }; + +zone "noixfr" { + type slave; + masters { 10.53.0.4; }; + inline-signing yes; + auto-dnssec maintain; + allow-update-forwarding { any; }; + file "noixfr.bk"; +}; diff --git a/bin/tests/system/inline/ns3/sign.sh b/bin/tests/system/inline/ns3/sign.sh new file mode 100644 index 00000000000..1eea982dc07 --- /dev/null +++ b/bin/tests/system/inline/ns3/sign.sh @@ -0,0 +1,35 @@ +#!/bin/sh -e +# +# Copyright (C) 2004, 2006-2011 Internet Systems Consortium, Inc. ("ISC") +# Copyright (C) 2000-2002 Internet Software Consortium. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +# $Id: sign.sh,v 1.1 2011/08/30 05:16:13 marka Exp $ + +SYSTEMTESTTOP=../.. +. $SYSTEMTESTTOP/conf.sh + +RANDFILE=../random.data + +zone=bits +rm -f K${zone}.+*+*.key +rm -f K${zone}.+*+*.private +keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 768 -n zone $zone` +keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -f KSK $zone` + +zone=noixfr +rm -f K${zone}.+*+*.key +rm -f K${zone}.+*+*.private +keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 768 -n zone $zone` +keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -f KSK $zone` diff --git a/bin/tests/system/inline/ns4/named.conf b/bin/tests/system/inline/ns4/named.conf new file mode 100644 index 00000000000..d02004d6ae3 --- /dev/null +++ b/bin/tests/system/inline/ns4/named.conf @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: named.conf,v 1.1 2011/08/30 05:16:13 marka Exp $ */ + +// NS2 + +controls { /* empty */ }; + +options { + query-source address 10.53.0.4; + notify-source 10.53.0.4; + transfer-source 10.53.0.4; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.4; }; + listen-on-v6 { none; }; + recursion no; + notify yes; + notify-delay 0; +}; + +zone "noixfr" { + type master; + file "noixfr.db"; + allow-update { any; }; +}; diff --git a/bin/tests/system/inline/ns4/noixfr.db.in b/bin/tests/system/inline/ns4/noixfr.db.in new file mode 100644 index 00000000000..0d7317638dc --- /dev/null +++ b/bin/tests/system/inline/ns4/noixfr.db.in @@ -0,0 +1,135 @@ +; Copyright (C) 2004, 2007-2011 Internet Systems Consortium, Inc. ("ISC") +; Copyright (C) 2000-2002 Internet Software Consortium. +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +; $Id: noixfr.db.in,v 1.1 2011/08/30 05:16:13 marka Exp $ + +$TTL 300 ; 5 minutes +@ IN SOA ns4 . ( + 2000042407 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns3 +ns4 A 10.53.0.4 +ns3 A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 + +; Used for testing ANY queries +foo TXT "testing" +foo A 10.0.1.0 + +bad-cname CNAME a +bad-dname DNAME @ + +; Used for testing CNAME queries +cname1 CNAME cname1-target +cname1-target TXT "testing cname" + +cname2 CNAME cname2-target +cname2-target TXT "testing cname" + +; Used for testing DNAME queries +dname1 DNAME dname1-target +foo.dname1-target TXT "testing dname" + +dname2 DNAME dname2-target +foo.dname2-target TXT "testing dname" + +; A secure subdomain +secure NS ns.secure +ns.secure A 10.53.0.3 + +; An insecure subdomain +insecure NS ns.insecure +ns.insecure A 10.53.0.3 + +; A secure subdomain we're going to inject bogus data into +bogus NS ns.bogus +ns.bogus A 10.53.0.3 + +; A dynamic secure subdomain +dynamic NS dynamic +dynamic A 10.53.0.3 + +; A insecure subdomain +mustbesecure NS ns.mustbesecure +ns.mustbesecure A 10.53.0.3 + +; A rfc2535 signed zone w/ CNAME +rfc2535 NS ns.rfc2535 +ns.rfc2535 A 10.53.0.3 + +z A 10.0.0.26 + +keyless NS ns.keyless +ns.keyless A 10.53.0.3 + +nsec3 NS ns.nsec3 +ns.nsec3 A 10.53.0.3 + +optout NS ns.optout +ns.optout A 10.53.0.3 + +nsec3-unknown NS ns.nsec3-unknown +ns.nsec3-unknown A 10.53.0.3 + +optout-unknown NS ns.optout-unknown +ns.optout-unknown A 10.53.0.3 + +multiple NS ns.multiple +ns.multiple A 10.53.0.3 + +*.wild A 10.0.0.27 + +rsasha256 NS ns.rsasha256 +ns.rsasha256 A 10.53.0.3 + +rsasha512 NS ns.rsasha512 +ns.rsasha512 A 10.53.0.3 + +kskonly NS ns.kskonly +ns.kskonly A 10.53.0.3 + +update-nsec3 NS ns.update-nsec3 +ns.update-nsec3 A 10.53.0.3 + +auto-nsec NS ns.auto-nsec +ns.auto-nsec A 10.53.0.3 + +auto-nsec3 NS ns.auto-nsec3 +ns.auto-nsec3 A 10.53.0.3 + + +below-cname CNAME some.where.else. + +insecure.below-cname NS ns.insecure.below-cname +ns.insecure.below-cname A 10.53.0.3 + +secure.below-cname NS ns.secure.below-cname +ns.secure.below-cname A 10.53.0.3 + +ttlpatch NS ns.ttlpatch +ns.ttlpatch A 10.53.0.3 + +split-dnssec NS ns.split-dnssec +ns.split-dnssec A 10.53.0.3 + +split-smart NS ns.split-smart +ns.split-smart A 10.53.0.3 diff --git a/bin/tests/system/inline/setup.sh b/bin/tests/system/inline/setup.sh new file mode 100644 index 00000000000..9c1a1d298a7 --- /dev/null +++ b/bin/tests/system/inline/setup.sh @@ -0,0 +1,23 @@ +sh clean.sh + +touch ns2/trusted.conf +cp ns2/bits.db.in ns2/bits.db +rm -f ns2/bits.db.jnl + +rm -f ns3/bits.bk +rm -f ns3/bits.bk.jnl +rm -f ns3/bits.bk.signed +rm -f ns3/bits.bk.signed.jnl + +touch ns4/trusted.conf +cp ns4/noixfr.db.in ns4/noixfr.db +rm -f ns4/noixfr.db.jnl + +rm -f ns3/noixfr.bk +rm -f ns3/noixfr.bk.jnl +rm -f ns3/noixfr.bk.signed +rm -f ns3/noixfr.bk.signed.jnl + +../../../tools/genrandom 400 random.data + +(cd ns3; sh -e sign.sh) diff --git a/bin/tests/system/inline/tests.sh b/bin/tests/system/inline/tests.sh new file mode 100644 index 00000000000..ee8dce937f8 --- /dev/null +++ b/bin/tests/system/inline/tests.sh @@ -0,0 +1,329 @@ +#!/bin/sh +# +# Copyright (C) 2008 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +# $Id: tests.sh,v 1.1 2011/08/30 05:16:12 marka Exp $ + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +DIGOPTS="+tcp +dnssec" + +status=0 +n=0 + +n=`expr $n + 1` +echo "I:checking that the zone is signed on initial transfer ($n)" +ret=0 +for i in 1 2 3 4 5 6 7 8 9 10 +do + ret=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 bits TYPE65534 > dig.out.ns3.test$n + grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 + grep "ANSWER: 3," dig.out.ns3.test$n > /dev/null || ret=1 + records=`grep "TYPE65534.*05[0-9A-F][0-9A-F][0-9A-F][0-9A-F]0001" dig.out.ns3.test$n | wc -l` + [ $records = 2 ] || ret=1 + if [ $ret = 0 ]; then break; fi + sleep 1 +done +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +$NSUPDATE << EOF +zone bits +server 10.53.0.2 5300 +update add added.bits 0 A 1.2.3.4 +send +EOF + +n=`expr $n + 1` +echo "I:checking that the record is added on the hidden master ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.2 -p 5300 added.bits A > dig.out.ns2.test$n +grep "status: NOERROR" dig.out.ns2.test$n > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns2.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:checking that update has been transfered and has been signed ($n)" +ret=0 +for i in 1 2 3 4 5 6 7 8 9 10 +do + ret=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 added.bits A > dig.out.ns3.test$n + grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 + grep "ANSWER: 2," dig.out.ns3.test$n > /dev/null || ret=1 + if [ $ret = 0 ]; then break; fi + sleep 1 +done +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +$NSUPDATE << EOF +zone bits +server 10.53.0.2 5300 +update add bits 0 SOA ns2.bits. . 2011072400 20 20 1814400 3600 +send +EOF + +n=`expr $n + 1` +echo "I:checking YYYYMMDDVV (2011072400) serial on hidden master ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.2 -p 5300 bits SOA > dig.out.ns2.test$n +grep "status: NOERROR" dig.out.ns2.test$n > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns2.test$n > /dev/null || ret=1 +grep "2011072400" dig.out.ns2.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:checking YYYYMMDDVV (2011072400) serial in signed zone ($n)" +for i in 1 2 3 4 5 6 7 8 9 10 +do + ret=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 bits SOA > dig.out.ns3.test$n + grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 + grep "ANSWER: 2," dig.out.ns3.test$n > /dev/null || ret=1 + grep "2011072400" dig.out.ns3.test$n > /dev/null || ret=1 + if [ $ret = 0 ]; then break; fi + sleep 1 +done +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` +n=`expr $n + 1` + +echo "I:checking that the zone is signed on initial transfer, noixfr ($n)" +ret=0 +for i in 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 +do + ret=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 noixfr TYPE65534 > dig.out.ns3.test$n + grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 + grep "ANSWER: 3," dig.out.ns3.test$n > /dev/null || ret=1 + records=`grep "TYPE65534.*05[0-9A-F][0-9A-F][0-9A-F][0-9A-F]0001" dig.out.ns3.test$n | wc -l` + [ $records = 2 ] || ret=1 + if [ $ret = 0 ]; then break; fi + sleep 1 +done +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +$NSUPDATE << EOF +zone noixfr +server 10.53.0.4 5300 +update add added.noixfr 0 A 1.2.3.4 +send +EOF + +n=`expr $n + 1` +echo "I:checking that the record is added on the hidden master, noixfr ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.4 -p 5300 added.noixfr A > dig.out.ns4.test$n +grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns4.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:checking that update has been transfered and has been signed, noixfr ($n)" +ret=0 +for i in 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 +do + ret=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 added.noixfr A > dig.out.ns3.test$n + grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 + grep "ANSWER: 2," dig.out.ns3.test$n > /dev/null || ret=1 + if [ $ret = 0 ]; then break; fi + sleep 1 +done +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +$NSUPDATE << EOF +zone noixfr +server 10.53.0.4 5300 +update add noixfr 0 SOA ns4.noixfr. . 2011072400 20 20 1814400 3600 +send +EOF + +n=`expr $n + 1` +echo "I:checking YYYYMMDDVV (2011072400) serial on hidden master, noixfr ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.4 -p 5300 noixfr SOA > dig.out.ns4.test$n +grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns4.test$n > /dev/null || ret=1 +grep "2011072400" dig.out.ns4.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:checking YYYYMMDDVV (2011072400) serial in signed zone, noixfr ($n)" +for i in 1 2 3 4 5 6 7 8 9 10 +do + ret=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 noixfr SOA > dig.out.ns3.test$n + grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 + grep "ANSWER: 2," dig.out.ns3.test$n > /dev/null || ret=1 + grep "2011072400" dig.out.ns3.test$n > /dev/null || ret=1 + if [ $ret = 0 ]; then break; fi + sleep 1 +done +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:stop bump in the wire signer server ($n)" +ret=0 +$PERL ../stop.pl . ns3 || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:restart bump in the wire signer server ($n)" +ret=0 +$PERL ../start.pl --noclean . ns3 || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +$NSUPDATE << EOF +zone bits +server 10.53.0.2 5300 +update add bits 0 SOA ns2.bits. . 2011072450 20 20 1814400 3600 +send +EOF + +n=`expr $n + 1` +echo "I:checking YYYYMMDDVV (2011072450) serial on hidden master ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.2 -p 5300 bits SOA > dig.out.ns2.test$n +grep "status: NOERROR" dig.out.ns2.test$n > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns2.test$n > /dev/null || ret=1 +grep "2011072450" dig.out.ns2.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:checking YYYYMMDDVV (2011072450) serial in signed zone ($n)" +for i in 1 2 3 4 5 6 7 8 9 10 +do + ret=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 bits SOA > dig.out.ns3.test$n + grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 + grep "ANSWER: 2," dig.out.ns3.test$n > /dev/null || ret=1 + grep "2011072450" dig.out.ns3.test$n > /dev/null || ret=1 + if [ $ret = 0 ]; then break; fi + sleep 1 +done +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +$NSUPDATE << EOF +zone noixfr +server 10.53.0.4 5300 +update add noixfr 0 SOA ns4.noixfr. . 2011072450 20 20 1814400 3600 +send +EOF + +n=`expr $n + 1` +echo "I:checking YYYYMMDDVV (2011072450) serial on hidden master, noixfr ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.4 -p 5300 noixfr SOA > dig.out.ns4.test$n +grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns4.test$n > /dev/null || ret=1 +grep "2011072450" dig.out.ns4.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:checking YYYYMMDDVV (2011072450) serial in signed zone, noixfr ($n)" +for i in 1 2 3 4 5 6 7 8 9 10 +do + ret=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 noixfr SOA > dig.out.ns3.test$n + grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 + grep "ANSWER: 2," dig.out.ns3.test$n > /dev/null || ret=1 + grep "2011072450" dig.out.ns3.test$n > /dev/null || ret=1 + if [ $ret = 0 ]; then break; fi + sleep 1 +done +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +$NSUPDATE << EOF +zone bits +server 10.53.0.3 5300 +update add bits 0 SOA ns2.bits. . 2011072460 20 20 1814400 3600 +send +EOF + +n=`expr $n + 1` +echo "I:checking forwarded update on hidden master ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.2 -p 5300 bits SOA > dig.out.ns2.test$n +grep "status: NOERROR" dig.out.ns2.test$n > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns2.test$n > /dev/null || ret=1 +grep "2011072460" dig.out.ns2.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:checking forwarded update on signed zone ($n)" +for i in 1 2 3 4 5 6 7 8 9 10 +do + ret=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 bits SOA > dig.out.ns3.test$n + grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 + grep "ANSWER: 2," dig.out.ns3.test$n > /dev/null || ret=1 + grep "2011072460" dig.out.ns3.test$n > /dev/null || ret=1 + if [ $ret = 0 ]; then break; fi + sleep 1 +done +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +$NSUPDATE << EOF +zone noixfr +server 10.53.0.3 5300 +update add noixfr 0 SOA ns4.noixfr. . 2011072460 20 20 1814400 3600 +send +EOF + +n=`expr $n + 1` +echo "I:checking forwarded update on hidden master, noixfr ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.4 -p 5300 noixfr SOA > dig.out.ns4.test$n +grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns4.test$n > /dev/null || ret=1 +grep "2011072460" dig.out.ns4.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:checking forwarded update on signed zone, noixfr ($n)" +for i in 1 2 3 4 5 6 7 8 9 10 +do + ret=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 noixfr SOA > dig.out.ns3.test$n + grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 + grep "ANSWER: 2," dig.out.ns3.test$n > /dev/null || ret=1 + grep "2011072460" dig.out.ns3.test$n > /dev/null || ret=1 + if [ $ret = 0 ]; then break; fi + sleep 1 +done +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +exit $status diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 01fd45cf509..e72a791530e 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 @@ -10115,6 +10115,7 @@ view "external" { max-retry-time number ; key-directory path_name; auto-dnssec allow|maintain|off; + inline-signing yes_or_no; zero-no-soa-ttl yes_or_no ; serial-update-method increment|unixtime; }; @@ -11277,6 +11278,20 @@ example.com. NS ns2.example.net. + + inline-signing + + + inline-signing is used to enable + bump in the wire signing of a zone, where a + unsigned zone is transfered in or loaded from + disk and a signed version of the zone is served, + with possibly, a different serial number. This + behaviour is disabled by default. + + + + multi-master diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 1a9174828f4..c14cc94006a 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: check.c,v 1.133 2011/06/17 07:05:02 each Exp $ */ +/* $Id: check.c,v 1.134 2011/08/30 05:16:14 marka Exp $ */ /*! \file */ @@ -1225,7 +1225,7 @@ check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) { #define STATICSTUBZONE 64 #define REDIRECTZONE 128 #define STREDIRECTZONE 0 /* Set to REDIRECTZONE to allow xfr-in. */ -#define CHECKACL 256 +#define CHECKACL 512 typedef struct { const char *name; @@ -1255,7 +1255,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, static optionstable options[] = { { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | REDIRECTZONE | - CHECKACL | STATICSTUBZONE }, + CHECKACL | STATICSTUBZONE }, { "allow-notify", SLAVEZONE | CHECKACL }, { "allow-transfer", MASTERZONE | SLAVEZONE | CHECKACL }, { "notify", MASTERZONE | SLAVEZONE }, @@ -1279,13 +1279,14 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, { "max-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE }, { "min-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE }, { "dnssec-secure-to-insecure", MASTERZONE }, - { "sig-validity-interval", MASTERZONE }, - { "sig-re-signing-interval", MASTERZONE }, - { "sig-signing-nodes", MASTERZONE }, - { "sig-signing-type", MASTERZONE }, - { "sig-signing-signatures", MASTERZONE }, + { "sig-re-signing-interval", MASTERZONE | SLAVEZONE }, + { "sig-signing-nodes", MASTERZONE | SLAVEZONE }, + { "sig-signing-signatures", MASTERZONE | SLAVEZONE }, + { "sig-signing-type", MASTERZONE | SLAVEZONE }, + { "sig-validity-interval", MASTERZONE | SLAVEZONE }, + { "signing", MASTERZONE | SLAVEZONE }, { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE | - STATICSTUBZONE| REDIRECTZONE }, + STATICSTUBZONE | REDIRECTZONE }, { "allow-update", MASTERZONE | CHECKACL }, { "allow-update-forwarding", SLAVEZONE | CHECKACL }, { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE | REDIRECTZONE }, @@ -1296,7 +1297,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE }, { "update-policy", MASTERZONE }, { "database", MASTERZONE | SLAVEZONE | STUBZONE | REDIRECTZONE }, - { "key-directory", MASTERZONE }, + { "key-directory", MASTERZONE | SLAVEZONE }, { "check-wildcard", MASTERZONE }, { "check-mx", MASTERZONE }, { "check-dup-records", MASTERZONE }, @@ -1308,7 +1309,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, { "update-check-ksk", MASTERZONE }, { "dnssec-dnskey-kskonly", MASTERZONE }, { "dnssec-loadkeys-interval", MASTERZONE }, - { "auto-dnssec", MASTERZONE }, + { "auto-dnssec", MASTERZONE | SLAVEZONE }, { "try-tcp-refresh", SLAVEZONE | STREDIRECTZONE }, { "server-addresses", STATICSTUBZONE }, { "server-names", STATICSTUBZONE }, diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h index 48b78b6dfb3..1368a7225c8 100644 --- a/lib/dns/include/dns/events.h +++ b/lib/dns/include/dns/events.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: events.h,v 1.56 2010/12/21 03:11:42 marka Exp $ */ +/* $Id: events.h,v 1.57 2011/08/30 05:16:15 marka Exp $ */ #ifndef DNS_EVENTS_H #define DNS_EVENTS_H 1 @@ -74,6 +74,8 @@ #define DNS_EVENT_CLIENTREQDONE (ISC_EVENTCLASS_DNS + 44) #define DNS_EVENT_ADBGROWENTRIES (ISC_EVENTCLASS_DNS + 45) #define DNS_EVENT_ADBGROWNAMES (ISC_EVENTCLASS_DNS + 46) +#define DNS_EVENT_ZONESECURESERIAL (ISC_EVENTCLASS_DNS + 47) +#define DNS_EVENT_ZONESECUREDB (ISC_EVENTCLASS_DNS + 48) #define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0) #define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535) diff --git a/lib/dns/include/dns/journal.h b/lib/dns/include/dns/journal.h index 8100ce1b967..2a98fd2a42f 100644 --- a/lib/dns/include/dns/journal.h +++ b/lib/dns/include/dns/journal.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: journal.h,v 1.37 2009/11/04 23:48:18 tbox Exp $ */ +/* $Id: journal.h,v 1.38 2011/08/30 05:16:15 marka Exp $ */ #ifndef DNS_JOURNAL_H #define DNS_JOURNAL_H 1 @@ -46,6 +46,10 @@ ***/ #define DNS_JOURNALOPT_RESIGN 0x00000001 +#define DNS_JOURNAL_READ 0x00000000 /* ISC_FALSE */ +#define DNS_JOURNAL_CREATE 0x00000001 /* ISC_TRUE */ +#define DNS_JOURNAL_WRITE 0x00000002 + /*** *** Types ***/ @@ -95,16 +99,15 @@ dns_db_createsoatuple(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx, */ isc_result_t -dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, +dns_journal_open(isc_mem_t *mctx, const char *filename, unsigned int mode, dns_journal_t **journalp); /*%< * Open the journal file 'filename' and create a dns_journal_t object for it. * - * If 'write' is ISC_TRUE, the journal is open for writing. If it does - * not exist, it is created. - * - * If 'write' is ISC_FALSE, the journal is open for reading. If it does - * not exist, ISC_R_NOTFOUND is returned. + * DNS_JOURNAL_CREATE open the journal for reading and writing and create + * the journal if it does not exist. + * DNS_JOURNAL_WRITE open the journal for readinge and writing. + * DNS_JOURNAL_READ open the journal for reading only. */ void @@ -284,6 +287,14 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, * exists and is non-empty 'serial' must exist in the journal. */ +isc_uint32_t +dns_journal_get_bitws(dns_journal_t *j); +void +dns_journal_set_bitws(dns_journal_t *j, isc_uint32_t bitws); +/*%< + * Get and set bump in the wire serial. + */ + ISC_LANG_ENDDECLS #endif /* DNS_JOURNAL_H */ diff --git a/lib/dns/include/dns/update.h b/lib/dns/include/dns/update.h index 117e5223d18..5bc58c9331e 100644 --- a/lib/dns/include/dns/update.h +++ b/lib/dns/include/dns/update.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.h,v 1.2 2011/07/01 02:25:48 marka Exp $ */ +/* $Id: update.h,v 1.3 2011/08/30 05:16:15 marka Exp $ */ #ifndef DNS_UPDATE_H #define DNS_UPDATE_H 1 @@ -29,6 +29,12 @@ #include +typedef struct { + void (*func)(void *arg, dns_zone_t *zone, int level, + const char *message); + void *arg; +} dns_update_log_t; + ISC_LANG_BEGINDECLS /*** @@ -47,6 +53,11 @@ dns_update_soaserial(isc_uint32_t serial, dns_updatemethod_t method); * if not. */ +isc_result_t +dns_update_signatures(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *oldver, dns_dbversion_t *newver, + dns_diff_t *diff, isc_uint32_t sigvalidityinterval); + ISC_LANG_ENDDECLS #endif /* DNS_UPDATE_H */ diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 7d6dffd9559..1808f3605e7 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.191 2011/07/06 01:36:32 each Exp $ */ +/* $Id: zone.h,v 1.192 2011/08/30 05:16:15 marka Exp $ */ #ifndef DNS_ZONE_H #define DNS_ZONE_H 1 @@ -44,7 +44,7 @@ typedef enum { dns_zone_staticstub, dns_zone_key, dns_zone_dlz, - dns_zone_redirect, + dns_zone_redirect } dns_zonetype_t; #define DNS_ZONEOPT_SERVERS 0x00000001U /*%< perform server checks */ @@ -1923,6 +1923,13 @@ dns_zone_getserialupdatemethod(dns_zone_t *zone); * Requires: * \li 'zone' to be valid. */ + +void +dns_zone_link(dns_zone_t *zone, dns_zone_t *raw); + +void +dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw); + ISC_LANG_ENDDECLS #endif /* DNS_ZONE_H */ diff --git a/lib/dns/journal.c b/lib/dns/journal.c index 8123afe614e..4c6b74153a1 100644 --- a/lib/dns/journal.c +++ b/lib/dns/journal.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: journal.c,v 1.114 2011/03/12 04:59:48 tbox Exp $ */ +/* $Id: journal.c,v 1.115 2011/08/30 05:16:14 marka Exp $ */ #include @@ -213,6 +213,8 @@ typedef union { journal_rawpos_t end; /*% Number of index entries following the header. */ unsigned char index_size[4]; + /*% Bump in the wire serial. */ + unsigned char bitws[4]; } h; /* Pad the header to a fixed size. */ unsigned char pad[JOURNAL_HEADER_SIZE]; @@ -252,6 +254,7 @@ typedef struct { journal_pos_t begin; journal_pos_t end; isc_uint32_t index_size; + isc_uint32_t bitws; } journal_header_t; /*% @@ -284,7 +287,7 @@ typedef struct { */ static journal_header_t -initial_journal_header = { ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0 }; +initial_journal_header = { ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0, 0 }; #define JOURNAL_EMPTY(h) ((h)->begin.offset == (h)->end.offset) @@ -292,7 +295,8 @@ typedef enum { JOURNAL_STATE_INVALID, JOURNAL_STATE_READ, JOURNAL_STATE_WRITE, - JOURNAL_STATE_TRANSACTION + JOURNAL_STATE_TRANSACTION, + JOURNAL_STATE_BITWS } journal_state_t; struct dns_journal { @@ -353,6 +357,7 @@ journal_header_decode(journal_rawheader_t *raw, journal_header_t *cooked) { journal_pos_decode(&raw->h.begin, &cooked->begin); journal_pos_decode(&raw->h.end, &cooked->end); cooked->index_size = decode_uint32(raw->h.index_size); + cooked->bitws = decode_uint32(raw->h.bitws); } static void @@ -363,6 +368,7 @@ journal_header_encode(journal_header_t *cooked, journal_rawheader_t *raw) { journal_pos_encode(&raw->h.begin, &cooked->begin); journal_pos_encode(&raw->h.end, &cooked->end); encode_uint32(cooked->index_size, raw->h.index_size); + encode_uint32(cooked->bitws, raw->h.bitws); } /* @@ -667,13 +673,17 @@ journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, } isc_result_t -dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, +dns_journal_open(isc_mem_t *mctx, const char *filename, unsigned int mode, dns_journal_t **journalp) { isc_result_t result; int namelen; char backup[1024]; + isc_boolean_t write, create; - result = journal_open(mctx, filename, write, write, journalp); + create = ISC_TF(mode & DNS_JOURNAL_CREATE); + write = ISC_TF(mode & (DNS_JOURNAL_WRITE|DNS_JOURNAL_CREATE)); + + result = journal_open(mctx, filename, write, create, journalp); if (result == ISC_R_NOTFOUND) { namelen = strlen(filename); if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0) @@ -944,7 +954,8 @@ dns_journal_begin_transaction(dns_journal_t *j) { journal_rawxhdr_t hdr; REQUIRE(DNS_JOURNAL_VALID(j)); - REQUIRE(j->state == JOURNAL_STATE_WRITE); + REQUIRE(j->state == JOURNAL_STATE_WRITE || + j->state == JOURNAL_STATE_BITWS); /* * Find the file offset where the new transaction should @@ -1067,7 +1078,21 @@ dns_journal_commit(dns_journal_t *j) { journal_rawheader_t rawheader; REQUIRE(DNS_JOURNAL_VALID(j)); - REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); + REQUIRE(j->state == JOURNAL_STATE_TRANSACTION || + j->state == JOURNAL_STATE_BITWS); + + /* + * Just write out a updated header. + */ + if (j->state == JOURNAL_STATE_BITWS) { + CHECK(journal_fsync(j)); + journal_header_encode(&j->header, &rawheader); + CHECK(journal_seek(j, 0)); + CHECK(journal_write(j, &rawheader, sizeof(rawheader))); + CHECK(journal_fsync(j)); + j->state = JOURNAL_STATE_WRITE; + return (ISC_R_SUCCESS); + } /* * Perform some basic consistency checks. @@ -1124,19 +1149,24 @@ dns_journal_commit(dns_journal_t *j) { */ CHECK(journal_fsync(j)); - /* - * Update the transaction header. - */ - CHECK(journal_seek(j, j->x.pos[0].offset)); - CHECK(journal_write_xhdr(j, (j->x.pos[1].offset - j->x.pos[0].offset) - - sizeof(journal_rawxhdr_t), - j->x.pos[0].serial, j->x.pos[1].serial)); + if (j->state == JOURNAL_STATE_TRANSACTION) { + isc_offset_t offset; + offset = (j->x.pos[1].offset - j->x.pos[0].offset) - + sizeof(journal_rawxhdr_t); + /* + * Update the transaction header. + */ + CHECK(journal_seek(j, j->x.pos[0].offset)); + CHECK(journal_write_xhdr(j, offset, j->x.pos[0].serial, + j->x.pos[1].serial)); + } /* * Update the journal header. */ if (JOURNAL_EMPTY(&j->header)) { j->header.begin = j->x.pos[0]; + j->header.bitws = j->header.begin.serial; } j->header.end = j->x.pos[1]; journal_header_encode(&j->header, &rawheader); @@ -1415,6 +1445,7 @@ dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { return (result); } + fprintf(file, "BITWS = %u\n", j->header.bitws); dns_diff_init(j->mctx, &diff); /* @@ -1497,14 +1528,33 @@ dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { /* * Miscellaneous accessors. */ -isc_uint32_t dns_journal_first_serial(dns_journal_t *j) { +isc_uint32_t +dns_journal_first_serial(dns_journal_t *j) { return (j->header.begin.serial); } -isc_uint32_t dns_journal_last_serial(dns_journal_t *j) { +isc_uint32_t +dns_journal_last_serial(dns_journal_t *j) { return (j->header.end.serial); } +void +dns_journal_set_bitws(dns_journal_t *j, isc_uint32_t bitws) { + + REQUIRE(j->state == JOURNAL_STATE_WRITE || + j->state == JOURNAL_STATE_BITWS || + j->state == JOURNAL_STATE_TRANSACTION); + + j->header.bitws = bitws; + if (j->state == JOURNAL_STATE_WRITE) + j->state = JOURNAL_STATE_BITWS; +} + +isc_uint32_t +dns_journal_get_bitws(dns_journal_t *j) { + return (j->header.bitws); +} + /**************************************************************************/ /* * Iteration support. @@ -2145,6 +2195,7 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, new->header.begin.offset = indexend; new->header.end.serial = j->header.end.serial; new->header.end.offset = indexend + copy_length; + new->header.bitws = j->header.bitws; /* * Update the journal header. diff --git a/lib/dns/update.c b/lib/dns/update.c index ae49640c11e..8c131f0b444 100644 --- a/lib/dns/update.c +++ b/lib/dns/update.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2011 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,14 +15,1839 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.c,v 1.3 2011/07/01 23:47:44 tbox Exp $ */ +/* $Id: update.c,v 1.4 2011/08/30 05:16:14 marka Exp $ */ -#include "config.h" +#include -#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include + + +/**************************************************************************/ + +/*% + * Log level for tracing dynamic update protocol requests. + */ +#define LOGLEVEL_PROTOCOL ISC_LOG_INFO + +/*% + * Log level for low-level debug tracing. + */ +#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8) + +/*% + * Check an operation for failure. These macros all assume that + * the function using them has a 'result' variable and a 'failure' + * label. + */ +#define CHECK(op) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/*% + * Fail unconditionally with result 'code', which must not + * be ISC_R_SUCCESS. The reason for failure presumably has + * been logged already. + * + * The test against ISC_R_SUCCESS is there to keep the Solaris compiler + * from complaining about "end-of-loop code not reached". + */ + +#define FAIL(code) \ + do { \ + result = (code); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/*% + * Fail unconditionally and log as a client error. + * The test against ISC_R_SUCCESS is there to keep the Solaris compiler + * from complaining about "end-of-loop code not reached". + */ +#define FAILC(code, msg) \ + do { \ + const char *_what = "failed"; \ + result = (code); \ + switch (result) { \ + case DNS_R_NXDOMAIN: \ + case DNS_R_YXDOMAIN: \ + case DNS_R_YXRRSET: \ + case DNS_R_NXRRSET: \ + _what = "unsuccessful"; \ + } \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "update %s: %s (%s)", _what, \ + msg, isc_result_totext(result)); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +#define FAILN(code, name, msg) \ + do { \ + const char *_what = "failed"; \ + result = (code); \ + switch (result) { \ + case DNS_R_NXDOMAIN: \ + case DNS_R_YXDOMAIN: \ + case DNS_R_YXRRSET: \ + case DNS_R_NXRRSET: \ + _what = "unsuccessful"; \ + } \ + if (isc_log_wouldlog(dns_lctx, LOGLEVEL_PROTOCOL)) { \ + char _nbuf[DNS_NAME_FORMATSIZE]; \ + dns_name_format(name, _nbuf, sizeof(_nbuf)); \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "update %s: %s: %s (%s)", _what, _nbuf, \ + msg, isc_result_totext(result)); \ + } \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +#define FAILNT(code, name, type, msg) \ + do { \ + const char *_what = "failed"; \ + result = (code); \ + switch (result) { \ + case DNS_R_NXDOMAIN: \ + case DNS_R_YXDOMAIN: \ + case DNS_R_YXRRSET: \ + case DNS_R_NXRRSET: \ + _what = "unsuccessful"; \ + } \ + if (isc_log_wouldlog(dns_lctx, LOGLEVEL_PROTOCOL)) { \ + char _nbuf[DNS_NAME_FORMATSIZE]; \ + char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \ + dns_name_format(name, _nbuf, sizeof(_nbuf)); \ + dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "update %s: %s/%s: %s (%s)", \ + _what, _nbuf, _tbuf, msg, \ + isc_result_totext(result)); \ + } \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/*% + * Fail unconditionally and log as a server error. + * The test against ISC_R_SUCCESS is there to keep the Solaris compiler + * from complaining about "end-of-loop code not reached". + */ +#define FAILS(code, msg) \ + do { \ + result = (code); \ + update_log(log, zone, LOGLEVEL_PROTOCOL, \ + "error: %s: %s", \ + msg, isc_result_totext(result)); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + +/**************************************************************************/ + +typedef struct rr rr_t; + +struct rr { + /* dns_name_t name; */ + isc_uint32_t ttl; + dns_rdata_t rdata; +}; + +typedef struct update_event update_event_t; + +/**************************************************************************/ + +static void +update_log(dns_update_log_t *callback, dns_zone_t *zone, + int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5); + +static void +update_log(dns_update_log_t *callback, dns_zone_t *zone, + int level, const char *fmt, ...) +{ + va_list ap; + char message[4096]; + + if (callback == NULL) + return; + + if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE) + return; + + + va_start(ap, fmt); + vsnprintf(message, sizeof(message), fmt, ap); + va_end(ap); + + (callback->func)(callback->arg, zone, level, message); +} + +/*% + * Update a single RR in version 'ver' of 'db' and log the + * update in 'diff'. + * + * Ensures: + * \li '*tuple' == NULL. Either the tuple is freed, or its + * ownership has been transferred to the diff. + */ +static isc_result_t +do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver, + dns_diff_t *diff) +{ + dns_diff_t temp_diff; + isc_result_t result; + + /* + * Create a singleton diff. + */ + dns_diff_init(diff->mctx, &temp_diff); + temp_diff.resign = diff->resign; + ISC_LIST_APPEND(temp_diff.tuples, *tuple, link); + + /* + * Apply it to the database. + */ + result = dns_diff_apply(&temp_diff, db, ver); + ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link); + if (result != ISC_R_SUCCESS) { + dns_difftuple_free(tuple); + return (result); + } + + /* + * Merge it into the current pending journal entry. + */ + dns_diff_appendminimal(diff, tuple); + + /* + * Do not clear temp_diff. + */ + return (ISC_R_SUCCESS); +} + +static isc_result_t +update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, + dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, + dns_rdata_t *rdata) +{ + dns_difftuple_t *tuple = NULL; + isc_result_t result; + result = dns_difftuple_create(diff->mctx, op, + name, ttl, rdata, &tuple); + if (result != ISC_R_SUCCESS) + return (result); + return (do_one_tuple(&tuple, db, ver, diff)); +} + +/**************************************************************************/ +/* + * Callback-style iteration over rdatasets and rdatas. + * + * foreach_rrset() can be used to iterate over the RRsets + * of a name and call a callback function with each + * one. Similarly, foreach_rr() can be used to iterate + * over the individual RRs at name, optionally restricted + * to RRs of a given type. + * + * The callback functions are called "actions" and take + * two arguments: a void pointer for passing arbitrary + * context information, and a pointer to the current RRset + * or RR. By convention, their names end in "_action". + */ + +/* + * XXXRTH We might want to make this public somewhere in libdns. + */ + +/*% + * Function type for foreach_rrset() iterator actions. + */ +typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset); + +/*% + * Function type for foreach_rr() iterator actions. + */ +typedef isc_result_t rr_func(void *data, rr_t *rr); + +/*% + * Internal context struct for foreach_node_rr(). + */ +typedef struct { + rr_func * rr_action; + void * rr_action_data; +} foreach_node_rr_ctx_t; + +/*% + * Internal helper function for foreach_node_rr(). + */ +static isc_result_t +foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) { + isc_result_t result; + foreach_node_rr_ctx_t *ctx = data; + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + rr_t rr = { 0, DNS_RDATA_INIT }; + + dns_rdataset_current(rdataset, &rr.rdata); + rr.ttl = rdataset->ttl; + result = (*ctx->rr_action)(ctx->rr_action_data, &rr); + if (result != ISC_R_SUCCESS) + return (result); + } + if (result != ISC_R_NOMORE) + return (result); + return (ISC_R_SUCCESS); +} + +/*% + * For each rdataset of 'name' in 'ver' of 'db', call 'action' + * with the rdataset and 'action_data' as arguments. If the name + * does not exist, do nothing. + * + * If 'action' returns an error, abort iteration and return the error. + */ +static isc_result_t +foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + rrset_func *action, void *action_data) +{ + isc_result_t result; + dns_dbnode_t *node; + dns_rdatasetiter_t *iter; + + node = NULL; + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + return (result); + + iter = NULL; + result = dns_db_allrdatasets(db, node, ver, + (isc_stdtime_t) 0, &iter); + if (result != ISC_R_SUCCESS) + goto cleanup_node; + + for (result = dns_rdatasetiter_first(iter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iter)) + { + dns_rdataset_t rdataset; + + dns_rdataset_init(&rdataset); + dns_rdatasetiter_current(iter, &rdataset); + + result = (*action)(action_data, &rdataset); + + dns_rdataset_disassociate(&rdataset); + if (result != ISC_R_SUCCESS) + goto cleanup_iterator; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + cleanup_iterator: + dns_rdatasetiter_destroy(&iter); + + cleanup_node: + dns_db_detachnode(db, &node); + + return (result); +} + +/*% + * For each RR of 'name' in 'ver' of 'db', call 'action' + * with the RR and 'action_data' as arguments. If the name + * does not exist, do nothing. + * + * If 'action' returns an error, abort iteration + * and return the error. + */ +static isc_result_t +foreach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + rr_func *rr_action, void *rr_action_data) +{ + foreach_node_rr_ctx_t ctx; + ctx.rr_action = rr_action; + ctx.rr_action_data = rr_action_data; + return (foreach_rrset(db, ver, name, + foreach_node_rr_action, &ctx)); +} + + +/*% + * For each of the RRs specified by 'db', 'ver', 'name', 'type', + * (which can be dns_rdatatype_any to match any type), and 'covers', call + * 'action' with the RR and 'action_data' as arguments. If the name + * does not exist, or if no RRset of the given type exists at the name, + * do nothing. + * + * If 'action' returns an error, abort iteration and return the error. + */ +static isc_result_t +foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, dns_rdatatype_t covers, rr_func *rr_action, + void *rr_action_data) +{ + + isc_result_t result; + dns_dbnode_t *node; + dns_rdataset_t rdataset; + + if (type == dns_rdatatype_any) + return (foreach_node_rr(db, ver, name, + rr_action, rr_action_data)); + + node = NULL; + if (type == dns_rdatatype_nsec3 || + (type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3)) + result = dns_db_findnsec3node(db, name, ISC_FALSE, &node); + else + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + return (result); + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, ver, type, covers, + (isc_stdtime_t) 0, &rdataset, NULL); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + goto cleanup_node; + } + if (result != ISC_R_SUCCESS) + goto cleanup_node; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) + { + rr_t rr = { 0, DNS_RDATA_INIT }; + dns_rdataset_current(&rdataset, &rr.rdata); + rr.ttl = rdataset.ttl; + result = (*rr_action)(rr_action_data, &rr); + if (result != ISC_R_SUCCESS) + goto cleanup_rdataset; + } + if (result != ISC_R_NOMORE) + goto cleanup_rdataset; + result = ISC_R_SUCCESS; + + cleanup_rdataset: + dns_rdataset_disassociate(&rdataset); + cleanup_node: + dns_db_detachnode(db, &node); + + return (result); +} + +/**************************************************************************/ +/* + * Various tests on the database contents (for prerequisites, etc). + */ + +/*% + * Function type for predicate functions that compare a database RR 'db_rr' + * against an update RR 'update_rr'. + */ +typedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr); + +/*% + * Helper function for rrset_exists(). + */ +static isc_result_t +rrset_exists_action(void *data, rr_t *rr) { + UNUSED(data); + UNUSED(rr); + return (ISC_R_EXISTS); +} + +/*% + * Utility macro for RR existence checking functions. + * + * If the variable 'result' has the value ISC_R_EXISTS or + * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE, + * respectively, and return success. + * + * If 'result' has any other value, there was a failure. + * Return the failure result code and do not set *exists. + * + * This would be more readable as "do { if ... } while(0)", + * but that form generates tons of warnings on Solaris 2.6. + */ +#define RETURN_EXISTENCE_FLAG \ + return ((result == ISC_R_EXISTS) ? \ + (*exists = ISC_TRUE, ISC_R_SUCCESS) : \ + ((result == ISC_R_SUCCESS) ? \ + (*exists = ISC_FALSE, ISC_R_SUCCESS) : \ + result)) + +/*% + * Set '*exists' to true iff an rrset of the given type exists, + * to false otherwise. + */ +static isc_result_t +rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, dns_rdatatype_t covers, + isc_boolean_t *exists) +{ + isc_result_t result; + result = foreach_rr(db, ver, name, type, covers, + rrset_exists_action, NULL); + RETURN_EXISTENCE_FLAG; +} + +/*% + * Set '*visible' to true if the RRset exists and is part of the + * visible zone. Otherwise '*visible' is set to false unless a + * error occurs. + */ +static isc_result_t +rrset_visible(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, isc_boolean_t *visible) +{ + isc_result_t result; + dns_fixedname_t fixed; + + dns_fixedname_init(&fixed); + result = dns_db_find(db, name, ver, type, DNS_DBFIND_NOWILD, + (isc_stdtime_t) 0, NULL, + dns_fixedname_name(&fixed), NULL, NULL); + switch (result) { + case ISC_R_SUCCESS: + *visible = ISC_TRUE; + break; + /* + * Glue, obscured, deleted or replaced records. + */ + case DNS_R_DELEGATION: + case DNS_R_DNAME: + case DNS_R_CNAME: + case DNS_R_NXDOMAIN: + case DNS_R_NXRRSET: + case DNS_R_EMPTYNAME: + case DNS_R_COVERINGNSEC: + *visible = ISC_FALSE; + result = ISC_R_SUCCESS; + break; + default: + break; + } + return (result); +} + +/*% + * Context struct and helper function for name_exists(). + */ + +static isc_result_t +name_exists_action(void *data, dns_rdataset_t *rrset) { + UNUSED(data); + UNUSED(rrset); + return (ISC_R_EXISTS); +} + +/*% + * Set '*exists' to true iff the given name exists, to false otherwise. + */ +static isc_result_t +name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + isc_boolean_t *exists) +{ + isc_result_t result; + result = foreach_rrset(db, ver, name, + name_exists_action, NULL); + RETURN_EXISTENCE_FLAG; +} + +/**************************************************************************/ +/* + * Checking of "RRset exists (value dependent)" prerequisites. + * + * In the RFC2136 section 3.2.5, this is the pseudocode involving + * a variable called "temp", a mapping of tuples to rrsets. + * + * Here, we represent the "temp" data structure as (non-minimal) "dns_diff_t" + * where each tuple has op==DNS_DIFFOP_EXISTS. + */ + +/*% + * A comparison function defining the sorting order for the entries + * in the "temp" data structure. The major sort key is the owner name, + * followed by the type and rdata. + */ +static int +temp_order(const void *av, const void *bv) { + dns_difftuple_t const * const *ap = av; + dns_difftuple_t const * const *bp = bv; + dns_difftuple_t const *a = *ap; + dns_difftuple_t const *b = *bp; + int r; + r = dns_name_compare(&a->name, &b->name); + if (r != 0) + return (r); + r = (b->rdata.type - a->rdata.type); + if (r != 0) + return (r); + r = dns_rdata_casecompare(&a->rdata, &b->rdata); + return (r); +} + +/**************************************************************************/ +/* + * Conditional deletion of RRs. + */ + +/*% + * Context structure for delete_if(). + */ + +typedef struct { + rr_predicate *predicate; + dns_db_t *db; + dns_dbversion_t *ver; + dns_diff_t *diff; + dns_name_t *name; + dns_rdata_t *update_rr; +} conditional_delete_ctx_t; + +/*% + * Predicate functions for delete_if(). + */ + +/*% + * Return true always. + */ +static isc_boolean_t +true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { + UNUSED(update_rr); + UNUSED(db_rr); + return (ISC_TRUE); +} + +/*% + * Return true if the record is a RRSIG. + */ +static isc_boolean_t +rrsig_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { + UNUSED(update_rr); + return ((db_rr->type == dns_rdatatype_rrsig) ? + ISC_TRUE : ISC_FALSE); +} + +/*% + * Internal helper function for delete_if(). + */ +static isc_result_t +delete_if_action(void *data, rr_t *rr) { + conditional_delete_ctx_t *ctx = data; + if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) { + isc_result_t result; + result = update_one_rr(ctx->db, ctx->ver, ctx->diff, + DNS_DIFFOP_DEL, ctx->name, + rr->ttl, &rr->rdata); + return (result); + } else { + return (ISC_R_SUCCESS); + } +} + +/*% + * Conditionally delete RRs. Apply 'predicate' to the RRs + * specified by 'db', 'ver', 'name', and 'type' (which can + * be dns_rdatatype_any to match any type). Delete those + * RRs for which the predicate returns true, and log the + * deletions in 'diff'. + */ +static isc_result_t +delete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers, + dns_rdata_t *update_rr, dns_diff_t *diff) +{ + conditional_delete_ctx_t ctx; + ctx.predicate = predicate; + ctx.db = db; + ctx.ver = ver; + ctx.diff = diff; + ctx.name = name; + ctx.update_rr = update_rr; + return (foreach_rr(db, ver, name, type, covers, + delete_if_action, &ctx)); +} + +/**************************************************************************/ +/* + * Incremental updating of NSECs and RRSIGs. + */ + +#define MAXZONEKEYS 32 /*%< Maximum number of zone keys supported. */ + +/*% + * We abuse the dns_diff_t type to represent a set of domain names + * affected by the update. + */ +static isc_result_t +namelist_append_name(dns_diff_t *list, dns_name_t *name) { + isc_result_t result; + dns_difftuple_t *tuple = NULL; + static dns_rdata_t dummy_rdata = DNS_RDATA_INIT; + + CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0, + &dummy_rdata, &tuple)); + dns_diff_append(list, &tuple); + failure: + return (result); +} + +static isc_result_t +namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected) +{ + isc_result_t result; + dns_fixedname_t fixedname; + dns_name_t *child; + dns_dbiterator_t *dbit = NULL; + + dns_fixedname_init(&fixedname); + child = dns_fixedname_name(&fixedname); + + CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit)); + + for (result = dns_dbiterator_seek(dbit, name); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbit)) + { + dns_dbnode_t *node = NULL; + CHECK(dns_dbiterator_current(dbit, &node, child)); + dns_db_detachnode(db, &node); + if (! dns_name_issubdomain(child, name)) + break; + CHECK(namelist_append_name(affected, child)); + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + failure: + if (dbit != NULL) + dns_dbiterator_destroy(&dbit); + return (result); +} + + + +/*% + * Helper function for non_nsec_rrset_exists(). + */ +static isc_result_t +is_non_nsec_action(void *data, dns_rdataset_t *rrset) { + UNUSED(data); + if (!(rrset->type == dns_rdatatype_nsec || + rrset->type == dns_rdatatype_nsec3 || + (rrset->type == dns_rdatatype_rrsig && + (rrset->covers == dns_rdatatype_nsec || + rrset->covers == dns_rdatatype_nsec3)))) + return (ISC_R_EXISTS); + return (ISC_R_SUCCESS); +} + +/*% + * Check whether there is an rrset other than a NSEC or RRSIG NSEC, + * i.e., anything that justifies the continued existence of a name + * after a secure update. + * + * If such an rrset exists, set '*exists' to ISC_TRUE. + * Otherwise, set it to ISC_FALSE. + */ +static isc_result_t +non_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver, + dns_name_t *name, isc_boolean_t *exists) +{ + isc_result_t result; + result = foreach_rrset(db, ver, name, is_non_nsec_action, NULL); + RETURN_EXISTENCE_FLAG; +} + +/*% + * A comparison function for sorting dns_diff_t:s by name. + */ +static int +name_order(const void *av, const void *bv) { + dns_difftuple_t const * const *ap = av; + dns_difftuple_t const * const *bp = bv; + dns_difftuple_t const *a = *ap; + dns_difftuple_t const *b = *bp; + return (dns_name_compare(&a->name, &b->name)); +} + +static isc_result_t +uniqify_name_list(dns_diff_t *list) { + isc_result_t result; + dns_difftuple_t *p, *q; + + CHECK(dns_diff_sort(list, name_order)); + + p = ISC_LIST_HEAD(list->tuples); + while (p != NULL) { + do { + q = ISC_LIST_NEXT(p, link); + if (q == NULL || ! dns_name_equal(&p->name, &q->name)) + break; + ISC_LIST_UNLINK(list->tuples, q, link); + dns_difftuple_free(&q); + } while (1); + p = ISC_LIST_NEXT(p, link); + } + failure: + return (result); +} + +static isc_result_t +is_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + isc_boolean_t *flag, isc_boolean_t *cut, isc_boolean_t *unsecure) +{ + isc_result_t result; + dns_fixedname_t foundname; + dns_fixedname_init(&foundname); + result = dns_db_find(db, name, ver, dns_rdatatype_any, + DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD, + (isc_stdtime_t) 0, NULL, + dns_fixedname_name(&foundname), + NULL, NULL); + if (result == ISC_R_SUCCESS || result == DNS_R_EMPTYNAME) { + *flag = ISC_TRUE; + *cut = ISC_FALSE; + if (unsecure != NULL) + *unsecure = ISC_FALSE; + return (ISC_R_SUCCESS); + } else if (result == DNS_R_ZONECUT) { + *flag = ISC_TRUE; + *cut = ISC_TRUE; + if (unsecure != NULL) { + /* + * We are at the zonecut. Check to see if there + * is a DS RRset. + */ + if (dns_db_find(db, name, ver, dns_rdatatype_ds, 0, + (isc_stdtime_t) 0, NULL, + dns_fixedname_name(&foundname), + NULL, NULL) == DNS_R_NXRRSET) + *unsecure = ISC_TRUE; + else + *unsecure = ISC_FALSE; + } + return (ISC_R_SUCCESS); + } else if (result == DNS_R_GLUE || result == DNS_R_DNAME || + result == DNS_R_DELEGATION || result == DNS_R_NXDOMAIN) { + *flag = ISC_FALSE; + *cut = ISC_FALSE; + if (unsecure != NULL) + *unsecure = ISC_FALSE; + return (ISC_R_SUCCESS); + } else { + /* + * Silence compiler. + */ + *flag = ISC_FALSE; + *cut = ISC_FALSE; + if (unsecure != NULL) + *unsecure = ISC_FALSE; + return (result); + } +} + +/*% + * Find the next/previous name that has a NSEC record. + * In other words, skip empty database nodes and names that + * have had their NSECs removed because they are obscured by + * a zone cut. + */ +static isc_result_t +next_active(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname, + isc_boolean_t forward) +{ + isc_result_t result; + dns_dbiterator_t *dbit = NULL; + isc_boolean_t has_nsec = ISC_FALSE; + unsigned int wraps = 0; + isc_boolean_t secure = dns_db_issecure(db); + + CHECK(dns_db_createiterator(db, 0, &dbit)); + + CHECK(dns_dbiterator_seek(dbit, oldname)); + do { + dns_dbnode_t *node = NULL; + + if (forward) + result = dns_dbiterator_next(dbit); + else + result = dns_dbiterator_prev(dbit); + if (result == ISC_R_NOMORE) { + /* + * Wrap around. + */ + if (forward) + CHECK(dns_dbiterator_first(dbit)); + else + CHECK(dns_dbiterator_last(dbit)); + wraps++; + if (wraps == 2) { + update_log(log, zone, ISC_LOG_ERROR, + "secure zone with no NSECs"); + result = DNS_R_BADZONE; + goto failure; + } + } + CHECK(dns_dbiterator_current(dbit, &node, newname)); + dns_db_detachnode(db, &node); + + /* + * The iterator may hold the tree lock, and + * rrset_exists() calls dns_db_findnode() which + * may try to reacquire it. To avoid deadlock + * we must pause the iterator first. + */ + CHECK(dns_dbiterator_pause(dbit)); + if (secure) { + CHECK(rrset_exists(db, ver, newname, + dns_rdatatype_nsec, 0, &has_nsec)); + } else { + dns_fixedname_t ffound; + dns_name_t *found; + dns_fixedname_init(&ffound); + found = dns_fixedname_name(&ffound); + result = dns_db_find(db, newname, ver, + dns_rdatatype_soa, + DNS_DBFIND_NOWILD, 0, NULL, found, + NULL, NULL); + if (result == ISC_R_SUCCESS || + result == DNS_R_EMPTYNAME || + result == DNS_R_NXRRSET || + result == DNS_R_CNAME || + (result == DNS_R_DELEGATION && + dns_name_equal(newname, found))) { + has_nsec = ISC_TRUE; + result = ISC_R_SUCCESS; + } else if (result != DNS_R_NXDOMAIN) + break; + } + } while (! has_nsec); + failure: + if (dbit != NULL) + dns_dbiterator_destroy(&dbit); + + return (result); +} + +/*% + * Add a NSEC record for "name", recording the change in "diff". + * The existing NSEC is removed. + */ +static isc_result_t +add_nsec(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl, + dns_diff_t *diff) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + unsigned char buffer[DNS_NSEC_BUFFERSIZE]; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_difftuple_t *tuple = NULL; + dns_fixedname_t fixedname; + dns_name_t *target; + + dns_fixedname_init(&fixedname); + target = dns_fixedname_name(&fixedname); + + /* + * Find the successor name, aka NSEC target. + */ + CHECK(next_active(log, zone, db, ver, name, target, ISC_TRUE)); + + /* + * Create the NSEC RDATA. + */ + CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); + dns_rdata_init(&rdata); + CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata)); + dns_db_detachnode(db, &node); + + /* + * Delete the old NSEC and record the change. + */ + CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0, + NULL, diff)); + /* + * Add the new NSEC and record the change. + */ + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, + nsecttl, &rdata, &tuple)); + CHECK(do_one_tuple(&tuple, db, ver, diff)); + INSIST(tuple == NULL); + + failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +/*% + * Add a placeholder NSEC record for "name", recording the change in "diff". + */ +static isc_result_t +add_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_diff_t *diff) +{ + isc_result_t result; + dns_difftuple_t *tuple = NULL; + isc_region_t r; + unsigned char data[1] = { 0 }; /* The root domain, no bits. */ + dns_rdata_t rdata = DNS_RDATA_INIT; + + r.base = data; + r.length = sizeof(data); + dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r); + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0, + &rdata, &tuple)); + CHECK(do_one_tuple(&tuple, db, ver, diff)); + failure: + return (result); +} + +static isc_result_t +find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, + isc_mem_t *mctx, unsigned int maxkeys, + dst_key_t **keys, unsigned int *nkeys) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + const char *directory = dns_zone_getkeydirectory(zone); + CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); + CHECK(dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db), + directory, mctx, maxkeys, keys, nkeys)); + failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +/*% + * Add RRSIG records for an RRset, recording the change in "diff". + */ +static isc_result_t +add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *name, dns_rdatatype_t type, + dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys, + isc_stdtime_t inception, isc_stdtime_t expire, + isc_boolean_t check_ksk, isc_boolean_t keyset_kskonly) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t sig_rdata = DNS_RDATA_INIT; + isc_buffer_t buffer; + unsigned char data[1024]; /* XXX */ + unsigned int i, j; + isc_boolean_t added_sig = ISC_FALSE; + isc_mem_t *mctx = diff->mctx; + + dns_rdataset_init(&rdataset); + isc_buffer_init(&buffer, data, sizeof(data)); + + /* Get the rdataset to sign. */ + if (type == dns_rdatatype_nsec3) + CHECK(dns_db_findnsec3node(db, name, ISC_FALSE, &node)); + else + CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); + CHECK(dns_db_findrdataset(db, node, ver, type, 0, + (isc_stdtime_t) 0, &rdataset, NULL)); + dns_db_detachnode(db, &node); + +#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0) +#define KSK(x) ((dst_key_flags(x) & DNS_KEYFLAG_KSK) != 0) +#define ALG(x) dst_key_alg(x) + + /* + * If we are honoring KSK flags then we need to check that we + * have both KSK and non-KSK keys that are not revoked per + * algorithm. + */ + for (i = 0; i < nkeys; i++) { + isc_boolean_t both = ISC_FALSE; + + if (!dst_key_isprivate(keys[i])) + continue; + + if (check_ksk && !REVOKE(keys[i])) { + isc_boolean_t have_ksk, have_nonksk; + if (KSK(keys[i])) { + have_ksk = ISC_TRUE; + have_nonksk = ISC_FALSE; + } else { + have_ksk = ISC_FALSE; + have_nonksk = ISC_TRUE; + } + for (j = 0; j < nkeys; j++) { + if (j == i || ALG(keys[i]) != ALG(keys[j])) + continue; + if (REVOKE(keys[j])) + continue; + if (KSK(keys[j])) + have_ksk = ISC_TRUE; + else + have_nonksk = ISC_TRUE; + both = have_ksk && have_nonksk; + if (both) + break; + } + } + + if (both) { + if (type == dns_rdatatype_dnskey) { + if (!KSK(keys[i]) && keyset_kskonly) + continue; + } else if (KSK(keys[i])) + continue; + } else if (REVOKE(keys[i]) && type != dns_rdatatype_dnskey) + continue; + + /* Calculate the signature, creating a RRSIG RDATA. */ + CHECK(dns_dnssec_sign(name, &rdataset, keys[i], + &inception, &expire, + mctx, &buffer, &sig_rdata)); + + /* Update the database and journal with the RRSIG. */ + /* XXX inefficient - will cause dataset merging */ + CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN, name, + rdataset.ttl, &sig_rdata)); + dns_rdata_reset(&sig_rdata); + isc_buffer_init(&buffer, data, sizeof(data)); + added_sig = ISC_TRUE; + } + if (!added_sig) { + update_log(log, zone, ISC_LOG_ERROR, + "found no active private keys, " + "unable to generate any signatures"); + result = ISC_R_NOTFOUND; + } + + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +/* + * Delete expired RRsigs and any RRsigs we are about to re-sign. + * See also zone.c:del_sigs(). + */ +static isc_result_t +del_keysigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int i; + dns_rdata_rrsig_t rrsig; + isc_boolean_t found; + + dns_rdataset_init(&rdataset); + + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig, + dns_rdatatype_dnskey, (isc_stdtime_t) 0, + &rdataset, NULL); + dns_db_detachnode(db, &node); + + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &rrsig, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + found = ISC_FALSE; + for (i = 0; i < nkeys; i++) { + if (rrsig.keyid == dst_key_id(keys[i])) { + found = ISC_TRUE; + if (!dst_key_isprivate(keys[i])) { + /* + * The re-signing code in zone.c + * will mark this as offline. + * Just skip the record for now. + */ + break; + } + result = update_one_rr(db, ver, diff, + DNS_DIFFOP_DEL, name, + rdataset.ttl, &rdata); + break; + } + } + /* + * If there is not a matching DNSKEY then delete the RRSIG. + */ + if (!found) + result = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, + name, rdataset.ttl, &rdata); + dns_rdata_reset(&rdata); + if (result != ISC_R_SUCCESS) + break; + } + dns_rdataset_disassociate(&rdataset); + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; +failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +static isc_result_t +add_exposed_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *ver, dns_name_t *name, isc_boolean_t cut, + dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys, + isc_stdtime_t inception, isc_stdtime_t expire, + isc_boolean_t check_ksk, isc_boolean_t keyset_kskonly) +{ + isc_result_t result; + dns_dbnode_t *node; + dns_rdatasetiter_t *iter; + + node = NULL; + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + return (result); + + iter = NULL; + result = dns_db_allrdatasets(db, node, ver, + (isc_stdtime_t) 0, &iter); + if (result != ISC_R_SUCCESS) + goto cleanup_node; + + for (result = dns_rdatasetiter_first(iter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iter)) + { + dns_rdataset_t rdataset; + dns_rdatatype_t type; + isc_boolean_t flag; + + dns_rdataset_init(&rdataset); + dns_rdatasetiter_current(iter, &rdataset); + type = rdataset.type; + dns_rdataset_disassociate(&rdataset); + + /* + * We don't need to sign unsigned NSEC records at the cut + * as they are handled elsewhere. + */ + if ((type == dns_rdatatype_rrsig) || + (cut && type != dns_rdatatype_ds)) + continue; + result = rrset_exists(db, ver, name, dns_rdatatype_rrsig, + type, &flag); + if (result != ISC_R_SUCCESS) + goto cleanup_iterator; + if (flag) + continue;; + result = add_sigs(log, zone, db, ver, name, type, diff, + keys, nkeys, inception, expire, + check_ksk, keyset_kskonly); + if (result != ISC_R_SUCCESS) + goto cleanup_iterator; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + + cleanup_iterator: + dns_rdatasetiter_destroy(&iter); + + cleanup_node: + dns_db_detachnode(db, &node); + + return (result); +} + +/*% + * Update RRSIG, NSEC and NSEC3 records affected by an update. The original + * update, including the SOA serial update but excluding the RRSIG & NSEC + * changes, is in "diff" and has already been applied to "newver" of "db". + * The database version prior to the update is "oldver". + * + * The necessary RRSIG, NSEC and NSEC3 changes will be applied to "newver" + * and added (as a minimal diff) to "diff". + * + * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds. + */ +isc_result_t +dns_update_signatures(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, + dns_dbversion_t *oldver, dns_dbversion_t *newver, + dns_diff_t *diff, isc_uint32_t sigvalidityinterval) +{ + isc_result_t result; + dns_difftuple_t *t; + dns_diff_t diffnames; + dns_diff_t affected; + dns_diff_t sig_diff; + dns_diff_t nsec_diff; + dns_diff_t nsec_mindiff; + isc_boolean_t flag, build_nsec, build_nsec3; + dst_key_t *zone_keys[MAXZONEKEYS]; + unsigned int nkeys = 0; + unsigned int i; + isc_stdtime_t now, inception, expire; + dns_ttl_t nsecttl; + dns_rdata_soa_t soa; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_t rdataset; + dns_dbnode_t *node = NULL; + isc_boolean_t check_ksk, keyset_kskonly; + isc_boolean_t unsecure; + isc_boolean_t cut; + dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); + + dns_diff_init(diff->mctx, &diffnames); + dns_diff_init(diff->mctx, &affected); + + dns_diff_init(diff->mctx, &sig_diff); + sig_diff.resign = dns_zone_getsigresigninginterval(zone); + dns_diff_init(diff->mctx, &nsec_diff); + dns_diff_init(diff->mctx, &nsec_mindiff); + + result = find_zone_keys(zone, db, newver, diff->mctx, + MAXZONEKEYS, zone_keys, &nkeys); + if (result != ISC_R_SUCCESS) { + update_log(log, zone, ISC_LOG_ERROR, + "could not get zone keys for secure dynamic update"); + goto failure; + } + + isc_stdtime_get(&now); + inception = now - 3600; /* Allow for some clock skew. */ + expire = now + sigvalidityinterval; + + /* + * Do we look at the KSK flag on the DNSKEY to determining which + * keys sign which RRsets? First check the zone option then + * check the keys flags to make sure at least one has a ksk set + * and one doesn't. + */ + check_ksk = ISC_TF((dns_zone_getoptions(zone) & + DNS_ZONEOPT_UPDATECHECKKSK) != 0); + keyset_kskonly = ISC_TF((dns_zone_getoptions(zone) & + DNS_ZONEOPT_DNSKEYKSKONLY) != 0); + + /* + * Get the NSEC/NSEC3 TTL from the SOA MINIMUM field. + */ + CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); + dns_rdataset_init(&rdataset); + CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa, 0, + (isc_stdtime_t) 0, &rdataset, NULL)); + CHECK(dns_rdataset_first(&rdataset)); + dns_rdataset_current(&rdataset, &rdata); + CHECK(dns_rdata_tostruct(&rdata, &soa, NULL)); + nsecttl = soa.minimum; + dns_rdataset_disassociate(&rdataset); + dns_db_detachnode(db, &node); + + /* + * Find all RRsets directly affected by the update, and + * update their RRSIGs. Also build a list of names affected + * by the update in "diffnames". + */ + CHECK(dns_diff_sort(diff, temp_order)); + + t = ISC_LIST_HEAD(diff->tuples); + while (t != NULL) { + dns_name_t *name = &t->name; + /* Now "name" is a new, unique name affected by the update. */ + + CHECK(namelist_append_name(&diffnames, name)); + + while (t != NULL && dns_name_equal(&t->name, name)) { + dns_rdatatype_t type; + type = t->rdata.type; + + /* + * Now "name" and "type" denote a new unique RRset + * affected by the update. + */ + + /* Don't sign RRSIGs. */ + if (type == dns_rdatatype_rrsig) + goto skip; + + /* + * Delete all old RRSIGs covering this type, since they + * are all invalid when the signed RRset has changed. + * We may not be able to recreate all of them - tough. + * Special case changes to the zone's DNSKEY records + * to support offline KSKs. + */ + if (type == dns_rdatatype_dnskey) + del_keysigs(db, newver, name, &sig_diff, + zone_keys, nkeys); + else + CHECK(delete_if(true_p, db, newver, name, + dns_rdatatype_rrsig, type, + NULL, &sig_diff)); + + /* + * If this RRset is still visible after the update, + * add a new signature for it. + */ + CHECK(rrset_visible(db, newver, name, type, &flag)); + if (flag) { + CHECK(add_sigs(log, zone, db, newver, name, + type, &sig_diff, zone_keys, + nkeys, inception, expire, + check_ksk, keyset_kskonly)); + } + skip: + /* Skip any other updates to the same RRset. */ + while (t != NULL && + dns_name_equal(&t->name, name) && + t->rdata.type == type) + { + t = ISC_LIST_NEXT(t, link); + } + } + } + update_log(log, zone, ISC_LOG_DEBUG(3), "updated data signatures"); + + /* Remove orphaned NSECs and RRSIG NSECs. */ + for (t = ISC_LIST_HEAD(diffnames.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + CHECK(non_nsec_rrset_exists(db, newver, &t->name, &flag)); + if (! flag) { + CHECK(delete_if(true_p, db, newver, &t->name, + dns_rdatatype_any, 0, + NULL, &sig_diff)); + } + } + update_log(log, zone, ISC_LOG_DEBUG(3), + "removed any orphaned NSEC records"); + + /* + * See if we need to build NSEC or NSEC3 chains. + */ + CHECK(dns_private_chains(db, newver, privatetype, &build_nsec, + &build_nsec3)); + if (!build_nsec) + goto update_nsec3; + + update_log(log, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC chain"); + + /* + * When a name is created or deleted, its predecessor needs to + * have its NSEC updated. + */ + for (t = ISC_LIST_HEAD(diffnames.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + isc_boolean_t existed, exists; + dns_fixedname_t fixedname; + dns_name_t *prevname; + + dns_fixedname_init(&fixedname); + prevname = dns_fixedname_name(&fixedname); + + if (oldver != NULL) + CHECK(name_exists(db, oldver, &t->name, &existed)); + else + existed = ISC_FALSE; + CHECK(name_exists(db, newver, &t->name, &exists)); + if (exists == existed) + continue; + + /* + * Find the predecessor. + * When names become obscured or unobscured in this update + * transaction, we may find the wrong predecessor because + * the NSECs have not yet been updated to reflect the delegation + * change. This should not matter because in this case, + * the correct predecessor is either the delegation node or + * a newly unobscured node, and those nodes are on the + * "affected" list in any case. + */ + CHECK(next_active(log, zone, db, newver, + &t->name, prevname, ISC_FALSE)); + CHECK(namelist_append_name(&affected, prevname)); + } + + /* + * Find names potentially affected by delegation changes + * (obscured by adding an NS or DNAME, or unobscured by + * removing one). + */ + for (t = ISC_LIST_HEAD(diffnames.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + isc_boolean_t ns_existed, dname_existed; + isc_boolean_t ns_exists, dname_exists; + + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, &t->name, + dns_rdatatype_ns, 0, &ns_existed)); + else + ns_existed = ISC_FALSE; + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, &t->name, + dns_rdatatype_dname, 0, + &dname_existed)); + else + dname_existed = ISC_FALSE; + CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0, + &ns_exists)); + CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_dname, 0, + &dname_exists)); + if ((ns_exists || dname_exists) == (ns_existed || dname_existed)) + continue; + /* + * There was a delegation change. Mark all subdomains + * of t->name as potentially needing a NSEC update. + */ + CHECK(namelist_append_subdomain(db, &t->name, &affected)); + } + + ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link); + INSIST(ISC_LIST_EMPTY(diffnames.tuples)); + + CHECK(uniqify_name_list(&affected)); + + /* + * Determine which names should have NSECs, and delete/create + * NSECs to make it so. We don't know the final NSEC targets yet, + * so we just create placeholder NSECs with arbitrary contents + * to indicate that their respective owner names should be part of + * the NSEC chain. + */ + for (t = ISC_LIST_HEAD(affected.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + isc_boolean_t exists; + dns_name_t *name = &t->name; + + CHECK(name_exists(db, newver, name, &exists)); + if (! exists) + continue; + CHECK(is_active(db, newver, name, &flag, &cut, NULL)); + if (!flag) { + /* + * This name is obscured. Delete any + * existing NSEC record. + */ + CHECK(delete_if(true_p, db, newver, name, + dns_rdatatype_nsec, 0, + NULL, &nsec_diff)); + CHECK(delete_if(rrsig_p, db, newver, name, + dns_rdatatype_any, 0, NULL, diff)); + } else { + /* + * This name is not obscured. It needs to have a + * NSEC unless it is the at the origin, in which + * case it should already exist if there is a complete + * NSEC chain and if there isn't a complete NSEC chain + * we don't want to add one as that would signal that + * there is a complete NSEC chain. + */ + if (!dns_name_equal(name, dns_db_origin(db))) { + CHECK(rrset_exists(db, newver, name, + dns_rdatatype_nsec, 0, + &flag)); + if (!flag) + CHECK(add_placeholder_nsec(db, newver, + name, diff)); + } + CHECK(add_exposed_sigs(log, zone, db, newver, name, + cut, &sig_diff, zone_keys, nkeys, + inception, expire, check_ksk, + keyset_kskonly)); + } + } + + /* + * Now we know which names are part of the NSEC chain. + * Make them all point at their correct targets. + */ + for (t = ISC_LIST_HEAD(affected.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + CHECK(rrset_exists(db, newver, &t->name, + dns_rdatatype_nsec, 0, &flag)); + if (flag) { + /* + * There is a NSEC, but we don't know if it is correct. + * Delete it and create a correct one to be sure. + * If the update was unnecessary, the diff minimization + * will take care of eliminating it from the journal, + * IXFRs, etc. + * + * The RRSIG bit should always be set in the NSECs + * we generate, because they will all get RRSIG NSECs. + * (XXX what if the zone keys are missing?). + * Because the RRSIG NSECs have not necessarily been + * created yet, the correctness of the bit mask relies + * on the assumption that NSECs are only created if + * there is other data, and if there is other data, + * there are other RRSIGs. + */ + CHECK(add_nsec(log, zone, db, newver, &t->name, + nsecttl, &nsec_diff)); + } + } + + /* + * Minimize the set of NSEC updates so that we don't + * have to regenerate the RRSIG NSECs for NSECs that were + * replaced with identical ones. + */ + while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_diff.tuples, t, link); + dns_diff_appendminimal(&nsec_mindiff, &t); + } + + update_log(log, zone, ISC_LOG_DEBUG(3), "signing rebuilt NSEC chain"); + + /* Update RRSIG NSECs. */ + for (t = ISC_LIST_HEAD(nsec_mindiff.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + if (t->op == DNS_DIFFOP_DEL) { + CHECK(delete_if(true_p, db, newver, &t->name, + dns_rdatatype_rrsig, dns_rdatatype_nsec, + NULL, &sig_diff)); + } else if (t->op == DNS_DIFFOP_ADD) { + CHECK(add_sigs(log, zone, db, newver, &t->name, + dns_rdatatype_nsec, &sig_diff, + zone_keys, nkeys, inception, expire, + check_ksk, keyset_kskonly)); + } else { + INSIST(0); + } + } + + update_nsec3: + + /* Record our changes for the journal. */ + while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(sig_diff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + + INSIST(ISC_LIST_EMPTY(sig_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); + + if (!build_nsec3) { + update_log(log, zone, ISC_LOG_DEBUG(3), + "no NSEC3 chains to rebuild"); + goto failure; + } + + update_log(log, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC3 chains"); + + dns_diff_clear(&diffnames); + dns_diff_clear(&affected); + + CHECK(dns_diff_sort(diff, temp_order)); + + /* + * Find names potentially affected by delegation changes + * (obscured by adding an NS or DNAME, or unobscured by + * removing one). + */ + t = ISC_LIST_HEAD(diff->tuples); + while (t != NULL) { + dns_name_t *name = &t->name; + + 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) { + t = ISC_LIST_NEXT(t, link); + continue; + } + + CHECK(namelist_append_name(&affected, name)); + + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, name, dns_rdatatype_ns, + 0, &ns_existed)); + else + ns_existed = ISC_FALSE; + if (oldver != NULL) + CHECK(rrset_exists(db, oldver, name, + dns_rdatatype_dname, 0, + &dname_existed)); + else + dname_existed = ISC_FALSE; + CHECK(rrset_exists(db, newver, name, dns_rdatatype_ns, 0, + &ns_exists)); + CHECK(rrset_exists(db, newver, name, dns_rdatatype_dname, 0, + &dname_exists)); + + exists = ns_exists || dname_exists; + existed = ns_existed || dname_existed; + if (exists == existed) + goto nextname; + /* + * There was a delegation change. Mark all subdomains + * of t->name as potentially needing a NSEC3 update. + */ + CHECK(namelist_append_subdomain(db, name, &affected)); + + nextname: + while (t != NULL && dns_name_equal(&t->name, name)) + t = ISC_LIST_NEXT(t, link); + } + + for (t = ISC_LIST_HEAD(affected.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) { + dns_name_t *name = &t->name; + + unsecure = ISC_FALSE; /* Silence compiler warning. */ + CHECK(is_active(db, newver, name, &flag, &cut, &unsecure)); + + if (!flag) { + CHECK(delete_if(rrsig_p, db, newver, name, + dns_rdatatype_any, 0, NULL, diff)); + CHECK(dns_nsec3_delnsec3sx(db, newver, name, + privatetype, &nsec_diff)); + } else { + CHECK(add_exposed_sigs(log, zone, db, newver, name, + cut, &sig_diff, zone_keys, nkeys, + inception, expire, check_ksk, + keyset_kskonly)); + CHECK(dns_nsec3_addnsec3sx(db, newver, name, nsecttl, + unsecure, privatetype, + &nsec_diff)); + } + } + + /* + * Minimize the set of NSEC3 updates so that we don't + * have to regenerate the RRSIG NSEC3s for NSEC3s that were + * replaced with identical ones. + */ + while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_diff.tuples, t, link); + dns_diff_appendminimal(&nsec_mindiff, &t); + } + + update_log(log, zone, ISC_LOG_DEBUG(3), + "signing rebuilt NSEC3 chain"); + + /* Update RRSIG NSEC3s. */ + for (t = ISC_LIST_HEAD(nsec_mindiff.tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) + { + if (t->op == DNS_DIFFOP_DEL) { + CHECK(delete_if(true_p, db, newver, &t->name, + dns_rdatatype_rrsig, + dns_rdatatype_nsec3, + NULL, &sig_diff)); + } else if (t->op == DNS_DIFFOP_ADD) { + CHECK(add_sigs(log, zone, db, newver, &t->name, + dns_rdatatype_nsec3, + &sig_diff, zone_keys, nkeys, + inception, expire, check_ksk, + keyset_kskonly)); + } else { + INSIST(0); + } + } + + /* Record our changes for the journal. */ + while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) { + ISC_LIST_UNLINK(sig_diff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) { + ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link); + dns_diff_appendminimal(diff, &t); + } + + INSIST(ISC_LIST_EMPTY(sig_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); + INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); + + failure: + dns_diff_clear(&sig_diff); + dns_diff_clear(&nsec_diff); + dns_diff_clear(&nsec_mindiff); + + dns_diff_clear(&affected); + dns_diff_clear(&diffnames); + + for (i = 0; i < nkeys; i++) + dst_key_free(&zone_keys[i]); + + return (result); +} isc_uint32_t dns_update_soaserial(isc_uint32_t serial, dns_updatemethod_t method) { diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 2ff1779f99b..ad11414c4c1 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: xfrin.c,v 1.170 2011/03/11 06:11:25 marka Exp $ */ +/* $Id: xfrin.c,v 1.171 2011/08/30 05:16:14 marka Exp $ */ /*! \file */ @@ -630,7 +630,8 @@ dns_xfrin_create2(dns_zone_t *zone, dns_rdatatype_t xfrtype, isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey, isc_mem_t *mctx, isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, - isc_task_t *task, dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp) + isc_task_t *task, dns_xfrindone_t done, + dns_xfrin_ctx_t **xfrp) { dns_name_t *zonename = dns_zone_getorigin(zone); dns_xfrin_ctx_t *xfr = NULL; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 395853ee384..b2a790c2550 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.623 2011/08/09 02:24:28 marka Exp $ */ +/* $Id: zone.c,v 1.624 2011/08/30 05:16:14 marka Exp $ */ /*! \file */ @@ -345,6 +345,9 @@ struct dns_zone { * Serial number update method. */ dns_updatemethod_t updatemethod; + + dns_zone_t *raw; + dns_zone_t *secure; }; #define DNS_ZONE_FLAG(z,f) (ISC_TF(((z)->flags & (f)) != 0)) @@ -863,6 +866,8 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->privatetype = (dns_rdatatype_t)0xffffU; zone->added = ISC_FALSE; zone->rpz_zone = ISC_FALSE; + zone->raw = NULL; + zone->secure = NULL; zone->magic = ZONE_MAGIC; @@ -1026,6 +1031,8 @@ dns_zone_setclass(dns_zone_t *zone, dns_rdataclass_t rdclass) { zone_rdclass_tostr(zone, namebuf, sizeof namebuf); zone->strrdclass = isc_mem_strdup(zone->mctx, namebuf); + if (zone->raw != NULL) + dns_zone_setclass(zone->raw, rdclass); UNLOCK_ZONE(zone); } @@ -1217,10 +1224,12 @@ dns_zone_setview(dns_zone_t *zone, dns_view_t *view) { zone_viewname_tostr(zone, namebuf, sizeof namebuf); zone->strviewname = isc_mem_strdup(zone->mctx, namebuf); + if (zone->raw != NULL) + dns_zone_setview(zone->raw, view); + UNLOCK_ZONE(zone); } - dns_view_t * dns_zone_getview(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); @@ -1254,6 +1263,8 @@ dns_zone_setorigin(dns_zone_t *zone, const dns_name_t *origin) { zone_name_tostr(zone, namebuf, sizeof namebuf); zone->strname = isc_mem_strdup(zone->mctx, namebuf); + if (result == ISC_R_SUCCESS && zone->raw != NULL) + result = dns_zone_setorigin(zone->raw, origin); UNLOCK_ZONE(zone); return (result); } @@ -1424,6 +1435,12 @@ zone_load(dns_zone_t *zone, unsigned int flags) { LOCK_ZONE(zone); TIME_NOW(&now); + if (zone->raw != NULL) { + result = zone_load(zone->raw, flags); + if (result != ISC_R_SUCCESS) + goto cleanup; + } + INSIST(zone->type != dns_zone_none); if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADING)) { @@ -3105,7 +3122,9 @@ update_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, * Write all transactions in 'diff' to the zone journal file. */ static isc_result_t -zone_journal(dns_zone_t *zone, dns_diff_t *diff, const char *caller) { +zone_journal(dns_zone_t *zone, dns_diff_t *diff, isc_uint32_t *bitws, + const char *caller) +{ const char me[] = "zone_journal"; const char *journalfile; isc_result_t result = ISC_R_SUCCESS; @@ -3115,13 +3134,15 @@ zone_journal(dns_zone_t *zone, dns_diff_t *diff, const char *caller) { journalfile = dns_zone_getjournal(zone); if (journalfile != NULL) { result = dns_journal_open(zone->mctx, journalfile, - ISC_TRUE, &journal); + DNS_JOURNAL_CREATE, &journal); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_ERROR, "%s:dns_journal_open -> %s\n", caller, dns_result_totext(result)); return (result); } + if (bitws != NULL) + dns_journal_set_bitws(journal, *bitws); result = dns_journal_write_transaction(journal, diff); dns_journal_destroy(&journal); @@ -3314,7 +3335,7 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { /* Write changes to journal file. */ CHECK(update_soa_serial(db, ver, &diff, zone->mctx, zone->updatemethod)); - CHECK(zone_journal(zone, &diff, "sync_keyzone")); + CHECK(zone_journal(zone, &diff, NULL, "sync_keyzone")); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); zone_needdump(zone, 30); @@ -3423,6 +3444,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "journal rollforward failed: %s", dns_result_totext(result)); goto cleanup; + + } if (result == ISC_R_NOTFOUND || result == ISC_R_RANGE) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -4030,6 +4053,8 @@ dns_zone_attach(dns_zone_t *source, dns_zone_t **target) { void dns_zone_detach(dns_zone_t **zonep) { dns_zone_t *zone; + dns_zone_t *raw = NULL; + dns_zone_t *secure = NULL; unsigned int refs; isc_boolean_t free_now = ISC_FALSE; @@ -4067,12 +4092,21 @@ dns_zone_detach(dns_zone_t **zonep) { */ INSIST(zone->view == NULL); free_now = ISC_TRUE; + raw = zone->raw; + zone->raw = NULL; + secure = zone->secure; + zone->secure = NULL; } UNLOCK_ZONE(zone); } *zonep = NULL; - if (free_now) + if (free_now) { + if (raw != NULL) + dns_zone_detach(&raw); + if (secure != NULL) + dns_zone_idetach(&secure); zone_free(zone); + } } void @@ -5185,7 +5219,7 @@ zone_resigninc(dns_zone_t *zone) { } /* Write changes to journal file. */ - CHECK(zone_journal(zone, &sig_diff, "zone_resigninc")); + CHECK(zone_journal(zone, &sig_diff, NULL, "zone_resigninc")); /* Everything has succeeded. Commit the changes. */ dns_db_closeversion(db, &version, ISC_TRUE); @@ -6561,7 +6595,7 @@ zone_nsec3chain(dns_zone_t *zone) { } /* Write changes to journal file. */ - CHECK(zone_journal(zone, &sig_diff, "zone_nsec3chain")); + CHECK(zone_journal(zone, &sig_diff, NULL, "zone_nsec3chain")); LOCK_ZONE(zone); zone_needdump(zone, DNS_DUMP_DELAY); @@ -7131,7 +7165,7 @@ zone_sign(dns_zone_t *zone) { /* * Write changes to journal file. */ - CHECK(zone_journal(zone, &sig_diff, "zone_sign")); + CHECK(zone_journal(zone, &sig_diff, NULL, "zone_sign")); pauseall: /* @@ -7888,7 +7922,7 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { /* Write changes to journal file. */ CHECK(update_soa_serial(kfetch->db, ver, &diff, mctx, zone->updatemethod)); - CHECK(zone_journal(zone, &diff, "keyfetch_done")); + CHECK(zone_journal(zone, &diff, NULL, "keyfetch_done")); commit = ISC_TRUE; DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); @@ -8046,7 +8080,7 @@ zone_refreshkeys(dns_zone_t *zone) { if (!ISC_LIST_EMPTY(diff.tuples)) { CHECK(update_soa_serial(db, ver, &diff, zone->mctx, zone->updatemethod)); - CHECK(zone_journal(zone, &diff, "sync_keyzone")); + CHECK(zone_journal(zone, &diff, NULL, "sync_keyzone")); commit = ISC_TRUE; DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); zone_needdump(zone, 30); @@ -8395,6 +8429,21 @@ dump_done(void *arg, isc_result_t result) { version = dns_dumpctx_version(zone->dctx); tresult = dns_db_getsoaserial(db, version, &serial); + /* + * If there is a secure version of this zone + * use its serial if it is less than ours. + */ + if (tresult == ISC_R_SUCCESS && + zone->secure != NULL && zone->secure->db != NULL) { + isc_uint32_t sserial; + isc_result_t mresult; + + mresult = dns_db_getsoaserial(zone->secure->db, + NULL, &sserial); + if (mresult == ISC_R_SUCCESS && + isc_serial_lt(sserial, serial)) + serial = sserial; + } /* * Note: we are task locked here so we can test * zone->xfr safely. @@ -10552,6 +10601,7 @@ static void zone_shutdown(isc_task_t *task, isc_event_t *event) { dns_zone_t *zone = (dns_zone_t *) event->ev_arg; isc_boolean_t free_needed, linked = ISC_FALSE; + dns_zone_t *raw = NULL, *secure = NULL; UNUSED(task); REQUIRE(DNS_ZONE_VALID(zone)); @@ -10631,7 +10681,19 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) { */ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_SHUTDOWN); free_needed = exit_check(zone); + if (zone->raw != NULL) { + raw = zone->raw; + zone->raw = NULL; + } + if (zone->secure != NULL) { + secure = zone->secure; + zone->secure = NULL; + } UNLOCK_ZONE(zone); + if (raw != NULL) + dns_zone_detach(&raw); + if (secure != NULL) + dns_zone_idetach(&secure); if (free_needed) zone_free(zone); } @@ -10991,9 +11053,17 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, isc_sockaddr_format(from, fromtext, sizeof(fromtext)); /* - * We only handle NOTIFY (SOA) at the present. + * Notify messages are processed by the raw zone. */ LOCK_ZONE(zone); + if (zone->raw != NULL) { + result = dns_zone_notifyreceive(zone->raw, from, msg); + UNLOCK_ZONE(zone); + return (result); + } + /* + * We only handle NOTIFY (SOA) at the present. + */ if (isc_sockaddr_pf(from) == PF_INET) inc_stats(zone, dns_zonestatscounter_notifyinv4); else @@ -11394,6 +11464,10 @@ zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length) { isc_buffer_putstr(&buffer, "/"); isc_buffer_putstr(&buffer, zone->view->name); } + if (zone->raw != NULL && 9U < isc_buffer_availablelength(&buffer)) + isc_buffer_putstr(&buffer, " (signed)"); + if (zone->secure != NULL && 11U < isc_buffer_availablelength(&buffer)) + isc_buffer_putstr(&buffer, " (unsigned)"); buf[isc_buffer_usedlength(&buffer)] = '\0'; } @@ -11711,6 +11785,337 @@ notify_done(isc_task_t *task, isc_event_t *event) { dns_message_destroy(&message); } +struct secure_serial { + isc_event_t e; + isc_uint32_t serial; +}; + +static void +update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) { + UNUSED(arg); + dns_zone_log(zone, level, "%s", message); +} + +static void +receive_secure_serial(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_journal_t *rjournal = NULL, *sjournal = NULL; + isc_uint32_t start, end; + dns_zone_t *zone; + int n_soa = 0; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + dns_dbversion_t *newver = NULL, *oldver = NULL; + unsigned int addopt, delopt; + isc_uint32_t oldserial, newserial; + dns_diffop_t op = DNS_DIFFOP_ADD; + dns_diff_t diff; + dns_difftuple_t *tuple = NULL, *soatuple = NULL; + dns_update_log_t log = { update_log_cb, NULL }; + isc_time_t timenow; + + zone = event->ev_arg; + end = ((struct secure_serial *)event)->serial; + + dns_diff_init(zone->mctx, &diff); + + UNUSED(task); + CHECK(dns_journal_open(zone->raw->mctx, zone->raw->journal, + DNS_JOURNAL_WRITE, &rjournal)); + result = dns_journal_open(zone->raw->mctx, zone->journal, + DNS_JOURNAL_READ, &sjournal); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto failure; + + start = dns_journal_get_bitws(rjournal); + if (sjournal != NULL) { + isc_uint32_t serial = dns_journal_get_bitws(sjournal); + /* + * We write the secure journal first so if that exists + * use its value provided it is greater that from the + * raw journal. + */ + if (isc_serial_gt(serial, start)) + start = serial; + dns_journal_destroy(&sjournal); + } + + if (start == end) + goto failure; + CHECK(dns_journal_iter_init(rjournal, start, end)); + + dns_db_attach(zone->db, &db); + dns_db_currentversion(db, &oldver); + CHECK(dns_db_newversion(db, &newver)); + + addopt = DNS_DBADD_MERGE | DNS_DBADD_EXACT | DNS_DBADD_EXACTTTL; + delopt = DNS_DBADD_EXACT; + + for (result = dns_journal_first_rr(rjournal); + result == ISC_R_SUCCESS; + result = dns_journal_next_rr(rjournal)) { + dns_name_t *name = NULL; + isc_uint32_t ttl; + dns_rdata_t *rdata = NULL; + dns_journal_current_rr(rjournal, &name, &ttl, &rdata); + + if (rdata->type == dns_rdatatype_soa) { + n_soa++; + if (n_soa == 2) { + /* + * Save the lastest raw SOA record. + */ + if (soatuple != NULL) + dns_difftuple_free(&soatuple); + CHECK(dns_difftuple_create(diff.mctx, + DNS_DIFFOP_ADD, + name, ttl, rdata, + &soatuple)); + } + if (n_soa == 3) + n_soa = 1; + continue; + } + + /* Sanity. */ + if (n_soa == 0) { + dns_zone_log(zone->raw, ISC_LOG_ERROR, + "corrupt journal file: '%s'\n", + zone->raw->journal); + goto failure; + } + + if (zone->privatetype != 0 && + rdata->type == zone->privatetype) + continue; + + if (rdata->type == dns_rdatatype_nsec || + rdata->type == dns_rdatatype_rrsig || + rdata->type == dns_rdatatype_nsec3 || + rdata->type == dns_rdatatype_dnskey || + rdata->type == dns_rdatatype_nsec3param) + continue; + + op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD; + + CHECK(dns_difftuple_create(diff.mctx, op, name, ttl, rdata, + &tuple)); + dns_diff_appendminimal(&diff, &tuple); + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + CHECK(result); + + CHECK(dns_diff_apply(&diff, db, newver)); + + if (soatuple != NULL) { + isc_uint32_t desired; + + CHECK(dns_db_createsoatuple(db, oldver, diff.mctx, + DNS_DIFFOP_DEL, &tuple)); + oldserial = dns_soa_getserial(&tuple->rdata); + newserial = desired = dns_soa_getserial(&soatuple->rdata); + if (!isc_serial_gt(newserial, oldserial)) { + newserial = oldserial + 1; + if (newserial == 0) + newserial++; + dns_soa_setserial(newserial, &soatuple->rdata); + } + CHECK(do_one_tuple(&tuple, db, newver, &diff)); + CHECK(do_one_tuple(&soatuple, db, newver, &diff)); + dns_zone_log(zone, ISC_LOG_INFO, "serial %u (unsigned %u)", + newserial, desired); + } else + CHECK(update_soa_serial(db, newver, &diff, zone->mctx, + zone->updatemethod)); + + CHECK(dns_update_signatures(&log, zone, db, oldver, newver, + &diff, zone->sigvalidityinterval)); + + CHECK(zone_journal(zone, &diff, &end, "receive_secure_serial")); + + dns_journal_set_bitws(rjournal, end); + dns_journal_commit(rjournal); + + LOCK_ZONE(zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); + + zone_needdump(zone, DNS_DUMP_DELAY); + + TIME_NOW(&timenow); + zone_settimer(zone, &timenow); + + UNLOCK_ZONE(zone); + + dns_db_closeversion(db, &oldver, ISC_FALSE); + dns_db_closeversion(db, &newver, ISC_TRUE); + + failure: + if (tuple != NULL) + dns_difftuple_free(&tuple); + if (soatuple != NULL) + dns_difftuple_free(&soatuple); + if (db != NULL) { + if (oldver != NULL) + dns_db_closeversion(db, &oldver, ISC_FALSE); + if (newver != NULL) + dns_db_closeversion(db, &newver, ISC_FALSE); + if (node != NULL) + dns_db_detachnode(db, &node); + dns_db_detach(&db); + } + if (rjournal != NULL) + dns_journal_destroy(&rjournal); + if (sjournal != NULL) + dns_journal_destroy(&sjournal); + dns_diff_clear(&diff); + isc_event_free(&event); +} + +static isc_result_t +zone_send_secureserial(dns_zone_t *zone, isc_uint32_t serial) { + isc_event_t *e; + + e = isc_event_allocate(zone->secure->mctx, zone, + DNS_EVENT_ZONESECURESERIAL, + receive_secure_serial, zone->secure, + sizeof(struct secure_serial)); + if (e == NULL) + return (ISC_R_NOMEMORY); + ((struct secure_serial *)e)->serial = serial; + + isc_task_send(zone->secure->task, &e); + return (ISC_R_SUCCESS); +} + +struct secure_db { + isc_event_t e; + dns_db_t *db; +}; + +static void +receive_secure_db(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_zone_t *zone; + dns_db_t *rawdb, *db = NULL; + dns_dbnode_t *rawnode = NULL, *node = NULL; + dns_fixedname_t fname; + dns_name_t *name; + dns_dbiterator_t *dbiterator = NULL; + dns_rdatasetiter_t *rdsit = NULL; + dns_rdataset_t rdataset; + dns_dbversion_t *version = NULL; + isc_time_t loadtime; + + UNUSED(task); + + zone = event->ev_arg; + rawdb = ((struct secure_db *)event)->db; + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_rdataset_init(&rdataset); + + TIME_NOW(&loadtime); + + result = dns_db_create(zone->mctx, zone->db_argv[0], + &zone->origin, dns_dbtype_zone, zone->rdclass, + zone->db_argc - 1, zone->db_argv + 1, &db); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_newversion(db, &version); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_createiterator(rawdb, 0, &dbiterator); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_dbiterator_first(dbiterator); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiterator)) { + result = dns_dbiterator_current(dbiterator, &rawnode, name); + if (result != ISC_R_SUCCESS) + continue; + + result = dns_db_findnode(db, name, ISC_TRUE, &node); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_allrdatasets(rawdb, rawnode, NULL, 0, &rdsit); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdatasetiter_first(rdsit); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsit)) { + dns_rdatasetiter_current(rdsit, &rdataset); + if (rdataset.type == dns_rdatatype_nsec || + rdataset.type == dns_rdatatype_rrsig || + rdataset.type == dns_rdatatype_nsec3 || + rdataset.type == dns_rdatatype_dnskey || + rdataset.type == dns_rdatatype_nsec3param) { + dns_rdataset_disassociate(&rdataset); + continue; + } + + result = dns_db_addrdataset(db, node, version, 0, + &rdataset, 0, NULL); + if (result != ISC_R_SUCCESS) + goto failure; + + dns_rdataset_disassociate(&rdataset); + } + dns_rdatasetiter_destroy(&rdsit); + dns_db_detachnode(rawdb, &rawnode); + dns_db_detachnode(db, &node); + } + + dns_db_closeversion(db, &version, ISC_TRUE); + LOCK_ZONE(zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); + result = zone_postload(zone, db, loadtime, ISC_R_SUCCESS); + zone_needdump(zone, 0); /* XXXMPA */ + UNLOCK_ZONE(zone); + + failure: + if (result != ISC_R_SUCCESS) + dns_zone_log(zone, ISC_LOG_ERROR, "receive_secure_db: %s", + dns_result_totext(result)); + + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (db != NULL) { + if (node != NULL) + dns_db_detachnode(db, &node); + dns_db_detach(&db); + } + if (rawnode != NULL) + dns_db_detachnode(rawdb, &rawnode); + dns_db_detach(&rawdb); + if (dbiterator != NULL) + dns_dbiterator_destroy(&dbiterator); + isc_event_free(&event); +} + +static isc_result_t +zone_send_securedb(dns_zone_t *zone, dns_db_t *db) { + isc_event_t *e; + dns_db_t *dummy = NULL; + + e = isc_event_allocate(zone->secure->mctx, zone, + DNS_EVENT_ZONESECUREDB, + receive_secure_db, zone->secure, + sizeof(struct secure_db)); + if (e == NULL) + return (ISC_R_NOMEMORY); + dns_db_attach(db, &dummy); + ((struct secure_db *)e)->db = dummy; + + isc_task_send(zone->secure->task, &e); + return (ISC_R_SUCCESS); +} + isc_result_t dns_zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { isc_result_t result; @@ -11831,6 +12236,10 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { break; } } +#if 0 + if (zone->secure != NULL) + zone_send_secureserial(zone, serial); +#endif } else { if (dump && zone->masterfile != NULL) { /* @@ -11881,6 +12290,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) { zone->journal, strbuf); } } + if (zone->secure != NULL) + zone_send_securedb(zone, db); } dns_db_closeversion(db, &ver, ISC_FALSE); @@ -12032,6 +12443,8 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { dns_zone_log(zone, ISC_LOG_INFO, "transferred serial %u%s", serial, buf); + if (zone->secure != NULL) + zone_send_secureserial(zone, serial); } /* @@ -12337,6 +12750,7 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) { "no database exists yet, requesting AXFR of " "initial version from %s", master); xfrtype = dns_rdatatype_axfr; +#if 0 } else if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS)) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "ixfr-from-differences " "set, requesting %sAXFR from %s", soa_before, @@ -12345,6 +12759,7 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) { xfrtype = dns_rdatatype_soa; else xfrtype = dns_rdatatype_axfr; +#endif } else if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "forced reload, requesting AXFR of " @@ -14364,7 +14779,8 @@ zone_rekey(dns_zone_t *zone) { zone->updatemethod)); CHECK(add_chains(zone, db, ver, &diff)); CHECK(sign_apex(zone, db, ver, &diff, &sig_diff)); - CHECK(zone_journal(zone, &sig_diff, "zone_rekey")); + CHECK(zone_journal(zone, &sig_diff, NULL, + "zone_rekey")); commit = ISC_TRUE; } } @@ -14664,3 +15080,32 @@ dns_zone_getserialupdatemethod(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); return(zone->updatemethod); } + +void +dns_zone_link(dns_zone_t *zone, dns_zone_t *raw) { + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(DNS_ZONE_VALID(raw)); + + LOCK(&zone->lock); + if (zone->raw != NULL) + dns_zone_detach(&zone->raw); + dns_zone_attach(raw, &zone->raw); + UNLOCK(&zone->lock); + + LOCK(&raw->lock); + if (raw->secure != NULL) + dns_zone_idetach(&raw->secure); + dns_zone_iattach(zone, &raw->secure); + UNLOCK(&raw->lock); +} + +void +dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw) { + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(raw != NULL && *raw == NULL); + + LOCK(&zone->lock); + if (zone->raw != NULL) + dns_zone_attach(zone->raw, raw); + UNLOCK(&zone->lock); +} diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index d7912e7f49e..c9486b82e74 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: namedconf.c,v 1.139 2011/07/01 02:25:48 marka Exp $ */ +/* $Id: namedconf.c,v 1.140 2011/08/30 05:16:15 marka Exp $ */ /*! \file */ @@ -1399,6 +1399,7 @@ zone_clauses[] = { { "sig-signing-signatures", &cfg_type_uint32, 0 }, { "sig-signing-type", &cfg_type_uint32, 0 }, { "sig-validity-interval", &cfg_type_validityinterval, 0 }, + { "inline-signing", &cfg_type_boolean, 0 }, { "transfer-source", &cfg_type_sockaddr4wild, 0 }, { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, { "try-tcp-refresh", &cfg_type_boolean, 0 },