From 1a890ff58a63c8f5be7565f0339324922cd40af6 Mon Sep 17 00:00:00 2001 From: Curtis Blackburn Date: Wed, 4 Dec 2013 12:32:21 -0600 Subject: [PATCH] [v9_9]3682. [bug] Correct the behavior of rndc retransfer to allow inline-signing slave zones to retain NSEC3 parameters instead of reverting to NSEC [RT #34745] --- CHANGES | 4 + bin/rndc/rndc.docbook | 10 +- bin/tests/system/inline/clean.sh | 4 + bin/tests/system/inline/ns1/root.db.in | 3 + bin/tests/system/inline/ns2/named.conf | 7 + bin/tests/system/inline/ns3/named.conf | 8 + bin/tests/system/inline/ns3/sign.sh | 7 + bin/tests/system/inline/tests.sh | 43 ++++- lib/dns/zone.c | 256 +++++++++++++++++++++++-- 9 files changed, 327 insertions(+), 15 deletions(-) mode change 100644 => 100755 bin/tests/system/inline/ns3/sign.sh mode change 100644 => 100755 bin/tests/system/inline/tests.sh diff --git a/CHANGES b/CHANGES index d88fe65cc2..284c902e59 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +3682. [bug] Correct the behavior of rndc retransfer to allow + inline-signing slave zones to retain NSEC3 parameters instead of + reverting to NSEC [RT #34745] + 3681. [port] Update the Windows build system to support feature selection and WIN64 builds. This is a work in progress. [RT #34160] diff --git a/bin/rndc/rndc.docbook b/bin/rndc/rndc.docbook index e71ee2970c..2b91d2d57a 100644 --- a/bin/rndc/rndc.docbook +++ b/bin/rndc/rndc.docbook @@ -239,7 +239,15 @@ retransfer zone class view - Retransfer the given zone from the master. + Retransfer the given slave zone from the master server. + + + If the zone is configured to use + inline-signing, the signed + version of the zone is discarded; after the + retransfer of the unsigned version is complete, the + signed version will be regenerated with all new + signatures. diff --git a/bin/tests/system/inline/clean.sh b/bin/tests/system/inline/clean.sh index 4913c733b3..462bf02c55 100644 --- a/bin/tests/system/inline/clean.sh +++ b/bin/tests/system/inline/clean.sh @@ -60,6 +60,10 @@ rm -f ns3/retransfer.bk rm -f ns3/retransfer.bk.jnl rm -f ns3/retransfer.bk.signed rm -f ns3/retransfer.bk.signed.jnl +rm -f ns3/retransfer3.bk +rm -f ns3/retransfer3.bk.jnl +rm -f ns3/retransfer3.bk.signed +rm -f ns3/retransfer3.bk.signed.jnl rm -f ns3/externalkey.db rm -f ns3/externalkey.db.signed rm -f ns3/externalkey.db.signed.jnl diff --git a/bin/tests/system/inline/ns1/root.db.in b/bin/tests/system/inline/ns1/root.db.in index a08db511bc..e319b37eb0 100644 --- a/bin/tests/system/inline/ns1/root.db.in +++ b/bin/tests/system/inline/ns1/root.db.in @@ -53,3 +53,6 @@ ns3.nsec3. A 10.53.0.3 externalkey. NS ns3.externalkey. ns3.externalkey. A 10.53.0.3 + +retransfer3. NS ns3.retransfer. +ns3.retransfer3. A 10.53.0.3 diff --git a/bin/tests/system/inline/ns2/named.conf b/bin/tests/system/inline/ns2/named.conf index bafbaf6782..c7e8d47bc4 100644 --- a/bin/tests/system/inline/ns2/named.conf +++ b/bin/tests/system/inline/ns2/named.conf @@ -48,3 +48,10 @@ zone "retransfer" { allow-update { any; }; notify no; }; + +zone "retransfer3" { + type master; + file "retransfer.db"; + allow-update { any; }; + notify no; +}; diff --git a/bin/tests/system/inline/ns3/named.conf b/bin/tests/system/inline/ns3/named.conf index 7c23edd1d7..6063569689 100644 --- a/bin/tests/system/inline/ns3/named.conf +++ b/bin/tests/system/inline/ns3/named.conf @@ -111,3 +111,11 @@ zone "externalkey" { allow-update { any; }; file "externalkey.db"; }; + +zone "retransfer3" { + type slave; + masters { 10.53.0.2; }; + inline-signing yes; + auto-dnssec maintain; + file "retransfer3.bk"; +}; diff --git a/bin/tests/system/inline/ns3/sign.sh b/bin/tests/system/inline/ns3/sign.sh old mode 100644 new mode 100755 index d606fe75db..7e9260ba9b --- a/bin/tests/system/inline/ns3/sign.sh +++ b/bin/tests/system/inline/ns3/sign.sh @@ -80,6 +80,13 @@ rm -f K${zone}.+*+*.private keyname=`$KEYGEN -q -r $RANDFILE -a NSEC3RSASHA1 -b 1024 -n zone -f KSK $zone` $DSFROMKEY -T 1200 $keyname >> ../ns1/root.db +zone=retransfer3 +rm -f K${zone}.+*+*.key +rm -f K${zone}.+*+*.private +keyname=`$KEYGEN -q -r $RANDFILE -a NSEC3RSASHA1 -b 768 -n zone $zone` +keyname=`$KEYGEN -q -r $RANDFILE -a NSEC3RSASHA1 -b 1024 -n zone -f KSK $zone` +$DSFROMKEY -T 1200 $keyname >> ../ns1/root.db + for s in a c d h k l m q z do zone=test-$s diff --git a/bin/tests/system/inline/tests.sh b/bin/tests/system/inline/tests.sh old mode 100644 new mode 100755 index f975c26ae7..c8719f72b0 --- a/bin/tests/system/inline/tests.sh +++ b/bin/tests/system/inline/tests.sh @@ -25,7 +25,7 @@ RANDFILE=random.data status=0 n=0 -$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -nsec3param 1 0 0 - nsec3 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -nsec3param 1 0 0 - nsec3 > /dev/null 2>&1 for i in 1 2 3 4 5 6 7 8 9 0 do @@ -34,6 +34,21 @@ do sleep 1 done +# Loop until retransfer3 has been transferred. +for i in 1 2 3 4 5 6 7 8 9 0 +do + ans=0 + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -nsec3param 1 0 0 - retransfer3 > /dev/null 2>&1 || ans=1 + [ $ans = 0 ] && break +done + +for i in 1 2 3 4 5 6 7 8 9 0 +do + nsec3param=`$DIG +short @10.53.0.3 -p 5300 nsec3param retransfer3.` + test -n "$nsec3param" && break + sleep 1 +done + n=`expr $n + 1` echo "I:checking that rrsigs are replaced with ksk only" ret=0 @@ -761,6 +776,32 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check rndc retransfer of a inline nsec3 slave retains nsec3 ($n)" +ret=0 +for i in 0 1 2 3 4 5 6 7 8 9 +do + ans=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 nonexist.retransfer3 A > dig.out.ns3.pre.test$n + grep "status: NXDOMAIN" dig.out.ns3.pre.test$n > /dev/null || ans=1 + grep "NSEC3" dig.out.ns3.pre.test$n > /dev/null || ans=1 + [ $ans = 0 ] && break + sleep 1 +done +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 retransfer retransfer3 2>&1 || ret=1 +for i in 0 1 2 3 4 5 6 7 8 9 +do + ans=0 + $DIG $DIGOPTS @10.53.0.3 -p 5300 nonexist.retransfer3 A > dig.out.ns3.post.test$n + grep "status: NXDOMAIN" dig.out.ns3.post.test$n > /dev/null || ans=1 + grep "NSEC3" dig.out.ns3.post.test$n > /dev/null || ans=1 + [ $ans = 0 ] && break + sleep 1 +done +[ $ans = 1 ] && ret=1 +n=`expr $n + 1` +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 diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 22cdb7c1bb..af85ac18bc 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -776,6 +776,20 @@ static const char *dbargv_default[] = { "rbt" }; } \ } while (0) +typedef struct nsec3param nsec3param_t; +struct nsec3param { + unsigned char data[DNS_NSEC3PARAM_BUFFERSIZE + 1]; + unsigned int length; + isc_boolean_t nsec; + isc_boolean_t replace; + ISC_LINK(nsec3param_t) link; +}; +typedef ISC_LIST(nsec3param_t) nsec3paramlist_t; +struct np3event { + isc_event_t event; + nsec3param_t params; +}; + /*% * Increment resolver-related statistics counters. Zone must be locked. */ @@ -13019,6 +13033,203 @@ checkandaddsoa(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, 0, NULL)); } +/* + * This function should populate an nsec3paramlist_t with the + * nsecparam_t data from a zone. + */ +static isc_result_t +save_nsec3param(dns_zone_t *zone, nsec3paramlist_t *nsec3list) { + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset, prdataset; + dns_rdata_t rdata_in, prdata_in, prdata_out; + dns_dbversion_t *version = NULL; + nsec3param_t *nsec3param = NULL; + nsec3param_t *nsec3p = NULL; + nsec3param_t *next; + dns_db_t *db = NULL; + unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(nsec3list != NULL); + REQUIRE(ISC_LIST_EMPTY(*nsec3list)); + + dns_db_attach(zone->db, &db); + CHECK(dns_db_getoriginnode(db, &node)); + + dns_rdataset_init(&rdataset); + dns_db_currentversion(db, &version); + result = dns_db_findrdataset(db, node, version, + dns_rdatatype_nsec3param, + dns_rdatatype_none, 0, &rdataset, NULL); + + if (result != ISC_R_SUCCESS) + goto getprivate; + + /* + * walk nsec3param rdataset making a list of parameters (note that + * multiple simultaneous nsec3 chains are annoyingly legal -- this + * is why we use an nsec3list, even tho we will usually only have + * one) + */ + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) + { + dns_rdata_init(&rdata_in); + dns_rdataset_current(&rdataset, &rdata_in); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3), + "looping through nsec3param data"); + nsec3param = isc_mem_get(zone->mctx, sizeof(nsec3param_t)); + if (nsec3param == NULL) + CHECK(ISC_R_NOMEMORY); + ISC_LINK_INIT(nsec3param, link); + + /* + * now transfer the data from the rdata to + * the nsec3param + */ + dns_rdata_init(&prdata_out); + dns_nsec3param_toprivate(&rdata_in, &prdata_out, + zone->privatetype, nsec3param->data, + sizeof(nsec3param->data)); + nsec3param->length = prdata_out.length; + ISC_LIST_APPEND(*nsec3list, nsec3param, link); + } + + getprivate: + dns_rdataset_init(&prdataset); + result = dns_db_findrdataset(db, node, version, zone->privatetype, + dns_rdatatype_none, 0, &prdataset, NULL); + if (result != ISC_R_SUCCESS) + goto done; + + /* + * walk private type records, converting them to nsec3 parameters + * using dns_nsec3param_fromprivate(), do the right thing based on + * CREATE and REMOVE flags + */ + for (result = dns_rdataset_first(&prdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&prdataset)) + { + dns_rdata_init(&prdata_in); + dns_rdataset_current(&prdataset, &prdata_in); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3), + "looping through nsec3param private data"); + + if (!dns_nsec3param_fromprivate(&prdata_in, &prdata_out, + buf, sizeof(buf))) + continue; + + if ((prdata_out.data[1] & DNS_NSEC3FLAG_REMOVE) !=0) { + prdata_out.data[1] = 0; + + for (nsec3p = ISC_LIST_HEAD(*nsec3list); + nsec3p != NULL; + nsec3p = next) + { + next = ISC_LIST_NEXT(nsec3p, link); + if (memcmp(prdata_out.data, nsec3p->data, + sizeof(nsec3p->data)) == 0) { + ISC_LIST_UNLINK(*nsec3list, + nsec3p, link); + isc_mem_put(zone->mctx, nsec3p, + sizeof(nsec3param_t)); + } + } + continue; + } + + nsec3param = isc_mem_get(zone->mctx, sizeof(nsec3param_t)); + if (nsec3param == NULL) + CHECK(ISC_R_NOMEMORY); + ISC_LINK_INIT(nsec3param, link); + + dns_rdata_init(&prdata_out); + dns_nsec3param_toprivate(&prdata_in, &prdata_out, + zone->privatetype, nsec3param->data, + sizeof(nsec3param->data)); + nsec3param->length = prdata_out.length; + ISC_LIST_APPEND(*nsec3list, nsec3param, link); + } + + done: + if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND) + result = ISC_R_SUCCESS; + + failure: + if (node != NULL) + dns_db_detachnode(db, &node); + if (version != NULL) + dns_db_closeversion(db, &version, ISC_FALSE); + if (db != NULL) + dns_db_detach(&db); + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (dns_rdataset_isassociated(&prdataset)) + dns_rdataset_disassociate(&prdataset); + return (result); +} + +/* + * Walk the list of the nsec3 chains desired for the zone, converting + * parameters to private type records using dns_nsec3param_toprivate(), + * and insert them into the new zone db. + */ +static isc_result_t +restore_nsec3param(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, + nsec3paramlist_t *nsec3list) +{ + isc_result_t result; + dns_diff_t diff; + dns_rdata_t rdata; + nsec3param_t *nsec3p = NULL; + nsec3param_t *next; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(!ISC_LIST_EMPTY(*nsec3list)); + + dns_diff_init(zone->mctx, &diff); + + /* + * Loop through the list of private-type records, set the INITIAL + * and CREATE flags, and the add the record to the apex of the tree + * in db. + */ + for (nsec3p = ISC_LIST_HEAD(*nsec3list); + nsec3p != NULL; + nsec3p = next) + { + next = ISC_LIST_NEXT(nsec3p, link); + dns_rdata_init(&rdata); + nsec3p->data[2] = DNS_NSEC3FLAG_CREATE | DNS_NSEC3FLAG_INITIAL; + rdata.length = nsec3p->length; + rdata.data = nsec3p->data; + rdata.type = zone->privatetype; + rdata.rdclass = zone->rdclass; + CHECK(update_one_rr(db, version, &diff, DNS_DIFFOP_ADD, + &zone->origin, 0, &rdata)); + } + + result = ISC_R_SUCCESS; + +failure: + for (nsec3p = ISC_LIST_HEAD(*nsec3list); + nsec3p != NULL; + nsec3p = next) + { + next = ISC_LIST_NEXT(nsec3p, link); + ISC_LIST_UNLINK(*nsec3list, nsec3p, link); + isc_mem_put(zone->mctx, nsec3p, sizeof(nsec3param_t)); + } + + dns_diff_clear(&diff); + return (result); +} + static void receive_secure_db(isc_task_t *task, isc_event_t *event) { isc_result_t result; @@ -13034,9 +13245,12 @@ receive_secure_db(isc_task_t *task, isc_event_t *event) { isc_time_t loadtime; unsigned int oldserial = 0; isc_boolean_t have_oldserial = ISC_FALSE; + nsec3paramlist_t nsec3list; UNUSED(task); + ISC_LIST_INIT(nsec3list); + zone = event->ev_arg; rawdb = ((struct secure_event *)event)->db; isc_event_free(&event); @@ -13057,6 +13271,16 @@ receive_secure_db(isc_task_t *task, isc_event_t *event) { result = dns_db_getsoaserial(zone->db, NULL, &oldserial); if (result == ISC_R_SUCCESS) have_oldserial = ISC_TRUE; + + /* + * assemble nsec3parameters from the old zone, and set a flag + * if any are found + */ + result = save_nsec3param(zone, &nsec3list); + if (result != ISC_R_SUCCESS) { + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + goto failure; + } } ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); @@ -13118,8 +13342,16 @@ receive_secure_db(isc_task_t *task, isc_event_t *event) { dns_db_detachnode(rawdb, &rawnode); dns_db_detachnode(db, &node); } - + + /* + * Call restore_nsec3param() to create private-type records from + * the old nsec3 parameters and insert them into db + */ + if (!ISC_LIST_EMPTY(nsec3list)) + restore_nsec3param(zone, db, version, &nsec3list); + dns_db_closeversion(db, &version, ISC_TRUE); + /* * Lock hierarchy: zmgr, zone, raw. */ @@ -16790,14 +17022,6 @@ dns_zone_keydone(dns_zone_t *zone, const char *keystr) { return (result); } -struct nsec3param { - isc_event_t event; - unsigned char data[DNS_NSEC3PARAM_BUFFERSIZE + 1]; - unsigned int length; - isc_boolean_t nsec; - isc_boolean_t replace; -}; - static void setnsec3param(isc_task_t *task, isc_event_t *event) { const char *me = "setnsec3param"; @@ -16809,7 +17033,8 @@ setnsec3param(isc_task_t *task, isc_event_t *event) { dns_dbnode_t *node = NULL; dns_rdataset_t prdataset, nrdataset; dns_diff_t diff; - struct nsec3param *np = (struct nsec3param *)event; + struct np3event *npe = (struct np3event *)event; + nsec3param_t *np; dns_update_log_t log = { update_log_cb, NULL }; dns_rdata_t rdata; isc_boolean_t nseconly; @@ -16821,6 +17046,8 @@ setnsec3param(isc_task_t *task, isc_event_t *event) { INSIST(DNS_ZONE_VALID(zone)); ENTER; + + np = &npe->params; dns_rdataset_init(&prdataset); dns_rdataset_init(&nrdataset); @@ -16973,7 +17200,8 @@ dns_zone_setnsec3param(dns_zone_t *zone, isc_uint8_t hash, isc_uint8_t flags, dns_rdata_t nrdata = DNS_RDATA_INIT; dns_rdata_t prdata = DNS_RDATA_INIT; unsigned char nbuf[DNS_NSEC3PARAM_BUFFERSIZE]; - struct nsec3param *np; + struct np3event *npe; + nsec3param_t *np; dns_zone_t *dummy = NULL; isc_buffer_t b; isc_event_t *e; @@ -16984,13 +17212,15 @@ dns_zone_setnsec3param(dns_zone_t *zone, isc_uint8_t hash, isc_uint8_t flags, LOCK_ZONE(zone); e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_SETNSEC3PARAM, - setnsec3param, zone, sizeof(struct nsec3param)); + setnsec3param, zone, sizeof(struct np3event)); if (e == NULL) { result = ISC_R_NOMEMORY; goto failure; } - np = (struct nsec3param *) e; + npe = (struct np3event *) e; + np = &npe->params; + np->replace = replace; if (hash == 0) { np->length = 0;