diff options
Diffstat (limited to 'lib/dns/zone.c')
-rw-r--r-- | lib/dns/zone.c | 283 |
1 files changed, 249 insertions, 34 deletions
diff --git a/lib/dns/zone.c b/lib/dns/zone.c index a398ab45..19f0546b 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.532 2009/11/24 03:42:32 each Exp $ */ +/* $Id: zone.c,v 1.540 2009/12/07 20:51:12 each Exp $ */ /*! \file */ @@ -365,6 +365,7 @@ struct dns_zone { #define DNS_ZONEFLG_NEEDCOMPACT 0x02000000U #define DNS_ZONEFLG_REFRESHING 0x04000000U /*%< Refreshing keydata */ #define DNS_ZONEFLG_THAW 0x08000000U +#define DNS_ZONEFLG_NOTIFYRESIGN 0x10000000U #define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0) #define DNS_ZONEKEY_OPTION(z,o) (((z)->keyopts & (o)) != 0) @@ -2022,6 +2023,113 @@ zone_check_glue(dns_zone_t *zone, dns_db_t *db, dns_name_t *name, } static isc_boolean_t +zone_rrset_check_dup(dns_zone_t *zone, dns_name_t *owner, + dns_rdataset_t *rdataset) +{ + dns_rdataset_t tmprdataset; + isc_result_t result; + isc_boolean_t answer = ISC_TRUE; + isc_boolean_t format = ISC_TRUE; + int level = ISC_LOG_WARNING; + char ownerbuf[DNS_NAME_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + unsigned int count1 = 0; + + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKDUPRRFAIL)) + level = ISC_LOG_ERROR; + + dns_rdataset_init(&tmprdataset); + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) { + dns_rdata_t rdata1 = DNS_RDATA_INIT; + unsigned int count2 = 0; + + count1++; + dns_rdataset_current(rdataset, &rdata1); + dns_rdataset_clone(rdataset, &tmprdataset); + for (result = dns_rdataset_first(&tmprdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&tmprdataset)) { + dns_rdata_t rdata2 = DNS_RDATA_INIT; + count2++; + if (count1 >= count2) + continue; + dns_rdataset_current(&tmprdataset, &rdata2); + if (dns_rdata_casecompare(&rdata1, &rdata2) == 0) { + if (format) { + dns_name_format(owner, ownerbuf, + sizeof ownerbuf); + dns_rdatatype_format(rdata1.type, + typebuf, + sizeof(typebuf)); + format = ISC_FALSE; + } + dns_zone_log(zone, level, "%s/%s has " + "semantically identical records", + ownerbuf, typebuf); + if (level == ISC_LOG_ERROR) + answer = ISC_FALSE; + break; + } + } + dns_rdataset_disassociate(&tmprdataset); + if (!format) + break; + } + return (answer); +} + +static isc_boolean_t +zone_check_dup(dns_zone_t *zone, dns_db_t *db) { + dns_dbiterator_t *dbiterator = NULL; + dns_dbnode_t *node = NULL; + dns_fixedname_t fixed; + dns_name_t *name; + dns_rdataset_t rdataset; + dns_rdatasetiter_t *rdsit = NULL; + isc_boolean_t ok = ISC_TRUE; + isc_result_t result; + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + dns_rdataset_init(&rdataset); + + result = dns_db_createiterator(db, 0, &dbiterator); + if (result != ISC_R_SUCCESS) + return (ISC_TRUE); + + for (result = dns_dbiterator_first(dbiterator); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiterator)) { + result = dns_dbiterator_current(dbiterator, &node, name); + if (result != ISC_R_SUCCESS) + continue; + + result = dns_db_allrdatasets(db, node, NULL, 0, &rdsit); + if (result != ISC_R_SUCCESS) + continue; + + for (result = dns_rdatasetiter_first(rdsit); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsit)) { + dns_rdatasetiter_current(rdsit, &rdataset); + if (!zone_rrset_check_dup(zone, name, &rdataset)) + ok = ISC_FALSE; + dns_rdataset_disassociate(&rdataset); + } + dns_rdatasetiter_destroy(&rdsit); + dns_db_detachnode(db, &node); + } + + if (node != NULL) + dns_db_detachnode(db, &node); + dns_dbiterator_destroy(&dbiterator); + + return (ok); +} + +static isc_boolean_t integrity_checks(dns_zone_t *zone, dns_db_t *db) { dns_dbiterator_t *dbiterator = NULL; dns_dbnode_t *node = NULL; @@ -2088,6 +2196,7 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) { result = dns_rdataset_next(&rdataset); } dns_rdataset_disassociate(&rdataset); + goto next; checkmx: result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_mx, @@ -2574,8 +2683,8 @@ set_refreshkeytimer(dns_zone_t *zone, dns_rdata_keydata_t *key, */ static isc_result_t create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, - dns_diff_t *diff, dns_name_t *name, dns_keytable_t *keytable, - dns_keynode_t *keynode, isc_boolean_t *changed) + dns_diff_t *diff, dns_keytable_t *keytable, + dns_keynode_t **keynodep, isc_boolean_t *changed) { const char me[] = "create_keydata"; isc_result_t result = ISC_R_SUCCESS; @@ -2584,16 +2693,21 @@ create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_rdata_keydata_t keydata; dns_rdata_dnskey_t dnskey; dns_rdata_t rdata = DNS_RDATA_INIT; - dns_keynode_t *nextnode = NULL; + dns_keynode_t *keynode; isc_stdtime_t now; isc_region_t r; dst_key_t *key; + REQUIRE(keynodep != NULL); + keynode = *keynodep; + ENTER; isc_stdtime_get(&now); /* Loop in case there's more than one key. */ while (result == ISC_R_SUCCESS) { + dns_keynode_t *nextnode = NULL; + key = dns_keynode_key(keynode); if (key == NULL) goto skip; @@ -2621,12 +2735,12 @@ create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, /* Add rdata to zone. */ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, - name, 0, &rdata)); + dst_key_name(key), 0, &rdata)); *changed = ISC_TRUE; skip: result = dns_keytable_nextkeynode(keytable, keynode, &nextnode); - if(result != ISC_R_NOTFOUND) { + if (result != ISC_R_NOTFOUND) { dns_keytable_detachkeynode(keytable, &keynode); keynode = nextnode; } @@ -2636,6 +2750,10 @@ create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, if (*changed) set_refreshkeytimer(zone, &keydata, now); + if (keynode != NULL) + dns_keytable_detachkeynode(keytable, &keynode); + *keynodep = NULL; + return (ISC_R_SUCCESS); failure: @@ -3099,12 +3217,12 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { continue; result = dns_keytable_find(sr, rrname, &keynode); - if ((result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH) || dns_keynode_managed(keynode) == ISC_FALSE) { CHECK(delete_keydata(db, ver, &diff, rrname, rdataset)); + changed = ISC_TRUE; } else { load_secroots(zone, rrname, rdataset); } @@ -3127,10 +3245,10 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { dns_rbtnode_t *rbtnode = NULL; dns_rbtnodechain_current(&chain, &foundname, origin, &rbtnode); - keynode = rbtnode->data; - if (keynode == NULL) + if (rbtnode->data == NULL) goto skip; + dns_keytable_attachkeynode(sr, rbtnode->data, &keynode); if (dns_keynode_managed(keynode)) { dns_fixedname_t fname; dns_name_t *keyname; @@ -3149,13 +3267,14 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { NULL, NULL); if (result != ISC_R_SUCCESS) result = create_keydata(zone, db, ver, &diff, - keyname, sr, keynode, - &changed); + sr, &keynode, &changed); if (result != ISC_R_SUCCESS) break; } skip: result = dns_rbtnodechain_next(&chain, &foundname, origin); + if (keynode != NULL) + dns_keytable_detachkeynode(sr, &keynode); } RWUNLOCK(&sr->rwlock, isc_rwlocktype_write); @@ -3173,6 +3292,8 @@ sync_keyzone(dns_zone_t *zone, dns_db_t *db) { } failure: + if (keynode != NULL) + dns_keytable_detachkeynode(sr, &keynode); if (sr != NULL) dns_keytable_detach(&sr); if (ver != NULL) @@ -3337,6 +3458,13 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, goto cleanup; } + if (zone->type == dns_zone_master && + DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKDUPRR) && + !zone_check_dup(zone, db)) { + result = DNS_R_BADZONE; + goto cleanup; + } + if (zone->db != NULL) { /* * This is checked in zone_replacedb() for slave zones @@ -3528,7 +3656,9 @@ exit_check(dns_zone_t *zone) { } static isc_boolean_t -zone_check_ns(dns_zone_t *zone, dns_db_t *db, dns_name_t *name) { +zone_check_ns(dns_zone_t *zone, dns_db_t *db, dns_name_t *name, + isc_boolean_t logit) +{ isc_result_t result; char namebuf[DNS_NAME_FORMATSIZE]; char altbuf[DNS_NAME_FORMATSIZE]; @@ -3559,30 +3689,33 @@ zone_check_ns(dns_zone_t *zone, dns_db_t *db, dns_name_t *name) { return (ISC_TRUE); } - dns_name_format(name, namebuf, sizeof namebuf); if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || result == DNS_R_EMPTYNAME) { - dns_zone_log(zone, level, - "NS '%s' has no address records (A or AAAA)", - namebuf); - /* XXX950 Make fatal ISC_FALSE for 9.5.0. */ - return (ISC_TRUE); + if (logit) { + dns_name_format(name, namebuf, sizeof namebuf); + dns_zone_log(zone, level, "NS '%s' has no address " + "records (A or AAAA)", namebuf); + } + return (ISC_FALSE); } if (result == DNS_R_CNAME) { - dns_zone_log(zone, level, "NS '%s' is a CNAME (illegal)", - namebuf); - /* XXX950 Make fatal ISC_FALSE for 9.5.0. */ - return (ISC_TRUE); + if (logit) { + dns_name_format(name, namebuf, sizeof namebuf); + dns_zone_log(zone, level, "NS '%s' is a CNAME " + "(illegal)", namebuf); + } + return (ISC_FALSE); } if (result == DNS_R_DNAME) { - dns_name_format(foundname, altbuf, sizeof altbuf); - dns_zone_log(zone, level, - "NS '%s' is below a DNAME '%s' (illegal)", - namebuf, altbuf); - /* XXX950 Make fatal ISC_FALSE for 9.5.0. */ - return (ISC_TRUE); + if (logit) { + dns_name_format(name, namebuf, sizeof namebuf); + dns_name_format(foundname, altbuf, sizeof altbuf); + dns_zone_log(zone, level, "NS '%s' is below a DNAME " + "'%s' (illegal)", namebuf, altbuf); + } + return (ISC_FALSE); } return (ISC_TRUE); @@ -3591,7 +3724,7 @@ zone_check_ns(dns_zone_t *zone, dns_db_t *db, dns_name_t *name) { static isc_result_t zone_count_ns_rr(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, unsigned int *nscount, - unsigned int *errors) + unsigned int *errors, isc_boolean_t logit) { isc_result_t result; unsigned int count = 0; @@ -3622,7 +3755,7 @@ zone_count_ns_rr(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node, result = dns_rdata_tostruct(&rdata, &ns, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); if (dns_name_issubdomain(&ns.name, &zone->origin) && - !zone_check_ns(zone, db, &ns.name)) + !zone_check_ns(zone, db, &ns.name, logit)) ecount++; } count++; @@ -3751,7 +3884,7 @@ zone_get_from_db(dns_zone_t *zone, dns_db_t *db, unsigned int *nscount, if (nscount != NULL || errors != NULL) { result = zone_count_ns_rr(zone, db, node, version, - nscount, errors); + nscount, errors, ISC_TRUE); if (result != ISC_R_SUCCESS) answer = result; } @@ -6350,6 +6483,7 @@ zone_sign(dns_zone_t *zone) { unsigned int nkeys = 0; isc_uint32_t nodes; isc_boolean_t was_ksk; + isc_time_t when; dns_rdataset_init(&rdataset); dns_fixedname_init(&fixed); @@ -6704,6 +6838,17 @@ zone_sign(dns_zone_t *zone) { */ zone_journal(zone, &sig_diff, "zone_sign"); + /* + * Notify slaves, if appropriate. + */ + TIME_NOW(&when); + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOTIFYRESIGN)) { + LOCK_ZONE(zone); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOTIFYRESIGN); + UNLOCK_ZONE(zone); + zone_notify(zone, &when); + } + pauseall: /* * Pause all iterators so that dns_db_closeversion() can succeed. @@ -7452,7 +7597,7 @@ keyfetch_done(isc_task_t *task, isc_event_t *event) { } dns_diff_clear(&diff); - dns_db_closeversion(kfetch->db, &ver, alldone); + dns_db_closeversion(kfetch->db, &ver, changed); dns_db_detach(&kfetch->db); if (dns_rdataset_isassociated(&kfetch->keydataset)) @@ -13437,6 +13582,53 @@ add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype, return (result); } +static void +sign_dnskey(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, + dns_diff_t *diff) +{ + isc_result_t result; + isc_stdtime_t now, inception, soaexpire; + isc_boolean_t check_ksk, keyset_kskonly; + dst_key_t *zone_keys[MAXZONEKEYS]; + unsigned int nkeys = 0, i; + + result = find_zone_keys(zone, db, ver, zone->mctx, MAXZONEKEYS, + zone_keys, &nkeys); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "sign_dnskey:find_zone_keys -> %s\n", + dns_result_totext(result)); + return; + } + + isc_stdtime_get(&now); + inception = now - 3600; /* Allow for clock skew. */ + soaexpire = now + dns_zone_getsigvalidityinterval(zone); + + check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK); + keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY); + + result = del_sigs(zone, db, ver, &zone->origin, dns_rdatatype_dnskey, + diff, zone_keys, nkeys, now); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "sign_dnskey:del_sigs -> %s\n", + dns_result_totext(result)); + goto failure; + } + + result = add_sigs(db, ver, &zone->origin, dns_rdatatype_dnskey, diff, + zone_keys, nkeys, zone->mctx, inception, soaexpire, + check_ksk, keyset_kskonly); + + if (result != ISC_R_SUCCESS) + dns_zone_log(zone, ISC_LOG_ERROR, "zone_rekey:add_sigs -> %s\n", + dns_result_totext(result)); + failure: + for (i = 0; i < nkeys; i++) + dst_key_free(&zone_keys[i]); +} + static isc_result_t zone_rekey(dns_zone_t *zone) { isc_result_t result; @@ -13471,6 +13663,8 @@ zone_rekey(dns_zone_t *zone) { CHECK(dns_db_newversion(db, &ver)); CHECK(dns_db_getoriginnode(db, &node)); + dns_zone_log(zone, ISC_LOG_INFO, "reconfiguring zone keys"); + /* Get the SOA record's TTL */ CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, dns_rdatatype_none, 0, &soaset, &soasigs)); @@ -13500,18 +13694,20 @@ zone_rekey(dns_zone_t *zone) { ISC_TF(!check_ksk), mctx, logmsg)); if (!ISC_LIST_EMPTY(diff.tuples)) { commit = ISC_TRUE; - add_signing_records(db, zone->privatetype, ver, &diff); dns_diff_apply(&diff, db, ver); + sign_dnskey(zone, db, ver, &diff); + add_signing_records(db, zone->privatetype, ver, &diff); result = increment_soa_serial(db, ver, &diff, mctx); if (result == ISC_R_SUCCESS) zone_journal(zone, &diff, "zone_rekey"); - } } dns_db_closeversion(db, &ver, commit); if (commit) { + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOTIFYRESIGN); + for (key = ISC_LIST_HEAD(rmkeys); key != NULL; key = ISC_LIST_NEXT(key, link)) { @@ -13594,3 +13790,22 @@ dns_zone_rekey(dns_zone_t *zone) { UNLOCK_ZONE(zone); return (result); } + +isc_result_t +dns_zone_nscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, + unsigned int *errors) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(errors != NULL); + + result = dns_db_getoriginnode(db, &node); + if (result != ISC_R_SUCCESS) + return (result); + result = zone_count_ns_rr(zone, db, node, version, NULL, errors, + ISC_FALSE); + dns_db_detachnode(db, &node); + return (result); +} |