From 1cd0d291d392558e7677c79902beaa2e87ff83ed Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Sun, 20 Oct 2024 01:04:06 -0700 Subject: [PATCH] enforce '*._er' requirement for error-reporting zones if "log-report-channel" is set to "yes", then the zone must contain a wildcard name matching '*._er' with a TXT record. --- bin/check/named-checkzone.c | 14 +++++- bin/check/named-checkzone.rst | 8 ++- bin/check/named-compilezone.rst | 8 ++- bin/tests/system/checkzone/tests.sh | 10 ++++ .../system/checkzone/zones/er-missing.db | 22 ++++++++ bin/tests/system/checkzone/zones/er.db | 23 +++++++++ doc/arm/reference.rst | 5 +- lib/dns/zone.c | 50 +++++++++++++++++++ 8 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 bin/tests/system/checkzone/zones/er-missing.db create mode 100644 bin/tests/system/checkzone/zones/er.db diff --git a/bin/check/named-checkzone.c b/bin/check/named-checkzone.c index f96930d30c..2770bf5be8 100644 --- a/bin/check/named-checkzone.c +++ b/bin/check/named-checkzone.c @@ -159,7 +159,7 @@ main(int argc, char **argv) { while ((c = isc_commandline_parse(argc, argv, "c:df:hi:jJ:k:L:l:m:n:qr:s:t:o:vw:C:" - "DF:M:S:T:W:")) != EOF) + "DF:M:R:S:T:W:")) != EOF) { switch (c) { case 'c': @@ -323,6 +323,18 @@ main(int argc, char **argv) { } break; + case 'R': + if (ARGCMP("fail")) { + zone_options |= DNS_ZONEOPT_LOGREPORTS; + } else if (ARGCMP("ignore")) { + zone_options &= ~DNS_ZONEOPT_LOGREPORTS; + } else { + fprintf(stderr, "invalid argument to -R: %s\n", + isc_commandline_argument); + exit(EXIT_FAILURE); + } + break; + case 's': if (ARGCMP("full")) { outputstyle = &dns_master_style_full; diff --git a/bin/check/named-checkzone.rst b/bin/check/named-checkzone.rst index e6ad56f73c..9b8a0909a4 100644 --- a/bin/check/named-checkzone.rst +++ b/bin/check/named-checkzone.rst @@ -23,7 +23,7 @@ named-checkzone - zone file validation tool Synopsis ~~~~~~~~ -:program:`named-checkzone` [**-d**] [**-h**] [**-j**] [**-q**] [**-v**] [**-c** class] [**-C** mode] [**-f** format] [**-F** format] [**-J** filename] [**-i** mode] [**-k** mode] [**-m** mode] [**-M** mode] [**-n** mode] [**-l** ttl] [**-L** serial] [**-o** filename] [**-r** mode] [**-s** style] [**-S** mode] [**-t** directory] [**-T** mode] [**-w** directory] [**-D**] [**-W** mode] {zonename} {filename} +:program:`named-checkzone` [**-d**] [**-h**] [**-j**] [**-q**] [**-v**] [**-c** class] [**-C** mode] [**-f** format] [**-F** format] [**-J** filename] [**-i** mode] [**-k** mode] [**-m** mode] [**-M** mode] [**-n** mode] [**-l** ttl] [**-L** serial] [**-o** filename] [**-r** mode] [**-R** mode] [**-s** style] [**-S** mode] [**-t** directory] [**-T** mode] [**-w** directory] [**-D**] [**-W** mode] {zonename} {filename} Description ~~~~~~~~~~~ @@ -166,6 +166,12 @@ Options semantically equal in plain DNS. Possible modes are ``fail``, ``warn`` (the default), and ``ignore``. +.. option:: -R mode + + This option checks whether a TXT wildcard record exists that + matches the name format for RFC 9567 error-reporting queries: ``*._er``. + Possible modes are ``fail`` and ``ignore`` (the default). + .. option:: -s style This option specifies the style of the dumped zone file. Possible styles are diff --git a/bin/check/named-compilezone.rst b/bin/check/named-compilezone.rst index 8c68d0d6d2..c067826194 100644 --- a/bin/check/named-compilezone.rst +++ b/bin/check/named-compilezone.rst @@ -23,7 +23,7 @@ named-compilezone - zone file converting tool Synopsis ~~~~~~~~ -:program:`named-compilezone` [**-d**] [**-h**] [**-j**] [**-q**] [**-v**] [**-c** class] [**-C** mode] [**-f** format] [**-F** format] [**-J** filename] [**-i** mode] [**-k** mode] [**-m** mode] [**-M** mode] [**-n** mode] [**-l** ttl] [**-L** serial] [**-r** mode] [**-s** style] [**-S** mode] [**-t** directory] [**-T** mode] [**-w** directory] [**-D**] [**-W** mode] {**-o** filename} {zonename} {filename} +:program:`named-compilezone` [**-d**] [**-h**] [**-j**] [**-q**] [**-v**] [**-c** class] [**-C** mode] [**-f** format] [**-F** format] [**-J** filename] [**-i** mode] [**-k** mode] [**-m** mode] [**-M** mode] [**-n** mode] [**-l** ttl] [**-L** serial] [**-r** mode] [**-R** mode] [**-s** style] [**-S** mode] [**-t** directory] [**-T** mode] [**-w** directory] [**-D**] [**-W** mode] {**-o** filename} {zonename} {filename} Description ~~~~~~~~~~~ @@ -175,6 +175,12 @@ Options semantically equal in plain DNS. Possible modes are ``fail``, ``warn``, and ``ignore`` (the default). +.. option:: -R mode + + This option checks whether a TXT wildcard record exists that + matches the name format for RFC 9567 error-reporting queries: ``*._er``. + Possible modes are ``fail`` and ``ignore`` (the default). + .. option:: -s style This option specifies the style of the dumped zone file. Possible styles are diff --git a/bin/tests/system/checkzone/tests.sh b/bin/tests/system/checkzone/tests.sh index 1cc8accce4..aa26c8fb43 100644 --- a/bin/tests/system/checkzone/tests.sh +++ b/bin/tests/system/checkzone/tests.sh @@ -124,6 +124,16 @@ n=$((n + 1)) if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) +echo_i "checking that log-report-channel zones fail if '*._er/TXT' is missing ($n)" +ret=0 +$CHECKZONE -R fail example zones/er.db >test.out2.$n 2>&1 || ret=1 +grep -F "no '*._er/TXT' wildcard found" test.out4.$n >/dev/null && ret=1 +$CHECKZONE example zones/er-missing.db >test.out3.$n 2>&1 || ret=1 +grep -F "no '*._er/TXT' wildcard found" test.out4.$n >/dev/null && ret=1 +$CHECKZONE -R fail example zones/er-missing.db >test.out4.$n 2>&1 && ret=1 +grep -F "no '*._er/TXT' wildcard found" test.out4.$n >/dev/null || ret=1 +status=$((status + ret)) + echo_i "checking that raw zone with bad class is handled ($n)" ret=0 $CHECKZONE -f raw example zones/bad-badclass.raw >test.out.$n 2>&1 && ret=1 diff --git a/bin/tests/system/checkzone/zones/er-missing.db b/bin/tests/system/checkzone/zones/er-missing.db new file mode 100644 index 0000000000..29885ca414 --- /dev/null +++ b/bin/tests/system/checkzone/zones/er-missing.db @@ -0,0 +1,22 @@ +; 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 ; 5 minutes +@ IN SOA ns root ( + 2018010100 ; serial + 1800 ; refresh (30 minutes) + 1800 ; retry (30 minutes) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns +ns A 10.53.0.1 +server A 10.53.0.100 diff --git a/bin/tests/system/checkzone/zones/er.db b/bin/tests/system/checkzone/zones/er.db new file mode 100644 index 0000000000..08d3197011 --- /dev/null +++ b/bin/tests/system/checkzone/zones/er.db @@ -0,0 +1,23 @@ +; 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 ; 5 minutes +@ IN SOA ns root ( + 2018010100 ; serial + 1800 ; refresh (30 minutes) + 1800 ; retry (30 minutes) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns +ns A 10.53.0.1 +server A 10.53.0.100 +*._er TXT "Report received" diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 4813ccd546..38eeb39ee2 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -7315,8 +7315,9 @@ Zone Options are logged to the ``dns-reporting-agent`` logging category at level ``info``. - The zone should have a wildcard record in place to respond to such - queries. For example: + The zone must have a wildcard record in place to respond to such + queries; it is a configuration error to use this option in a + zone without such a record. For example: :: diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 188c23f6b6..5c71e68be3 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -4727,6 +4727,47 @@ process_zone_setnsec3param(dns_zone_t *zone) { } } +static unsigned char er_offset[] = { 0, 1 }; +static unsigned char er_ndata[] = "\001*\003_er"; +static dns_name_t er = DNS_NAME_INITNONABSOLUTE(er_ndata, er_offset); + +static isc_result_t +check_reportchannel(dns_zone_t *zone, dns_db_t *db) { + isc_result_t result; + dns_rdataset_t rdataset = DNS_RDATASET_INIT; + dns_dbnode_t *node = NULL; + dns_dbversion_t *version = NULL; + dns_fixedname_t fixed; + dns_name_t *name = NULL; + + /* + * If this zone isn't logging reports, it's fine. + */ + if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_LOGREPORTS)) { + return (ISC_R_SUCCESS); + } + + /* + * Otherwise, we need a '*._er' wildcard with a TXT rdataset. + */ + name = dns_fixedname_initname(&fixed); + CHECK(dns_name_concatenate(&er, &zone->origin, name, NULL)); + CHECK(dns_db_findnode(db, name, false, &node)); + + dns_db_currentversion(db, &version); + + result = dns_db_findrdataset(db, node, version, dns_rdatatype_txt, + dns_rdatatype_none, 0, &rdataset, NULL); + dns_db_closeversion(db, &version, false); + dns_db_detachnode(db, &node); + if (result == ISC_R_SUCCESS) { + dns_rdataset_disassociate(&rdataset); + } + +failure: + return (result); +} + /* * The zone is presumed to be locked. * If this is a inline_raw zone the secure version is also locked. @@ -4972,6 +5013,15 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, } } + result = check_reportchannel(zone, db); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "'log-report-channel' is set, but no " + "'*._er/TXT' wildcard found"); + result = DNS_R_BADZONE; + goto cleanup; + } + result = dns_zone_verifydb(zone, db, NULL); if (result != ISC_R_SUCCESS) { goto cleanup;