From 9c03f13e18c1b0c32f62391a17300378605bbc7b Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Fri, 28 Oct 2011 06:20:07 +0000 Subject: [PATCH] 3185. [func] New 'rndc signing' option for auto-dnssec zones: - 'rndc signing -list' displays the current state of signing operations - 'rndc signing -clear' clears the signing state records for keys that have fully signed the zone - 'rndc signing -nsec3param' sets the NSEC3 parameters for the zone The 'rndc keydone' syntax is removed. [RT #23729] --- CHANGES | 9 + bin/named/control.c | 6 +- bin/named/include/named/control.h | 4 +- bin/named/include/named/server.h | 7 +- bin/named/server.c | 160 +++++++++--- bin/named/update.c | 4 +- bin/rndc/rndc.c | 14 +- bin/tests/system/autosign/tests.sh | 42 +++- bin/tests/system/dnssec/clean.sh | 3 +- bin/tests/system/dnssec/ns3/named.conf | 11 +- bin/tests/system/dnssec/tests.sh | 29 ++- bin/tests/system/inline/clean.sh | 3 +- bin/tests/system/inline/tests.sh | 99 +++----- doc/arm/Bv9ARM-book.xml | 55 ++-- lib/dns/include/dns/events.h | 3 +- lib/dns/include/dns/nsec3.h | 5 +- lib/dns/include/dns/private.h | 19 +- lib/dns/include/dns/zone.h | 16 +- lib/dns/nsec3.c | 21 +- lib/dns/private.c | 73 +++++- lib/dns/tests/Makefile.in | 13 +- lib/dns/tests/private_test.c | 225 +++++++++++++++++ lib/dns/zone.c | 331 +++++++++++++++++++++++-- 23 files changed, 987 insertions(+), 165 deletions(-) create mode 100644 lib/dns/tests/private_test.c diff --git a/CHANGES b/CHANGES index f1c1fdb2a0..2e90fb9347 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,12 @@ +3185. [func] New 'rndc signing' option for auto-dnssec zones: + - 'rndc signing -list' displays the current + state of signing operations + - 'rndc signing -clear' clears the signing state + records for keys that have fully signed the zone + - 'rndc signing -nsec3param' sets the NSEC3 + parameters for the zone + The 'rndc keydone' syntax is removed. [RT #23729] + 3184. [bug] named had excessive cpu usage when a redirect zone was configured. [RT #26013] diff --git a/bin/named/control.c b/bin/named/control.c index 006aba2115..8a67b97ec0 100644 --- a/bin/named/control.c +++ b/bin/named/control.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: control.c,v 1.45 2011/10/25 01:54:18 marka Exp $ */ +/* $Id: control.c,v 1.46 2011/10/28 06:20:04 each Exp $ */ /*! \file */ @@ -205,8 +205,8 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { result = ns_server_add_zone(ns_g_server, command); } else if (command_compare(command, NS_COMMAND_DELZONE)) { result = ns_server_del_zone(ns_g_server, command); - } else if (command_compare(command, NS_COMMAND_KEYDONE)) { - result = ns_server_keydone(ns_g_server, command); + } else if (command_compare(command, NS_COMMAND_SIGNING)) { + result = ns_server_signing(ns_g_server, command, text); } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h index 31ca427de5..064ec50e2a 100644 --- a/bin/named/include/named/control.h +++ b/bin/named/include/named/control.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: control.h,v 1.35 2011/10/25 01:54:19 marka Exp $ */ +/* $Id: control.h,v 1.36 2011/10/28 06:20:04 each Exp $ */ #ifndef NAMED_CONTROL_H #define NAMED_CONTROL_H 1 @@ -64,7 +64,7 @@ #define NS_COMMAND_ADDZONE "addzone" #define NS_COMMAND_DELZONE "delzone" #define NS_COMMAND_SYNC "sync" -#define NS_COMMAND_KEYDONE "keydone" +#define NS_COMMAND_SIGNING "signing" isc_result_t ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp); diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index 332bef738c..e05222aaaa 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.h,v 1.114 2011/10/25 01:54:19 marka Exp $ */ +/* $Id: server.h,v 1.115 2011/10/28 06:20:04 each Exp $ */ #ifndef NAMED_SERVER_H #define NAMED_SERVER_H 1 @@ -343,9 +343,8 @@ isc_result_t ns_server_del_zone(ns_server_t *server, char *args); /*% - * Deletes the matching key done private record from the zone. + * Lists the status of the signing records for a given zone. */ isc_result_t -ns_server_keydone(ns_server_t *server, char *args); - +ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text); #endif /* NAMED_SERVER_H */ diff --git a/bin/named/server.c b/bin/named/server.c index 18fa5317b3..9b71eae6f6 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.c,v 1.623 2011/10/25 01:54:19 marka Exp $ */ +/* $Id: server.c,v 1.624 2011/10/28 06:20:04 each Exp $ */ /*! \file */ @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -73,6 +74,7 @@ #include #include #include +#include #include #include #include @@ -7858,46 +7860,142 @@ newzone_cfgctx_destroy(void **cfgp) { *cfgp = NULL; } -/* - * Act on a "keydone" command from the command channel. - */ isc_result_t -ns_server_keydone(ns_server_t *server, char *args) { - isc_result_t result; +ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) { + isc_result_t result = ISC_R_SUCCESS; dns_zone_t *zone = NULL; - const char *ptr = NULL; + dns_name_t *origin; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + dns_dbversion_t *version = NULL; + dns_rdatatype_t privatetype; + dns_rdataset_t privset; + isc_boolean_t first = ISC_TRUE; + isc_boolean_t list = ISC_FALSE, clear = ISC_FALSE; + isc_boolean_t chain = ISC_FALSE; + char keystr[DNS_SECALG_FORMATSIZE + 7]; + isc_uint8_t hash = 0, flags = 0, iter = 0, saltlen = 0; + unsigned char salt[255]; + const char *ptr; + size_t n; + dns_rdataset_init(&privset); + + (void) next_token(&args, " \t"); ptr = next_token(&args, " \t"); - if (ptr == NULL) - return (ISC_R_UNEXPECTEDEND); + if (strcasecmp(ptr, "-list") == 0) + list = ISC_TRUE; + else if (strcasecmp(ptr, "-clear") == 0) { + clear = ISC_TRUE; + ptr = next_token(&args, " \t"); + memcpy(keystr, ptr, sizeof(keystr)); + } else if(strcasecmp(ptr, "-nsec3param") == 0) { + const char *hashstr, *flagstr, *iterstr; + isc_buffer_t buf; + char nbuf[512]; - ptr = next_token(&args, " \t"); - if (ptr == NULL) - return (ISC_R_UNEXPECTEDEND); - /* - * Is the rdata sane? - */ - if (strspn(ptr, "0123456789ABCDEFabcdef") != 10U || - strncmp(ptr, "00", 2) == 0 || strcmp(ptr + 6, "0001") != 0) - return (DNS_R_SYNTAX); + chain = ISC_TRUE; + hashstr = next_token(&args, " \t"); - /* - * Find the zone. - */ - result = zone_from_args(server, args, &zone, NULL, ISC_FALSE); - if (result != ISC_R_SUCCESS) - return (result); + if (strcasecmp(hashstr, "none") == 0) + hash = 0; + else { + flagstr = next_token(&args, " \t"); + iterstr = next_token(&args, " \t"); + n = snprintf(nbuf, sizeof(nbuf), "%s %s %s", + hashstr, flagstr, iterstr); + if (n == sizeof(nbuf)) + return (ISC_R_NOSPACE); + n = sscanf(nbuf, "%hhd %hhd %hhd", + &hash, &flags, &iter); + if (n != 3) + return (ISC_R_BADNUMBER); + + ptr = next_token(&args, " \t"); + isc_buffer_init(&buf, salt, sizeof(salt)); + CHECK(isc_hex_decodestring(ptr, &buf)); + saltlen = isc_buffer_usedlength(&buf); + } + } else + CHECK(ISC_R_NOTFOUND); + + CHECK(zone_from_args(server, args, &zone, NULL, ISC_FALSE)); if (zone == NULL) - return(ISC_R_NOTFOUND); + CHECK(ISC_R_UNEXPECTEDEND); - if (dns_zone_gettype(zone) != dns_zone_master) { - result = DNS_R_NOTMASTER; - goto cleanup; + if (clear) { + result = dns_zone_keydone(zone, keystr); + if (result == ISC_R_SUCCESS) { + isc_buffer_putstr(text, "request queued"); + isc_buffer_putuint8(text, 0); + } else + CHECK(result); + } else if (chain) { + result = dns_zone_setnsec3param(zone, hash, flags, iter, + saltlen, salt, ISC_TRUE); + if (result == ISC_R_SUCCESS) { + isc_buffer_putstr(text, "request queued"); + isc_buffer_putuint8(text, 0); + } else + CHECK(result); + } else { + privatetype = dns_zone_getprivatetype(zone); + origin = dns_zone_getorigin(zone); + CHECK(dns_zone_getdb(zone, &db)); + CHECK(dns_db_findnode(db, origin, ISC_FALSE, &node)); + dns_db_currentversion(db, &version); + + result = dns_db_findrdataset(db, node, version, privatetype, + dns_rdatatype_none, 0, + &privset, NULL); + if (result == ISC_R_NOTFOUND) { + isc_buffer_putstr(text, "No signing records found"); + isc_buffer_putuint8(text, 0); + result = ISC_R_SUCCESS; + goto cleanup; + } + + for (result = dns_rdataset_first(&privset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&privset)) + { + dns_rdata_t priv = DNS_RDATA_INIT; + char output[BUFSIZ]; + isc_buffer_t buf; + + dns_rdataset_current(&privset, &priv); + + isc_buffer_init(&buf, output, sizeof(output)); + CHECK(dns_private_totext(&priv, &buf)); + + if (!first) + isc_buffer_putstr(text, "\n"); + first = ISC_FALSE; + + n = snprintf((char *)isc_buffer_used(text), + isc_buffer_availablelength(text), + "%s", output); + if (n >= isc_buffer_availablelength(text)) + CHECK(ISC_R_NOSPACE); + + isc_buffer_add(text, n); + } + + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; } - result = dns_zone_keydone(zone, ptr); - cleanup: - dns_zone_detach(&zone); + if (dns_rdataset_isassociated(&privset)) + dns_rdataset_disassociate(&privset); + 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 (zone != NULL) + dns_zone_detach(&zone); + return (result); } diff --git a/bin/named/update.c b/bin/named/update.c index c51ff43f9e..d83fcbae35 100644 --- a/bin/named/update.c +++ b/bin/named/update.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.c,v 1.197 2011/08/31 06:49:09 marka Exp $ */ +/* $Id: update.c,v 1.198 2011/10/28 06:20:04 each Exp $ */ #include @@ -3068,7 +3068,7 @@ update_action(isc_task_t *task, isc_event_t *event) { * remove any NSEC chain present will also be removed. */ CHECK(dns_nsec3param_deletechains(db, ver, zone, - &diff)); + ISC_TRUE, &diff)); } else if (has_dnskey && isdnssec(db, ver, privatetype)) { isc_uint32_t interval; dns_update_log_t log; diff --git a/bin/rndc/rndc.c b/bin/rndc/rndc.c index b39c5fbb3b..75f9328222 100644 --- a/bin/rndc/rndc.c +++ b/bin/rndc/rndc.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rndc.c,v 1.135 2011/10/25 01:54:19 marka Exp $ */ +/* $Id: rndc.c,v 1.136 2011/10/28 06:20:05 each Exp $ */ /*! \file */ @@ -154,9 +154,15 @@ command is one of the following:\n\ Add zone to given view. Requires new-zone-file option.\n\ delzone [\"file\"] zone [class [view]]\n\ Removes zone from given view. Requires new-zone-file option.\n\ - keydone rdata zone [class [view]]\n\ - Remove the private record with the corresponding rdata from\n\ - the given zone.\n\ + signing -list zone [class [view]]\n\ + List the private records showing the state of DNSSEC\n\ + signing in the given zone.\n\ + signing -clear / zone [class [view]]\n\ + Remove the private record that indicating the given key\n\ + has finished signing the given zone.\n\ + signing -clear all zone [class [view]]\n\ + Remove the private records for all keys that have\n\ + finished signing the given zone.\n\ \n\ * == not yet implemented\n\ Version: %s\n", diff --git a/bin/tests/system/autosign/tests.sh b/bin/tests/system/autosign/tests.sh index e16bcc7b9a..ef69cea785 100644 --- a/bin/tests/system/autosign/tests.sh +++ b/bin/tests/system/autosign/tests.sh @@ -14,7 +14,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: tests.sh,v 1.36 2011/10/20 21:20:01 marka Exp $ +# $Id: tests.sh,v 1.37 2011/10/28 06:20:05 each Exp $ SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh @@ -126,7 +126,7 @@ zone nsec3.example. update add nsec3.example. 3600 NSEC3PARAM 1 0 10 BEEF send zone autonsec3.example. -update add autonsec3.example. 3600 NSEC3PARAM 1 1 10 BEEF +update add autonsec3.example. 3600 NSEC3PARAM 1 0 20 DEAF send zone nsec3.optout.example. update add nsec3.optout.example. 3600 NSEC3PARAM 1 0 10 BEEF @@ -140,6 +140,7 @@ send END # try to convert nsec.example; this should fail due to non-NSEC key +echo "I:preset nsec3param in unsigned zone via nsupdate ($n)" $NSUPDATE > nsupdate.out 2>&1 < signing.out.test$n 2>&1 +grep "Pending NSEC3 chain 1 0 20 DEAF" signing.out.test$n > /dev/null || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:resetting nsec3param via rndc signing ($n)" +ret=0 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -clear all autonsec3.example. > /dev/null 2>&1 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -nsec3param 1 1 10 beef autonsec3.example. > /dev/null 2>&1 +sleep 1 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list autonsec3.example. > signing.out.test$n 2>&1 +grep "Pending NSEC3 chain 1 1 10 BEEF" signing.out.test$n > /dev/null || ret=1 +num=`grep "Pending " signing.out.test$n | wc -l` +[ $num -eq 1 ] || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:signing preset nsec3 zone" zsk=`cat autozsk.key` ksk=`cat autoksk.key` @@ -275,6 +297,22 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:checking NSEC3->NSEC conversion with 'rndc signing -nsec3param none' ($n)" +ret=0 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -nsec3param none autonsec3.example. > /dev/null 2>&1 +sleep 2 +# this command should result in an empty file: +$DIG $DIGOPTS +noall +answer autonsec3.example. nsec3param @10.53.0.3 > dig.out.ns3.nx.test$n || ret=1 +grep "NSEC3PARAM" dig.out.ns3.nx.test$n > /dev/null && ret=1 +$DIG $DIGOPTS +noauth q.autonsec3.example. @10.53.0.3 a > dig.out.ns3.test$n || ret=1 +$DIG $DIGOPTS +noauth q.autonsec3.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1 +$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1 +grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1 +grep "status: NXDOMAIN" dig.out.ns4.test$n > /dev/null || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:checking TTLs of imported DNSKEYs (no default) ($n)" ret=0 $DIG $DIGOPTS +tcp +noall +answer dnskey ttl1.example. @10.53.0.3 > dig.out.ns3.test$n || ret=1 diff --git a/bin/tests/system/dnssec/clean.sh b/bin/tests/system/dnssec/clean.sh index 89922138bb..5c2f062905 100644 --- a/bin/tests/system/dnssec/clean.sh +++ b/bin/tests/system/dnssec/clean.sh @@ -15,7 +15,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: clean.sh,v 1.43 2011/10/11 19:26:06 each Exp $ +# $Id: clean.sh,v 1.44 2011/10/28 06:20:05 each Exp $ rm -f */K* */keyset-* */dsset-* */dlvset-* */signedkey-* */*.signed rm -f */trusted.conf */managed.conf */tmp* */*.jnl */*.bk @@ -59,3 +59,4 @@ rm -f ns3/ttlpatch.example.db ns3/ttlpatch.example.db.signed rm -f ns3/ttlpatch.example.db.patched rm -f ns3/split-smart.example.db rm -f nosign.before +rm -f signing.out* diff --git a/bin/tests/system/dnssec/ns3/named.conf b/bin/tests/system/dnssec/ns3/named.conf index 2ed281d8d7..060edca461 100644 --- a/bin/tests/system/dnssec/ns3/named.conf +++ b/bin/tests/system/dnssec/ns3/named.conf @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: named.conf,v 1.48 2011/05/23 20:10:02 each Exp $ */ +/* $Id: named.conf,v 1.49 2011/10/28 06:20:05 each Exp $ */ // NS3 @@ -35,6 +35,15 @@ options { dnssec-validation yes; }; +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-md5; +}; + +controls { + inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; }; +}; + zone "." { type hint; file "../../common/root.hint"; diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 8f0d6eca8f..58f9a0310a 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -15,7 +15,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: tests.sh,v 1.100 2011/10/26 05:32:56 marka Exp $ +# $Id: tests.sh,v 1.101 2011/10/28 06:20:05 each Exp $ SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh @@ -1330,6 +1330,33 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:check rndc signing -list output ($n)" +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list dynamic.example 2>&1 > signing.out +grep "No signing records found" signing.out > /dev/null 2>&1 || { + ret=1 + sed 's/^/I:ns3 /' signing.out +} +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list update-nsec3.example 2>&1 > signing.out +grep "Done signing with key .*/NSEC3RSASHA1" signing.out > /dev/null 2>&1 || { + ret=1 + sed 's/^/I:ns3 /' signing.out +} +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:clear signing records ($n)" +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -clear all update-nsec3.example > /dev/null || ret=1 +sleep 1 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list update-nsec3.example 2>&1 > signing.out +grep "No signing records found" signing.out > /dev/null 2>&1 || { + ret=1 + sed 's/^/I:ns3 /' signing.out +} +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:checking that a insecure zone beneath a cname resolves ($n)" ret=0 $DIG $DIGOPTS soa insecure.below-cname.example. @10.53.0.4 > dig.out.ns4.test$n || ret=1 diff --git a/bin/tests/system/inline/clean.sh b/bin/tests/system/inline/clean.sh index bca624b4a0..65db2cbb31 100644 --- a/bin/tests/system/inline/clean.sh +++ b/bin/tests/system/inline/clean.sh @@ -12,7 +12,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: clean.sh,v 1.4 2011/10/25 01:54:19 marka Exp $ +# $Id: clean.sh,v 1.5 2011/10/28 06:20:05 each Exp $ rm -f */named.memstats rm -f */named.run @@ -43,3 +43,4 @@ rm -f ns5/bits.bk.signed rm -f ns5/bits.bk.signed.jnl rm -f random.data rm -f dig.out.ns*.test* +rm -f signing.out* diff --git a/bin/tests/system/inline/tests.sh b/bin/tests/system/inline/tests.sh index 3dc32dd934..bb93632b51 100644 --- a/bin/tests/system/inline/tests.sh +++ b/bin/tests/system/inline/tests.sh @@ -14,7 +14,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: tests.sh,v 1.5 2011/10/26 20:56:45 marka Exp $ +# $Id: tests.sh,v 1.6 2011/10/28 06:20:05 each Exp $ SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh @@ -31,11 +31,9 @@ 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 + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list bits > signing.out.test$n 2>&1 + keys=`grep '^Done signing' signing.out.test$n | wc -l` + [ $keys = 2 ] || ret=1 if [ $ret = 0 ]; then break; fi sleep 1 done @@ -43,22 +41,21 @@ if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` n=`expr $n + 1` -echo "I:checking removal of private type record via 'rndc keydone' ($n)" +echo "I:checking removal of private type record via 'rndc signing -clear' ($n)" ret=0 -$DIG $DIGOPTS @10.53.0.3 -p 5300 bits TYPE65534 > dig.out.ns3.test$n -records=`sed -n -e 's/.*TYPE65534.*\(05[0-9A-F][0-9A-F][0-9A-F][0-9A-F]0001\)$/\1/p' dig.out.ns3.test$n` -for record in $records -do - $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 keydone "${record}" bits || ret=1 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list bits > signing.out.test$n 2>&1 +keys=`sed -n -e 's/Done signing with key \(.*\)$/\1/p' signing.out.test$n` +for key in $keys; do + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -clear ${key} bits > /dev/null || ret=1 break; # We only want to remove 1 record for now. done 2>&1 |sed 's/^/I:ns3 /' for i in 1 2 3 4 5 6 7 8 9 10 do ans=0 - $DIG $DIGOPTS @10.53.0.3 -p 5300 bits TYPE65534 > dig.out.ns3.test$n - grep "ANSWER: 2," dig.out.ns3.test$n > /dev/null || ans=1 - [ $ans = 1 ] || break + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list bits > signing.out.test$n 2>&1 + num=`grep "Done signing with" signing.out.test$n | wc -l` + [ $num = 1 ] && break sleep 1 done [ $ans = 0 ] || ret=1 @@ -77,21 +74,15 @@ if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` n=`expr $n + 1` -echo "I:checking removal of remaining private type record via 'rndc keydone' ($n)" +echo "I:checking removal of remaining private type record via 'rndc signing -clear all' ($n)" ret=0 -$DIG $DIGOPTS @10.53.0.3 -p 5300 bits TYPE65534 > dig.out.ns3.test$n -records=`sed -n -e 's/.*TYPE65534.*\(05[0-9A-F][0-9A-F][0-9A-F][0-9A-F]0001\)$/\1/p' dig.out.ns3.test$n` -for record in $records -do - $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 keydone "${record}" bits || ret=1 -done 2>&1 |sed 's/^/I:ns3 /' +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -clear all bits > /dev/null || ret=1 for i in 1 2 3 4 5 6 7 8 9 10 do ans=0 - $DIG $DIGOPTS @10.53.0.3 -p 5300 bits TYPE65534 > dig.out.ns3.test$n - grep "ANSWER: 0," dig.out.ns3.test$n > /dev/null || ans=1 - grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ans=1 + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list bits > signing.out.test$n 2>&1 + grep "No signing records found" signing.out.test$n > /dev/null || ans=1 [ $ans = 1 ] || break sleep 1 done @@ -180,11 +171,9 @@ 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 + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list noixfr > signing.out.test$n 2>&1 + keys=`grep '^Done signing' signing.out.test$n | wc -l` + [ $keys = 2 ] || ret=1 if [ $ret = 0 ]; then break; fi sleep 1 done @@ -260,33 +249,30 @@ 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 master 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 + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list master > signing.out.test$n 2>&1 + keys=`grep '^Done signing' signing.out.test$n | wc -l` + [ $keys = 2 ] || ret=1 if [ $ret = 0 ]; then break; fi sleep 1 done if [ $ret != 0 ]; then echo "I:failed"; fi n=`expr $n + 1` -echo "I:checking removal of private type record via 'rndc keydone' (master) ($n)" +echo "I:checking removal of private type record via 'rndc signing -clear' (master) ($n)" ret=0 -$DIG $DIGOPTS @10.53.0.3 -p 5300 master TYPE65534 > dig.out.ns3.test$n -records=`sed -n -e 's/.*TYPE65534.*\(05[0-9A-F][0-9A-F][0-9A-F][0-9A-F]0001\)$/\1/p' dig.out.ns3.test$n` -for record in $records -do - $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 keydone "${record}" master || ret=1 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list master > signing.out.test$n 2>&1 +keys=`sed -n -e 's/Done signing with key \(.*\)$/\1/p' signing.out.test$n` +for key in $keys; do + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -clear ${key} master > /dev/null || ret=1 break; # We only want to remove 1 record for now. done 2>&1 |sed 's/^/I:ns3 /' for i in 1 2 3 4 5 6 7 8 9 do ans=0 - $DIG $DIGOPTS @10.53.0.3 -p 5300 master TYPE65534 > dig.out.ns3.test$n - grep "ANSWER: 2," dig.out.ns3.test$n > /dev/null || ans=1 - [ $ans = 1 ] || break + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list master > signing.out.test$n 2>&1 + num=`grep "Done signing with" signing.out.test$n | wc -l` + [ $num = 1 ] && break sleep 1 done [ $ans = 0 ] || ret=1 @@ -305,21 +291,14 @@ if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` n=`expr $n + 1` -echo "I:checking removal of remaining private type record via 'rndc keydone' (master) ($n)" +echo "I:checking removal of remaining private type record via 'rndc signing -clear' (master) ($n)" ret=0 -$DIG $DIGOPTS @10.53.0.3 -p 5300 master TYPE65534 > dig.out.ns3.test$n -records=`sed -n -e 's/.*TYPE65534.*\(05[0-9A-F][0-9A-F][0-9A-F][0-9A-F]0001\)$/\1/p' dig.out.ns3.test$n` -for record in $records -do - $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 keydone "${record}" master || ret=1 -done 2>&1 |sed 's/^/I:ns3 /' - -for i in 1 2 3 4 5 6 7 8 9 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -clear all master > /dev/null || ret=1 +for i in 1 2 3 4 5 6 7 8 9 10 do ans=0 - $DIG $DIGOPTS @10.53.0.3 -p 5300 master TYPE65534 > dig.out.ns3.test$n - grep "ANSWER: 0," dig.out.ns3.test$n > /dev/null || ans=1 - grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ans=1 + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list master > signing.out.test$n 2>&1 + grep "No signing records found" signing.out.test$n > /dev/null || ans=1 [ $ans = 1 ] || break sleep 1 done @@ -366,11 +345,9 @@ 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 dynamic 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 + $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -list dynamic > signing.out.test$n 2>&1 + keys=`grep '^Done signing' signing.out.test$n | wc -l` + [ $keys = 2 ] || ret=1 if [ $ret = 0 ]; then break; fi sleep 1 done diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index dc375203a8..1e6bcffde3 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 @@ -1556,23 +1556,50 @@ zone "eng.example.com" { - keydone - rdata + signing + ( -list | -clear keyid/algorithm | -clear all | -nsec3param ( parameters | none ) ) zone class view - Remove the sig-signing-type record - which matches the rdata - (in hexadecimal) from the specified zone. Only - rdata that - indicate that named has finished signing the zone - with the corresponding key will be removed. (i.e. - the first two characters are not "00", the - last four characters are "0001" and the total - length is 10 hexadecimal characters. + List, edit, or remove the DNSSEC signing state for + the specified zone. The status of ongoing DNSSEC + operations (such as signing or generating + NSEC3 chains) is stored in the zone in the form + of DNS resource records of type + sig-signing-type. + rndc signing -list converts + these records into a human-readable form, + indicating which keys are currently signing + or have finished signing the zone, and which NSEC3 + NSEC3 chains are being created or removed. + + + rndc signing -clear can remove + a single key (specified in the same format that + rndc signing -list uses to + display it), or all keys. In either case, only + completed keys are removed; any record indicating + that a key has not yet finished signing the zone + will be retained. + + + rndc signing -nsec3param sets + the NSEC3 parameters for a zone. This is the + only supported mechanism for using NSEC3 with + inline-signing zones. + Parameters are specified in the same format as + an NSEC3PARAM resource record: hash algorithm, + flags, iterations, salt. For example, to set an + NSEC3 chain using the SHA-1 hash algorithm, + no opt-out flag, 10 iterations, and a salt value + of "FFFF", use: + rndc signing -nsec3param 1 0 10 FFFF. + rndc signing -nsec3param none + removes an existing NSEC3 chain and replaces it + with NSEC. @@ -8730,8 +8757,8 @@ avoid-v6-udp-ports { 40000; range 50000 60000; }; These records can be removed from the zone once named has completed signing the zone with the matching key using nsupdate or - rndc keydone. - rndc keydone is the only supported + rndc signing -clear. + rndc signing -clear is the only supported way to remove these records from inline-signing zones. diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h index 3ff3576f1a..fd2144f649 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.60 2011/10/25 01:54:22 marka Exp $ */ +/* $Id: events.h,v 1.61 2011/10/28 06:20:06 each Exp $ */ #ifndef DNS_EVENTS_H #define DNS_EVENTS_H 1 @@ -78,6 +78,7 @@ #define DNS_EVENT_ZONESECUREDB (ISC_EVENTCLASS_DNS + 48) #define DNS_EVENT_ZONELOAD (ISC_EVENTCLASS_DNS + 49) #define DNS_EVENT_KEYDONE (ISC_EVENTCLASS_DNS + 50) +#define DNS_EVENT_SETNSEC3PARAM (ISC_EVENTCLASS_DNS + 51) #define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0) #define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535) diff --git a/lib/dns/include/dns/nsec3.h b/lib/dns/include/dns/nsec3.h index 7ed1dd9e82..35e963ff85 100644 --- a/lib/dns/include/dns/nsec3.h +++ b/lib/dns/include/dns/nsec3.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: nsec3.h,v 1.12 2010/05/18 02:38:10 tbox Exp $ */ +/* $Id: nsec3.h,v 1.13 2011/10/28 06:20:06 each Exp $ */ #ifndef DNS_NSEC3_H #define DNS_NSEC3_H 1 @@ -241,7 +241,8 @@ dns_nsec3param_toprivate(dns_rdata_t *src, dns_rdata_t *target, isc_result_t dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, - dns_zone_t *zone, dns_diff_t *diff); + dns_zone_t *zone, isc_boolean_t nonsec, + dns_diff_t *diff); /*%< * Mark NSEC3PARAM for deletion. diff --git a/lib/dns/include/dns/private.h b/lib/dns/include/dns/private.h index 0df13d89aa..ddc8f40099 100644 --- a/lib/dns/include/dns/private.h +++ b/lib/dns/include/dns/private.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: private.h,v 1.3 2009/10/09 23:48:09 tbox Exp $ */ +/* $Id: private.h,v 1.4 2011/10/28 06:20:06 each Exp $ */ #include #include @@ -50,6 +50,23 @@ dns_private_chains(dns_db_t *db, dns_dbversion_t *ver, * \li other on error */ +isc_result_t +dns_private_totext(dns_rdata_t *private, isc_buffer_t *buffer); +/*%< + * Convert a private-type RR 'private' to human-readable form, + * and place the result in 'buffer'. The text should indicate + * which action the private-type record specifies and whether the + * action has been completed. + * + * Requires: + * \li 'private' is a valid rdata containing at least five bytes + * \li 'buffer' is a valid buffer + * + * Returns: + * \li ISC_R_SUCCESS + * \li other on error + */ + ISC_LANG_ENDDECLS #endif diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index c4efe075fd..55a279a4b4 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.195 2011/10/25 01:54:22 marka Exp $ */ +/* $Id: zone.h,v 1.196 2011/10/28 06:20:06 each Exp $ */ #ifndef DNS_ZONE_H #define DNS_ZONE_H 1 @@ -1981,6 +1981,20 @@ dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw); isc_result_t dns_zone_keydone(dns_zone_t *zone, const char *data); +isc_result_t +dns_zone_setnsec3param(dns_zone_t *zone, isc_uint8_t hash, isc_uint8_t flags, + isc_uint8_t iter, isc_uint8_t saltlen, + unsigned char *salt, isc_boolean_t replace); +/*% + * Set the NSEC3 parameters for the zone. + * + * If 'replace' is ISC_TRUE, then the existing NSEC3 chain, if any, will + * be replaced with the new one. If 'hash' is zero, then the replacement + * chain will be NSEC rather than NSEC3. + * + * Requires: + * \li 'zone' to be valid. + */ ISC_LANG_ENDDECLS #endif /* DNS_ZONE_H */ diff --git a/lib/dns/nsec3.c b/lib/dns/nsec3.c index 4df286b5df..2403fe8d08 100644 --- a/lib/dns/nsec3.c +++ b/lib/dns/nsec3.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: nsec3.c,v 1.23 2011/06/10 01:51:09 each Exp $ */ +/* $Id: nsec3.c,v 1.24 2011/10/28 06:20:06 each Exp $ */ #include @@ -1054,7 +1054,8 @@ rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, #ifdef BIND9 isc_result_t dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, - dns_zone_t *zone, dns_diff_t *diff) + dns_zone_t *zone, isc_boolean_t nonsec, + dns_diff_t *diff) { dns_dbnode_t *node = NULL; dns_difftuple_t *tuple = NULL; @@ -1098,7 +1099,9 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3param_toprivate(&rdata, &private, privatetype, buf, sizeof(buf)); - buf[2] = DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC; + buf[2] = DNS_NSEC3FLAG_REMOVE; + if (nonsec) + buf[2] |= DNS_NSEC3FLAG_NONSEC; CHECK(rr_exists(db, ver, origin, &private, &flag)); @@ -1129,15 +1132,14 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) { + dns_rdata_reset(&rdata); dns_rdataset_current(&rdataset, &rdata); INSIST(rdata.length <= sizeof(buf)); memcpy(buf, rdata.data, rdata.length); - if (buf[0] != 0 || - buf[2] == (DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC)) { - dns_rdata_reset(&rdata); + if (buf[0] != 0 || (buf[2] & DNS_NSEC3FLAG_REMOVE) != 0 || + (nonsec && (buf[2] & DNS_NSEC3FLAG_NONSEC) != 0)) continue; - } CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, origin, 0, &rdata, &tuple)); @@ -1145,7 +1147,9 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, INSIST(tuple == NULL); rdata.data = buf; - buf[2] = DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC; + buf[2] = DNS_NSEC3FLAG_REMOVE; + if (nonsec) + buf[2] |= DNS_NSEC3FLAG_NONSEC; CHECK(rr_exists(db, ver, origin, &rdata, &flag)); @@ -1155,7 +1159,6 @@ dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver, CHECK(do_one_tuple(&tuple, db, ver, diff)); INSIST(tuple == NULL); } - dns_rdata_reset(&rdata); } if (result != ISC_R_NOMORE) goto failure; diff --git a/lib/dns/private.c b/lib/dns/private.c index 21f45c7b5c..311f0f7c8c 100644 --- a/lib/dns/private.c +++ b/lib/dns/private.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: private.c,v 1.6 2011/06/10 01:51:09 each Exp $ */ +/* $Id: private.c,v 1.7 2011/10/28 06:20:06 each Exp $ */ #include "config.h" @@ -294,3 +294,74 @@ dns_private_chains(dns_db_t *db, dns_dbversion_t *ver, dns_db_detachnode(db, &node); return (result); } + +isc_result_t +dns_private_totext(dns_rdata_t *private, isc_buffer_t *buf) { + isc_result_t result; + + if (private->data[0] == 0) { + unsigned char nsec3buf[DNS_NSEC3PARAM_BUFFERSIZE]; + unsigned char newbuf[DNS_NSEC3PARAM_BUFFERSIZE]; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_nsec3param_t nsec3param; + isc_boolean_t remove, init, nonsec; + isc_buffer_t b; + + if (!dns_nsec3param_fromprivate(private, &rdata, nsec3buf, + sizeof(nsec3buf))) + CHECK(ISC_R_FAILURE); + + CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); + + remove = ISC_TF((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0); + init = ISC_TF((nsec3param.flags & DNS_NSEC3FLAG_INITIAL) != 0); + nonsec = ISC_TF((nsec3param.flags & DNS_NSEC3FLAG_NONSEC) != 0); + + nsec3param.flags &= ~(DNS_NSEC3FLAG_CREATE| + DNS_NSEC3FLAG_REMOVE| + DNS_NSEC3FLAG_INITIAL| + DNS_NSEC3FLAG_NONSEC); + + if (init) + isc_buffer_putstr(buf, "Pending NSEC3 chain "); + else if (remove) + isc_buffer_putstr(buf, "Removing NSEC3 chain "); + else + isc_buffer_putstr(buf, "Creating NSEC3 chain "); + + dns_rdata_reset(&rdata); + isc_buffer_init(&b, newbuf, sizeof(newbuf)); + CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_in, + dns_rdatatype_nsec3param, + &nsec3param, &b)); + + CHECK(dns_rdata_totext(&rdata, NULL, buf)); + + if (remove && !nonsec) + isc_buffer_putstr(buf, " / creating NSEC chain"); + } else { + unsigned char alg = private->data[0]; + dns_keytag_t keyid = (private->data[2] | private->data[1] << 8); + char keybuf[BUFSIZ], algbuf[DNS_SECALG_FORMATSIZE]; + isc_boolean_t remove = ISC_TF(private->data[3] != 0); + isc_boolean_t complete = ISC_TF(private->data[4] != 0); + + if (remove && complete) + isc_buffer_putstr(buf, "Done removing signatures for "); + else if (remove) + isc_buffer_putstr(buf, "Removing signatures for "); + else if (complete) + isc_buffer_putstr(buf, "Done signing with "); + else + isc_buffer_putstr(buf, "Signing with "); + + dns_secalg_format(alg, algbuf, sizeof(algbuf)); + sprintf(keybuf, "key %d/%s", keyid, algbuf); + isc_buffer_putstr(buf, keybuf); + } + + isc_buffer_putuint8(buf, 0); + result = ISC_R_SUCCESS; + failure: + return (result); +} diff --git a/lib/dns/tests/Makefile.in b/lib/dns/tests/Makefile.in index 1a3b80947d..d66fa2ab05 100644 --- a/lib/dns/tests/Makefile.in +++ b/lib/dns/tests/Makefile.in @@ -12,7 +12,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.9 2011/10/12 23:09:35 marka Exp $ +# $Id: Makefile.in,v 1.10 2011/10/28 06:20:07 each Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -38,12 +38,12 @@ LIBS = @LIBS@ @ATFLIBS@ OBJS = dnstest.@O@ SRCS = dnstest.c master_test.c dbiterator_test.c time_test.c \ - update_test.c zonemgr_test.c zt_test.c + private_test.c update_test.c zonemgr_test.c zt_test.c SUBDIRS = TARGETS = master_test@EXEEXT@ dbiterator_test@EXEEXT@ time_test@EXEEXT@ \ - update_test@EXEEXT@ zonemgr_test@EXEEXT@ zt_test@EXEEXT@ \ - dbversion_test@EXEEXT@ + private_test@EXEEXT@ update_test@EXEEXT@ zonemgr_test@EXEEXT@ \ + zt_test@EXEEXT@ dbversion_test@EXEEXT@ @BIND9_MAKE_RULES@ @@ -57,6 +57,11 @@ time_test@EXEEXT@: time_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} time_test.@O@ dnstest.@O@ ${DNSLIBS} \ ${ISCLIBS} ${LIBS} +private_test@EXEEXT@: private_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + private_test.@O@ dnstest.@O@ ${DNSLIBS} \ + ${ISCLIBS} ${LIBS} + update_test@EXEEXT@: update_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ update_test.@O@ dnstest.@O@ ${DNSLIBS} \ diff --git a/lib/dns/tests/private_test.c b/lib/dns/tests/private_test.c new file mode 100644 index 0000000000..7ca2e16089 --- /dev/null +++ b/lib/dns/tests/private_test.c @@ -0,0 +1,225 @@ +/* + * 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: private_test.c,v 1.2 2011/10/28 06:20:07 each Exp $ */ + +/*! \file */ + +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +#include "dnstest.h" + +static dns_rdatatype_t privatetype = 65534; + +typedef struct { + unsigned char alg; + dns_keytag_t keyid; + isc_boolean_t remove; + isc_boolean_t complete; +} signing_testcase_t; + +typedef struct { + unsigned char hash; + unsigned char flags; + unsigned int iterations; + unsigned long salt; + isc_boolean_t remove; + isc_boolean_t pending; + isc_boolean_t nonsec; +} nsec3_testcase_t; + +/* + * Helper functions + */ +static void +make_signing(signing_testcase_t *testcase, dns_rdata_t *private, + unsigned char *buf) +{ + dns_rdata_init(private); + + buf[0] = testcase->alg; + buf[1] = (testcase->keyid & 0xff00) >> 8; + buf[2] = (testcase->keyid & 0xff); + buf[3] = testcase->remove; + buf[4] = testcase->complete; + private->data = buf; + private->length = sizeof(buf); + private->type = privatetype; + private->rdclass = dns_rdataclass_in; +} + +static void +make_nsec3(nsec3_testcase_t *testcase, dns_rdata_t *private, + unsigned char *pbuf) +{ + dns_rdata_nsec3param_t params; + dns_rdata_t nsec3param = DNS_RDATA_INIT; + unsigned char bufdata[BUFSIZ]; + isc_buffer_t buf; + isc_uint32_t salt; + unsigned char *sp; + int slen = 4; + + /* for simplicity, we're using a maximum salt length of 4 */ + salt = htonl(testcase->salt); + sp = (unsigned char *) &salt; + while (*sp == '\0' && slen > 0) { + slen--; + sp++; + } + + params.common.rdclass = dns_rdataclass_in; + params.common.rdtype = dns_rdatatype_nsec3param; + params.hash = testcase->hash; + params.iterations = testcase->iterations; + params.salt = sp; + params.salt_length = slen; + + params.flags = testcase->flags; + if (testcase->remove) { + params.flags |= DNS_NSEC3FLAG_REMOVE; + if (testcase->nonsec) + params.flags |= DNS_NSEC3FLAG_NONSEC; + } else { + params.flags |= DNS_NSEC3FLAG_CREATE; + if (testcase->pending) + params.flags |= DNS_NSEC3FLAG_INITIAL; + } + + isc_buffer_init(&buf, bufdata, sizeof(bufdata)); + dns_rdata_fromstruct(&nsec3param, dns_rdataclass_in, + dns_rdatatype_nsec3param, ¶ms, &buf); + + dns_rdata_init(private); + + dns_nsec3param_toprivate(&nsec3param, private, privatetype, + pbuf, DNS_NSEC3PARAM_BUFFERSIZE + 1); +} + +/* + * Individual unit tests + */ +ATF_TC(private_signing_totext); +ATF_TC_HEAD(private_signing_totext, tc) { + atf_tc_set_md_var(tc, "descr", + "convert private signing records to text"); +} +ATF_TC_BODY(private_signing_totext, tc) { + isc_result_t result; + dns_rdata_t private; + int i; + + signing_testcase_t testcases[] = { + { DST_ALG_RSASHA512, 12345, 0, 0 }, + { DST_ALG_RSASHA256, 54321, 1, 0 }, + { DST_ALG_NSEC3RSASHA1, 22222, 0, 1 }, + { DST_ALG_RSASHA1, 33333, 1, 1 } + }; + const char *results[] = { + "Signing with key 12345/RSASHA512", + "Removing signatures for key 54321/RSASHA256", + "Done signing with key 22222/NSEC3RSASHA1", + "Done removing signatures for key 33333/RSASHA1" + }; + int ncases = 4; + + UNUSED(tc); + + result = dns_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + for (i = 0; i < ncases; i++) { + unsigned char data[5]; + char output[BUFSIZ]; + isc_buffer_t buf; + + isc_buffer_init(&buf, output, sizeof(output)); + + make_signing(&testcases[i], &private, data); + dns_private_totext(&private, &buf); + ATF_CHECK_STREQ(output, results[i]); + } + + dns_test_end(); +} + +ATF_TC(private_nsec3_totext); +ATF_TC_HEAD(private_nsec3_totext, tc) { + atf_tc_set_md_var(tc, "descr", "convert private chain records to text"); +} +ATF_TC_BODY(private_nsec3_totext, tc) { + isc_result_t result; + dns_rdata_t private; + int i; + + nsec3_testcase_t testcases[] = { + { 1, 0, 1, 0xbeef, 0, 0, 0 }, + { 1, 1, 10, 0xdadd, 0, 0, 0 }, + { 1, 0, 20, 0xbead, 0, 1, 0 }, + { 1, 0, 30, 0xdeaf, 1, 0, 0 }, + { 1, 0, 100, 0xfeedabee, 1, 0, 1 }, + }; + const char *results[] = { + "Creating NSEC3 chain 1 0 1 BEEF", + "Creating NSEC3 chain 1 1 10 DADD", + "Pending NSEC3 chain 1 0 20 BEAD", + "Removing NSEC3 chain 1 0 30 DEAF / creating NSEC chain", + "Removing NSEC3 chain 1 0 100 FEEDABEE" + }; + int ncases = 5; + + UNUSED(tc); + + result = dns_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + for (i = 0; i < ncases; i++) { + unsigned char data[DNS_NSEC3PARAM_BUFFERSIZE + 1]; + char output[BUFSIZ]; + isc_buffer_t buf; + + isc_buffer_init(&buf, output, sizeof(output)); + + make_nsec3(&testcases[i], &private, data); + dns_private_totext(&private, &buf); + ATF_CHECK_STREQ(output, results[i]); + } + + dns_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, private_signing_totext); + ATF_TP_ADD_TC(tp, private_nsec3_totext); + return (atf_no_error()); +} + diff --git a/lib/dns/zone.c b/lib/dns/zone.c index e263ca41a9..413d6bc2f7 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.641 2011/10/28 00:36:54 marka Exp $ */ +/* $Id: zone.c,v 1.642 2011/10/28 06:20:06 each Exp $ */ /*! \file */ @@ -14808,7 +14808,7 @@ clean_nsec3param(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, if (result != ISC_R_NOTFOUND) goto failure; - result = dns_nsec3param_deletechains(db, ver, zone, diff); + result = dns_nsec3param_deletechains(db, ver, zone, ISC_TRUE, diff); failure: if (node != NULL) @@ -15340,9 +15340,12 @@ dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw) { struct keydone { isc_event_t event; - unsigned int data[5]; + isc_boolean_t all; + unsigned char data[5]; }; +#define PENDINGFLAGS (DNS_NSEC3FLAG_CREATE|DNS_NSEC3FLAG_INITIAL) + static void keydone(isc_task_t *task, isc_event_t *event) { const char *me = "keydone"; @@ -15357,6 +15360,7 @@ keydone(isc_task_t *task, isc_event_t *event) { dns_diff_t diff; struct keydone *keydone = (struct keydone *)event; dns_update_log_t log = { update_log_cb, NULL }; + isc_boolean_t clear_pending = ISC_FALSE; UNUSED(task); @@ -15398,17 +15402,31 @@ keydone(isc_task_t *task, isc_event_t *event) { INSIST(!dns_rdataset_isassociated(&rdataset)); goto failure; } + for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) { + isc_boolean_t found = ISC_FALSE; + dns_rdataset_current(&rdataset, &rdata); - if (rdata.length != 5 || - memcmp(rdata.data, keydone->data, 5) != 0) { - dns_rdata_reset(&rdata); - continue; - } - CHECK(update_one_rr(db, newver, &diff, DNS_DIFFOP_DEL, - &zone->origin, rdataset.ttl, &rdata)); + + if (keydone->all) { + if (rdata.length == 5 && rdata.data[0] != 0 && + rdata.data[3] == 0 && rdata.data[4] == 1) + found = ISC_TRUE; + else if (rdata.data[0] == 0 && + (rdata.data[2] & PENDINGFLAGS) != 0) { + found = ISC_TRUE; + clear_pending = ISC_TRUE; + } + } else if (rdata.length == 5 && + memcmp(rdata.data, keydone->data, 5) == 0) + found = ISC_TRUE; + + if (found) + CHECK(update_one_rr(db, newver, &diff, DNS_DIFFOP_DEL, + &zone->origin, rdataset.ttl, + &rdata)); dns_rdata_reset(&rdata); } @@ -15416,8 +15434,13 @@ keydone(isc_task_t *task, isc_event_t *event) { /* Write changes to journal file. */ CHECK(update_soa_serial(db, newver, &diff, zone->mctx, zone->updatemethod)); - CHECK(dns_update_signatures(&log, zone, db, oldver, newver, - &diff, zone->sigvalidityinterval)); + + result = dns_update_signatures(&log, zone, db, + oldver, newver, &diff, + zone->sigvalidityinterval); + if (!clear_pending) + CHECK(result); + CHECK(zone_journal(zone, &diff, NULL, "keydone")); commit = ISC_TRUE; @@ -15445,11 +15468,12 @@ keydone(isc_task_t *task, isc_event_t *event) { } isc_result_t -dns_zone_keydone(dns_zone_t *zone, const char *data) { - isc_result_t result; +dns_zone_keydone(dns_zone_t *zone, const char *keystr) { + isc_result_t result = ISC_R_SUCCESS; isc_event_t *e; isc_buffer_t b; dns_zone_t *dummy = NULL; + struct keydone *kd; REQUIRE(DNS_ZONE_VALID(zone)); @@ -15462,11 +15486,280 @@ dns_zone_keydone(dns_zone_t *zone, const char *data) { goto failure; } - isc_buffer_init(&b, ((struct keydone*)e)->data, - sizeof(((struct keydone*)e)->data)); - result = isc_hex_decodestring(data, &b); - if (result != ISC_R_SUCCESS) - goto failure; + kd = (struct keydone *) e; + if (strcasecmp(keystr, "all") == 0) + kd->all = ISC_TRUE; + else { + isc_textregion_t r; + char *algstr; + dns_keytag_t keyid; + dns_secalg_t alg; + size_t n; + + kd->all = ISC_FALSE; + + n = sscanf(keystr, "%hd/", &keyid); + if (n == 0) + CHECK(ISC_R_FAILURE); + + algstr = strchr(keystr, '/'); + if (algstr != NULL) + algstr++; + else + CHECK(ISC_R_FAILURE); + + DE_CONST(algstr, r.base); + r.length = strlen(algstr); + result = dns_secalg_fromtext(&alg, (isc_textregion_t *) &r); + + if (result != ISC_R_SUCCESS) { + n = sscanf(algstr, "%hhd", &alg); + if (n == 0) + CHECK(result); + } + + /* construct a private-type rdata */ + isc_buffer_init(&b, kd->data, sizeof(kd->data)); + isc_buffer_putuint8(&b, alg); + isc_buffer_putuint8(&b, (keyid & 0xff00) >> 8); + isc_buffer_putuint8(&b, (keyid & 0xff)); + isc_buffer_putuint8(&b, 0); + isc_buffer_putuint8(&b, 1); + } + + zone_iattach(zone, &dummy); + isc_task_send(zone->task, &e); + + failure: + if (e != NULL) + isc_event_free(&e); + UNLOCK_ZONE(zone); + 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"; + isc_boolean_t commit = ISC_FALSE; + isc_result_t result; + dns_dbversion_t *oldver = NULL, *newver = NULL; + dns_zone_t *zone; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + dns_rdataset_t prdataset, nrdataset; + dns_diff_t diff; + struct nsec3param *np = (struct nsec3param *)event; + dns_update_log_t log = { update_log_cb, NULL }; + dns_rdata_t rdata; + isc_boolean_t nseconly; + isc_boolean_t exists = ISC_FALSE; + + UNUSED(task); + + zone = event->ev_arg; + INSIST(DNS_ZONE_VALID(zone)); + + ENTER; + + dns_rdataset_init(&prdataset); + dns_rdataset_init(&nrdataset); + dns_diff_init(zone->mctx, &diff); + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) { + dns_db_attach(zone->db, &db); + dns_db_currentversion(db, &oldver); + result = dns_db_newversion(db, &newver); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "setnsec3param:dns_db_newversion -> %s\n", + dns_result_totext(result)); + goto failure; + } + } + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + if (db == NULL) + goto failure; + + CHECK(dns_db_getoriginnode(db, &node)); + + /* + * Does a private-type record already exist for this chain? + */ + result = dns_db_findrdataset(db, node, newver, zone->privatetype, + dns_rdatatype_none, 0, &prdataset, NULL); + if (result == ISC_R_SUCCESS) { + for (result = dns_rdataset_first(&prdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&prdataset)) { + dns_rdata_init(&rdata); + dns_rdataset_current(&prdataset, &rdata); + + if (np->length == rdata.length && + memcmp(rdata.data, np->data, np->length) == 0) { + exists = ISC_TRUE; + break; + } + } + } else if (result != ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&prdataset)); + goto failure; + } + + /* + * Does the chain already exist? + */ + result = dns_db_findrdataset(db, node, newver, + dns_rdatatype_nsec3param, + dns_rdatatype_none, 0, &nrdataset, NULL); + if (result == ISC_R_SUCCESS) { + for (result = dns_rdataset_first(&nrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&nrdataset)) { + dns_rdata_init(&rdata); + dns_rdataset_current(&nrdataset, &rdata); + + if (np->length == (rdata.length + 1) && + memcmp(rdata.data, np->data + 1, + np->length - 1) == 0) + { + exists = ISC_TRUE; + break; + } + } + } else if (result != ISC_R_NOTFOUND) { + INSIST(!dns_rdataset_isassociated(&nrdataset)); + goto failure; + } + + + /* + * We need to remove any existing NSEC3 chains. + */ + if (!exists && np->replace && (np->length != 0 || np->nsec)) + CHECK(dns_nsec3param_deletechains(db, newver, zone, + !np->nsec, &diff)); + + if (!exists && np->length != 0) { + /* + * We're creating an NSEC3 chain. + * + * If the zone is not currently capable of supporting + * an NSEC3 chain, add the INITIAL flag, so these + * parameters can be used later when NSEC3 becomes + * available. + */ + dns_rdata_init(&rdata); + + np->data[2] |= DNS_NSEC3FLAG_CREATE; + result = dns_nsec_nseconly(db, newver, &nseconly); + if (result == ISC_R_NOTFOUND || nseconly) + np->data[2] |= DNS_NSEC3FLAG_INITIAL; + + rdata.length = np->length; + rdata.data = np->data; + rdata.type = zone->privatetype; + rdata.rdclass = zone->rdclass; + CHECK(update_one_rr(db, newver, &diff, DNS_DIFFOP_ADD, + &zone->origin, 0, &rdata)); + } + + if (!ISC_LIST_EMPTY(diff.tuples)) { + /* Write changes to journal file. */ + CHECK(update_soa_serial(db, newver, &diff, zone->mctx, + zone->updatemethod)); + result = dns_update_signatures(&log, zone, db, + oldver, newver, &diff, + zone->sigvalidityinterval); + if (result != ISC_R_NOTFOUND) + CHECK(result); + CHECK(zone_journal(zone, &diff, NULL, "setnsec3param")); + commit = ISC_TRUE; + + LOCK_ZONE(zone); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); + zone_needdump(zone, 30); + UNLOCK_ZONE(zone); + } + + failure: + if (dns_rdataset_isassociated(&prdataset)) + dns_rdataset_disassociate(&prdataset); + if (dns_rdataset_isassociated(&nrdataset)) + dns_rdataset_disassociate(&nrdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + if (oldver != NULL) + dns_db_closeversion(db, &oldver, ISC_FALSE); + if (newver != NULL) + dns_db_closeversion(db, &newver, commit); + if (db != NULL) + dns_db_detach(&db); + if (commit) + resume_addnsec3chain(zone); + dns_diff_clear(&diff); + isc_event_free(&event); + dns_zone_idetach(&zone); +} + +isc_result_t +dns_zone_setnsec3param(dns_zone_t *zone, isc_uint8_t hash, isc_uint8_t flags, + isc_uint8_t iter, isc_uint8_t saltlen, + unsigned char *salt, isc_boolean_t replace) +{ + isc_result_t result; + dns_rdata_nsec3param_t param; + dns_rdata_t nrdata = DNS_RDATA_INIT; + dns_rdata_t prdata = DNS_RDATA_INIT; + unsigned char nbuf[DNS_NSEC3PARAM_BUFFERSIZE]; + struct nsec3param *np; + dns_zone_t *dummy = NULL; + isc_buffer_t b; + isc_event_t *e; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(salt != NULL); + + LOCK_ZONE(zone); + + e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_SETNSEC3PARAM, + setnsec3param, zone, sizeof(struct nsec3param)); + if (e == NULL) { + result = ISC_R_NOMEMORY; + goto failure; + } + + np = (struct nsec3param *) e; + np->replace = replace; + if (hash == 0) { + np->length = 0; + np->nsec = ISC_TRUE; + } else { + param.common.rdclass = zone->rdclass; + param.common.rdtype = dns_rdatatype_nsec3param; + ISC_LINK_INIT(¶m.common, link); + param.mctx = NULL; + param.hash = hash; + param.flags = flags; + param.iterations = iter; + param.salt_length = saltlen; + param.salt = salt; + isc_buffer_init(&b, nbuf, sizeof(nbuf)); + CHECK(dns_rdata_fromstruct(&nrdata, zone->rdclass, + dns_rdatatype_nsec3param, + ¶m, &b)); + dns_nsec3param_toprivate(&nrdata, &prdata, zone->privatetype, + np->data, sizeof(np->data)); + np->length = prdata.length; + } zone_iattach(zone, &dummy); isc_task_send(zone->task, &e);