From: Mark Andrews Date: Tue, 21 Oct 2014 07:15:42 +0000 (+1100) Subject: 3987. [func] Allow the zone serial of a dynamically updatable X-Git-Tag: v9.11.0a1~1334 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=4140a96f22b2319a658c17723c976ddff0e2633a;p=thirdparty%2Fbind9.git 3987. [func] Allow the zone serial of a dynamically updatable zone to be updated via rndc. [RT #37404] --- diff --git a/bin/named/server.c b/bin/named/server.c index 11ba8efcce1..4310e33412b 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -9449,6 +9449,8 @@ ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) { isc_boolean_t first = ISC_TRUE; isc_boolean_t list = ISC_FALSE, clear = ISC_FALSE; isc_boolean_t chain = ISC_FALSE; + isc_boolean_t setserial = ISC_FALSE; + isc_uint32_t serial = 0; char keystr[DNS_SECALG_FORMATSIZE + 7]; unsigned short hash = 0, flags = 0, iter = 0, saltlen = 0; unsigned char salt[255]; @@ -9524,6 +9526,12 @@ ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) { saltlen = isc_buffer_usedlength(&buf); } } + } else if (strcasecmp(ptr, "-serial") == 0) { + ptr = next_token(&args, " \t"); + if (ptr == NULL) + return (ISC_R_UNEXPECTEDEND); + CHECK(isc_parse_uint32(&serial, ptr, 10)); + setserial = ISC_TRUE; } else CHECK(DNS_R_SYNTAX); @@ -9541,7 +9549,11 @@ ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) { (isc_uint8_t)flags, iter, (isc_uint8_t)saltlen, salt, ISC_TRUE)); - putstr(text, "request queued"); + putstr(text, "nsec3param request queued"); + putnull(text); + } else if (setserial) { + CHECK(dns_zone_setserial(zone, serial)); + putstr(text, "serial request queued"); putnull(text); } else if (list) { privatetype = dns_zone_getprivatetype(zone); diff --git a/bin/rndc/rndc.docbook b/bin/rndc/rndc.docbook index ace6f116517..b394411b9ae 100644 --- a/bin/rndc/rndc.docbook +++ b/bin/rndc/rndc.docbook @@ -745,7 +745,7 @@ - signing ( -list | -clear keyid/algorithm | -clear all | -nsec3param ( parameters | none ) ) zone class view + signing ( -list | -clear keyid/algorithm | -clear all | -nsec3param ( parameters | none ) | -serial value ) zone class view List, edit, or remove the DNSSEC signing state records @@ -807,6 +807,13 @@ removes an existing NSEC3 chain and replaces it with NSEC. + + rndc signing -serial value sets + the serial number of the zone to value. If the value + would cause the serial number to go backwards it will + be rejected. The primary use is to set the serial on + inline signed zones. + diff --git a/bin/tests/system/inline/tests.sh b/bin/tests/system/inline/tests.sh index 97e92fefd06..49646f56224 100755 --- a/bin/tests/system/inline/tests.sh +++ b/bin/tests/system/inline/tests.sh @@ -890,4 +890,93 @@ $IMPORTKEY -f import.key import.example > /dev/null 2>&1 || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +n=`expr $n + 1` +echo "I:testing updating inline secure serial via 'rndc signing -serial' ($n)" +ret=0 +$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.n3.pre.test$n +newserial=`awk '$4 == "SOA" { print $7 + 10}' dig.out.n3.pre.test$n` +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -serial ${newserial:-0} nsec3 > /dev/null 2>&1 +sleep 1 +$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.ns3.post.test$n +serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns3.post.test$n` +[ ${newserial:-0} -eq ${serial:-1} ] || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:testing updating inline secure serial via 'rndc signing -serial' with negative change ($n)" +ret=0 +$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.n3.pre.test$n +oldserial=`awk '$4 == "SOA" { print $7 }' dig.out.n3.pre.test$n` +newserial=`awk '$4 == "SOA" { print $7 - 10}' dig.out.n3.pre.test$n` +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -serial ${newserial:-0} nsec3 > /dev/null 2>&1 +sleep 1 +$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.ns3.post.test$n +serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns3.post.test$n` +[ ${oldserial:-0} -eq ${serial:-1} ] || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +# +# Freezing only operates on the raw zone. +# +n=`expr $n + 1` +echo "I:testing updating inline secure serial via 'rndc signing -serial' when frozen ($n)" +ret=0 +$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.n3.pre.test$n +oldserial=`awk '$4 == "SOA" { print $7 }' dig.out.n3.pre.test$n` +newserial=`awk '$4 == "SOA" { print $7 + 10}' dig.out.n3.pre.test$n` +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 freeze nsec3 > /dev/null 2>&1 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 signing -serial ${newserial:-0} nsec3 > /dev/null 2>&1 +$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 thaw nsec3 > /dev/null 2>&1 +sleep 1 +$DIG nsec3. SOA -p 5300 @10.53.0.3 > dig.out.ns3.post.test$n +serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns3.post.test$n` +[ ${newserial:-0} -eq ${serial:-1} ] || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` +n=`expr $n + 1` + +echo "I:testing updating dynamic serial via 'rndc signing -serial' ($n)" +ret=0 +$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.pre.test$n +newserial=`awk '$4 == "SOA" { print $7 + 10}' dig.out.ns2.pre.test$n` +$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 signing -serial ${newserial:-0} bits > /dev/null 2>&1 +sleep 1 +$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.post.test$n +serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns2.post.test$n` +[ ${newserial:-0} -eq ${serial:-1} ] || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:testing updating dynamic serial via 'rndc signing -serial' with negative change ($n)" +ret=0 +$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.pre.test$n +oldserial=`awk '$4 == "SOA" { print $7 }' dig.out.ns2.pre.test$n` +newserial=`awk '$4 == "SOA" { print $7 - 10}' dig.out.ns2.pre.test$n` +$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 signing -serial ${newserial:-0} bits > /dev/null 2>&1 +sleep 1 +$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.post.test$n +serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns2.post.test$n` +[ ${oldserial:-0} -eq ${serial:-1} ] || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo "I:testing updating dynamic serial via 'rndc signing -serial' when frozen ($n)" +ret=0 +$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.pre.test$n +oldserial=`awk '$4 == "SOA" { print $7 }' dig.out.ns2.pre.test$n` +newserial=`awk '$4 == "SOA" { print $7 + 10}' dig.out.ns2.pre.test$n` +$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 freeze bits > /dev/null 2>&1 +$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 signing -serial ${newserial:-0} bits > /dev/null 2>&1 +$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 thaw bits > /dev/null 2>&1 +sleep 1 +$DIG bits. SOA -p 5300 @10.53.0.2 > dig.out.ns2.post.test$n +serial=`awk '$4 == "SOA" { print $7 }' dig.out.ns2.post.test$n` +[ ${oldserial:-0} -eq ${serial:-1} ] || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + exit $status diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 0bd6a7975f8..3bd26e174b4 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -47,7 +47,14 @@ New Features - None + + rndc can be used to set the serial of dynamically updatable + zones using "rndc signing -serial # zone". This is + particularly useful with inline zones which have been + reset. The serial should be set to a value larger than + that on the slaves. This will trigger a AXFR style + transfer. + diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h index fd2144f6493..72ea306b28e 100644 --- a/lib/dns/include/dns/events.h +++ b/lib/dns/include/dns/events.h @@ -79,6 +79,7 @@ #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_SETSERIAL (ISC_EVENTCLASS_DNS + 52) #define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0) #define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535) diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index d6ab7cac7f1..0a789ab0361 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -2378,6 +2378,11 @@ dns_zone_getstatlevel(dns_zone_t *zone); * full, terse, or none. */ +isc_result_t +dns_zone_setserial(dns_zone_t *zone, isc_uint32_t serial); +/*% + * Set the zone's serial to 'serial'. + */ ISC_LANG_ENDDECLS diff --git a/lib/dns/zone.c b/lib/dns/zone.c index af3681ac60f..4fd84f79297 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -847,6 +847,11 @@ struct np3event { nsec3param_t params; }; +struct ssevent { + isc_event_t event; + isc_uint32_t serial; +}; + /*% * Increment resolver-related statistics counters. Zone must be locked. */ @@ -17705,21 +17710,21 @@ keydone(isc_task_t *task, isc_event_t *event) { dns_diff_init(zone->mctx, &diff); ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); - if (zone->db != NULL) { + 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, - "keydone:dns_db_newversion -> %s", - dns_result_totext(result)); - goto failure; - } - } ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); if (db == NULL) goto failure; + dns_db_currentversion(db, &oldver); + result = dns_db_newversion(db, &newver); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "keydone:dns_db_newversion -> %s", + dns_result_totext(result)); + goto failure; + } + result = dns_db_getoriginnode(db, &node); if (result != ISC_R_SUCCESS) goto failure; @@ -17901,21 +17906,22 @@ setnsec3param(isc_task_t *task, isc_event_t *event) { dns_diff_init(zone->mctx, &diff); ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); - if (zone->db != NULL) { + 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", - dns_result_totext(result)); - goto failure; - } - } ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); if (db == NULL) goto failure; + dns_db_currentversion(db, &oldver); + result = dns_db_newversion(db, &newver); + if (result != ISC_R_SUCCESS) { + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + dns_zone_log(zone, ISC_LOG_ERROR, + "setnsec3param:dns_db_newversion -> %s", + dns_result_totext(result)); + goto failure; + } + CHECK(dns_db_getoriginnode(db, &node)); /* @@ -18191,3 +18197,143 @@ dns_zone_getstatlevel(dns_zone_t *zone) { return (zone->statlevel); } + +static void +setserial(isc_task_t *task, isc_event_t *event) { + isc_uint32_t oldserial, desired; + const char *me = "setserial"; + 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_diff_t diff; + struct ssevent *sse = (struct ssevent *)event; + dns_update_log_t log = { update_log_cb, NULL }; + dns_difftuple_t *oldtuple = NULL, *newtuple = NULL; + + UNUSED(task); + + zone = event->ev_arg; + INSIST(DNS_ZONE_VALID(zone)); + + ENTER; + + if (zone->update_disabled) + goto failure; + + desired = sse->serial; + + dns_diff_init(zone->mctx, &diff); + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (zone->db != NULL) + dns_db_attach(zone->db, &db); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + if (db == NULL) + goto failure; + + dns_db_currentversion(db, &oldver); + result = dns_db_newversion(db, &newver); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "setserial:dns_db_newversion -> %s", + dns_result_totext(result)); + goto failure; + } + + CHECK(dns_db_createsoatuple(db, oldver, diff.mctx, + DNS_DIFFOP_DEL, &oldtuple)); + CHECK(dns_difftuple_copy(oldtuple, &newtuple)); + newtuple->op = DNS_DIFFOP_ADD; + + oldserial = dns_soa_getserial(&oldtuple->rdata); + if (desired == 0U) + desired = 1; + if (!isc_serial_gt(desired, oldserial)) { + if (desired != oldserial) + dns_zone_log(zone, ISC_LOG_INFO, + "setserial: desired serial (%u) " + "out of range (%u-%u)", desired, + oldserial + 1, (oldserial + 0x7fffffff)); + goto failure; + } + + dns_soa_setserial(desired, &newtuple->rdata); + CHECK(do_one_tuple(&oldtuple, db, newver, &diff)); + CHECK(do_one_tuple(&newtuple, db, newver, &diff)); + result = dns_update_signatures(&log, zone, db, + oldver, newver, &diff, + zone->sigvalidityinterval); + if (result != ISC_R_NOTFOUND) + CHECK(result); + + /* Write changes to journal file. */ + CHECK(zone_journal(zone, &diff, NULL, "setserial")); + commit = ISC_TRUE; + + LOCK_ZONE(zone); + zone_needdump(zone, 30); + UNLOCK_ZONE(zone); + + failure: + if (oldtuple != NULL) + dns_difftuple_free(&oldtuple); + if (newtuple != NULL) + dns_difftuple_free(&newtuple); + 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); + dns_diff_clear(&diff); + isc_event_free(&event); + dns_zone_idetach(&zone); + + INSIST(oldver == NULL); + INSIST(newver == NULL); +} + +isc_result_t +dns_zone_setserial(dns_zone_t *zone, isc_uint32_t serial) { + isc_result_t result = ISC_R_SUCCESS; + dns_zone_t *dummy = NULL; + isc_event_t *e = NULL; + struct ssevent *sse; + + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + + if (!inline_secure(zone)) { + if (!dns_zone_isdynamic(zone, ISC_TRUE)) { + result = DNS_R_NOTDYNAMIC; + goto failure; + } + } + + if (zone->update_disabled) { + result = DNS_R_FROZEN; + goto failure; + } + + e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_SETSERIAL, + setserial, zone, sizeof(struct ssevent)); + if (e == NULL) { + result = ISC_R_NOMEMORY; + goto failure; + } + + sse = (struct ssevent *)e; + sse->serial = serial; + + zone_iattach(zone, &dummy); + isc_task_send(zone->task, &e); + + failure: + if (e != NULL) + isc_event_free(&e); + UNLOCK_ZONE(zone); + return (result); +}