From f983a6415293bf26e5001b44a202a618f60dbf34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Mon, 23 Feb 2026 06:13:59 +0100 Subject: [PATCH 1/2] Fail DNSKEY validation when supported but invalid DS is found A regression was introduced when adding the EDE code for unsupported DNSKEY and DS algorithms. When the parent has both supported and unsupported algorithm in the DS record, the validator would treat the supported DS algorithm as insecure when validating DNSKEY records instead of BOGUS. This has not security impact as the rest of the child zone correctly ends with BOGUS status, but it is incorrect and thus the regression has been fixed. --- bin/tests/system/dnssec/tests_validation.py | 2 +- lib/dns/include/dns/validator.h | 1 + lib/dns/validator.c | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/bin/tests/system/dnssec/tests_validation.py b/bin/tests/system/dnssec/tests_validation.py index 777bb693b1..1122180eaa 100644 --- a/bin/tests/system/dnssec/tests_validation.py +++ b/bin/tests/system/dnssec/tests_validation.py @@ -408,7 +408,7 @@ def test_private_algorithms(ns4): isctest.check.noerror(res1) isctest.check.servfail(res2) watcher.wait_for_line( - "No DNSKEY for extradsunknownoid.example/DS with PRIVATEOID" + "no DNSKEY matching DS" ) diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h index faa5ea1533..7676fe534b 100644 --- a/lib/dns/include/dns/validator.h +++ b/lib/dns/include/dns/validator.h @@ -150,6 +150,7 @@ struct dns_validator { bool digest_sha1; uint8_t unsupported_algorithm; uint8_t unsupported_digest; + uint8_t validation_attempts; dns_rdata_t rdata; bool resume; isc_counter_t *nvalidations; diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 2e731a7576..ed2931b744 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -2089,6 +2089,8 @@ validate_dnskey_dsset(dns_validator_t *val) { } } + val->validation_attempts++; + /* * Find the DNSKEY matching the DS... */ @@ -2113,6 +2115,12 @@ validate_dnskey_dsset(dns_validator_t *val) { val->name, key.algorithm, key.data, key.datalen)) { + /* + * Don't count the unsupported algorithm into the + * validation attempts. + */ + val->validation_attempts--; + if (val->unsupported_algorithm == 0) { val->unsupported_algorithm = key.algorithm; /* @@ -2184,6 +2192,11 @@ validate_dnskey_dsset_next_done(void *arg) { return; } + if (val->validation_attempts != 0) { + val->unsupported_algorithm = 0; + val->unsupported_digest = 0; + } + validate_dnskey_dsset_done(val, result); return; } From 46f15f4f9d2e83b92d1c04896f07a2253cc6041c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Mon, 23 Feb 2026 11:17:40 +0100 Subject: [PATCH 2/2] Add test for mixed unsupported DS records Add a system test that has one invalid DS record with supported algorithm and one unsupported DS record. Both DNSKEY and A queries must fail with SERVFAIL. --- .../dnssec-unsupported-ds/ns1/named.conf.j2 | 38 +++++++++++++++ .../system/dnssec-unsupported-ds/ns1/sign.sh | 37 +++++++++++++++ .../ns1/zones/root.db.in | 16 +++++++ .../dnssec-unsupported-ds/ns2/named.conf.j2 | 47 +++++++++++++++++++ .../system/dnssec-unsupported-ds/ns2/sign.sh | 35 ++++++++++++++ .../ns2/zones/example.db.in | 16 +++++++ .../dnssec-unsupported-ds/ns3/named.conf.j2 | 47 +++++++++++++++++++ .../system/dnssec-unsupported-ds/ns3/sign.sh | 32 +++++++++++++ .../ns3/zones/child.example.db.in | 18 +++++++ .../dnssec-unsupported-ds/ns4/named.conf.j2 | 42 +++++++++++++++++ .../system/dnssec-unsupported-ds/setup.sh | 32 +++++++++++++ .../dnssec-unsupported-ds/tests_mixed_ds.py | 36 ++++++++++++++ bin/tests/system/dnssec/tests_validation.py | 4 +- 13 files changed, 397 insertions(+), 3 deletions(-) create mode 100644 bin/tests/system/dnssec-unsupported-ds/ns1/named.conf.j2 create mode 100755 bin/tests/system/dnssec-unsupported-ds/ns1/sign.sh create mode 100644 bin/tests/system/dnssec-unsupported-ds/ns1/zones/root.db.in create mode 100644 bin/tests/system/dnssec-unsupported-ds/ns2/named.conf.j2 create mode 100755 bin/tests/system/dnssec-unsupported-ds/ns2/sign.sh create mode 100644 bin/tests/system/dnssec-unsupported-ds/ns2/zones/example.db.in create mode 100644 bin/tests/system/dnssec-unsupported-ds/ns3/named.conf.j2 create mode 100755 bin/tests/system/dnssec-unsupported-ds/ns3/sign.sh create mode 100644 bin/tests/system/dnssec-unsupported-ds/ns3/zones/child.example.db.in create mode 100644 bin/tests/system/dnssec-unsupported-ds/ns4/named.conf.j2 create mode 100755 bin/tests/system/dnssec-unsupported-ds/setup.sh create mode 100644 bin/tests/system/dnssec-unsupported-ds/tests_mixed_ds.py diff --git a/bin/tests/system/dnssec-unsupported-ds/ns1/named.conf.j2 b/bin/tests/system/dnssec-unsupported-ds/ns1/named.conf.j2 new file mode 100644 index 0000000000..abfad948c6 --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/ns1/named.conf.j2 @@ -0,0 +1,38 @@ +/* + * 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. + */ + +// NS1 + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + minimal-any no; + minimal-responses no; + recursion no; + notify yes; + dnssec-validation yes; + /* test that we can turn off trust-anchor-telemetry */ + trust-anchor-telemetry no; +}; + +zone "." { + type primary; + file "zones/root.db.signed"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/dnssec-unsupported-ds/ns1/sign.sh b/bin/tests/system/dnssec-unsupported-ds/ns1/sign.sh new file mode 100755 index 0000000000..ac42bb859d --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/ns1/sign.sh @@ -0,0 +1,37 @@ +#!/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 + +echo_i "ns1/sign.sh" + +zone=. + +mkdir -p keys +ksk=$("$KEYGEN" -K keys -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -f KSK "${zone}") +zsk=$("$KEYGEN" -K keys -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "${zone}") + +cat "zones/root.db.in" "keys/$ksk.key" "keys/$zsk.key" ../ns2/dsset-example. >"zones/root.db" + +"$SIGNER" -S -K "keys" \ + -o . \ + -f "zones/root.db.signed" \ + "zones/root.db" >/dev/null 2>&1 + +keyfile_to_static_ds "keys/$ksk" >trusted.conf +cp trusted.conf ../ns2/trusted.conf +cp trusted.conf ../ns3/trusted.conf +cp trusted.conf ../ns4/trusted.conf diff --git a/bin/tests/system/dnssec-unsupported-ds/ns1/zones/root.db.in b/bin/tests/system/dnssec-unsupported-ds/ns1/zones/root.db.in new file mode 100644 index 0000000000..5744823ca2 --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/ns1/zones/root.db.in @@ -0,0 +1,16 @@ +; 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. + +. 300 IN SOA gson.nominum.com. a.root.servers.nil. 2000042100 600 600 1200 600 +ns2.example. 300 IN A 10.53.0.2 +example. 300 IN NS ns2.example. +a.root-servers.nil. 300 IN A 10.53.0.1 +. 300 IN NS a.root-servers.nil. diff --git a/bin/tests/system/dnssec-unsupported-ds/ns2/named.conf.j2 b/bin/tests/system/dnssec-unsupported-ds/ns2/named.conf.j2 new file mode 100644 index 0000000000..5de17b11c2 --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/ns2/named.conf.j2 @@ -0,0 +1,47 @@ +/* + * 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. + */ + +// NS2 + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + recursion no; + dnssec-validation yes; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; + +zone "example." { + type primary; + file "zones/example.db.signed"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/dnssec-unsupported-ds/ns2/sign.sh b/bin/tests/system/dnssec-unsupported-ds/ns2/sign.sh new file mode 100755 index 0000000000..8bfd0dd939 --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/ns2/sign.sh @@ -0,0 +1,35 @@ +#!/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 + +echo_i "ns2/sign.sh" + +zone=example. + +mkdir -p keys +ksk=$("$KEYGEN" -K keys -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -f KSK "${zone}") +zsk=$("$KEYGEN" -K keys -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "${zone}") + +cat "zones/${zone}db.in" "keys/$ksk.key" "keys/$zsk.key" >"zones/${zone}db" + +<"../ns3/dsset-child.${zone}" sed -E "s/[[:space:]]+$DEFAULT_ALGORITHM_NUMBER[[:space:]]+/ 12 /" >>"zones/${zone}db" +<"../ns3/dsset-child.${zone}" sed -E "s/[[:space:]]+$DEFAULT_ALGORITHM_NUMBER[[:space:]]+2[[:space:]]+.*/ $DEFAULT_ALGORITHM_NUMBER 2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/" >>"zones/${zone}db" + +"$SIGNER" -S -K "keys" \ + -o "${zone}" \ + -f "zones/${zone}db.signed" \ + "zones/${zone}db" >/dev/null 2>&1 diff --git a/bin/tests/system/dnssec-unsupported-ds/ns2/zones/example.db.in b/bin/tests/system/dnssec-unsupported-ds/ns2/zones/example.db.in new file mode 100644 index 0000000000..2ca37a78ce --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/ns2/zones/example.db.in @@ -0,0 +1,16 @@ +; 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. + +example. 300 IN SOA ns1.example. admin.example. 2026021901 3600 900 86400 300 +ns1.example. 300 IN A 10.53.0.2 +ns1.child.example. 300 IN A 10.53.0.3 +child.example. 300 IN NS ns1.child.example. +example. 300 IN NS ns1.example. diff --git a/bin/tests/system/dnssec-unsupported-ds/ns3/named.conf.j2 b/bin/tests/system/dnssec-unsupported-ds/ns3/named.conf.j2 new file mode 100644 index 0000000000..511de253ca --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/ns3/named.conf.j2 @@ -0,0 +1,47 @@ +/* + * 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 + +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; }; + recursion no; + dnssec-validation yes; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; + +zone "child.example." { + type primary; + file "zones/child.example.db.signed"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/dnssec-unsupported-ds/ns3/sign.sh b/bin/tests/system/dnssec-unsupported-ds/ns3/sign.sh new file mode 100755 index 0000000000..08a5c797e6 --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/ns3/sign.sh @@ -0,0 +1,32 @@ +#!/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 + +echo_i "ns3/sign.sh" + +zone=child.example. + +mkdir -p keys +ksk=$("$KEYGEN" -K keys -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -f KSK "${zone}") +zsk=$("$KEYGEN" -K keys -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "${zone}") + +cat "zones/${zone}db.in" "keys/$ksk.key" "keys/$zsk.key" >"zones/${zone}db" + +"$SIGNER" -S -K "keys" \ + -o "${zone}" \ + -f "zones/${zone}db.signed" \ + "zones/${zone}db" >/dev/null 2>&1 diff --git a/bin/tests/system/dnssec-unsupported-ds/ns3/zones/child.example.db.in b/bin/tests/system/dnssec-unsupported-ds/ns3/zones/child.example.db.in new file mode 100644 index 0000000000..d35bf87f7d --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/ns3/zones/child.example.db.in @@ -0,0 +1,18 @@ +; 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. + +child.example. 300 IN SOA ns1.child.example. admin.child.example. 2026021901 3600 900 86400 300 +api.child.example. 300 IN A 192.0.2.102 +child.example. 300 IN MX 10 mail.child.example. +mail.child.example. 300 IN A 192.0.2.101 +www.child.example. 300 IN A 192.0.2.100 +ns1.child.example. 300 IN A 10.53.0.3 +child.example. 300 IN NS ns1.child.example. diff --git a/bin/tests/system/dnssec-unsupported-ds/ns4/named.conf.j2 b/bin/tests/system/dnssec-unsupported-ds/ns4/named.conf.j2 new file mode 100644 index 0000000000..af986ef593 --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/ns4/named.conf.j2 @@ -0,0 +1,42 @@ +/* + * 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 + +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; }; + recursion yes; + dnssec-validation yes; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/dnssec-unsupported-ds/setup.sh b/bin/tests/system/dnssec-unsupported-ds/setup.sh new file mode 100755 index 0000000000..3b40ffc5bd --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/setup.sh @@ -0,0 +1,32 @@ +#!/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 + +( + cd ns3 + $SHELL sign.sh +) + +( + cd ns2 + $SHELL sign.sh +) + +( + cd ns1 + $SHELL sign.sh +) diff --git a/bin/tests/system/dnssec-unsupported-ds/tests_mixed_ds.py b/bin/tests/system/dnssec-unsupported-ds/tests_mixed_ds.py new file mode 100644 index 0000000000..e1cf82c132 --- /dev/null +++ b/bin/tests/system/dnssec-unsupported-ds/tests_mixed_ds.py @@ -0,0 +1,36 @@ +# 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. + +import pytest + +import isctest + +pytestmark = pytest.mark.extra_artifacts( + [ + "ns*/dsset-*", + "ns*/keys", + "ns*/keys/*.key", + "ns*/keys/*.private", + "ns*/trusted.conf", + "ns*/zones/*.db", + "ns*/zones/*.db.signed", + ] +) + + +def test_mixed_ds(): + msg = isctest.query.create("child.example.", "DNSKEY") + res = isctest.query.tcp(msg, "10.53.0.4") + isctest.check.servfail(res) + + msg = isctest.query.create("child.example.", "A") + res = isctest.query.tcp(msg, "10.53.0.4") + isctest.check.servfail(res) diff --git a/bin/tests/system/dnssec/tests_validation.py b/bin/tests/system/dnssec/tests_validation.py index 1122180eaa..8d88a2693d 100644 --- a/bin/tests/system/dnssec/tests_validation.py +++ b/bin/tests/system/dnssec/tests_validation.py @@ -407,9 +407,7 @@ def test_private_algorithms(ns4): res2 = isctest.query.tcp(msg, "10.53.0.4") isctest.check.noerror(res1) isctest.check.servfail(res2) - watcher.wait_for_line( - "no DNSKEY matching DS" - ) + watcher.wait_for_line("no DNSKEY matching DS") @isctest.mark.extended_ds_digest