diff --git a/CHANGES b/CHANGES index c483560dff..c872c16a8f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,10 +1,12 @@ +6178. [func] Add support for the multi-signer model 2 (RFC 8901) when + using inline-signing. [GL #2710] + 6177. [placeholder] 6176. [test] Add support for using pytest & pytest-xdist to execute the system test suite. [GL #3978] 6175. [test] Fix the `upforwd` system test to be more reliable, - especially when using thread sanitizer. [GL #4069] 6174. [placeholder] diff --git a/bin/tests/system/Makefile.am b/bin/tests/system/Makefile.am index b0dfbecbac..245d02240c 100644 --- a/bin/tests/system/Makefile.am +++ b/bin/tests/system/Makefile.am @@ -201,7 +201,7 @@ endif HAVE_PERLMOD_NET_DNS_NAMESERVER endif HAVE_PERLMOD_NET_DNS if HAVE_PYTHON -TESTS += kasp keymgr2kasp tcp pipelined +TESTS += kasp keymgr2kasp multisigner tcp pipelined if HAVE_PYTEST TESTS += checkds dispatch rpzextra shutdown timeouts diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index ebf4d52522..d15791696b 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -108,6 +108,7 @@ keyfromlabel keymgr2kasp legacy logfileconfig +multisigner nzd2nzf pipelined qmin diff --git a/bin/tests/system/dnssec/ns3/named.conf.in b/bin/tests/system/dnssec/ns3/named.conf.in index afca54747d..fbcb8d865f 100644 --- a/bin/tests/system/dnssec/ns3/named.conf.in +++ b/bin/tests/system/dnssec/ns3/named.conf.in @@ -209,6 +209,7 @@ zone "expired.example" { zone "update-nsec3.example" { type primary; + auto-dnssec maintain; allow-update { any; }; file "update-nsec3.example.db.signed"; }; diff --git a/bin/tests/system/multisigner/clean.sh b/bin/tests/system/multisigner/clean.sh new file mode 100644 index 0000000000..393efbbf37 --- /dev/null +++ b/bin/tests/system/multisigner/clean.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +set -e + +rm -f *.created +rm -f cdnskey.ns* +rm -f cds.ns* +rm -f secondary.cdnskey.ns* +rm -f secondary.cds.ns* +rm -f created.key-* +rm -f dig.out.* +rm -f python.out.* +rm -f rndc.dnssec.status.out.* +rm -f unused.key-* +rm -f verify.out.* + +rm -f ns*/*.jbk +rm -f ns*/*.jnl +rm -f ns*/*.journal.out.test* +rm -f ns*/*.signed +rm -f ns*/*.signed.jnl +rm -f ns*/*.zsk +rm -f ns*/db-* +rm -f ns*/K* +rm -f ns*/keygen.out.* +rm -f ns*/managed-keys* +rm -f ns*/model2.secondary.db +rm -f ns*/model2.secondary.db +rm -f ns*/named.conf +rm -f ns*/named.memstats +rm -f ns*/named.run +rm -f ns*/settime.out.* diff --git a/bin/tests/system/multisigner/kasp.conf b/bin/tests/system/multisigner/kasp.conf new file mode 100644 index 0000000000..5fe6de862f --- /dev/null +++ b/bin/tests/system/multisigner/kasp.conf @@ -0,0 +1,19 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +dnssec-policy "model2" { + keys { + ksk lifetime unlimited algorithm ecdsap256sha256; + zsk lifetime unlimited algorithm ecdsap256sha256; + }; +}; diff --git a/bin/tests/system/multisigner/ns3/model2.multisigner.db b/bin/tests/system/multisigner/ns3/model2.multisigner.db new file mode 100644 index 0000000000..010b05b3cb --- /dev/null +++ b/bin/tests/system/multisigner/ns3/model2.multisigner.db @@ -0,0 +1,27 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns3 +ns3 A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +c A 10.0.0.3 + diff --git a/bin/tests/system/multisigner/ns3/named.conf.in b/bin/tests/system/multisigner/ns3/named.conf.in new file mode 100644 index 0000000000..ce570696b6 --- /dev/null +++ b/bin/tests/system/multisigner/ns3/named.conf.in @@ -0,0 +1,54 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS3 + +include "../kasp.conf"; + +options { + query-source address 10.53.0.3; + notify-source 10.53.0.3; + transfer-source 10.53.0.3; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; + key-directory "."; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "model2.multisigner." { + type primary; + allow-update { any; }; + file "model2.multisigner.db"; + dnssec-policy model2; + inline-signing no; +}; + +zone "model2.secondary." { + type secondary; + primaries { 10.53.0.5; }; + file "model2.secondary.db"; + dnssec-policy model2; + inline-signing yes; +}; diff --git a/bin/tests/system/multisigner/ns3/setup.sh b/bin/tests/system/multisigner/ns3/setup.sh new file mode 100644 index 0000000000..0f6b343e14 --- /dev/null +++ b/bin/tests/system/multisigner/ns3/setup.sh @@ -0,0 +1,44 @@ +#!/bin/sh -e + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../../conf.sh + +echo_i "ns3/setup.sh" + +O="OMNIPRESENT" +ksktimes="-P now -A now -P sync now" +zsktimes="-P now -A now" + +zone="model2.multisigner" +echo_i "setting up zone: $zone" +zonefile="${zone}.db" + +KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone 2> keygen.out.$zone.1) +ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone 2> keygen.out.$zone.2) +$SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" > settime.out.$zone.1 2>&1 +$SETTIME -s -g $O -k $O now -z $O now "$ZSK" > settime.out.$zone.2 2>&1 +# ZSK will be added to the other provider with nsupdate. +cat "${ZSK}.key" | grep -v ";.*" > "${zone}.zsk" + +zone="model2.secondary" +echo_i "setting up zone: $zone" +zonefile="${zone}.db" +cp "../ns5/${zonefile}.in" "$zonefile" + +KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone 2> keygen.out.$zone.1) +ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone 2> keygen.out.$zone.2) +$SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" > settime.out.$zone.1 2>&1 +$SETTIME -s -g $O -k $O now -z $O now "$ZSK" > settime.out.$zone.2 2>&1 +# ZSK will be added to the other provider with nsupdate. +cat "${ZSK}.key" | grep -v ";.*" > "${zone}.zsk" diff --git a/bin/tests/system/multisigner/ns4/model2.multisigner.db b/bin/tests/system/multisigner/ns4/model2.multisigner.db new file mode 100644 index 0000000000..86a1708b45 --- /dev/null +++ b/bin/tests/system/multisigner/ns4/model2.multisigner.db @@ -0,0 +1,26 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns4 +ns4 A 10.53.0.4 + +a A 10.0.0.1 +b A 10.0.0.2 +c A 10.0.0.3 diff --git a/bin/tests/system/multisigner/ns4/named.conf.in b/bin/tests/system/multisigner/ns4/named.conf.in new file mode 100644 index 0000000000..e19fd35f0d --- /dev/null +++ b/bin/tests/system/multisigner/ns4/named.conf.in @@ -0,0 +1,54 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS4 + +include "../kasp.conf"; + +options { + query-source address 10.53.0.4; + notify-source 10.53.0.4; + transfer-source 10.53.0.4; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.4; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; + key-directory "."; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "model2.multisigner." { + type primary; + allow-update { any; }; + file "model2.multisigner.db"; + dnssec-policy model2; + inline-signing yes; +}; + +zone "model2.secondary." { + type secondary; + primaries { 10.53.0.5; }; + file "model2.secondary.db"; + dnssec-policy model2; + inline-signing yes; +}; diff --git a/bin/tests/system/multisigner/ns4/setup.sh b/bin/tests/system/multisigner/ns4/setup.sh new file mode 100644 index 0000000000..94ac16ad8d --- /dev/null +++ b/bin/tests/system/multisigner/ns4/setup.sh @@ -0,0 +1,44 @@ +#!/bin/sh -e + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../../conf.sh + +echo_i "ns4/setup.sh" + +O="OMNIPRESENT" +ksktimes="-P now -A now -P sync now" +zsktimes="-P now -A now" + +zone="model2.multisigner" +echo_i "setting up zone: $zone" +zonefile="${zone}.db" + +KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone 2> keygen.out.$zone.1) +ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone 2> keygen.out.$zone.2) +$SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" > settime.out.$zone.1 2>&1 +$SETTIME -s -g $O -k $O now -z $O now "$ZSK" > settime.out.$zone.2 2>&1 +# ZSK will be added to the other provider with nsupdate. +cat "${ZSK}.key" | grep -v ";.*" > "${zone}.zsk" + +zone="model2.secondary" +echo_i "setting up zone: $zone" +zonefile="${zone}.db" +cp "../ns5/${zonefile}.in" "$zonefile" + +KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone 2> keygen.out.$zone.1) +ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone 2> keygen.out.$zone.2) +$SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" > settime.out.$zone.1 2>&1 +$SETTIME -s -g $O -k $O now -z $O now "$ZSK" > settime.out.$zone.2 2>&1 +# ZSK will be added to the other provider with nsupdate. +cat "${ZSK}.key" | grep -v ";.*" > "${zone}.zsk" diff --git a/bin/tests/system/multisigner/ns5/model2.secondary.db.in b/bin/tests/system/multisigner/ns5/model2.secondary.db.in new file mode 100644 index 0000000000..ea0f3ccda8 --- /dev/null +++ b/bin/tests/system/multisigner/ns5/model2.secondary.db.in @@ -0,0 +1,29 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns3 + NS ns4 +ns3 A 10.53.0.3 +ns4 A 10.53.0.4 + +a A 10.0.0.1 +b A 10.0.0.2 +c A 10.0.0.3 + diff --git a/bin/tests/system/multisigner/ns5/named.conf.in b/bin/tests/system/multisigner/ns5/named.conf.in new file mode 100644 index 0000000000..3518fe38f8 --- /dev/null +++ b/bin/tests/system/multisigner/ns5/named.conf.in @@ -0,0 +1,44 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS5 + +include "../kasp.conf"; + +options { + query-source address 10.53.0.5; + notify-source 10.53.0.5; + transfer-source 10.53.0.5; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.5; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; + key-directory "."; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.5 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "model2.secondary." { + type primary; + allow-update { any; }; + file "model2.secondary.db"; +}; diff --git a/bin/tests/system/multisigner/ns5/setup.sh b/bin/tests/system/multisigner/ns5/setup.sh new file mode 100644 index 0000000000..6bf3e6ffbc --- /dev/null +++ b/bin/tests/system/multisigner/ns5/setup.sh @@ -0,0 +1,22 @@ +#!/bin/sh -e + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../../conf.sh + +echo_i "ns5/setup.sh" + +zone="model2.secondary" +echo_i "setting up zone: $zone" +zonefile="${zone}.db" +cp "${zonefile}.in" "$zonefile" diff --git a/bin/tests/system/multisigner/setup.sh b/bin/tests/system/multisigner/setup.sh new file mode 100644 index 0000000000..f7fc496ca8 --- /dev/null +++ b/bin/tests/system/multisigner/setup.sh @@ -0,0 +1,36 @@ +#!/bin/sh -e + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../conf.sh + +set -e + +$SHELL clean.sh + +copy_setports ns3/named.conf.in ns3/named.conf +copy_setports ns4/named.conf.in ns4/named.conf +copy_setports ns5/named.conf.in ns5/named.conf + +( + cd ns3 + $SHELL setup.sh +) +( + cd ns4 + $SHELL setup.sh +) +( + cd ns5 + $SHELL setup.sh +) diff --git a/bin/tests/system/multisigner/tests.sh b/bin/tests/system/multisigner/tests.sh new file mode 100644 index 0000000000..aac1c2ecdc --- /dev/null +++ b/bin/tests/system/multisigner/tests.sh @@ -0,0 +1,664 @@ +#!/bin/sh + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../conf.sh +# shellcheck source=kasp.sh +. ../kasp.sh + +dig_with_opts() { + $DIG +tcp +noadd +nosea +nostat +nocmd +dnssec -p $PORT "$@" +} + +start_time="$(TZ=UTC date +%s)" +status=0 +n=0 + +set_zone "model2.multisigner" +set_policy "model2" "2" "3600" + +# Key properties and states. +key_clear "KEY1" +set_keyrole "KEY1" "ksk" +set_keylifetime "KEY1" "0" +set_keyalgorithm "KEY1" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY1" "yes" +set_zonesigning "KEY1" "no" +set_keystate "KEY1" "GOAL" "omnipresent" +set_keystate "KEY1" "STATE_DNSKEY" "omnipresent" +set_keystate "KEY1" "STATE_KRRSIG" "omnipresent" +set_keystate "KEY1" "STATE_DS" "omnipresent" + +key_clear "KEY2" +set_keyrole "KEY2" "zsk" +set_keylifetime "KEY2" "0" +set_keyalgorithm "KEY2" "13" "ECDSAP256SHA256" "256" +set_keysigning "KEY2" "no" +set_zonesigning "KEY2" "yes" +set_keystate "KEY2" "GOAL" "omnipresent" +set_keystate "KEY2" "STATE_DNSKEY" "omnipresent" +set_keystate "KEY2" "STATE_ZRRSIG" "omnipresent" + +key_clear "KEY3" +key_clear "KEY4" + +set_keytimes_model2() { + # The first KSK is immediately published and activated. + created=$(key_get KEY1 CREATED) + set_keytime "KEY1" "PUBLISHED" "${created}" + set_keytime "KEY1" "ACTIVE" "${created}" + set_keytime "KEY1" "SYNCPUBLISH" "${created}" + + # The first ZSKs are immediately published and activated. + created=$(key_get KEY2 CREATED) + set_keytime "KEY2" "PUBLISHED" "${created}" + set_keytime "KEY2" "ACTIVE" "${created}" +} + +set_server "ns3" "10.53.0.3" +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +set_keytimes_model2 +check_keytimes +check_apex +dnssec_verify + +set_server "ns4" "10.53.0.4" +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +set_keytimes_model2 +check_keytimes +check_apex +dnssec_verify + +# +# Update DNSKEY RRset. +# + +# Check that the ZSKs from the other provider are published. +zsks_are_published() { + dig_with_opts "$ZONE" "@${SERVER}" DNSKEY > "dig.out.$DIR.test$n" || return 1 + cat dig.out.$DIR.test$n | tr [:blank:] ' ' > dig.out.$DIR.test$n.tr || return 1 + # We should have two ZSKs. + lines=$(grep "256 3 13" dig.out.$DIR.test$n.tr | wc -l) + test "$lines" -eq 2 || return 1 + # Both ZSKs are published. + grep "$(cat ns3/${ZONE}.zsk | tr [:blank:] ' ')" dig.out.$DIR.test$n.tr > /dev/null || return 1 + grep "$(cat ns4/${ZONE}.zsk | tr [:blank:] ' ')" dig.out.$DIR.test$n.tr > /dev/null || return 1 + # And one KSK. + lines=$(grep "257 3 13" dig.out.$DIR.test$n.tr | wc -l) + test "$lines" -eq 1 || return 1 +} + +# Test to make sure no DNSSEC records end up in the raw journal. +no_dnssec_in_journal() { + n=$((n+1)) + ret=0 + echo_i "check zone ${ZONE} raw journal has no DNSSEC ($n)" + $JOURNALPRINT "${DIR}/${ZONE}.db.jnl" > "${DIR}/${ZONE}.journal.out.test$n" + rrset_exists NSEC "${DIR}/${ZONE}.journal.out.test$n" && ret=1 + rrset_exists NSEC3 "${DIR}/${ZONE}.journal.out.test$n" && ret=1 + rrset_exists NSEC3PARAM "${DIR}/${ZONE}.journal.out.test$n" && ret=1 + rrset_exists RRSIG "${DIR}/${ZONE}.journal.out.test$n" && ret= 1 + test "$ret" -eq 0 || echo_i "failed" + status=$((status+ret)) +} + +# Check if a certain RRtype is present in the journal file. +rrset_exists() ( + rrtype=$1 + file=$2 + lines=$(awk -v rt="${rrtype}" '$5 == rt {print}' ${file} | wc -l) + test "$lines" -gt 0 +) + +n=$((n+1)) +echo_i "add dnskey record: update zone ${ZONE} at ns3 with ZSK from provider ns4 ($n)" +ret=0 +set_server "ns3" "10.53.0.3" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update add $(cat "ns4/${ZONE}.zsk") +echo send +) | $NSUPDATE +echo_i "check zone ${ZONE} DNSKEY RRset after update ($n)" +retry_quiet 10 zsks_are_published || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# Verify again. +dnssec_verify + +n=$((n+1)) +echo_i "add dnskey record: - update zone ${ZONE} at ns4 with ZSK from provider ns3 ($n)" +ret=0 +set_server "ns4" "10.53.0.4" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update add $(cat "ns3/${ZONE}.zsk") +echo send +) | $NSUPDATE +echo_i "check zone ${ZONE} DNSKEY RRset after update ($n)" +retry_quiet 10 zsks_are_published || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# Verify again. +dnssec_verify +no_dnssec_in_journal + +n=$((n+1)) +echo_i "remove dnskey record: - try to remove ns3 ZSK from provider ns3 (should fail) ($n)" +ret=0 +set_server "ns3" "10.53.0.3" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "ns3/${ZONE}.zsk") +echo send +) | $NSUPDATE +# Both ZSKs should still be published. +retry_quiet 10 zsks_are_published || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +n=$((n+1)) +echo_i "remove dnskey record: remove ns4 ZSK from provider ns3 ($n)" +ret=0 +set_server "ns3" "10.53.0.3" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "ns4/${ZONE}.zsk") +echo send +) | $NSUPDATE +check_keys +check_apex +dnssec_verify + +n=$((n+1)) +echo_i "remove dnskey record: try to remove ns4 ZSK from provider ns4 (should fail) ($n)" +ret=0 +set_server "ns4" "10.53.0.4" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "ns4/${ZONE}.zsk") +echo send +) | $NSUPDATE +# Both ZSKs should still be published. +retry_quiet 10 zsks_are_published || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +n=$((n+1)) +echo_i "remove dnskey record: remove ns3 ZSK from provider ns4 ($n)" +ret=0 +set_server "ns4" "10.53.0.4" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "ns3/${ZONE}.zsk") +echo send +) | $NSUPDATE +check_keys +check_apex +dnssec_verify +no_dnssec_in_journal + +# +# Update CDNSKEY RRset. +# + +# Check that the CDNSKEY from both providers are published. +records_published() { + _rrtype=$1 + _expect=$2 + + dig_with_opts "$ZONE" "@${SERVER}" "${_rrtype}" > "dig.out.$DIR.test$n" || return 1 + lines=$(awk -v rt="${_rrtype}" '$4 == rt {print}' dig.out.$DIR.test$n | wc -l) + test "$lines" -eq "$_expect" || return 1 +} + +# Retrieve CDNSKEY records from the other provider. +dig_with_opts ${ZONE} @10.53.0.3 CDNSKEY > dig.out.ns3.cdnskey +awk '$4 == "CDNSKEY" {print}' dig.out.ns3.cdnskey > cdnskey.ns3 +dig_with_opts ${ZONE} @10.53.0.4 CDNSKEY > dig.out.ns4.cdnskey +awk '$4 == "CDNSKEY" {print}' dig.out.ns4.cdnskey > cdnskey.ns4 + +n=$((n+1)) +echo_i "add cdnskey record: update zone ${ZONE} at ns3 with CDNSKEY from provider ns4 ($n)" +ret=0 +set_server "ns3" "10.53.0.3" +# Initially there should be one CDNSKEY. +retry_quiet 10 records_published CDNSKEY 1 || ret=1 +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update add $(cat "cdnskey.ns4") +echo send +) | $NSUPDATE +# Now there should be two CDNSKEY records (we test that BIND does not +# skip it during DNSSEC maintenance). +echo_i "check zone ${ZONE} CDNSKEY RRset after update ($n)" +retry_quiet 10 records_published CDNSKEY 2 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +n=$((n+1)) +echo_i "add cdnskey record: update zone ${ZONE} at ns4 with CDNSKEY from provider ns3 ($n)" +ret=0 +set_server "ns4" "10.53.0.4" +# Initially there should be one CDNSKEY. +retry_quiet 10 records_published CDNSKEY 1 || ret=1 +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update add $(cat "cdnskey.ns3") +echo send +) | $NSUPDATE +# Now there should be two CDNSKEY records (we test that BIND does not +# skip it during DNSSEC maintenance). +echo_i "check zone ${ZONE} CDNSKEY RRset after update ($n)" +retry_quiet 10 records_published CDNSKEY 2 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# No DNSSEC in raw journal. +no_dnssec_in_journal + +n=$((n+1)) +echo_i "remove cdnskey record: remove ns4 CDNSKEY from provider ns3 ($n)" +ret=0 +set_server "ns3" "10.53.0.3" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "cdnskey.ns4") +echo send +) | $NSUPDATE +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# Now there should be one CDNSKEY record again. +echo_i "check zone ${ZONE} CDNSKEY RRset after update ($n)" +retry_quiet 10 records_published CDNSKEY 1 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +n=$((n+1)) +echo_i "remove cdnskey record: remove ns3 CDNSKEY from provider ns4 ($n)" +ret=0 +set_server "ns4" "10.53.0.4" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "cdnskey.ns3") +echo send +) | $NSUPDATE +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# Now there should be one CDNSKEY record again. +echo_i "check zone ${ZONE} CDNSKEY RRset after update ($n)" +retry_quiet 10 records_published CDNSKEY 1 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# No DNSSEC in raw journal. +no_dnssec_in_journal + +# +# Update CDS RRset. +# + +# Retrieve CDS records from the other provider. +dig_with_opts ${ZONE} @10.53.0.3 CDS > dig.out.ns3.cds +awk '$4 == "CDS" {print}' dig.out.ns3.cds > cds.ns3 +dig_with_opts ${ZONE} @10.53.0.4 CDS > dig.out.ns4.cds +awk '$4 == "CDS" {print}' dig.out.ns4.cds > cds.ns4 + +n=$((n+1)) +echo_i "add cds record: update zone ${ZONE} at ns3 with CDS from provider ns4 ($n)" +ret=0 +set_server "ns3" "10.53.0.3" +# Initially there should be one CDS. +retry_quiet 10 records_published CDS 1 || ret=1 +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update add $(cat "cds.ns4") +echo send +) | $NSUPDATE +# Now there should be two CDS records (we test that BIND does not +# skip it during DNSSEC maintenance). +echo_i "check zone ${ZONE} CDS RRset after update ($n)" +retry_quiet 10 records_published CDS 2 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +n=$((n+1)) +echo_i "add cds record: update zone ${ZONE} at ns4 with CDS from provider ns3 ($n)" +ret=0 +set_server "ns4" "10.53.0.4" +# Initially there should be one CDS. +retry_quiet 10 records_published CDS 1 || ret=1 +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update add $(cat "cds.ns3") +echo send +) | $NSUPDATE +# Now there should be two CDS records (we test that BIND does not +# skip it during DNSSEC maintenance). +echo_i "check zone ${ZONE} CDS RRset after update ($n)" +retry_quiet 10 records_published CDS 2 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# No DNSSEC in raw journal. +no_dnssec_in_journal + +n=$((n+1)) +echo_i "remove cds record: remove ns4 CDS from provider ns3 ($n)" +ret=0 +set_server "ns3" "10.53.0.3" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "cds.ns4") +echo send +) | $NSUPDATE +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# Now there should be one CDS record again. +echo_i "check zone ${ZONE} CDS RRset after update ($n)" +retry_quiet 10 records_published CDS 1 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +n=$((n+1)) +echo_i "remove cds record: remove ns3 CDS from provider ns4 ($n)" +ret=0 +set_server "ns4" "10.53.0.4" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "cds.ns3") +echo send +) | $NSUPDATE +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# Now there should be one CDS record again. +echo_i "check zone ${ZONE} CDS RRset after update ($n)" +retry_quiet 10 records_published CDS 1 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# No DNSSEC in raw journal. +no_dnssec_in_journal + +# +# Check secondary server behaviour. +# +set_zone "model2.secondary" +set_policy "model2" "2" "3600" + +set_server "ns3" "10.53.0.3" +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +set_keytimes_model2 +check_keytimes +check_apex +dnssec_verify + +set_server "ns4" "10.53.0.4" +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +set_keytimes_model2 +check_keytimes +check_apex +dnssec_verify + +# +# Update DNSKEY RRset. +# +n=$((n+1)) +echo_i "add dnskey record: update zone ${ZONE} at ns5 with ZSKs from providers ns3 and ns4 ($n)" +ret=0 +set_server "ns5" "10.53.0.5" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update add $(cat "ns3/${ZONE}.zsk") +echo update add $(cat "ns4/${ZONE}.zsk") +echo send +) | $NSUPDATE +# NS3 +set_server "ns3" "10.53.0.3" +echo_i "check server ${DIR} zone ${ZONE} DNSKEY RRset after update ($n)" +retry_quiet 10 zsks_are_published || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +dnssec_verify +no_dnssec_in_journal +# NS4 +set_server "ns4" "10.53.0.4" +echo_i "check server ${DIR} zone ${ZONE} DNSKEY RRset after update ($n)" +retry_quiet 10 zsks_are_published || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +dnssec_verify +no_dnssec_in_journal + +n=$((n+1)) +echo_i "remove dnskey record: remove ns3 and ns4 DNSKEY records from primary ns5 ($n)" +ret=0 +set_server "ns5" "10.53.0.5" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "ns3/${ZONE}.zsk") +echo update del $(cat "ns4/${ZONE}.zsk") +echo send +) | $NSUPDATE +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# Now there should be one DNSKEY record again. +# While we did remove both DNSKEY records, the bump in the wire signer, i.e +# the secondary inline-signing zone, should add back the DNSKEY belonging to +# its own KSK when re-signing the zone. +# +# NS3 +set_server "ns3" "10.53.0.3" +echo_i "check server ${DIR} zone ${ZONE} DNSKEY RRset after update ($n)" +check_keys +check_apex +dnssec_verify +no_dnssec_in_journal +# NS4 +set_server "ns4" "10.53.0.4" +echo_i "check server ${DIR} zone ${ZONE} DNSKEY RRset after update ($n)" +check_keys +check_apex +dnssec_verify +no_dnssec_in_journal + +# +# Update CDNSKEY RRset. +# + +# Retrieve CDNSKEY records from the providers. +n=$((n+1)) +echo_i "check initial CDSNKEY response for zone ${ZONE} at ns3 and ns4 ($n)" +ret=0 +dig_with_opts ${ZONE} @10.53.0.3 CDNSKEY > dig.out.ns3.secondary.cdnskey +awk '$4 == "CDNSKEY" {print}' dig.out.ns3.secondary.cdnskey > secondary.cdnskey.ns3 +dig_with_opts ${ZONE} @10.53.0.4 CDNSKEY > dig.out.ns4.secondary.cdnskey +awk '$4 == "CDNSKEY" {print}' dig.out.ns4.secondary.cdnskey > secondary.cdnskey.ns4 +# Initially there should be one CDNSKEY. +set_server "ns3" "10.53.0.3" +retry_quiet 10 records_published CDNSKEY 1 || ret=1 +set_server "ns4" "10.53.0.4" +retry_quiet 10 records_published CDNSKEY 1 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +n=$((n+1)) +echo_i "add cdnskey record: update zone ${ZONE} at ns5 with CDNSKEY records from providers ns3 and ns4 ($n)" +ret=0 +set_server "ns5" "10.53.0.5" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update add $(cat "secondary.cdnskey.ns3") +echo update add $(cat "secondary.cdnskey.ns4") +echo send +) | $NSUPDATE +# Now there should be two CDNSKEY records (we test that BIND does not +# skip it during DNSSEC maintenance). +# +# NS3 +set_server "ns3" "10.53.0.3" +echo_i "check server ${DIR} zone ${ZONE} CDNSKEY RRset after update ($n)" +retry_quiet 10 records_published CDNSKEY 2 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +dnssec_verify +no_dnssec_in_journal +# NS4 +set_server "ns4" "10.53.0.4" +echo_i "check server ${DIR} zone ${ZONE} CDNSKEY RRset after update ($n)" +retry_quiet 10 records_published CDNSKEY 2 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +dnssec_verify +no_dnssec_in_journal + +n=$((n+1)) +echo_i "remove cdnskey record: remove ns3 and ns4 CDNSKEY records from primary ns5 ($n)" +ret=0 +set_server "ns5" "10.53.0.5" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "secondary.cdnskey.ns3") +echo update del $(cat "secondary.cdnskey.ns4") +echo send +) | $NSUPDATE +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# Now there should be one CDNSKEY record again. +# While we did remove both CDNSKEY records, the bump in the wire signer, i.e +# the secondary inline-signing zone, should add back the CDNSKEY belonging to +# its own KSK when re-signing the zone. +# +# NS3 +set_server "ns3" "10.53.0.3" +echo_i "check server ${DIR} zone ${ZONE} CDNSKEY RRset after update ($n)" +retry_quiet 10 records_published CDNSKEY 1 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +dnssec_verify +no_dnssec_in_journal +# NS4 +set_server "ns4" "10.53.0.4" +echo_i "check server ${DIR} zone ${ZONE} CDNSKEY RRset after update ($n)" +retry_quiet 10 records_published CDNSKEY 1 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +dnssec_verify +no_dnssec_in_journal + +# +# Update CDS RRset. +# + +# Retrieve CDS records from the other provider. +n=$((n+1)) +echo_i "check initial CDS response for zone ${ZONE} at ns3 and ns4 ($n)" +ret=0 +dig_with_opts ${ZONE} @10.53.0.3 CDS > dig.out.ns3.secondary.cds +awk '$4 == "CDS" {print}' dig.out.ns3.secondary.cds > secondary.cds.ns3 +dig_with_opts ${ZONE} @10.53.0.4 CDS > dig.out.ns4.secondary.cds +awk '$4 == "CDS" {print}' dig.out.ns4.secondary.cds > secondary.cds.ns4 +# Initially there should be one CDS. +set_server "ns3" "10.53.0.3" +retry_quiet 10 records_published CDS 1 || ret=1 +set_server "ns4" "10.53.0.4" +retry_quiet 10 records_published CDS 1 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) + +n=$((n+1)) +echo_i "add cds record: update zone ${ZONE} at ns5 with CDS from provider ns4 ($n)" +ret=0 +set_server "ns5" "10.53.0.5" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update add $(cat "secondary.cds.ns3") +echo update add $(cat "secondary.cds.ns4") +echo send +) | $NSUPDATE +# Now there should be two CDS records (we test that BIND does not +# skip it during DNSSEC maintenance). +# +# NS3 +set_server "ns3" "10.53.0.3" +echo_i "check server ${DIR} zone ${ZONE} CDS RRset after update ($n)" +retry_quiet 10 records_published CDS 2 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +dnssec_verify +no_dnssec_in_journal +# NS4 +set_server "ns4" "10.53.0.4" +echo_i "check server ${DIR} zone ${ZONE} CDS RRset after update ($n)" +retry_quiet 10 records_published CDS 2 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +dnssec_verify +no_dnssec_in_journal + +n=$((n+1)) +echo_i "remove cds record: remove ns3 and ns4 CDS records from primary ns5 ($n)" +ret=0 +set_server "ns5" "10.53.0.5" +( +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" +echo update del $(cat "secondary.cds.ns3") +echo update del $(cat "secondary.cds.ns4") +echo send +) | $NSUPDATE +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +# Now there should be one CDS record again. +# While we did remove both CDS records, the bump in the wire signer, i.e +# the secondary inline-signing zone, should add back the CDS belonging to +# its own KSK when re-signing the zone. +# +# NS3 +set_server "ns3" "10.53.0.3" +echo_i "check server ${DIR} zone ${ZONE} CDS RRset after update ($n)" +retry_quiet 10 records_published CDS 1 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +dnssec_verify +no_dnssec_in_journal +# NS4 +set_server "ns4" "10.53.0.4" +echo_i "check server ${DIR} zone ${ZONE} CDS RRset after update ($n)" +retry_quiet 10 records_published CDS 1 || ret=1 +test "$ret" -eq 0 || echo_i "failed" +status=$((status+ret)) +dnssec_verify +no_dnssec_in_journal + +echo_i "exit status: $status" +[ $status -eq 0 ] || exit 1 diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 179d7de495..53a5e83a96 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -20,6 +20,9 @@ Security Fixes New Features ~~~~~~~~~~~~ +- Add support for multi-signer model 2 (RFC 8901) when using + ``inline-signing``. :gl:`#2710` + - A new option to :any:`dnssec-policy` has been added, :any:`cdnskey`, that allows you to enable or disable the publication of CDNSKEY records. :gl:`#4050` diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 034d697eec..ca0bf413f4 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -1835,9 +1835,9 @@ failure: return (result); } -static isc_result_t -make_dnskey(dst_key_t *key, unsigned char *buf, int bufsize, - dns_rdata_t *target) { +isc_result_t +dns_dnssec_make_dnskey(dst_key_t *key, unsigned char *buf, int bufsize, + dns_rdata_t *target) { isc_result_t result; isc_buffer_t b; isc_region_t r; @@ -1893,7 +1893,7 @@ publish_key(dns_diff_t *diff, dns_dnsseckey_t *key, const dns_name_t *origin, dns_rdata_t dnskey = DNS_RDATA_INIT; dns_rdata_reset(&dnskey); - RETERR(make_dnskey(key->key, buf, sizeof(buf), &dnskey)); + RETERR(dns_dnssec_make_dnskey(key->key, buf, sizeof(buf), &dnskey)); dst_key_format(key->key, keystr, sizeof(keystr)); report("Fetching %s (%s) from key %s.", keystr, @@ -1933,7 +1933,7 @@ remove_key(dns_diff_t *diff, dns_dnsseckey_t *key, const dns_name_t *origin, report("Removing %s key %s/%d/%s from DNSKEY RRset.", reason, namebuf, dst_key_id(key->key), alg); - RETERR(make_dnskey(key->key, buf, sizeof(buf), &dnskey)); + RETERR(dns_dnssec_make_dnskey(key->key, buf, sizeof(buf), &dnskey)); result = delrdata(&dnskey, diff, origin, ttl, mctx); failure: @@ -2045,8 +2045,8 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, dns_rdata_t cdnskeyrdata = DNS_RDATA_INIT; dns_name_t *origin = dst_key_name(key->key); - RETERR(make_dnskey(key->key, keybuf, sizeof(keybuf), - &cdnskeyrdata)); + RETERR(dns_dnssec_make_dnskey(key->key, keybuf, sizeof(keybuf), + &cdnskeyrdata)); cdnskeyrdata.type = dns_rdatatype_cdnskey; if (syncpublish(key->key, now)) { @@ -2127,8 +2127,8 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, char keystr[DST_KEY_FORMATSIZE]; dst_key_format(key->key, keystr, sizeof(keystr)); - RETERR(make_dnskey(key->key, keybuf, sizeof(keybuf), - &cdnskeyrdata)); + RETERR(dns_dnssec_make_dnskey(key->key, keybuf, sizeof(keybuf), + &cdnskeyrdata)); if (dns_rdataset_isassociated(cds)) { delete_cds(key, &cdnskeyrdata, (const char *)keystr, diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h index eb754bf459..903d40c4f7 100644 --- a/lib/dns/include/dns/dnssec.h +++ b/lib/dns/include/dns/dnssec.h @@ -95,6 +95,23 @@ dns_dnssec_keyfromrdata(const dns_name_t *name, const dns_rdata_t *rdata, *\li various errors from dns_name_totext */ +isc_result_t +dns_dnssec_make_dnskey(dst_key_t *key, unsigned char *buf, int bufsize, + dns_rdata_t *target); +/*%< + * Convert a DST key into a DNS record. + * + * Requires: + *\li 'key' is not NULL + *\li 'buf' is not NULL + *\li 'bufsize' equals DST_KEY_MAXSIZE + *\li 'target' is not NULL + * + * Returns: + *\li #ISC_R_SUCCESS + *\li various errors from dst_key_todns + */ + isc_result_t dns_dnssec_sign(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, isc_stdtime_t *inception, isc_stdtime_t *expire, diff --git a/lib/dns/include/dns/rdata.h b/lib/dns/include/dns/rdata.h index 1ff135e697..36f79c8edd 100644 --- a/lib/dns/include/dns/rdata.h +++ b/lib/dns/include/dns/rdata.h @@ -565,6 +565,13 @@ dns_rdatatype_isdnssec(dns_rdatatype_t type); * \li 'type' is a valid rdata type. */ +bool +dns_rdatatype_iskeymaterial(dns_rdatatype_t type); +/*%< + * Return true iff the rdata type 'type' is a DNSSEC key + * related type, like DNSKEY, CDNSKEY, or CDS. + */ + bool dns_rdatatype_iszonecutauth(dns_rdatatype_t type); /*%< diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index db67203797..7041523702 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -395,6 +395,24 @@ dns_zone_unlock_keyfiles(dns_zone_t *zone); *\li 'zone' to be a valid zone. */ +isc_result_t +dns_zone_dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, bool *inuse); +/*%< + * Check if the DNSKEY record 'rdata' is used by 'zone' for zone signing. + * Store the result in 'inuse'. + * + * Require: + *\li 'zone' to be a valid zone. + *\li 'rdata' to represent a DNSKEY, CDNSKEY, or CDS record. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li Any error result from dns_dnssec_keyfromrdata, dns_rdata_tostruct, + * dns_dnssec_make_dnskey, dns_ds_buildrdata, or + * dns_dnssec_findmatchingkeys. + * + */ + isc_result_t dns_zone_load(dns_zone_t *zone, bool newonly); @@ -2299,6 +2317,12 @@ dns_zone_link(dns_zone_t *zone, dns_zone_t *raw); void dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw); +bool +dns_zone_israw(dns_zone_t *zone); + +bool +dns_zone_issecure(dns_zone_t *zone); + isc_result_t dns_zone_keydone(dns_zone_t *zone, const char *data); /*%< diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c index d90ef212f4..13218d069c 100644 --- a/lib/dns/rdata.c +++ b/lib/dns/rdata.c @@ -2272,6 +2272,12 @@ dns_rdatatype_isdnssec(dns_rdatatype_t type) { return (false); } +bool +dns_rdatatype_iskeymaterial(dns_rdatatype_t type) { + return (type == dns_rdatatype_dnskey || type == dns_rdatatype_cdnskey || + type == dns_rdatatype_cds); +} + bool dns_rdatatype_iszonecutauth(dns_rdatatype_t type) { if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_ZONECUTAUTH) != diff --git a/lib/dns/update.c b/lib/dns/update.c index c547c041a8..43a30a311f 100644 --- a/lib/dns/update.c +++ b/lib/dns/update.c @@ -1201,10 +1201,7 @@ add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, } } - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || - type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { /* * DNSKEY RRset is signed with KSK. * CDS and CDNSKEY RRsets too (RFC 7344, 4.1). @@ -1238,10 +1235,7 @@ add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, /* * CDS and CDNSKEY are signed with KSK (RFC 7344, 4.1). */ - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || - type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { if (!KSK(keys[i]) && keyset_kskonly) { continue; } @@ -1670,10 +1664,7 @@ next_state: &flag)); if (flag) { isc_stdtime_t exp; - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || - type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { exp = state->keyexpire; } else if (type == dns_rdatatype_soa) { exp = state->soaexpire; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index be7eb70c90..8ea68a6549 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -5739,9 +5739,19 @@ dns_zone_setdefaultkasp(dns_zone_t *zone, dns_kasp_t *kasp) { dns_kasp_t * dns_zone_getkasp(dns_zone_t *zone) { + dns_kasp_t *kasp; + REQUIRE(DNS_ZONE_VALID(zone)); - return (zone->kasp); + LOCK_ZONE(zone); + if (inline_raw(zone) && zone->secure != NULL) { + kasp = zone->secure->kasp; + } else { + kasp = zone->kasp; + } + UNLOCK_ZONE(zone); + + return (kasp); } void @@ -6108,7 +6118,7 @@ dns_zone_getdnsseckeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_dnsseckey_t *key, *key_next; dns_dnsseckeylist_t dnskeys; dns_name_t *origin = dns_zone_getorigin(zone); - dns_kasp_t *kasp = dns_zone_getkasp(zone); + dns_kasp_t *kasp = zone->kasp; dns_rdataset_t keyset; REQUIRE(DNS_ZONE_VALID(zone)); @@ -6335,7 +6345,7 @@ del_sigs(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_rdataset_t rdataset; unsigned int i; dns_rdata_rrsig_t rrsig; - bool kasp = (dns_zone_getkasp(zone) != NULL); + bool kasp = zone->kasp; bool found; int64_t timewarn = 0, timemaybe = 0; @@ -6374,9 +6384,7 @@ del_sigs(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, result = dns_rdata_tostruct(&rdata, &rrsig, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); - if (type != dns_rdatatype_dnskey && type != dns_rdatatype_cds && - type != dns_rdatatype_cdnskey) - { + if (!dns_rdatatype_iskeymaterial(type)) { bool warn = false, deleted = false; if (delsig_ok(&rrsig, keys, nkeys, kasp, &warn)) { result = update_one_rr(db, ver, zonediff->diff, @@ -6536,7 +6544,7 @@ add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_zone_t *zone, unsigned int i, j; bool use_kasp = false; - if (dns_zone_getkasp(zone) != NULL) { + if (zone->kasp != NULL) { check_ksk = false; keyset_kskonly = true; use_kasp = true; @@ -6693,10 +6701,7 @@ add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_zone_t *zone, both = have_ksk && have_zsk; } - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || - type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { /* * DNSKEY RRset is signed with KSK. * CDS and CDNSKEY RRsets too (RFC 7344, 4.1). @@ -6736,10 +6741,7 @@ add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_zone_t *zone, /* * CDS and CDNSKEY are signed with KSK (RFC 7344, 4.1). */ - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || - type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { if (!KSK(keys[i]) && keyset_kskonly) { continue; } @@ -7095,7 +7097,7 @@ signed_with_good_key(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node, dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_rrsig_t rrsig; int count = 0; - dns_kasp_t *kasp = dns_zone_getkasp(zone); + dns_kasp_t *kasp = zone->kasp; dns_rdataset_init(&rdataset); result = dns_db_findrdataset(db, node, version, dns_rdatatype_rrsig, @@ -7122,7 +7124,7 @@ signed_with_good_key(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node, dns_rdata_reset(&rdata); } - if (dns_zone_getkasp(zone) != NULL) { + if (zone->kasp != NULL) { dns_kasp_key_t *kkey; int zsk_count = 0; bool approved; @@ -7140,9 +7142,7 @@ signed_with_good_key(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node, } KASP_UNLOCK(kasp); - if (type == dns_rdatatype_dnskey || - type == dns_rdatatype_cdnskey || type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(type)) { /* * CDS and CDNSKEY are signed with KSK like DNSKEY. * (RFC 7344, section 4.1 specifies that they must @@ -7317,10 +7317,7 @@ sign_a_node(dns_db_t *db, dns_zone_t *zone, dns_name_t *name, { goto next_rdataset; } - if (rdataset.type == dns_rdatatype_dnskey || - rdataset.type == dns_rdatatype_cdnskey || - rdataset.type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(rdataset.type)) { /* * CDS and CDNSKEY are signed with KSK like DNSKEY. * (RFC 7344, section 4.1 specifies that they must @@ -7336,7 +7333,7 @@ sign_a_node(dns_db_t *db, dns_zone_t *zone, dns_name_t *name, inception, &when)) { /* Only applies to dnssec-policy. */ - if (dns_zone_getkasp(zone) != NULL) { + if (zone->kasp != NULL) { goto next_rdataset; } } @@ -7934,9 +7931,7 @@ dns__zone_updatesigs(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *version, isc_stdtime_t exp = expire; if (keyexpire != 0 && - (tuple->rdata.type == dns_rdatatype_dnskey || - tuple->rdata.type == dns_rdatatype_cdnskey || - tuple->rdata.type == dns_rdatatype_cds)) + dns_rdatatype_iskeymaterial(tuple->rdata.type)) { exp = keyexpire; } @@ -9057,7 +9052,7 @@ dns_zone_check_dnskey_nsec3(dns_zone_t *zone, dns_db_t *db, /* Check kasp for NSEC3PARAM settings */ if (!nsec3) { - dns_kasp_t *kasp = dns_zone_getkasp(zone); + dns_kasp_t *kasp = zone->kasp; if (kasp != NULL) { nsec3 = dns_kasp_nsec3(kasp); } @@ -9157,7 +9152,7 @@ zone_sign(dns_zone_t *zone) { goto cleanup; } - kasp = dns_zone_getkasp(zone); + kasp = zone->kasp; sigvalidityinterval = dns_zone_getsigvalidityinterval(zone); inception = now - 3600; /* Allow for clock skew. */ soaexpire = now + sigvalidityinterval; @@ -9194,7 +9189,7 @@ zone_sign(dns_zone_t *zone) { signing = ISC_LIST_HEAD(zone->signing); first = true; - if (dns_zone_getkasp(zone) != NULL) { + if (kasp != NULL) { check_ksk = false; keyset_kskonly = true; use_kasp = true; @@ -15850,6 +15845,184 @@ update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) { dns_zone_log(zone, level, "%s", message); } +static isc_result_t +dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, isc_mem_t *mctx, + dns_dnsseckeylist_t *keylist, bool *inuse) { + isc_result_t result; + dst_key_t *dstkey = NULL; + + result = dns_dnssec_keyfromrdata(dns_zone_getorigin(zone), rdata, mctx, + &dstkey); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_dnssec_keyfromrdata() failed: %s", + isc_result_totext(result)); + return (result); + } + + for (dns_dnsseckey_t *k = ISC_LIST_HEAD(*keylist); k != NULL; + k = ISC_LIST_NEXT(k, link)) + { + if (dst_key_pubcompare(k->key, dstkey, false)) { + *inuse = true; + break; + } + } + + dst_key_free(&dstkey); + return (ISC_R_SUCCESS); +} + +static isc_result_t +cdnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, + dns_dnsseckeylist_t *keylist, bool *inuse) { + isc_result_t result; + dns_rdata_cdnskey_t cdnskey; + + result = dns_rdata_tostruct(rdata, &cdnskey, NULL); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_rdata_tostruct(cdnskey) failed: %s", + isc_result_totext(result)); + return (result); + } + + for (dns_dnsseckey_t *k = ISC_LIST_HEAD(*keylist); k != NULL; + k = ISC_LIST_NEXT(k, link)) + { + dns_rdata_t cdnskeyrdata = DNS_RDATA_INIT; + unsigned char keybuf[DST_KEY_MAXSIZE]; + + result = dns_dnssec_make_dnskey(k->key, keybuf, sizeof(keybuf), + &cdnskeyrdata); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_dnssec_make_dnskey() failed: %s", + isc_result_totext(result)); + return (result); + } + + cdnskeyrdata.type = dns_rdatatype_cdnskey; + if (dns_rdata_compare(rdata, &cdnskeyrdata) == 0) { + *inuse = true; + break; + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +cds_inuse(dns_zone_t *zone, dns_rdata_t *rdata, dns_dnsseckeylist_t *keylist, + bool *inuse) { + isc_result_t result; + dns_rdata_ds_t cds; + + result = dns_rdata_tostruct(rdata, &cds, NULL); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_rdata_tostruct(cds) failed: %s", + isc_result_totext(result)); + return (result); + } + + for (dns_dnsseckey_t *k = ISC_LIST_HEAD(*keylist); k != NULL; + k = ISC_LIST_NEXT(k, link)) + { + dns_rdata_t dnskey = DNS_RDATA_INIT; + dns_rdata_t cdsrdata = DNS_RDATA_INIT; + unsigned char keybuf[DST_KEY_MAXSIZE]; + unsigned char cdsbuf[DNS_DS_BUFFERSIZE]; + + if (dst_key_id(k->key) != cds.key_tag || + dst_key_alg(k->key) != cds.algorithm) + { + continue; + } + result = dns_dnssec_make_dnskey(k->key, keybuf, sizeof(keybuf), + &dnskey); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_dnssec_make_dnskey() failed: %s", + isc_result_totext(result)); + return (result); + } + result = dns_ds_buildrdata(dns_zone_getorigin(zone), &dnskey, + cds.digest_type, cdsbuf, &cdsrdata); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_ds_buildrdata(keytag=%d, algo=%d, " + "digest=%d) failed: %s", + cds.key_tag, cds.algorithm, + cds.digest_type, + isc_result_totext(result)); + return (result); + } + + cdsrdata.type = dns_rdatatype_cds; + if (dns_rdata_compare(rdata, &cdsrdata) == 0) { + *inuse = true; + break; + } + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_zone_dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, bool *inuse) { + dns_dnsseckeylist_t keylist; + dns_dnsseckey_t *key = NULL; + isc_result_t result = ISC_R_SUCCESS; + isc_stdtime_t now = isc_stdtime_now(); + isc_mem_t *mctx; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(dns_rdatatype_iskeymaterial(rdata->type)); + + mctx = zone->mctx; + + ISC_LIST_INIT(keylist); + + *inuse = false; + + dns_zone_lock_keyfiles(zone); + result = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone), + dns_zone_getkeydirectory(zone), + now, mctx, &keylist); + dns_zone_unlock_keyfiles(zone); + if (result == ISC_R_NOTFOUND) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_dnssec_findmatchingkeys() failed: %s", + isc_result_totext(result)); + return (result); + } + + switch (rdata->type) { + case dns_rdatatype_dnskey: + result = dnskey_inuse(zone, rdata, mctx, &keylist, inuse); + break; + case dns_rdatatype_cdnskey: + result = cdnskey_inuse(zone, rdata, &keylist, inuse); + break; + case dns_rdatatype_cds: + result = cds_inuse(zone, rdata, &keylist, inuse); + break; + default: + UNREACHABLE(); + break; + } + + while (!ISC_LIST_EMPTY(keylist)) { + key = ISC_LIST_HEAD(keylist); + ISC_LIST_UNLINK(keylist, key, link); + dns_dnsseckey_destroy(mctx, &key); + } + return (result); +} + static isc_result_t sync_secure_journal(dns_zone_t *zone, dns_zone_t *raw, dns_journal_t *journal, uint32_t start, uint32_t end, dns_difftuple_t **soatuplep, @@ -15906,14 +16079,29 @@ sync_secure_journal(dns_zone_t *zone, dns_zone_t *raw, dns_journal_t *journal, continue; } + /* + * Skip DNSSEC records that BIND maintains with inline-signing. + */ if (rdata->type == dns_rdatatype_nsec || rdata->type == dns_rdatatype_rrsig || rdata->type == dns_rdatatype_nsec3 || - rdata->type == dns_rdatatype_dnskey || rdata->type == dns_rdatatype_nsec3param) { continue; } + /* + * Allow DNSKEY, CDNSKEY, CDS because users should be able to + * update the zone with these records from a different provider, + * but skip records that are under our control. + */ + if (dns_rdatatype_iskeymaterial(rdata->type)) { + bool inuse = false; + isc_result_t r = dns_zone_dnskey_inuse(zone, rdata, + &inuse); + if (r == ISC_R_SUCCESS && inuse) { + continue; + } + } op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD; @@ -15959,9 +16147,11 @@ sync_secure_db(dns_zone_t *seczone, dns_zone_t *raw, dns_db_t *secdb, for (tuple = ISC_LIST_HEAD(diff->tuples); tuple != NULL; tuple = next) { next = ISC_LIST_NEXT(tuple, link); + /* + * Skip DNSSEC records that BIND maintains with inline-signing. + */ if (tuple->rdata.type == dns_rdatatype_nsec || tuple->rdata.type == dns_rdatatype_rrsig || - tuple->rdata.type == dns_rdatatype_dnskey || tuple->rdata.type == dns_rdatatype_nsec3 || tuple->rdata.type == dns_rdatatype_nsec3param) { @@ -15969,6 +16159,23 @@ sync_secure_db(dns_zone_t *seczone, dns_zone_t *raw, dns_db_t *secdb, dns_difftuple_free(&tuple); continue; } + + /* + * Allow DNSKEY, CDNSKEY, CDS because users should be able to + * update the zone with these records from a different provider, + * but skip records that are under our control. + */ + if (dns_rdatatype_iskeymaterial(tuple->rdata.type)) { + bool inuse = false; + isc_result_t r = dns_zone_dnskey_inuse( + seczone, &tuple->rdata, &inuse); + if (r == ISC_R_SUCCESS && inuse) { + ISC_LIST_UNLINK(diff->tuples, tuple, link); + dns_difftuple_free(&tuple); + continue; + } + } + if (tuple->rdata.type == dns_rdatatype_soa) { if (tuple->op == DNS_DIFFOP_DEL) { INSIST(oldtuple == NULL); @@ -19710,7 +19917,7 @@ make_dnskey(dst_key_t *key, unsigned char *buf, int bufsize, static bool do_checkds(dns_zone_t *zone, dst_key_t *key, isc_stdtime_t now, bool dspublish) { - dns_kasp_t *kasp = dns_zone_getkasp(zone); + dns_kasp_t *kasp = zone->kasp; const char *dir = dns_zone_getkeydirectory(zone); isc_result_t result; uint32_t count = 0; @@ -20923,7 +21130,7 @@ zone_rekey(dns_zone_t *zone) { timenow = isc_time_now(); now = isc_time_seconds(&timenow); - kasp = dns_zone_getkasp(zone); + kasp = zone->kasp; dnssec_log(zone, ISC_LOG_INFO, "reconfiguring zone keys"); @@ -21961,6 +22168,26 @@ dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw) { UNLOCK(&zone->lock); } +bool +dns_zone_israw(dns_zone_t *zone) { + bool israw; + REQUIRE(DNS_ZONE_VALID(zone)); + LOCK(&zone->lock); + israw = zone->secure != NULL; + UNLOCK(&zone->lock); + return (israw); +} + +bool +dns_zone_issecure(dns_zone_t *zone) { + bool issecure; + REQUIRE(DNS_ZONE_VALID(zone)); + LOCK(&zone->lock); + issecure = zone->raw != NULL; + UNLOCK(&zone->lock); + return (issecure); +} + struct keydone { bool all; unsigned char data[5]; diff --git a/lib/ns/query.c b/lib/ns/query.c index 5d223e5483..99fee3e775 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -11989,9 +11989,7 @@ ns_query_start(ns_client_t *client, isc_nmhandle_t *handle) { /* * Turn on minimal response for (C)DNSKEY and (C)DS queries. */ - if (qtype == dns_rdatatype_dnskey || qtype == dns_rdatatype_ds || - qtype == dns_rdatatype_cdnskey || qtype == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(qtype) || qtype == dns_rdatatype_ds) { client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | NS_QUERYATTR_NOADDITIONAL); } else if (qtype == dns_rdatatype_ns) { diff --git a/lib/ns/update.c b/lib/ns/update.c index bd3d3af3d4..3103263366 100644 --- a/lib/ns/update.c +++ b/lib/ns/update.c @@ -2900,6 +2900,7 @@ update_action(void *arg) { dns_ttl_t maxttl = 0; uint32_t maxrecords; uint64_t records; + bool is_inline, is_maintain, is_signing; dns_diff_init(mctx, &diff); dns_diff_init(mctx, &temp); @@ -2910,6 +2911,10 @@ update_action(void *arg) { dns_zone_getssutable(zone, &ssutable); options = dns_zone_getoptions(zone); + is_inline = (!dns_zone_israw(zone) && dns_zone_issecure(zone)); + is_maintain = ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0); + is_signing = is_inline || (!is_inline && is_maintain); + /* * Get old and new versions now that queryacl has been checked. */ @@ -3377,6 +3382,27 @@ update_action(void *arg) { continue; } } + /* + * Don't remove DNSKEY, CDNSKEY, CDS records + * that are in use (under our control). + */ + if (dns_rdatatype_iskeymaterial(rdata.type)) { + isc_result_t r; + bool inuse = false; + r = dns_zone_dnskey_inuse(zone, &rdata, + &inuse); + if (r != ISC_R_SUCCESS) { + FAIL(r); + } + if (inuse) { + update_log(client, zone, + LOGLEVEL_PROTOCOL, + "attempt to " + "delete in use " + "DNSKEY ignored"); + continue; + } + } } dns_name_format(name, namestr, sizeof(namestr)); dns_rdatatype_format(rdata.type, typestr, @@ -3411,7 +3437,7 @@ update_action(void *arg) { goto failure; } } - if (!ISC_LIST_EMPTY(diff.tuples)) { + if (!ISC_LIST_EMPTY(diff.tuples) && is_signing) { result = dns_zone_cdscheck(zone, db, ver); if (result == DNS_R_BADCDS || result == DNS_R_BADCDNSKEY) { update_log(client, zone, LOGLEVEL_PROTOCOL, @@ -3457,11 +3483,13 @@ update_action(void *arg) { CHECK(rollback_private(db, privatetype, ver, &diff)); - CHECK(add_signing_records(db, privatetype, ver, &diff)); + if (is_signing) { + CHECK(add_signing_records(db, privatetype, ver, &diff)); + } CHECK(add_nsec3param_records(client, zone, db, ver, &diff)); - if (had_dnskey && !has_dnskey) { + if (is_signing && had_dnskey && !has_dnskey) { /* * We are transitioning from secure to insecure. * Cause all NSEC3 chains to be deleted. When the