From 6acf326969862c68559d699be113c28dc35b35e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Sat, 17 Mar 2018 00:12:23 +0100 Subject: [PATCH] Apply raw zone deltas to yet unsigned secure zones When inline signing is enabled for a zone without creating signing keys for it, changes subsequently applied to the raw zone will not be reflected in the secure zone due to the dns_update_signaturesinc() call inside receive_secure_serial() failing. Given that an inline zone will be served (without any signatures) even with no associated signing keys being present, keep applying raw zone deltas to the secure zone until keys become available in an attempt to follow the principle of least astonishment. --- bin/tests/system/inline/clean.sh | 18 +++ bin/tests/system/inline/ns2/named.conf.in | 12 ++ bin/tests/system/inline/ns3/named.conf.in | 25 ++++ bin/tests/system/inline/ns3/sign.sh | 12 ++ bin/tests/system/inline/setup.sh | 5 + bin/tests/system/inline/tests.sh | 161 +++++++++++++++++++++- lib/dns/zone.c | 11 +- 7 files changed, 242 insertions(+), 2 deletions(-) diff --git a/bin/tests/system/inline/clean.sh b/bin/tests/system/inline/clean.sh index 8e4199a60a..80e5eb914d 100644 --- a/bin/tests/system/inline/clean.sh +++ b/bin/tests/system/inline/clean.sh @@ -10,6 +10,7 @@ rm -f */named.conf rm -f */named.memstats rm -f */named.run +rm -f */named.run.prev rm -f */trusted.conf rm -f ns1/K* rm -f ns1/dsset-* @@ -23,6 +24,10 @@ rm -f ns2/inactiveksk.db rm -f ns2/inactiveksk.db.jnl rm -f ns2/inactivezsk.db rm -f ns2/inactivezsk.db.jnl +rm -f ns2/nokeys.db +rm -f ns2/nokeys.db.jnl +rm -f ns2/removedkeys-secondary.db +rm -f ns2/removedkeys-secondary.db.jnl rm -f ns2/retransfer.db rm -f ns2/retransfer.db.jnl rm -f ns2/retransfer3.db @@ -60,10 +65,22 @@ rm -f ns3/inactivezsk.bk rm -f ns3/inactivezsk.bk.jnl rm -f ns3/inactivezsk.bk.signed rm -f ns3/inactivezsk.bk.signed.jnl +rm -f ns3/nokeys.bk +rm -f ns3/nokeys.bk.jnl +rm -f ns3/nokeys.bk.signed +rm -f ns3/nokeys.bk.signed.jnl rm -f ns3/nsec3.db rm -f ns3/nsec3.db.jnl rm -f ns3/nsec3.db.signed rm -f ns3/nsec3.db.signed.jnl +rm -f ns3/removedkeys-primary.db +rm -f ns3/removedkeys-primary.db.jnl +rm -f ns3/removedkeys-primary.db.signed +rm -f ns3/removedkeys-primary.db.signed.jnl +rm -f ns3/removedkeys-secondary.bk +rm -f ns3/removedkeys-secondary.bk.jnl +rm -f ns3/removedkeys-secondary.bk.signed +rm -f ns3/removedkeys-secondary.bk.signed.jnl rm -f ns3/retransfer.bk rm -f ns3/retransfer.bk.jnl rm -f ns3/retransfer.bk.signed @@ -103,3 +120,4 @@ rm -f ns*/named.lock rm -f dig.out.* rm -f ns3/nzf-* rm -f rndc.out.ns* +rm -rf ns3/removedkeys diff --git a/bin/tests/system/inline/ns2/named.conf.in b/bin/tests/system/inline/ns2/named.conf.in index 5f4585b532..dbe5967755 100644 --- a/bin/tests/system/inline/ns2/named.conf.in +++ b/bin/tests/system/inline/ns2/named.conf.in @@ -68,3 +68,15 @@ zone "inactivezsk" { file "inactivezsk.db"; allow-update { any; }; }; + +zone "nokeys" { + type master; + file "nokeys.db"; + allow-update { any; }; +}; + +zone "removedkeys-secondary" { + type master; + file "removedkeys-secondary.db"; + allow-update { any; }; +}; diff --git a/bin/tests/system/inline/ns3/named.conf.in b/bin/tests/system/inline/ns3/named.conf.in index ce3da9e870..9513d2581e 100644 --- a/bin/tests/system/inline/ns3/named.conf.in +++ b/bin/tests/system/inline/ns3/named.conf.in @@ -132,3 +132,28 @@ zone "inactivezsk" { auto-dnssec maintain; file "inactivezsk.bk"; }; + +zone "nokeys" { + type slave; + masters { 10.53.0.2; }; + inline-signing yes; + auto-dnssec maintain; + file "nokeys.bk"; +}; + +zone "removedkeys-primary" { + type master; + inline-signing yes; + auto-dnssec maintain; + allow-update { any; }; + also-notify { 10.53.0.2; }; + file "removedkeys-primary.db"; +}; + +zone "removedkeys-secondary" { + type slave; + masters { 10.53.0.2; }; + inline-signing yes; + auto-dnssec maintain; + file "removedkeys-secondary.bk"; +}; diff --git a/bin/tests/system/inline/ns3/sign.sh b/bin/tests/system/inline/ns3/sign.sh index c3a3544240..c36100d136 100755 --- a/bin/tests/system/inline/ns3/sign.sh +++ b/bin/tests/system/inline/ns3/sign.sh @@ -96,6 +96,18 @@ keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA256 -b 1024 -n zone $zone` keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA256 -b 1024 -n zone -f KSK $zone` $DSFROMKEY -T 1200 $keyname >> ../ns1/root.db +zone=removedkeys-primary +rm -f K${zone}.+*+*.key +rm -f K${zone}.+*+*.private +keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone` +keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -f KSK $zone` + +zone=removedkeys-secondary +rm -f K${zone}.+*+*.key +rm -f K${zone}.+*+*.private +keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone` +keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -f KSK $zone` + for s in a c d h k l m q z do zone=test-$s diff --git a/bin/tests/system/inline/setup.sh b/bin/tests/system/inline/setup.sh index 99e696a93d..7c17b45012 100644 --- a/bin/tests/system/inline/setup.sh +++ b/bin/tests/system/inline/setup.sh @@ -21,6 +21,8 @@ touch ns2/trusted.conf cp ns2/bits.db.in ns2/bits.db cp ns2/bits.db.in ns2/inactiveksk.db cp ns2/bits.db.in ns2/inactivezsk.db +cp ns2/bits.db.in ns2/nokeys.db +cp ns2/bits.db.in ns2/removedkeys-secondary.db cp ns2/bits.db.in ns2/retransfer.db cp ns2/bits.db.in ns2/retransfer3.db rm -f ns2/bits.db.jnl @@ -31,6 +33,9 @@ cp ns3/master.db.in ns3/updated.db cp ns3/master.db.in ns3/expired.db cp ns3/master.db.in ns3/nsec3.db cp ns3/master.db.in ns3/externalkey.db +cp ns3/master.db.in ns3/removedkeys-primary.db + +mkdir ns3/removedkeys touch ns4/trusted.conf cp ns4/noixfr.db.in ns4/noixfr.db diff --git a/bin/tests/system/inline/tests.sh b/bin/tests/system/inline/tests.sh index 8d6fc546c4..6e4ad8e06a 100755 --- a/bin/tests/system/inline/tests.sh +++ b/bin/tests/system/inline/tests.sh @@ -1087,14 +1087,173 @@ grep "DNSKEY 8 1 [0-9]* [0-9]* [0-9]* ${kskid} " dig.out.ns3.test$n > /dev/null if [ $ret != 0 ]; then echo_i "failed"; fi status=`expr $status + $ret` +# Wait until an update to the raw part of a given inline signed zone is fully +# processed. As waiting for a fixed amount of time is suboptimal and there is +# no single message that would signify both a successful modification and an +# error in a race-free manner, instead wait until either notifies are sent +# (which means the secure zone was modified) or a receive_secure_serial() error +# is logged (which means the zone was not modified and will not be modified any +# further in response to the relevant raw zone update). +wait_until_raw_zone_update_is_processed() { + zone="$1" + for i in 1 2 3 4 5 6 7 8 9 10 + do + if nextpart ns3/named.run | egrep "zone ${zone}.*(sending notifies|receive_secure_serial)" > /dev/null; then + return + fi + sleep 1 + done +} + +n=`expr $n + 1` +echo_i "checking that changes to raw zone are applied to a previously unsigned secure zone ($n)" +ret=0 +# Query for bar.nokeys/A and ensure the response is negative. As this zone +# does not have any signing keys set up, the response must be unsigned. +$DIG $DIGOPTS @10.53.0.3 bar.nokeys. A > dig.out.ns3.pre.test$n 2>&1 || ret=1 +grep "status: NOERROR" dig.out.ns3.pre.test$n > /dev/null && ret=1 +grep "RRSIG" dig.out.ns3.pre.test$n > /dev/null && ret=1 +# Ensure the wait_until_raw_zone_update_is_processed() call below will ignore +# log messages generated before the raw zone is updated. +nextpart ns3/named.run > /dev/null +# Add a record to the raw zone on the master. +$NSUPDATE << EOF || ret=1 +zone nokeys. +server 10.53.0.2 ${PORT} +update add bar.nokeys. 0 A 127.0.0.1 +send +EOF +wait_until_raw_zone_update_is_processed "nokeys" +# Query for bar.nokeys/A again and ensure the signer now returns a positive, +# yet still unsigned response. +$DIG $DIGOPTS @10.53.0.3 bar.nokeys. A > dig.out.ns3.post.test$n 2>&1 +grep "status: NOERROR" dig.out.ns3.post.test$n > /dev/null || ret=1 +grep "RRSIG" dig.out.ns3.pre.test$n > /dev/null && ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "checking that changes to raw zone are not applied to a previously signed secure zone with no keys available (primary) ($n)" +ret=0 +# Query for bar.removedkeys-primary/A and ensure the response is negative. As +# this zone has signing keys set up, the response must be signed. +$DIG $DIGOPTS @10.53.0.3 bar.removedkeys-primary. A > dig.out.ns3.pre.test$n 2>&1 || ret=1 +grep "status: NOERROR" dig.out.ns3.pre.test$n > /dev/null && ret=1 +grep "RRSIG" dig.out.ns3.pre.test$n > /dev/null || ret=1 +# Remove the signing keys for this zone. +mv -f ns3/Kremovedkeys-primary* ns3/removedkeys +# Ensure the wait_until_raw_zone_update_is_processed() call below will ignore +# log messages generated before the raw zone is updated. +nextpart ns3/named.run > /dev/null +# Add a record to the raw zone on the master. +$NSUPDATE << EOF || ret=1 +zone removedkeys-primary. +server 10.53.0.3 ${PORT} +update add bar.removedkeys-primary. 0 A 127.0.0.1 +send +EOF +wait_until_raw_zone_update_is_processed "removedkeys-primary" +# Query for bar.removedkeys-primary/A again and ensure the signer still returns +# a negative, signed response. +$DIG $DIGOPTS @10.53.0.3 bar.removedkeys-primary. A > dig.out.ns3.post.test$n 2>&1 +grep "status: NOERROR" dig.out.ns3.post.test$n > /dev/null && ret=1 +grep "RRSIG" dig.out.ns3.pre.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "checking that backlogged changes to raw zone are applied after keys become available (primary) ($n)" +ret=0 +# Restore the signing keys for this zone. +mv ns3/removedkeys/Kremovedkeys-primary* ns3 +$RNDCCMD 10.53.0.3 loadkeys removedkeys-primary > /dev/null 2>&1 +# Determine what a SOA record with a bumped serial number should look like. +BUMPED_SOA=`sed -n 's/.*\(add removedkeys-primary.*IN.*SOA\)/\1/p;' ns3/named.run | tail -1 | awk '{$8 += 1; print $0}'` +# Ensure the wait_until_raw_zone_update_is_processed() call below will ignore +# log messages generated before the raw zone is updated. +nextpart ns3/named.run > /dev/null +# Bump the SOA serial number of the raw zone. +$NSUPDATE << EOF || ret=1 +zone removedkeys-primary. +server 10.53.0.3 ${PORT} +update del removedkeys-primary. SOA +update ${BUMPED_SOA} +send +EOF +wait_until_raw_zone_update_is_processed "removedkeys-primary" +# Query for bar.removedkeys-primary/A again and ensure the signer now returns a +# positive, signed response. +$DIG $DIGOPTS @10.53.0.3 bar.removedkeys-primary. A > dig.out.ns3.test$n 2>&1 +grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 +grep "RRSIG" dig.out.ns3.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "checking that changes to raw zone are not applied to a previously signed secure zone with no keys available (secondary) ($n)" +ret=0 +# Query for bar.removedkeys-secondary/A and ensure the response is negative. As this +# zone does have signing keys set up, the response must be signed. +$DIG $DIGOPTS @10.53.0.3 bar.removedkeys-secondary. A > dig.out.ns3.pre.test$n 2>&1 || ret=1 +grep "status: NOERROR" dig.out.ns3.pre.test$n > /dev/null && ret=1 +grep "RRSIG" dig.out.ns3.pre.test$n > /dev/null || ret=1 +# Remove the signing keys for this zone. +mv -f ns3/Kremovedkeys-secondary* ns3/removedkeys +# Ensure the wait_until_raw_zone_update_is_processed() call below will ignore +# log messages generated before the raw zone is updated. +nextpart ns3/named.run > /dev/null +# Add a record to the raw zone on the master. +$NSUPDATE << EOF || ret=1 +zone removedkeys-secondary. +server 10.53.0.2 ${PORT} +update add bar.removedkeys-secondary. 0 A 127.0.0.1 +send +EOF +wait_until_raw_zone_update_is_processed "removedkeys-secondary" +# Query for bar.removedkeys-secondary/A again and ensure the signer still returns a +# negative, signed response. +$DIG $DIGOPTS @10.53.0.3 bar.removedkeys-secondary. A > dig.out.ns3.post.test$n 2>&1 +grep "status: NOERROR" dig.out.ns3.post.test$n > /dev/null && ret=1 +grep "RRSIG" dig.out.ns3.pre.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "checking that backlogged changes to raw zone are applied after keys become available (secondary) ($n)" +ret=0 +# Restore the signing keys for this zone. +mv ns3/removedkeys/Kremovedkeys-secondary* ns3 +$RNDCCMD 10.53.0.3 loadkeys removedkeys-secondary > /dev/null 2>&1 +# Determine what a SOA record with a bumped serial number should look like. +BUMPED_SOA=`sed -n 's/.*\(add removedkeys-secondary.*IN.*SOA\)/\1/p;' ns2/named.run | tail -1 | awk '{$8 += 1; print $0}'` +# Ensure the wait_until_raw_zone_update_is_processed() call below will ignore +# log messages generated before the raw zone is updated. +nextpart ns3/named.run > /dev/null +# Bump the SOA serial number of the raw zone on the master. +$NSUPDATE << EOF || ret=1 +zone removedkeys-secondary. +server 10.53.0.2 ${PORT} +update del removedkeys-secondary. SOA +update ${BUMPED_SOA} +send +EOF +wait_until_raw_zone_update_is_processed "removedkeys-secondary" +# Query for bar.removedkeys-secondary/A again and ensure the signer now returns +# a positive, signed response. +$DIG $DIGOPTS @10.53.0.3 bar.removedkeys-secondary. A > dig.out.ns3.test$n 2>&1 +grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 +grep "RRSIG" dig.out.ns3.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + n=`expr $n + 1` echo_i "check that zonestatus reports 'type: master' for a inline master zone ($n)" ret=0 $RNDCCMD 10.53.0.3 zonestatus master > rndc.out.ns3.test$n grep "type: master" rndc.out.ns3.test$n > /dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi - status=`expr $status + $ret` + n=`expr $n + 1` echo_i "check that zonestatus reports 'type: slave' for a inline slave zone ($n)" ret=0 diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 59cd5aaca2..ddee2f2dfa 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -14230,8 +14230,17 @@ receive_secure_serial(isc_task_t *task, isc_event_t *event) { fprintf(stderr, "looping on dns_update_signaturesinc\n"); return; } - if (result != ISC_R_SUCCESS) + /* + * If something went wrong while trying to update the secure zone and + * the latter was already signed before, do not apply raw zone deltas + * to it as that would break existing DNSSEC signatures. However, if + * the secure zone was not yet signed (e.g. because no signing keys + * were created for it), commence applying raw zone deltas to it so + * that contents of the raw zone and the secure zone are kept in sync. + */ + if (result != ISC_R_SUCCESS && dns_db_issecure(zone->rss_db)) { goto failure; + } if (rjournal == NULL) CHECK(dns_journal_open(zone->rss_raw->mctx,