From 4e18991fedf6cff15292885eaa482869311e10b4 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 4 Oct 2022 15:42:03 +0200 Subject: [PATCH 01/13] Add multisigner system test Add a new system test to test multisigner model use cases. This initial test just tests a small part of the model 2, and uses two providers for the same zone, ns3 and ns4, each with their own unique key set. This commit tests that each provider can import their ZSK of the other provider into their DNSKEY RRset, using dynamic update. Both providers use dnssec-policy, ns3 applies the DNSSEC records directly, while ns4 uses inline-signing. --- bin/tests/system/Makefile.am | 2 +- bin/tests/system/conf.sh.in | 1 + bin/tests/system/multisigner/clean.sh | 35 +++++ bin/tests/system/multisigner/kasp.conf | 19 +++ .../multisigner/ns3/model2.multisigner.db | 27 ++++ .../system/multisigner/ns3/named.conf.in | 46 +++++++ bin/tests/system/multisigner/ns3/setup.sh | 31 +++++ .../multisigner/ns4/model2.multisigner.db | 26 ++++ .../system/multisigner/ns4/named.conf.in | 46 +++++++ bin/tests/system/multisigner/ns4/setup.sh | 31 +++++ bin/tests/system/multisigner/setup.sh | 31 +++++ bin/tests/system/multisigner/tests.sh | 129 ++++++++++++++++++ 12 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 bin/tests/system/multisigner/clean.sh create mode 100644 bin/tests/system/multisigner/kasp.conf create mode 100644 bin/tests/system/multisigner/ns3/model2.multisigner.db create mode 100644 bin/tests/system/multisigner/ns3/named.conf.in create mode 100644 bin/tests/system/multisigner/ns3/setup.sh create mode 100644 bin/tests/system/multisigner/ns4/model2.multisigner.db create mode 100644 bin/tests/system/multisigner/ns4/named.conf.in create mode 100644 bin/tests/system/multisigner/ns4/setup.sh create mode 100644 bin/tests/system/multisigner/setup.sh create mode 100644 bin/tests/system/multisigner/tests.sh 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/multisigner/clean.sh b/bin/tests/system/multisigner/clean.sh new file mode 100644 index 0000000000..0cd0a18f7d --- /dev/null +++ b/bin/tests/system/multisigner/clean.sh @@ -0,0 +1,35 @@ +#!/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 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*/*.signed +rm -f ns*/*.signed.jnl +rm -f ns*/*.zsk +rm -f ns*/K* +rm -f ns*/keygen.out.* +rm -f ns*/managed-keys* +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..893e79d87b --- /dev/null +++ b/bin/tests/system/multisigner/ns3/named.conf.in @@ -0,0 +1,46 @@ +/* + * 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; +}; diff --git a/bin/tests/system/multisigner/ns3/setup.sh b/bin/tests/system/multisigner/ns3/setup.sh new file mode 100644 index 0000000000..54ee7dcb45 --- /dev/null +++ b/bin/tests/system/multisigner/ns3/setup.sh @@ -0,0 +1,31 @@ +#!/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" + +zone="model2.multisigner" +echo_i "setting up zone: $zone" +zonefile="${zone}.db" + +O="OMNIPRESENT" +ksktimes="-P now -A now -P sync now" +zsktimes="-P now -A now" +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..ba1f6b85fa --- /dev/null +++ b/bin/tests/system/multisigner/ns4/named.conf.in @@ -0,0 +1,46 @@ +/* + * 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; +}; diff --git a/bin/tests/system/multisigner/ns4/setup.sh b/bin/tests/system/multisigner/ns4/setup.sh new file mode 100644 index 0000000000..05d1060be4 --- /dev/null +++ b/bin/tests/system/multisigner/ns4/setup.sh @@ -0,0 +1,31 @@ +#!/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" + +zone="model2.multisigner" +echo_i "setting up zone: $zone" +zonefile="${zone}.db" + +O="OMNIPRESENT" +ksktimes="-P now -A now -P sync now" +zsktimes="-P now -A now" +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/setup.sh b/bin/tests/system/multisigner/setup.sh new file mode 100644 index 0000000000..a28917fdd9 --- /dev/null +++ b/bin/tests/system/multisigner/setup.sh @@ -0,0 +1,31 @@ +#!/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 + +( + cd ns3 + $SHELL setup.sh +) +( + cd ns4 + $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..f1a477f100 --- /dev/null +++ b/bin/tests/system/multisigner/tests.sh @@ -0,0 +1,129 @@ +#!/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 + +# 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 + # We should have two ZSKs. + lines=$(grep "256 3 13" dig.out.$DIR.test$n | wc -l) + test "$lines" -eq 2 || return 1 + # And one KSK. + lines=$(grep "257 3 13" dig.out.$DIR.test$n | wc -l) + test "$lines" -eq 1 || return 1 +} + +n=$((n+1)) +echo_i "update zone ${ZONE} at ns3 with ZSK from provider ns4" +ret=0 +( +echo zone ${ZONE} +echo server 10.53.0.3 "$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 + +set_server "ns4" "10.53.0.4" +check_keys +check_dnssecstatus "$SERVER" "$POLICY" "$ZONE" +set_keytimes_model2 +check_keytimes +check_apex +dnssec_verify + +n=$((n+1)) +echo_i "update zone ${ZONE} at ns4 with ZSK from provider ns3" +ret=0 +( +echo zone ${ZONE} +echo server 10.53.0.4 "$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 + +echo_i "exit status: $status" +[ $status -eq 0 ] || exit 1 From 3b6e9a5fa77c9655faa1bc94abc520f40dd65bf2 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 3 Mar 2023 14:19:14 +0100 Subject: [PATCH 02/13] Add function to check if a DNSKEY record is in use Add a function that checks whether a DNSKEY, CDNSKEY, or CDS record belongs to a key that is being used for signing. --- lib/dns/include/dns/zone.h | 18 ++++ lib/dns/zone.c | 179 +++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index db67203797..c431f38703 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); diff --git a/lib/dns/zone.c b/lib/dns/zone.c index be7eb70c90..ae96df07ab 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15850,6 +15850,185 @@ 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; + } + +out: + 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, From b0b3e2f12e995718174f382b352a22f9a8ef1d67 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 5 Oct 2022 13:36:42 +0200 Subject: [PATCH 03/13] Allow DNSKEY when syncing secure journal/db When synchronizing the journal or database from the unsigned version of the zone to the secure version of the zone, allow DNSKEY records to be synced, because these may be added by the user with the sole intent to publish the record (not used for signing). This may be the case for example in the multisigner model 2 (RFC 8901). Additional code needs to be added to ensure that we do not remove DNSKEY records that are under our control. Keys under our control are keys that are used for signing the zone and thus that we have key files for. Same counts for CDNSKEY and CDS (records that are derived from keys). --- lib/dns/zone.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- lib/ns/update.c | 24 ++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/lib/dns/zone.c b/lib/dns/zone.c index ae96df07ab..c18fe4bff8 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -16020,7 +16020,6 @@ dns_zone_dnskey_inuse(dns_zone_t *zone, dns_rdata_t *rdata, bool *inuse) { break; } -out: while (!ISC_LIST_EMPTY(keylist)) { key = ISC_LIST_HEAD(keylist); ISC_LIST_UNLINK(keylist, key, link); @@ -16085,14 +16084,32 @@ 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 (rdata->type == dns_rdatatype_dnskey || + rdata->type == dns_rdatatype_cdnskey || + rdata->type == dns_rdatatype_cds) + { + 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; @@ -16138,9 +16155,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) { @@ -16148,6 +16167,26 @@ 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 (tuple->rdata.type == dns_rdatatype_dnskey || + tuple->rdata.type == dns_rdatatype_cdnskey || + tuple->rdata.type == dns_rdatatype_cds) + { + 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); diff --git a/lib/ns/update.c b/lib/ns/update.c index bd3d3af3d4..493637f147 100644 --- a/lib/ns/update.c +++ b/lib/ns/update.c @@ -3377,6 +3377,30 @@ update_action(void *arg) { continue; } } + /* + * Don't remove DNSKEY, CDNSKEY, CDS records + * that are in use (under our control). + */ + if (rdata.type == dns_rdatatype_dnskey || + rdata.type == dns_rdatatype_cdnskey || + rdata.type == dns_rdatatype_cds) + { + 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, From b493c8505e5579b70a2da6882e9c1801595e031c Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 5 Oct 2022 16:22:37 +0200 Subject: [PATCH 04/13] Fix dns_zone_getkasp() function For inline-signing zones, sometimes kasp was not detected because the function was called on the raw (unsigned) version of the zone, but the kasp is only set on the secure (signed) version of the zone. Fix the dns_zone_getkasp() function to check whether the zone structure is inline_raw(), and if so, use the kasp from the secure version. In zone.c we can access the kasp pointer directly. --- lib/dns/zone.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/dns/zone.c b/lib/dns/zone.c index c18fe4bff8..922cdd3334 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; @@ -6536,7 +6546,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; @@ -7095,7 +7105,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 +7132,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; @@ -7336,7 +7346,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; } } @@ -9057,7 +9067,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 +9167,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 +9204,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; @@ -19928,7 +19938,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; @@ -21141,7 +21151,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"); From a565cb9c31085634dfb52105b9503d9c38e196b7 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 5 Oct 2022 17:11:50 +0200 Subject: [PATCH 05/13] Add more multisigner tests A zone in multisigner model 2 should also be possible to publish the CDS and CDNSKEY records from their KSK into the zone operated by the other provider. --- bin/tests/system/multisigner/clean.sh | 2 + bin/tests/system/multisigner/tests.sh | 140 ++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 11 deletions(-) diff --git a/bin/tests/system/multisigner/clean.sh b/bin/tests/system/multisigner/clean.sh index 0cd0a18f7d..fb75cfe407 100644 --- a/bin/tests/system/multisigner/clean.sh +++ b/bin/tests/system/multisigner/clean.sh @@ -14,6 +14,8 @@ set -e rm -f *.created +rm -f cdnskey.ns* +rm -f cds.ns* rm -f created.key-* rm -f dig.out.* rm -f python.out.* diff --git a/bin/tests/system/multisigner/tests.sh b/bin/tests/system/multisigner/tests.sh index f1a477f100..6c1ac20b8f 100644 --- a/bin/tests/system/multisigner/tests.sh +++ b/bin/tests/system/multisigner/tests.sh @@ -74,6 +74,19 @@ 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 @@ -88,9 +101,10 @@ zsks_are_published() { n=$((n+1)) echo_i "update zone ${ZONE} at ns3 with ZSK from provider ns4" ret=0 +set_server "ns3" "10.53.0.3" ( -echo zone ${ZONE} -echo server 10.53.0.3 "$PORT" +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" echo update add $(cat "ns4/${ZONE}.zsk") echo send ) | $NSUPDATE @@ -101,20 +115,14 @@ status=$((status+ret)) # Verify again. 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 n=$((n+1)) echo_i "update zone ${ZONE} at ns4 with ZSK from provider ns3" ret=0 +set_server "ns4" "10.53.0.4" ( -echo zone ${ZONE} -echo server 10.53.0.4 "$PORT" +echo zone "${ZONE}" +echo server "${SERVER}" "${PORT}" echo update add $(cat "ns3/${ZONE}.zsk") echo send ) | $NSUPDATE @@ -125,5 +133,115 @@ status=$((status+ret)) # Verify again. dnssec_verify + +# +# 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 "update zone ${ZONE} at ns3 with CDNSKEY from provider ns4" +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 "update zone ${ZONE} at ns4 with CDNSKEY from provider ns3" +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)) + + +# +# 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 "update zone ${ZONE} at ns3 with CDS from provider ns4" +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 "update zone ${ZONE} at ns4 with CDS from provider ns3" +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)) + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 From 7a39651da55d189dc25cd7b8583f16a78d8fd44f Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 5 Oct 2022 16:32:35 +0200 Subject: [PATCH 06/13] Add notes and change entry --- CHANGES | 4 +++- doc/notes/notes-current.rst | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) 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/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` From 508c60ad90aad316fc1cb3729da588cab0510a45 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 13 Oct 2022 09:09:12 +0200 Subject: [PATCH 07/13] Ensure no DNSSEC records are in the raw journal Add checks to the multisigner test to make sure no DNSSEC related records (NSEC, NSEC3, NSEC3PARAM, RRSIG) end up in the raw journal. --- bin/tests/system/multisigner/clean.sh | 1 + bin/tests/system/multisigner/tests.sh | 45 ++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/bin/tests/system/multisigner/clean.sh b/bin/tests/system/multisigner/clean.sh index fb75cfe407..0dd6eb6d48 100644 --- a/bin/tests/system/multisigner/clean.sh +++ b/bin/tests/system/multisigner/clean.sh @@ -25,6 +25,7 @@ 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 diff --git a/bin/tests/system/multisigner/tests.sh b/bin/tests/system/multisigner/tests.sh index 6c1ac20b8f..d6e56f9c8c 100644 --- a/bin/tests/system/multisigner/tests.sh +++ b/bin/tests/system/multisigner/tests.sh @@ -20,7 +20,6 @@ dig_with_opts() { $DIG +tcp +noadd +nosea +nostat +nocmd +dnssec -p $PORT "$@" } - start_time="$(TZ=UTC date +%s)" status=0 n=0 @@ -82,7 +81,6 @@ check_keytimes check_apex dnssec_verify - # # Update DNSKEY RRset. # @@ -98,6 +96,14 @@ zsks_are_published() { test "$lines" -eq 1 || return 1 } +# 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 "update zone ${ZONE} at ns3 with ZSK from provider ns4" ret=0 @@ -132,7 +138,17 @@ test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) # Verify again. dnssec_verify - +# No DNSSEC in raw journal. +n=$((n+1)) +echo_i "check zone ${ZONE} raw journal has no DNSSEC ($n)" +ret=0 +$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)) # # Update CDNSKEY RRset. @@ -173,7 +189,6 @@ retry_quiet 10 records_published CDNSKEY 2 || ret=1 test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) - n=$((n+1)) echo_i "update zone ${ZONE} at ns4 with CDNSKEY from provider ns3" ret=0 @@ -192,6 +207,17 @@ 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. +n=$((n+1)) +echo_i "check zone ${ZONE} raw journal has no DNSSEC ($n)" +ret=0 +$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)) # @@ -242,6 +268,17 @@ 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. +n=$((n+1)) +echo_i "check zone ${ZONE} raw journal has no DNSSEC ($n)" +ret=0 +$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)) echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 From d24297343f094e2316f8cc7c6652476dd8d18f5c Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 12 Oct 2022 17:01:57 +1100 Subject: [PATCH 08/13] Don't sign the raw zone The raw zone is not supposed to be signed. DNSKEY records in a raw zone should not trigger zone signing. The update code needs to be able to identify when it is working on a raw zone. Add dns_zone_israw() and dns_zone_issecure() enable it to do this. Also, we need to check the case for 'auto-dnssec maintain'. --- lib/dns/include/dns/zone.h | 6 ++++++ lib/dns/zone.c | 20 ++++++++++++++++++++ lib/ns/update.c | 13 ++++++++++--- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index c431f38703..7041523702 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -2317,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/zone.c b/lib/dns/zone.c index 922cdd3334..40682992fa 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -22189,6 +22189,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/update.c b/lib/ns/update.c index 493637f147..efffd4054d 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. */ @@ -3435,7 +3440,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, @@ -3481,11 +3486,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 From 70e10b3c50510e0f67a28c4c2eccc04d114c9153 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 3 Mar 2023 11:46:48 +0100 Subject: [PATCH 09/13] Add bump in the wire multisigner test Add a test case where updates are being made against a hidden primary and two bump in the wire signers (the providers in the multisigner model) serve the zone. The test covers the same cases as for two primary providers that is: - Add DNSKEY - Remove (previously added) DNSKEY - Add CDNSKEY - Remove (previously added) CDNSKEY - Add CDS - Remove (previously added) CDS --- bin/tests/system/multisigner/clean.sh | 5 + .../system/multisigner/ns3/named.conf.in | 8 + bin/tests/system/multisigner/ns3/setup.sh | 19 +- .../system/multisigner/ns4/named.conf.in | 8 + bin/tests/system/multisigner/ns4/setup.sh | 19 +- .../multisigner/ns5/model2.secondary.db.in | 29 ++ .../system/multisigner/ns5/named.conf.in | 44 +++ bin/tests/system/multisigner/ns5/setup.sh | 22 ++ bin/tests/system/multisigner/setup.sh | 5 + bin/tests/system/multisigner/tests.sh | 275 +++++++++++++++++- 10 files changed, 422 insertions(+), 12 deletions(-) create mode 100644 bin/tests/system/multisigner/ns5/model2.secondary.db.in create mode 100644 bin/tests/system/multisigner/ns5/named.conf.in create mode 100644 bin/tests/system/multisigner/ns5/setup.sh diff --git a/bin/tests/system/multisigner/clean.sh b/bin/tests/system/multisigner/clean.sh index 0dd6eb6d48..393efbbf37 100644 --- a/bin/tests/system/multisigner/clean.sh +++ b/bin/tests/system/multisigner/clean.sh @@ -16,6 +16,8 @@ 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.* @@ -29,9 +31,12 @@ 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 diff --git a/bin/tests/system/multisigner/ns3/named.conf.in b/bin/tests/system/multisigner/ns3/named.conf.in index 893e79d87b..ce570696b6 100644 --- a/bin/tests/system/multisigner/ns3/named.conf.in +++ b/bin/tests/system/multisigner/ns3/named.conf.in @@ -44,3 +44,11 @@ zone "model2.multisigner." { 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 index 54ee7dcb45..0f6b343e14 100644 --- a/bin/tests/system/multisigner/ns3/setup.sh +++ b/bin/tests/system/multisigner/ns3/setup.sh @@ -16,16 +16,29 @@ 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" -O="OMNIPRESENT" -ksktimes="-P now -A now -P sync now" -zsktimes="-P now -A now" 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/named.conf.in b/bin/tests/system/multisigner/ns4/named.conf.in index ba1f6b85fa..e19fd35f0d 100644 --- a/bin/tests/system/multisigner/ns4/named.conf.in +++ b/bin/tests/system/multisigner/ns4/named.conf.in @@ -44,3 +44,11 @@ zone "model2.multisigner." { 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 index 05d1060be4..94ac16ad8d 100644 --- a/bin/tests/system/multisigner/ns4/setup.sh +++ b/bin/tests/system/multisigner/ns4/setup.sh @@ -16,13 +16,26 @@ 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" -O="OMNIPRESENT" -ksktimes="-P now -A now -P sync now" -zsktimes="-P now -A now" +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 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 index a28917fdd9..f7fc496ca8 100644 --- a/bin/tests/system/multisigner/setup.sh +++ b/bin/tests/system/multisigner/setup.sh @@ -20,6 +20,7 @@ $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 @@ -29,3 +30,7 @@ copy_setports ns4/named.conf.in ns4/named.conf 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 index d6e56f9c8c..b3fa7948b0 100644 --- a/bin/tests/system/multisigner/tests.sh +++ b/bin/tests/system/multisigner/tests.sh @@ -96,6 +96,20 @@ zsks_are_published() { 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 @@ -269,16 +283,265 @@ 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 + +# +# 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 "check zone ${ZONE} raw journal has no DNSSEC ($n)" +echo_i "add dnskey record: update zone ${ZONE} at ns5 with ZSKs from providers ns3 and ns4 ($n)" ret=0 -$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 +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 From f636aceb508b3f14e5b85ae950d4f88e1a22e391 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 3 Mar 2023 11:51:55 +0100 Subject: [PATCH 10/13] Add more multisigner tests, removing records A zone in multisigner model 2 should also be possible to remove previously added DNSKEY, CDS and CDNSKEY records from the zone operated by the other provider. --- bin/tests/system/multisigner/tests.sh | 163 ++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 23 deletions(-) diff --git a/bin/tests/system/multisigner/tests.sh b/bin/tests/system/multisigner/tests.sh index b3fa7948b0..aac1c2ecdc 100644 --- a/bin/tests/system/multisigner/tests.sh +++ b/bin/tests/system/multisigner/tests.sh @@ -88,11 +88,15 @@ dnssec_verify # 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 | wc -l) + 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 | wc -l) + lines=$(grep "257 3 13" dig.out.$DIR.test$n.tr | wc -l) test "$lines" -eq 1 || return 1 } @@ -119,7 +123,7 @@ rrset_exists() ( ) n=$((n+1)) -echo_i "update zone ${ZONE} at ns3 with ZSK from provider ns4" +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" ( @@ -135,9 +139,8 @@ status=$((status+ret)) # Verify again. dnssec_verify - n=$((n+1)) -echo_i "update zone ${ZONE} at ns4 with ZSK from provider ns3" +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" ( @@ -152,18 +155,67 @@ test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) # Verify again. dnssec_verify -# No DNSSEC in raw journal. +no_dnssec_in_journal + n=$((n+1)) -echo_i "check zone ${ZONE} raw journal has no DNSSEC ($n)" +echo_i "remove dnskey record: - try to remove ns3 ZSK from provider ns3 (should fail) ($n)" ret=0 -$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 +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. # @@ -185,7 +237,7 @@ 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 "update zone ${ZONE} at ns3 with CDNSKEY from provider ns4" +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. @@ -204,7 +256,7 @@ test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) n=$((n+1)) -echo_i "update zone ${ZONE} at ns4 with CDNSKEY from provider ns3" +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. @@ -222,17 +274,45 @@ 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 "check zone ${ZONE} raw journal has no DNSSEC ($n)" +echo_i "remove cdnskey record: remove ns4 CDNSKEY from provider ns3 ($n)" ret=0 -$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 +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. @@ -245,7 +325,7 @@ 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 "update zone ${ZONE} at ns3 with CDS from provider ns4" +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. @@ -263,9 +343,8 @@ retry_quiet 10 records_published CDS 2 || ret=1 test "$ret" -eq 0 || echo_i "failed" status=$((status+ret)) - n=$((n+1)) -echo_i "update zone ${ZONE} at ns4 with CDS from provider ns3" +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. @@ -285,6 +364,44 @@ 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. # From 81cb18b8a230441d0b5fe5e34bad728cad4e4e9b Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 3 Mar 2023 14:15:59 +0100 Subject: [PATCH 11/13] Make make_dnskey() a public funcion It can be used to compare DNSKEY, CDNSKEY, and CDS records with signing keys. --- lib/dns/dnssec.c | 18 +++++++++--------- lib/dns/include/dns/dnssec.h | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 9 deletions(-) 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, From ef58f2444f4bc76891f89ad3344348232994a4df Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 15 Mar 2023 11:51:33 +0100 Subject: [PATCH 12/13] Add new dns_rdatatype_iskeymaterial() function The following code block repeats quite often: if (rdata.type == dns_rdatatype_dnskey || rdata.type == dns_rdatatype_cdnskey || rdata.type == dns_rdatatype_cds) Introduce a new function to reduce the repetition. --- lib/dns/include/dns/rdata.h | 7 +++++++ lib/dns/rdata.c | 6 ++++++ lib/dns/update.c | 15 +++------------ lib/dns/zone.c | 37 ++++++++----------------------------- lib/ns/query.c | 4 +--- lib/ns/update.c | 5 +---- 6 files changed, 26 insertions(+), 48 deletions(-) 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/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 40682992fa..8ea68a6549 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -6384,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, @@ -6703,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). @@ -6746,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; } @@ -7150,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 @@ -7327,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 @@ -7944,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; } @@ -16109,10 +16094,7 @@ sync_secure_journal(dns_zone_t *zone, dns_zone_t *raw, dns_journal_t *journal, * update the zone with these records from a different provider, * but skip records that are under our control. */ - if (rdata->type == dns_rdatatype_dnskey || - rdata->type == dns_rdatatype_cdnskey || - rdata->type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(rdata->type)) { bool inuse = false; isc_result_t r = dns_zone_dnskey_inuse(zone, rdata, &inuse); @@ -16183,10 +16165,7 @@ sync_secure_db(dns_zone_t *seczone, dns_zone_t *raw, dns_db_t *secdb, * update the zone with these records from a different provider, * but skip records that are under our control. */ - if (tuple->rdata.type == dns_rdatatype_dnskey || - tuple->rdata.type == dns_rdatatype_cdnskey || - tuple->rdata.type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(tuple->rdata.type)) { bool inuse = false; isc_result_t r = dns_zone_dnskey_inuse( seczone, &tuple->rdata, &inuse); 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 efffd4054d..3103263366 100644 --- a/lib/ns/update.c +++ b/lib/ns/update.c @@ -3386,10 +3386,7 @@ update_action(void *arg) { * Don't remove DNSKEY, CDNSKEY, CDS records * that are in use (under our control). */ - if (rdata.type == dns_rdatatype_dnskey || - rdata.type == dns_rdatatype_cdnskey || - rdata.type == dns_rdatatype_cds) - { + if (dns_rdatatype_iskeymaterial(rdata.type)) { isc_result_t r; bool inuse = false; r = dns_zone_dnskey_inuse(zone, &rdata, From 7ab3bfd596052ecf8e632255792e8b1b0c88e8af Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Mon, 6 Mar 2023 16:48:17 +0100 Subject: [PATCH 13/13] Fix dnssec system test The 'update-nsec3.example' requires to be DNSSEC maintained via dynamic update. Commit 03b22983cd20cec51ad8b9f25f2e7d0e472dc79c adds checks to make sure the raw zone is not signed. So the test case neesd to be updated to allow for DNSSEC maintenance. --- bin/tests/system/dnssec/ns3/named.conf.in | 1 + 1 file changed, 1 insertion(+) 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"; };