diff options
Diffstat (limited to 'lib/dns/zone.c')
-rw-r--r-- | lib/dns/zone.c | 200 |
1 files changed, 184 insertions, 16 deletions
diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 155f44bf..5a6f02cd 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.540.2.26 2010/06/02 01:00:28 marka Exp $ */ +/* $Id: zone.c,v 1.540.2.29 2010/08/16 23:46:30 tbox Exp $ */ /*! \file */ @@ -317,6 +317,11 @@ struct dns_zone { * Autosigning/key-maintenance options */ isc_uint32_t keyopts; + + /*% + * True if added by "rndc addzone" + */ + isc_boolean_t added; }; #define DNS_ZONE_FLAG(z,f) (ISC_TF(((z)->flags & (f)) != 0)) @@ -827,6 +832,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->signatures = 10; zone->nodes = 100; zone->privatetype = (dns_rdatatype_t)0xffffU; + zone->added = ISC_FALSE; zone->magic = ZONE_MAGIC; @@ -13724,6 +13730,36 @@ clean_nsec3param(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, return (result); } +/* + * Given an RRSIG rdataset and an algorithm, determine whether there + * are any signatures using that algorithm. + */ +static isc_boolean_t +signed_with_alg(dns_rdataset_t *rdataset, dns_secalg_t alg) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_rrsig_t rrsig; + isc_result_t result; + + REQUIRE(rdataset == NULL || rdataset->type == dns_rdatatype_rrsig); + if (rdataset == NULL || !dns_rdataset_isassociated(rdataset)) { + return (ISC_FALSE); + } + + 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); + dns_rdata_reset(&rdata); + if (rrsig.algorithm == alg) + return (ISC_TRUE); + } + + return (ISC_FALSE); +} + static void zone_rekey(dns_zone_t *zone) { isc_result_t result; @@ -13735,12 +13771,14 @@ zone_rekey(dns_zone_t *zone) { dns_dnsseckey_t *key; dns_diff_t diff; isc_boolean_t commit = ISC_FALSE, newactive = ISC_FALSE; + isc_boolean_t fullsign; dns_ttl_t ttl = 3600; const char *dir; isc_mem_t *mctx; isc_stdtime_t now; isc_time_t timenow; isc_interval_t ival; + char timebuf[80]; REQUIRE(DNS_ZONE_VALID(zone)); @@ -13783,6 +13821,12 @@ zone_rekey(dns_zone_t *zone) { } else if (result != ISC_R_NOTFOUND) goto failure; + /* + * True when called from "rndc sign". Indicates the zone should be + * fully signed now. + */ + fullsign = ISC_TF(DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_FULLSIGN) != 0); + result = dns_dnssec_findmatchingkeys(&zone->origin, dir, mctx, &keys); if (result == ISC_R_SUCCESS) { isc_boolean_t check_ksk; @@ -13812,7 +13856,7 @@ zone_rekey(dns_zone_t *zone) { } } - if ((newactive || !ISC_LIST_EMPTY(diff.tuples)) && + if ((newactive || fullsign || !ISC_LIST_EMPTY(diff.tuples)) && dnskey_sane(zone, db, ver, &diff)) { CHECK(dns_diff_apply(&diff, db, ver)); CHECK(clean_nsec3param(zone, db, ver, &diff)); @@ -13833,6 +13877,8 @@ zone_rekey(dns_zone_t *zone) { if (commit) { isc_time_t timenow; dns_difftuple_t *tuple; + isc_boolean_t newkey = ISC_FALSE; + isc_boolean_t newalg = ISC_FALSE; LOCK_ZONE(zone); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); @@ -13842,33 +13888,126 @@ zone_rekey(dns_zone_t *zone) { TIME_NOW(&timenow); zone_settimer(zone, &timenow); + /* + * Has a new key become active? If so, is it for + * a new algorithm? + */ for (tuple = ISC_LIST_HEAD(diff.tuples); tuple != NULL; tuple = ISC_LIST_NEXT(tuple, link)) { dns_rdata_dnskey_t dnskey; - dns_secalg_t algorithm; - isc_region_t r; - isc_uint16_t keyid; if (tuple->rdata.type != dns_rdatatype_dnskey) continue; - result = dns_rdata_tostruct(&tuple->rdata, &dnskey, - NULL); + newkey = ISC_TRUE; + if (!dns_rdataset_isassociated(&keysigs)) { + newalg = ISC_TRUE; + break; + } + + result = dns_rdata_tostruct(&tuple->rdata, + &dnskey, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - dns_rdata_toregion(&tuple->rdata, &r); - algorithm = dnskey.algorithm; - keyid = dst_region_computeid(&r, algorithm); + if (!signed_with_alg(&keysigs, + dnskey.algorithm)) { + newalg = ISC_TRUE; + break; + } + } - result = zone_signwithkey(zone, algorithm, keyid, - ISC_TF(tuple->op == DNS_DIFFOP_DEL)); - if (result != ISC_R_SUCCESS) { - dns_zone_log(zone, ISC_LOG_ERROR, + /* + * If we found a new algorithm, we need to sign the + * zone fully. If there's a new key, but it's for an + * already-existing algorithm, then the zone signing + * can be handled incrementally. + */ + if (newkey && !newalg) + set_resigntime(zone); + + /* Remove any signatures from removed keys. */ + if (!ISC_LIST_EMPTY(rmkeys)) { + for (key = ISC_LIST_HEAD(rmkeys); + key != NULL; + key = ISC_LIST_NEXT(key, link)) { + result = zone_signwithkey(zone, + dst_key_alg(key->key), + dst_key_id(key->key), + ISC_TRUE); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, "zone_signwithkey failed: %s", - dns_result_totext(result)); + dns_result_totext(result)); + } } } + + if (fullsign) { + /* + * "rndc sign" was called, so we now sign the zone + * with all active keys, whether they're new or not. + */ + for (key = ISC_LIST_HEAD(dnskeys); + key != NULL; + key = ISC_LIST_NEXT(key, link)) { + if (!key->force_sign && !key->hint_sign) + continue; + + result = zone_signwithkey(zone, + dst_key_alg(key->key), + dst_key_id(key->key), + ISC_FALSE); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_signwithkey failed: %s", + dns_result_totext(result)); + } + } + } else if (newalg) { + /* + * We haven't been told to sign fully, but a new + * algorithm was added to the DNSKEY. We sign + * the full zone, but only with the newly-added + * keys. + */ + for (tuple = ISC_LIST_HEAD(diff.tuples); + tuple != NULL; + tuple = ISC_LIST_NEXT(tuple, link)) { + dns_rdata_dnskey_t dnskey; + dns_secalg_t algorithm; + isc_region_t r; + isc_uint16_t keyid; + + if (tuple->rdata.type != dns_rdatatype_dnskey || + tuple->op == DNS_DIFFOP_DEL) + continue; + + result = dns_rdata_tostruct(&tuple->rdata, + &dnskey, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + dns_rdata_toregion(&tuple->rdata, &r); + algorithm = dnskey.algorithm; + keyid = dst_region_computeid(&r, algorithm); + + result = zone_signwithkey(zone, algorithm, + keyid, + ISC_TF(tuple->op == + DNS_DIFFOP_DEL)); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_signwithkey failed: %s", + dns_result_totext(result)); + } + } + } + + /* + * Clear fullsign flag, if it was set, so we don't do + * another full signing next time + */ + zone->keyopts &= ~DNS_ZONEKEY_FULLSIGN; + /* * Cause the zone to add/delete NSEC3 chains for the * deferred NSEC3PARAM changes. @@ -13933,6 +14072,17 @@ zone_rekey(dns_zone_t *zone) { UNLOCK_ZONE(zone); } + /* + * If no key event is scheduled, we should still check the key + * repository for updates every so often. (Currently this is + * hard-coded to 12 hours, but it could be configurable.) + */ + if (isc_time_isepoch(&zone->refreshkeytime)) + DNS_ZONE_TIME_ADD(&timenow, (3600 * 12), &zone->refreshkeytime); + + isc_time_formattimestamp(&zone->refreshkeytime, timebuf, 80); + dns_zone_log(zone, ISC_LOG_INFO, "next key event: %s", timebuf); + failure: dns_diff_clear(&diff); @@ -13961,15 +14111,19 @@ zone_rekey(dns_zone_t *zone) { } void -dns_zone_rekey(dns_zone_t *zone) { +dns_zone_rekey(dns_zone_t *zone, isc_boolean_t fullsign) { isc_time_t now; if (zone->type == dns_zone_master && zone->task != NULL) { LOCK_ZONE(zone); + if (fullsign) + zone->keyopts |= DNS_ZONEKEY_FULLSIGN; + TIME_NOW(&now); zone->refreshkeytime = now; zone_settimer(zone, &now); + UNLOCK_ZONE(zone); } } @@ -13992,3 +14146,17 @@ dns_zone_nscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, dns_db_detachnode(db, &node); return (result); } + +void +dns_zone_setadded(dns_zone_t *zone, isc_boolean_t added) { + REQUIRE(DNS_ZONE_VALID(zone)); + LOCK_ZONE(zone); + zone->added = added; + UNLOCK_ZONE(zone); +} + +isc_boolean_t +dns_zone_getadded(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (zone->added); +} |