diff --git a/CHANGES b/CHANGES index ca80b0d14b..94801c811e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +5767. [func] Extend allow-transfer option with 'port' and + 'transport' options to restrict zone transfers to + a specific port and DNS transport protocol. + [GL #2776] + 5766. [func] Unused 'tls' clause options 'ca-file' and 'hostname' were disabled. [GL !5600] diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 62fc949b39..38f8021ad7 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -3149,7 +3149,7 @@ launch_next_query(dig_query_t *query) { xfr = query->lookup->rdtype == dns_rdatatype_ixfr || query->lookup->rdtype == dns_rdatatype_axfr; - if (xfr && isc_nm_is_tlsdns_handle(query->handle) && + if (xfr && isc_nm_socket_type(query->handle) == isc_nm_tlsdnssocket && !isc_nm_xfr_allowed(query->handle)) { dighost_error("zone transfers over the " diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst index 28a39c45e0..644c70430a 100644 --- a/bin/named/named.conf.rst +++ b/bin/named/named.conf.rst @@ -166,7 +166,8 @@ OPTIONS allow-query-on { address_match_element; ... }; allow-recursion { address_match_element; ... }; allow-recursion-on { address_match_element; ... }; - allow-transfer { address_match_element; ... }; + allow-transfer [ port integer ] [ transport string ] { + address_match_element; ... }; allow-update { address_match_element; ... }; allow-update-forwarding { address_match_element; ... }; also-notify [ port integer ] [ dscp integer ] { ( @@ -605,7 +606,8 @@ VIEW allow-query-on { address_match_element; ... }; allow-recursion { address_match_element; ... }; allow-recursion-on { address_match_element; ... }; - allow-transfer { address_match_element; ... }; + allow-transfer [ port integer ] [ transport string ] { + address_match_element; ... }; allow-update { address_match_element; ... }; allow-update-forwarding { address_match_element; ... }; also-notify [ port integer ] [ dscp integer ] { ( @@ -889,7 +891,8 @@ VIEW allow-notify { address_match_element; ... }; allow-query { address_match_element; ... }; allow-query-on { address_match_element; ... }; - allow-transfer { address_match_element; ... }; + allow-transfer [ port integer ] [ transport string ] { + address_match_element; ... }; allow-update { address_match_element; ... }; allow-update-forwarding { address_match_element; ... }; also-notify [ port integer ] [ dscp integer ] { ( @@ -1009,7 +1012,8 @@ ZONE allow-notify { address_match_element; ... }; allow-query { address_match_element; ... }; allow-query-on { address_match_element; ... }; - allow-transfer { address_match_element; ... }; + allow-transfer [ port integer ] [ transport string ] { + address_match_element; ... }; allow-update { address_match_element; ... }; allow-update-forwarding { address_match_element; ... }; also-notify [ port integer ] [ dscp integer ] { ( diff --git a/bin/tests/system/Makefile.am b/bin/tests/system/Makefile.am index f47678ba59..5fe5b2d833 100644 --- a/bin/tests/system/Makefile.am +++ b/bin/tests/system/Makefile.am @@ -157,6 +157,7 @@ TESTS += \ synthfromdnssec \ tkey \ tools \ + transport-acl \ tsig \ tsiggss \ ttl \ diff --git a/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-port.conf b/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-port.conf new file mode 100644 index 0000000000..4534126ddf --- /dev/null +++ b/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-port.conf @@ -0,0 +1,16 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone "example1" { + type primary; + file "example1.db"; + allow-transfer port 99999 { any; }; +}; diff --git a/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-1.conf b/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-1.conf new file mode 100644 index 0000000000..46bec68e48 --- /dev/null +++ b/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-1.conf @@ -0,0 +1,16 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone "example1" { + type primary; + file "example1.db"; + allow-transfer port 44344 transport blah { any; }; +}; diff --git a/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-2.conf b/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-2.conf new file mode 100644 index 0000000000..b52929129b --- /dev/null +++ b/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-2.conf @@ -0,0 +1,16 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone "example1" { + type primary; + file "example1.db"; + allow-transfer port 44344 transport udp { any; }; +}; diff --git a/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-3.conf b/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-3.conf new file mode 100644 index 0000000000..e94c4b56a4 --- /dev/null +++ b/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-3.conf @@ -0,0 +1,16 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone "example1" { + type primary; + file "example1.db"; + allow-transfer port 44344 transport http { any; }; +}; diff --git a/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-4.conf b/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-4.conf new file mode 100644 index 0000000000..0bec62f113 --- /dev/null +++ b/bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-4.conf @@ -0,0 +1,16 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone "example1" { + type primary; + file "example1.db"; + allow-transfer port 44344 transport http-plain { any; }; +}; diff --git a/bin/tests/system/checkconf/good-dot-allow-transfer-encrypted.conf b/bin/tests/system/checkconf/good-dot-allow-transfer-encrypted.conf new file mode 100644 index 0000000000..96ec4cd857 --- /dev/null +++ b/bin/tests/system/checkconf/good-dot-allow-transfer-encrypted.conf @@ -0,0 +1,47 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone "example1" { + type primary; + file "example1.db"; + allow-transfer port 44344 transport tls { any; }; +}; + +zone "example2" { + type primary; + file "example2.db"; + allow-transfer port 44344 transport tcp { any; }; +}; + +zone "example3" { + type primary; + file "example3.db"; + allow-transfer transport tls { any; }; +}; + +zone "example4" { + type primary; + file "example4.db"; + allow-transfer transport tcp { any; }; +}; + + +zone "example5" { + type primary; + file "example5.db"; + allow-transfer port 53 { any; }; +}; + +zone "example6" { + type primary; + file "example6.db"; + allow-transfer { any; }; +}; diff --git a/bin/tests/system/doth/ns1/named.conf.in b/bin/tests/system/doth/ns1/named.conf.in index a78e30c40f..0cb6b41ddd 100644 --- a/bin/tests/system/doth/ns1/named.conf.in +++ b/bin/tests/system/doth/ns1/named.conf.in @@ -44,11 +44,11 @@ options { zone "." { type primary; file "root.db"; - allow-transfer { any; }; + allow-transfer port @TLSPORT@ transport tls { any; }; }; zone "example" { type primary; file "example.db"; - allow-transfer { any; }; + allow-transfer port @TLSPORT@ transport tls { any; }; }; diff --git a/bin/tests/system/doth/tests.sh b/bin/tests/system/doth/tests.sh index ee25102f2a..166f0410f1 100644 --- a/bin/tests/system/doth/tests.sh +++ b/bin/tests/system/doth/tests.sh @@ -30,6 +30,11 @@ dig_with_http_opts() { "$DIG" +http-plain $common_dig_options -p "${HTTPPORT}" "$@" } +dig_with_opts() { + # shellcheck disable=SC2086 + "$DIG" $common_dig_options -p "${PORT}" "$@" +} + wait_for_tls_xfer() ( dig_with_tls_opts -b 10.53.0.3 @10.53.0.2 example. AXFR > "dig.out.ns2.test$n" || return 1 grep "^;" "dig.out.ns2.test$n" > /dev/null && return 1 @@ -101,6 +106,24 @@ grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) +# zone transfers are allowed only via TLS +n=$((n+1)) +echo_i "testing zone transfer over Do53 server functionality (using dig, failure expected) ($n)" +ret=0 +dig_with_opts example. -b 10.53.0.3 @10.53.0.1 axfr > dig.out.ns1.test$n || ret=1 +grep "; Transfer failed." dig.out.ns1.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# querying zones is still allowed via UDP/TCP +n=$((n + 1)) +echo_i "checking Do53 query ($n)" +ret=0 +dig_with_opts @10.53.0.1 example SOA > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + # In this test we are trying to establish a DoT connection over the # DoH port. That is intentional, as dig should fail right after # handshake has happened and before sending any queries, as XFRs, per diff --git a/bin/tests/system/transport-acl/clean.sh b/bin/tests/system/transport-acl/clean.sh new file mode 100644 index 0000000000..bd6739e94e --- /dev/null +++ b/bin/tests/system/transport-acl/clean.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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. + +# +# Clean up after zone transfer tests. +# + +rm -f ./*/named.conf +rm -f ./*/named.memstats +rm -f ./*/named.run +rm -f ./*/named.run.prev +rm -f ./dig.out.* +rm -f ./*/example.db +rm -rf ./headers.* diff --git a/bin/tests/system/transport-acl/ns1/named.conf.in b/bin/tests/system/transport-acl/ns1/named.conf.in new file mode 100644 index 0000000000..e46130f024 --- /dev/null +++ b/bin/tests/system/transport-acl/ns1/named.conf.in @@ -0,0 +1,127 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +include "../../common/rndc.key"; + +controls { + inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +tls self-signed { + cert-file "../self-signed-cert.pem"; + key-file "../self-signed-key.pem"; +}; + +options { + pid-file "named.pid"; + ## + # generic test + listen-on port @PORT@ { 10.53.0.1; }; + listen-on port @TLSPORT@ tls self-signed { 10.53.0.1; }; + # test #1 + listen-on port @EXTRAPORT1@ { 10.53.0.1; }; + listen-on port @EXTRAPORT1@ tls self-signed { 10.53.0.2; }; + listen-on port @EXTRAPORT2@ { 10.53.0.1; }; + listen-on port @EXTRAPORT2@ tls self-signed { 10.53.0.2; }; + # test #2 + listen-on port @EXTRAPORT1@ { 10.53.0.3; }; + listen-on port @EXTRAPORT2@ { 10.53.0.3; }; + listen-on port @EXTRAPORT1@ tls self-signed { 10.53.0.4; }; + listen-on port @EXTRAPORT2@ tls self-signed { 10.53.0.4; }; + # test #3 + listen-on port @EXTRAPORT3@ tls self-signed { 10.53.0.3; }; + listen-on port @EXTRAPORT4@ tls self-signed { 10.53.0.3; }; + listen-on port @EXTRAPORT3@ { 10.53.0.4; }; + listen-on port @EXTRAPORT4@ { 10.53.0.4; }; + # test #4 + listen-on port @EXTRAPORT1@ { 10.53.0.5; }; + listen-on port @EXTRAPORT2@ { 10.53.0.5; }; + listen-on port @EXTRAPORT1@ tls self-signed { 10.53.0.6; }; + # test #5 + listen-on port @EXTRAPORT3@ tls self-signed { 10.53.0.1; }; + listen-on port @EXTRAPORT4@ tls self-signed { 10.53.0.1; }; + listen-on port @EXTRAPORT3@ { 10.53.0.2; }; + # test #6 + listen-on port @EXTRAPORT5@ { 10.53.0.1; }; + # test #7 + listen-on port @EXTRAPORT6@ tls self-signed { 10.53.0.1; }; + # test #7 + listen-on port @EXTRAPORT7@ tls self-signed { 10.53.0.1; }; + # test #8 + listen-on port @EXTRAPORT8@ { 10.53.0.1; }; + ## + listen-on-v6 { none; }; + recursion no; + notify explicit; + statistics-file "named.stats"; + dnssec-validation yes; + tcp-initial-timeout 1200; +}; + +zone "example0" { + type primary; + file "example.db"; + allow-transfer port @TLSPORT@ transport tls { any; }; +}; + +zone "example1" { + type primary; + file "example.db"; + allow-transfer port @EXTRAPORT1@ { any; }; +}; + +zone "example2" { + type primary; + file "example.db"; + allow-transfer transport tcp { any; }; +}; + +zone "example3" { + type primary; + file "example.db"; + allow-transfer transport tls { any; }; +}; + +zone "example4" { + type primary; + file "example.db"; + allow-transfer port @EXTRAPORT1@ transport tcp { any; }; +}; + +zone "example5" { + type primary; + file "example.db"; + allow-transfer port @EXTRAPORT3@ transport tls { any; }; +}; + +zone "example6" { + type primary; + file "example.db"; + allow-transfer port @EXTRAPORT5@ transport tcp { 10.53.0.7; 10.53.0.8; 10.53.0.9; }; +}; + +zone "example7" { + type primary; + file "example.db"; + allow-transfer port @EXTRAPORT6@ transport tls { 10.53.0.7; 10.53.0.8; 10.53.0.9; }; +}; + +zone "example8" { + type primary; + file "example.db"; + allow-transfer port @EXTRAPORT7@ transport tls { 10.53.0.1; 10.53.0.2; 10.53.0.3; }; +}; + +zone "example9" { + type primary; + file "example.db"; + allow-transfer port @EXTRAPORT8@ transport tcp { 10.53.0.7; !10.53.0.8; 10.53.0.9; }; +}; diff --git a/bin/tests/system/transport-acl/self-signed-cert.pem b/bin/tests/system/transport-acl/self-signed-cert.pem new file mode 100644 index 0000000000..d56935317f --- /dev/null +++ b/bin/tests/system/transport-acl/self-signed-cert.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEwTCCAymgAwIBAgIUJm/nnhqH3omkx9PqEyewJhYg/sQwDQYJKoZIhvcNAQEL +BQAwbzELMAkGA1UEBhMCVUExGDAWBgNVBAgMD0toYXJraXYgT2JsYXN0JzEQMA4G +A1UEBwwHS2hhcmtpdjEMMAoGA1UECgwDSVNDMQ8wDQYDVQQLDAZTVy1FbmcxFTAT +BgNVBAMMDHRlc3QuaXNjLm9yZzAgFw0yMTExMjkxMTQ0MDRaGA8yMTIxMTEzMDEx +NDQwNFowbzELMAkGA1UEBhMCVUExGDAWBgNVBAgMD0toYXJraXYgT2JsYXN0JzEQ +MA4GA1UEBwwHS2hhcmtpdjEMMAoGA1UECgwDSVNDMQ8wDQYDVQQLDAZTVy1Fbmcx +FTATBgNVBAMMDHRlc3QuaXNjLm9yZzCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCC +AYoCggGBAM8hzYSedQFajsjJKVnZ3BeWLOGULJO2ixQZ/vMnAk6q5a6JFST5DYVA +G84S8GKzswZibNNuKJnuuQO3mBE2+Pioc+vxtewxlzbcQ2EaKgbx5IVezzHtQUYw +WUUdSv7ViKOVeaI9jvXqpYUbbtLogSVkPB+/oWU1Wu4y/TkXc4wEqBxQx+P4kNnj +stCP7r5HMkvBqQgmod5rjqLFohtIQbEhjSBaoK+td25vWUvfG/isduiKx52tC4k3 +CBnBOIfvgkNmJk5Rh3RufbiyBSCtgBcH3wp9VSByqC7roFQqzBkZm0aCmuggNmXb +OXU7klEyVmAeiqLvfQSkjNsDmlaTsHCszgIB9RPA4f07KV62uFsdOu0K48yXBnEa +nZeIFqwuTS+PU7T+SnWQGoJLDvCa6IPERqk+5j94BET84/z942WLVqSLlqAoa1rF +5686m2Dgj10SRUpE99bmVg+HZRwO/ZbkLgu+tILqpYpnKP6n8FDpjW0Jnl77uw9S +UeAvbGyw5QIDAQABo1MwUTAdBgNVHQ4EFgQUJV5YRDD9iF+uz9AFx5fA86CtlVQw +HwYDVR0jBBgwFoAUJV5YRDD9iF+uz9AFx5fA86CtlVQwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAYEAi8sOMYGFs6n1C23vXorx5Zbbym5QkUVgYbxe +9VaBy0Y/PgvXaxtz8zytbtFhyU5izXNZ7k8A4vnJ/TGxoIj503ArBMZj+CiwIBVI +yMzheDp+MY4F19OIy/TsQglYeOEhK/PA9uj5GZYE1Ar6Qck4wl2vk3iaTMsaniyV +zPqCiso2YDLISSvF3nvLcTQ8nX6JyYR/3J0t5biLcissPvubgzguoULRn2VwWw/7 +MaRXXPMTBTyCAylJrSgfBKvYmJcnHHocTAZkGElDaYHfALlR+5K9wi/QYwz3kFpN +mS55yjSBlPPxH0rZw8fOdCLNbyzPjP+aXXoTUJa5/X7RNGKQTcuohektsuU1quxo +lugrRYjhiytqBUek3qtBJfmX28LnfZHyKpDpHO6wykQS7FTWb69c6tvAzlwFbH7o +onyhZz1Z2iXw4u7N4nTlj1VqHVMiEr2KUfxtOm5HQ7tZFSaWIA0HfIRB7WD3Escz +DY3Bbu9bS711Yywp+NpvOqBSvMon +-----END CERTIFICATE----- diff --git a/bin/tests/system/transport-acl/self-signed-key.pem b/bin/tests/system/transport-acl/self-signed-key.pem new file mode 100644 index 0000000000..5d9748bac7 --- /dev/null +++ b/bin/tests/system/transport-acl/self-signed-key.pem @@ -0,0 +1,40 @@ +-----BEGIN PRIVATE KEY----- +MIIG/AIBADANBgkqhkiG9w0BAQEFAASCBuYwggbiAgEAAoIBgQDPIc2EnnUBWo7I +ySlZ2dwXlizhlCyTtosUGf7zJwJOquWuiRUk+Q2FQBvOEvBis7MGYmzTbiiZ7rkD +t5gRNvj4qHPr8bXsMZc23ENhGioG8eSFXs8x7UFGMFlFHUr+1YijlXmiPY716qWF +G27S6IElZDwfv6FlNVruMv05F3OMBKgcUMfj+JDZ47LQj+6+RzJLwakIJqHea46i +xaIbSEGxIY0gWqCvrXdub1lL3xv4rHboisedrQuJNwgZwTiH74JDZiZOUYd0bn24 +sgUgrYAXB98KfVUgcqgu66BUKswZGZtGgproIDZl2zl1O5JRMlZgHoqi730EpIzb +A5pWk7BwrM4CAfUTwOH9OyletrhbHTrtCuPMlwZxGp2XiBasLk0vj1O0/kp1kBqC +Sw7wmuiDxEapPuY/eARE/OP8/eNli1aki5agKGtaxeevOptg4I9dEkVKRPfW5lYP +h2UcDv2W5C4LvrSC6qWKZyj+p/BQ6Y1tCZ5e+7sPUlHgL2xssOUCAwEAAQKCAYAy +VN9wy2RZKN0rUx5WNAc0QAy13+CZIDFZeBuokCESZpqbN7pImrA7YeGfyKBbC5mE +AqS5F7qL9SNGEPXFsRr8qUpJ2hk/xKke7pT84nO17k9+TRSB6EoFOThn//86Pz8N +qQO+dcDoZtVDq+/ZFiBTqrClclZQlo969C7uEZHFQ1hqUQLRlZP1LkxEO8VivUAu +gmeFkIWi23X0fZuvj3ZPCX0WkI8dQUSVND95nURZv+bBCQAKg4MbG6E/SOFovrzz +ohKK2zqSU+ncfWROYX/ulKMJKIhOKtxkprBnj2nSemTUEf5gDk9oDqsYClGmEcSL +XvNxq3WpVt4u7Fsr1QZ6fh/IYIQnKvI/H0wwYojtzkh3FGdb/K0dnKeoebUqlc9Q +4UwKGshhcbk2130t/zIdd5wnL5uj+xjh0cYSO5JqlcZwXC97SWDmEowCo8M/k8ie +c9cQeIOXUKvT3DvnEh1LAtfI8gW3g9GVHad4k25dQ4ZSiyXsKL2+mOWn+4WmQx0C +gcEA6UqykoDp2j6nfMA+5fEfNOplyXJMyTBxMoaFb+cO8P2qjjKOMyLJewXqW/3g +wWaPcl3dGVCPaqmQxf+fDEarSkDxkroN02YaQy3xdAAZvoUDc00VKq9BFe3TZEuP +7/sN3t3Ey7K5KVyKgh4cGPqSCCXrk3OPCyiRFxWa4wQAXuntT1iXkXGzXuoDPzCH +xWRiM+z3se6PdoPXMbJhuL04b4CIUmHSrGbqtO5bi6IDOksIhaKMFs4c7escSF+7 +jj0zAoHBAONLPcUT9uhzMIXe9BBdRYms65G3VjsTbS8MC/QiR6nl5/evQb0hDp0G +/tbLf9F9QVMA2onhK1mjafHFC4oVrwrLT+VZezKsQm3ICoqOFqxL+6dAu93A2dDA +99YCc6pCrmagaDpA5tz1UwBwA77pl2aMV2g7iIe2p+hmL6dx6Tp8jN+Mu0KXViyT +gPG9LITJQSu13EZgRukNnYu7+L2+NWfyGCbfCJ5/2qXmryjefoboR48sa8jZyUmQ +rf/VAG3phwKBwDE/lqD82+E5tsvMHbsXAtp93Q0AtxsFwe/DnCm6YloXgsjP/Vro +LhZtckMHPko1p3SiQgmVCyGeODTEOMQzqvda7GRoKIEHHeYurbkqSEUC+W5+yEgh +hSDm+uhCV1l26z+wG1pRGWuU4JyFVLMlOmzD7I5NJ9ZYMwDni7H+50EiKvnEHwMS +OKaByjutuAvAnEaP8N48GUcQn/4axSxlraNERAL4KaxBcazOYL8CbaIBswPbA63Q +xySmrGrO4t4tJwKBwGITmnDKv5Tn930cimXxSUsyAWgcGypcpJVTdmj+zbuDCAg5 +aH1qoTqixR38K4hCqwhc6u/p6GHCgLmhU+xelOxsdGo7pUxlRjjGw72ruB7anpk5 +9pamW5aXXZnL7wr9wPFpr+/LB5M6jHk43HTpqLnIPwMsBSrCZ0uBpHh1T7U7/zGL +MVZ3pOiRMWeeQHJ/wQ5SZ906N/7iMCQWlSuSwsq6jS9guABknP1PQC+7ag9edVpT +SaMeTpvewSYOTCQhSwKBwEmZP/Jh76G3bETPSPcIyPB0vgYmYiAftmvtwHzUL14V +dOfNbwXF6WiepSceLbw99LNpMwfRfKBGVDLRhKMqL7QR8ZKNew5AvfXVZ1yDNKu+ +/4hqFLUhsAARsfNofAzvKOtWmghVBzO9TauAyv3prFgjfvDkA+EZ2amDvXChkP/Q +7ck2aIUu9Sr4kPTUigIRlu6c18QQiLobXC7yKx6GhEpJsh9xGHHDJqkG16l+u1ju +bEd5UJArJoST5lff5y7MyQ== +-----END PRIVATE KEY----- diff --git a/bin/tests/system/transport-acl/setup.sh b/bin/tests/system/transport-acl/setup.sh new file mode 100644 index 0000000000..a54dba7732 --- /dev/null +++ b/bin/tests/system/transport-acl/setup.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 disable=SC1091 +. ../conf.sh + +$SHELL clean.sh + +$SHELL "${TOP_SRCDIR}"/bin/tests/system/genzone.sh 2 > ns1/example.db + +copy_setports ns1/named.conf.in ns1/named.conf diff --git a/bin/tests/system/transport-acl/tests.sh b/bin/tests/system/transport-acl/tests.sh new file mode 100644 index 0000000000..339ad41c58 --- /dev/null +++ b/bin/tests/system/transport-acl/tests.sh @@ -0,0 +1,120 @@ +#!/bin/sh +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 disable=SC1091 +. ../conf.sh + +dig_out_basename="dig.out.test" +testing="testing allow-transfer transport ACL functionality" + +dig_with_opts() { + # shellcheck disable=SC2086 + "$DIG" +noadd +nosea +nostat +noquest +nocmd "$@" +} + +status=0 +n=0 + +run_dig_test () { + test_message="$1" + shift + n=$((n+1)) + echo_i "$test_message ($n)" + ret=0 + dig_with_opts "$@" > "$dig_out_basename$n" || ret=1 +} + +run_dig_expect_axfr_success () { + run_dig_test "$@" + grep "; Transfer failed" "$dig_out_basename$n" > /dev/null && ret=1 + if [ $ret != 0 ]; then echo_i "failed"; fi + status=$((status+ret)) +} + +run_dig_expect_axfr_failure () { + run_dig_test "$@" + grep "; Transfer failed" "$dig_out_basename$n" > /dev/null || ret=1 + if [ $ret != 0 ]; then echo_i "failed"; fi + status=$((status + ret)) +} + +# generic tests +run_dig_expect_axfr_success "$testing for XoT" -p "${TLSPORT}" +tls -b 10.53.0.10 @10.53.0.1 axfr example0 + +run_dig_expect_axfr_failure "$testing XFR via TCP (failure expected)" -p "${PORT}" +tcp -b 10.53.0.10 @10.53.0.1 axfr example0 + +# 1. Test allow-transfer port X, transfer works with TCP and TLS on port X but not port Y. + +run_dig_expect_axfr_success "$testing for XFR via TCP" -p "${EXTRAPORT1}" +tcp -b 10.53.0.10 @10.53.0.1 axfr example1 + +run_dig_expect_axfr_success "$testing for XoT" -p "${EXTRAPORT1}" +tls -b 10.53.0.10 @10.53.0.2 axfr example1 + +run_dig_expect_axfr_failure "$testing for XFR via TCP (failure expected)" -p "${EXTRAPORT2}" +tcp -b 10.53.0.10 @10.53.0.1 axfr example1 + +run_dig_expect_axfr_failure "$testing for XoT (failure expected)" -p "${EXTRAPORT2}" +tls -b 10.53.0.10 @10.53.0.2 axfr example1 + +# 2. Test allow-transfer transport tcp, transfer works with TCP on any port but not TLS. + +run_dig_expect_axfr_success "$testing for XFR via TCP" -p "${EXTRAPORT1}" +tcp -b 10.53.0.10 @10.53.0.3 axfr example2 + +run_dig_expect_axfr_success "$testing for XFR via TCP" -p "${EXTRAPORT2}" +tcp -b 10.53.0.10 @10.53.0.3 axfr example2 + +run_dig_expect_axfr_failure "$testing for XoT (failure expected)" -p "${EXTRAPORT1}" +tls -b 10.53.0.10 @10.53.0.4 axfr example2 + +run_dig_expect_axfr_failure "$testing for XoT (failure expected)" -p "${EXTRAPORT2}" +tls -b 10.53.0.10 @10.53.0.4 axfr example2 + +# 3. Test allow-transfer transport tls, transfer works with TLS on any port but not TCP. +run_dig_expect_axfr_success "$testing for XoT" -p "${EXTRAPORT3}" +tls -b 10.53.0.10 @10.53.0.3 axfr example3 + +run_dig_expect_axfr_success "$testing for XoT" -p "${EXTRAPORT4}" +tls -b 10.53.0.10 @10.53.0.3 axfr example3 + +run_dig_expect_axfr_failure "$testing for XFR via TCP (failure expected)" -p "${EXTRAPORT3}" +tcp -b 10.53.0.10 @10.53.0.4 axfr example3 + +run_dig_expect_axfr_failure "$testing for XFR via TCP (failure expected)" -p "${EXTRAPORT4}" +tcp -b 10.53.0.10 @10.53.0.4 axfr example3 + +# 4. Test allow-transfer port X transport tcp, transfer works with TCP on port X but not port Y and not with TLS on port X. + +run_dig_expect_axfr_success "$testing for XFR via TCP" -p "${EXTRAPORT1}" +tcp -b 10.53.0.10 @10.53.0.5 axfr example4 + +run_dig_expect_axfr_failure "$testing for XFR via TCP (failure expected)" -p "${EXTRAPORT2}" +tcp -b 10.53.0.10 @10.53.0.5 axfr example4 + +run_dig_expect_axfr_failure "$testing for XoT (failure expected)" -p "${EXTRAPORT1}" +tls -b 10.53.0.10 @10.53.0.6 axfr example4 + +# 5. Test allow-transfer port X transport tls, transfer works with TLS on port X but not port Y and not with TCP on port X. + +run_dig_expect_axfr_success "$testing for XoT" -p "${EXTRAPORT3}" +tls -b 10.53.0.10 @10.53.0.1 axfr example5 + +run_dig_expect_axfr_failure "$testing for XoT (failure expected)" -p "${EXTRAPORT4}" +tls -b 10.53.0.10 @10.53.0.1 axfr example5 + +run_dig_expect_axfr_failure "$testing for XFR via TCP (failure expected)" -p "${EXTRAPORT3}" +tcp -b 10.53.0.10 @10.53.0.2 axfr example5 + +# 6. Test with multiple allow-transfer available, first ACL is a match. +run_dig_expect_axfr_success "$testing for XFR via TCP" -p "${EXTRAPORT5}" +tcp -b 10.53.0.7 @10.53.0.1 axfr example6 + +run_dig_expect_axfr_failure "$testing for XFR via TCP (failure expected)" -p "${EXTRAPORT5}" +tcp -b 10.53.0.6 @10.53.0.1 axfr example6 + +# 7. Test with multiple allow-transfer available, last ACL is a match. +run_dig_expect_axfr_success "$testing for XoT" -p "${EXTRAPORT6}" +tls -b 10.53.0.9 @10.53.0.1 axfr example7 + +run_dig_expect_axfr_failure "$testing for XoT (failure expected)" -p "${EXTRAPORT6}" +tls -b 10.53.0.6 @10.53.0.1 axfr example7 + +# 8. Test with multiple allow-transfer available, no ACL is a match. +run_dig_expect_axfr_failure "$testing for XoT (failure expected)" -p "${EXTRAPORT7}" +tls -b 10.53.0.7 @10.53.0.1 axfr example8 + +# 9. Test with multiple allow-transfer available, negated ACL is used. +run_dig_expect_axfr_success "$testing for XFR via TCP" -p "${EXTRAPORT8}" +tcp -b 10.53.0.7 @10.53.0.1 axfr example9 + +run_dig_expect_axfr_failure "$testing for XoT (failure expected)" -p "${EXTRAPORT8}" +tcp -b 10.53.0.8 @10.53.0.1 axfr example9 + +run_dig_expect_axfr_success "$testing for XFR via TCP" -p "${EXTRAPORT8}" +tcp -b 10.53.0.9 @10.53.0.1 axfr example9 + +echo_i "exit status: $status" +[ $status -eq 0 ] || exit 1 diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 0562bbe627..c69148989c 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -2416,6 +2416,14 @@ for details on how to specify IP address lists. statement set in ``options`` or ``view``. If not specified, the default is to allow transfers to all hosts. + The transport level limitations can also be specified. In + particular, zone transfers can be restricted to a specific port and + DNS transport protocol by using the options ``port`` and + ``transport``. Zone transfers are currently only possible via the + TCP and TLS transports; either option can be specified. + + For example: ``allow-transfer port 853 transport tls { any; };`` + ``blackhole`` This specifies a list of addresses which the server does not accept queries from or use to resolve a query. Queries from these addresses are not diff --git a/doc/man/named.conf.5in b/doc/man/named.conf.5in index de092a77df..1e285a2d0e 100644 --- a/doc/man/named.conf.5in +++ b/doc/man/named.conf.5in @@ -233,7 +233,8 @@ options { allow\-query\-on { address_match_element; ... }; allow\-recursion { address_match_element; ... }; allow\-recursion\-on { address_match_element; ... }; - allow\-transfer { address_match_element; ... }; + allow\-transfer [ port integer ] [ transport string ] { + address_match_element; ... }; allow\-update { address_match_element; ... }; allow\-update\-forwarding { address_match_element; ... }; also\-notify [ port integer ] [ dscp integer ] { ( @@ -708,7 +709,8 @@ view string [ class ] { allow\-query\-on { address_match_element; ... }; allow\-recursion { address_match_element; ... }; allow\-recursion\-on { address_match_element; ... }; - allow\-transfer { address_match_element; ... }; + allow\-transfer [ port integer ] [ transport string ] { + address_match_element; ... }; allow\-update { address_match_element; ... }; allow\-update\-forwarding { address_match_element; ... }; also\-notify [ port integer ] [ dscp integer ] { ( @@ -992,7 +994,8 @@ view string [ class ] { allow\-notify { address_match_element; ... }; allow\-query { address_match_element; ... }; allow\-query\-on { address_match_element; ... }; - allow\-transfer { address_match_element; ... }; + allow\-transfer [ port integer ] [ transport string ] { + address_match_element; ... }; allow\-update { address_match_element; ... }; allow\-update\-forwarding { address_match_element; ... }; also\-notify [ port integer ] [ dscp integer ] { ( @@ -1116,7 +1119,8 @@ zone string [ class ] { allow\-notify { address_match_element; ... }; allow\-query { address_match_element; ... }; allow\-query\-on { address_match_element; ... }; - allow\-transfer { address_match_element; ... }; + allow\-transfer [ port integer ] [ transport string ] { + address_match_element; ... }; allow\-update { address_match_element; ... }; allow\-update\-forwarding { address_match_element; ... }; also\-notify [ port integer ] [ dscp integer ] { ( diff --git a/doc/misc/master.zoneopt b/doc/misc/master.zoneopt index e51eee9bcc..69134fd1f4 100644 --- a/doc/misc/master.zoneopt +++ b/doc/misc/master.zoneopt @@ -2,7 +2,7 @@ zone [ ] { type ( master | primary ); allow-query { ; ... }; allow-query-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { ; ... }; allow-update { ; ... }; also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; diff --git a/doc/misc/master.zoneopt.rst b/doc/misc/master.zoneopt.rst index 985e2b404f..597a8dbdd9 100644 --- a/doc/misc/master.zoneopt.rst +++ b/doc/misc/master.zoneopt.rst @@ -4,7 +4,7 @@ type ( master | primary ); allow-query { ; ... }; allow-query-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { ; ... }; allow-update { ; ... }; also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index 50556fc9ec..a21182789d 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -3,7 +3,7 @@ zone [ ] { allow-notify { ; ... }; allow-query { ; ... }; allow-query-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; diff --git a/doc/misc/mirror.zoneopt.rst b/doc/misc/mirror.zoneopt.rst index d481248fb0..668207601c 100644 --- a/doc/misc/mirror.zoneopt.rst +++ b/doc/misc/mirror.zoneopt.rst @@ -5,7 +5,7 @@ allow-notify { ; ... }; allow-query { ; ... }; allow-query-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; diff --git a/doc/misc/options b/doc/misc/options index 86967657ae..b62967ef0c 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -90,7 +90,8 @@ options { allow-query-on { ; ... }; allow-recursion { ; ... }; allow-recursion-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { + ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( @@ -484,7 +485,8 @@ view [ ] { allow-query-on { ; ... }; allow-recursion { ; ... }; allow-recursion-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { + ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( @@ -770,7 +772,8 @@ view [ ] { allow-notify { ; ... }; allow-query { ; ... }; allow-query-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { + ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( @@ -886,7 +889,8 @@ zone [ ] { allow-notify { ; ... }; allow-query { ; ... }; allow-query-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { + ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( diff --git a/doc/misc/options.active b/doc/misc/options.active index bd4ceb26ae..a3e2fbe3f0 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -89,7 +89,8 @@ options { allow-query-on { ; ... }; allow-recursion { ; ... }; allow-recursion-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { + ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( @@ -481,7 +482,8 @@ view [ ] { allow-query-on { ; ... }; allow-recursion { ; ... }; allow-recursion-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { + ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( @@ -765,7 +767,8 @@ view [ ] { allow-notify { ; ... }; allow-query { ; ... }; allow-query-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { + ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( @@ -880,7 +883,8 @@ zone [ ] { allow-notify { ; ... }; allow-query { ; ... }; allow-query-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { + ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( diff --git a/doc/misc/options.grammar.rst b/doc/misc/options.grammar.rst index 2c440420f1..8e69a09686 100644 --- a/doc/misc/options.grammar.rst +++ b/doc/misc/options.grammar.rst @@ -9,7 +9,8 @@ allow-query-on { ; ... }; allow-recursion { ; ... }; allow-recursion-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { + ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( diff --git a/doc/misc/slave.zoneopt b/doc/misc/slave.zoneopt index be63c04e10..5fbbc065bc 100644 --- a/doc/misc/slave.zoneopt +++ b/doc/misc/slave.zoneopt @@ -3,7 +3,7 @@ zone [ ] { allow-notify { ; ... }; allow-query { ; ... }; allow-query-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; diff --git a/doc/misc/slave.zoneopt.rst b/doc/misc/slave.zoneopt.rst index 93996f47d8..3a603a044c 100644 --- a/doc/misc/slave.zoneopt.rst +++ b/doc/misc/slave.zoneopt.rst @@ -5,7 +5,7 @@ allow-notify { ; ... }; allow-query { ; ... }; allow-query-on { ; ... }; - allow-transfer { ; ... }; + allow-transfer [ port ] [ transport ] { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 9fe3c2bfef..8932ffcca1 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -35,7 +35,13 @@ Removed Features Feature Changes ~~~~~~~~~~~~~~~ -- None. +- The ``allow-transfers`` option was extended to accept additional + ``port`` and ``transport`` parameters, to further restrict zone + transfers to a particular port and DNS transport protocol. Either of + these options can be specified. + + For example: ``allow-transfer port 853 transport tls { any; };`` + :gl:`#2776` Bug Fixes ~~~~~~~~~ diff --git a/lib/bind9/check.c b/lib/bind9/check.c index d127b6efa5..1130983732 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -475,6 +475,47 @@ checkacl(const char *aclname, cfg_aclconfctx_t *actx, const cfg_obj_t *zconfig, if (acl != NULL) { dns_acl_detach(&acl); } + + if (strcasecmp(aclname, "allow-transfer") == 0 && + cfg_obj_istuple(aclobj)) { + const cfg_obj_t *obj_port = cfg_tuple_get( + cfg_tuple_get(aclobj, "port-transport"), "port"); + const cfg_obj_t *obj_proto = cfg_tuple_get( + cfg_tuple_get(aclobj, "port-transport"), "transport"); + + if (cfg_obj_isuint32(obj_port) && + cfg_obj_asuint32(obj_port) >= UINT16_MAX) { + cfg_obj_log(obj_port, logctx, ISC_LOG_ERROR, + "port value '%u' is out of range", + + cfg_obj_asuint32(obj_port)); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + + if (cfg_obj_isstring(obj_proto)) { + const char *allowed[] = { "tcp", "tls" }; + const char *transport = cfg_obj_asstring(obj_proto); + bool found = false; + for (size_t i = 0; i < ARRAY_SIZE(allowed); i++) { + if (strcasecmp(transport, allowed[i]) == 0) { + found = true; + } + } + + if (!found) { + cfg_obj_log(obj_proto, logctx, ISC_LOG_ERROR, + "'%s' is not a valid transport " + "protocol for " + "zone " + "transfers. Please specify either " + "'tcp' or 'tls'", + transport); + result = ISC_R_FAILURE; + } + } + } return (result); } diff --git a/lib/dns/acl.c b/lib/dns/acl.c index 33fcefef0a..a3cd6d4214 100644 --- a/lib/dns/acl.c +++ b/lib/dns/acl.c @@ -71,6 +71,9 @@ dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) { acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t)); acl->alloc = n; memset(acl->elements, 0, n * sizeof(dns_aclelement_t)); + ISC_LIST_INIT(acl->ports_and_transports); + acl->port_proto_entries = 0; + *target = acl; return (ISC_R_SUCCESS); } @@ -241,6 +244,54 @@ dns_acl_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner, return (ISC_R_SUCCESS); } +isc_result_t +dns_acl_match_port_transport(const isc_netaddr_t *reqaddr, + const in_port_t local_port, + const isc_nmsocket_type_t transport, + const bool encrypted, const dns_name_t *reqsigner, + const dns_acl_t *acl, const dns_aclenv_t *env, + int *match, const dns_aclelement_t **matchelt) { + isc_result_t result = ISC_R_SUCCESS; + dns_acl_port_transports_t *next; + + REQUIRE(reqaddr != NULL); + REQUIRE(DNS_ACL_VALID(acl)); + + if (!ISC_LIST_EMPTY(acl->ports_and_transports)) { + result = ISC_R_FAILURE; + for (next = ISC_LIST_HEAD(acl->ports_and_transports); + next != NULL; next = ISC_LIST_NEXT(next, link)) + { + bool match_port = true; + bool match_transport = true; + + if (next->port != 0) { + /* Port is specified. */ + match_port = (local_port == next->port); + } + if (next->transports != 0) { + /* Transport protocol is specified. */ + match_transport = + ((transport & next->transports) == + transport && + next->encrypted == encrypted); + } + + if (match_port && match_transport) { + result = next->negative ? ISC_R_FAILURE + : ISC_R_SUCCESS; + break; + } + } + } + + if (result != ISC_R_SUCCESS) { + return (result); + } + + return (dns_acl_match(reqaddr, reqsigner, acl, env, match, matchelt)); +} + /* * Merge the contents of one ACL into another. Call dns_iptable_merge() * for the IP tables, then concatenate the element arrays. @@ -347,6 +398,11 @@ dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, bool pos) { dns_acl_node_count(dest) = nodes; } + /* + * Merge ports and transports + */ + dns_acl_merge_ports_transports(dest, source, pos); + return (ISC_R_SUCCESS); } @@ -449,6 +505,7 @@ dns_acl_attach(dns_acl_t *source, dns_acl_t **target) { static void destroy(dns_acl_t *dacl) { unsigned int i; + dns_acl_port_transports_t *port_proto; INSIST(!ISC_LINK_LINKED(dacl, nextincache)); @@ -470,6 +527,17 @@ destroy(dns_acl_t *dacl) { if (dacl->iptable != NULL) { dns_iptable_detach(&dacl->iptable); } + + port_proto = ISC_LIST_HEAD(dacl->ports_and_transports); + while (port_proto != NULL) { + dns_acl_port_transports_t *next = NULL; + + next = ISC_LIST_NEXT(port_proto, link); + ISC_LIST_DEQUEUE(dacl->ports_and_transports, port_proto, link); + isc_mem_put(dacl->mctx, port_proto, sizeof(*port_proto)); + port_proto = next; + } + isc_refcount_destroy(&dacl->refcount); dacl->magic = 0; isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl)); @@ -707,3 +775,57 @@ dns_aclenv_detach(dns_aclenv_t **aclenvp) { dns__aclenv_destroy(aclenv); } } + +void +dns_acl_add_port_transports(dns_acl_t *acl, const in_port_t port, + const uint32_t transports, const bool encrypted, + const bool negative) { + dns_acl_port_transports_t *port_proto; + REQUIRE(DNS_ACL_VALID(acl)); + REQUIRE(port != 0 || transports != 0); + + port_proto = isc_mem_get(acl->mctx, sizeof(*port_proto)); + *port_proto = (dns_acl_port_transports_t){ .port = port, + .transports = transports, + .encrypted = encrypted, + .negative = negative }; + + ISC_LINK_INIT(port_proto, link); + + ISC_LIST_APPEND(acl->ports_and_transports, port_proto, link); + acl->port_proto_entries++; +} + +void +dns_acl_merge_ports_transports(dns_acl_t *dest, dns_acl_t *source, bool pos) { + dns_acl_port_transports_t *next; + + REQUIRE(DNS_ACL_VALID(dest)); + REQUIRE(DNS_ACL_VALID(source)); + + const bool negative = !pos; + + /* + * Merge ports and transports + */ + for (next = ISC_LIST_HEAD(source->ports_and_transports); next != NULL; + next = ISC_LIST_NEXT(next, link)) + { + const bool next_positive = !next->negative; + bool add_negative; + + /* + * Reverse sense of positives if this is a negative acl. The + * logic is used (and, thus, enforced) by dns_acl_merge(), + * from which dns_acl_merge_ports_transports() is called. + */ + if (negative && next_positive) { + add_negative = true; + } else { + add_negative = next->negative; + } + + dns_acl_add_port_transports(dest, next->port, next->transports, + next->encrypted, add_negative); + } +} diff --git a/lib/dns/include/dns/acl.h b/lib/dns/include/dns/acl.h index e122340d1b..51678eee3d 100644 --- a/lib/dns/include/dns/acl.h +++ b/lib/dns/include/dns/acl.h @@ -52,6 +52,14 @@ typedef enum { dns_aclelementtype_any } dns_aclelementtype_t; +typedef struct dns_acl_port_transports { + in_port_t port; + uint32_t transports; + bool encrypted; /* for protocols with optional encryption (e.g. HTTP) */ + bool negative; + ISC_LINK(struct dns_acl_port_transports) link; +} dns_acl_port_transports_t; + typedef struct dns_aclipprefix dns_aclipprefix_t; struct dns_aclipprefix { @@ -83,6 +91,8 @@ struct dns_acl { unsigned int length; /*%< Elements initialized */ char *name; /*%< Temporary use only */ ISC_LINK(dns_acl_t) nextincache; /*%< Ditto */ + ISC_LIST(dns_acl_port_transports_t) ports_and_transports; + size_t port_proto_entries; }; struct dns_aclenv { @@ -270,4 +280,46 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner, * returned through 'matchelt' is not necessarily 'e' itself. */ +isc_result_t +dns_acl_match_port_transport(const isc_netaddr_t *reqaddr, + const in_port_t local_port, + const isc_nmsocket_type_t transport, + const bool encrypted, const dns_name_t *reqsigner, + const dns_acl_t *acl, const dns_aclenv_t *env, + int *match, const dns_aclelement_t **matchelt); +/*%< + * Like dns_acl_match, but able to match the server port and + * transport, as well as encryption status. + * + * Requires: + *\li 'reqaddr' is not 'NULL'; + *\li 'acl' is a valid ACL object. + */ + +void +dns_acl_add_port_transports(dns_acl_t *acl, const in_port_t port, + const uint32_t transports, const bool encrypted, + const bool negative); +/*%< + * Adds a "port-transports" entry to the specified ACL. Transports + * are specified as a bit-set 'transports' consisting of entries + * defined in the isc_nmsocket_type enumeration. + * + * Requires: + *\li 'acl' is a valid ACL object; + *\li either 'port' or 'transports' is not equal to 0. + */ + +void +dns_acl_merge_ports_transports(dns_acl_t *dest, dns_acl_t *source, bool pos); +/*%< + * Merges "port-transports" entries from the 'dest' ACL into + * the 'source' ACL. The 'pos' parameter works in a way similar to + * 'dns_acl_merge()'. + * + * Requires: + *\li 'dest' is a valid ACL object; + *\li 'source' is a valid ACL object. + */ + ISC_LANG_ENDDECLS diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index f662421a55..94fed339a3 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -500,8 +500,6 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, * 'cb'. */ -bool -isc_nm_is_tlsdns_handle(isc_nmhandle_t *handle); /*%< * Returns 'true' iff 'handle' is associated with a socket of type * 'isc_nm_tlsdnssocket'. @@ -647,6 +645,24 @@ isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl); * connection. */ +isc_nmsocket_type +isc_nm_socket_type(const isc_nmhandle_t *handle); +/*%< + * Returns the handle's underlying socket type. + * + * Requires: + * \li 'handle' is a valid netmgr handle object. + */ + +bool +isc_nm_has_encryption(const isc_nmhandle_t *handle); +/*%< + * Returns 'true' iff the handle's underlying transport does encryption. + * + * Requires: + * \li 'handle' is a valid netmgr handle object. + */ + void isc_nm_task_enqueue(isc_nm_t *mgr, isc_task_t *task, int threadid); /*%< diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h index b68365cd93..eb39ed8204 100644 --- a/lib/isc/include/isc/types.h +++ b/lib/isc/include/isc/types.h @@ -122,3 +122,23 @@ typedef enum { isc_statsformat_xml, isc_statsformat_json } isc_statsformat_t; + +typedef enum isc_nmsocket_type { + isc_nm_nonesocket = 0, + isc_nm_udpsocket = 1 << 1, + isc_nm_tcpsocket = 1 << 2, + isc_nm_tcpdnssocket = 1 << 3, + isc_nm_tlssocket = 1 << 4, + isc_nm_tlsdnssocket = 1 << 5, + isc_nm_httpsocket = 1 << 6, + isc_nm_maxsocket, + + isc_nm_udplistener, /* Aggregate of nm_udpsocks */ + isc_nm_tcplistener, + isc_nm_tlslistener, + isc_nm_tcpdnslistener, + isc_nm_tlsdnslistener, + isc_nm_httplistener +} isc_nmsocket_type; + +typedef isc_nmsocket_type isc_nmsocket_type_t; diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index 466ae5497a..539fef6fd3 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -2863,6 +2863,22 @@ isc__nm_http_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) { sock->h2.min_ttl = ttl; } +bool +isc__nm_http_has_encryption(const isc_nmhandle_t *handle) { + isc_nm_http_session_t *session; + isc_nmsocket_t *sock; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + + sock = handle->sock; + session = sock->h2.session; + + INSIST(VALID_HTTP2_SESSION(session)); + + return (isc_nm_socket_type(session->handle) == isc_nm_tlssocket); +} + static const bool base64url_validation_table[256] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index a281fbde76..da892b8415 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -709,21 +709,6 @@ struct isc_nm { #endif }; -typedef enum isc_nmsocket_type { - isc_nm_udpsocket, - isc_nm_udplistener, /* Aggregate of nm_udpsocks */ - isc_nm_tcpsocket, - isc_nm_tcplistener, - isc_nm_tcpdnslistener, - isc_nm_tcpdnssocket, - isc_nm_tlslistener, - isc_nm_tlssocket, - isc_nm_tlsdnslistener, - isc_nm_tlsdnssocket, - isc_nm_httplistener, - isc_nm_httpsocket -} isc_nmsocket_type; - /*% * A universal structure for either a single socket or a group of * dup'd/SO_REUSE_PORT-using sockets listening on the same interface. @@ -1710,6 +1695,9 @@ isc__nm_http_bad_request(isc_nmhandle_t *handle); * socket */ +bool +isc__nm_http_has_encryption(const isc_nmhandle_t *handle); + void isc__nm_http_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index e2d57f0ac1..3404d19ba0 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -3448,14 +3448,6 @@ isc_nm_xfr_allowed(isc_nmhandle_t *handle) { return (false); } -bool -isc_nm_is_tlsdns_handle(isc_nmhandle_t *handle) { - REQUIRE(VALID_NMHANDLE(handle)); - REQUIRE(VALID_NMSOCK(handle->sock)); - - return (handle->sock->type == isc_nm_tlsdnssocket); -} - bool isc_nm_is_http_handle(isc_nmhandle_t *handle) { REQUIRE(VALID_NMHANDLE(handle)); @@ -3500,6 +3492,36 @@ isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) { } } +isc_nmsocket_type +isc_nm_socket_type(const isc_nmhandle_t *handle) { + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + + return (handle->sock->type); +} + +bool +isc_nm_has_encryption(const isc_nmhandle_t *handle) { + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + + switch (handle->sock->type) { + case isc_nm_tlsdnssocket: +#if HAVE_LIBNGHTTP2 + case isc_nm_tlssocket: +#endif /* HAVE_LIBNGHTTP2 */ + return (true); +#if HAVE_LIBNGHTTP2 + case isc_nm_httpsocket: + return (isc__nm_http_has_encryption(handle)); +#endif /* HAVE_LIBNGHTTP2 */ + default: + return (false); + }; + + return (false); +} + #ifdef NETMGR_TRACE /* * Dump all active sockets in netmgr. We output to stderr diff --git a/lib/isccfg/aclconf.c b/lib/isccfg/aclconf.c index f748199b74..e74fa3b918 100644 --- a/lib/isccfg/aclconf.c +++ b/lib/isccfg/aclconf.c @@ -627,7 +627,7 @@ cfg_acl_fromconfig(const cfg_obj_t *caml, const cfg_obj_t *cctx, } isc_result_t -cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx, +cfg_acl_fromconfig2(const cfg_obj_t *acl_data, const cfg_obj_t *cctx, isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx, unsigned int nest_level, uint16_t family, dns_acl_t **target) { @@ -638,6 +638,10 @@ cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx, dns_iptable_t *iptab; int new_nest_level = 0; bool setpos; + const cfg_obj_t *caml = NULL; + const cfg_obj_t *obj_acl_tuple = NULL; + const cfg_obj_t *obj_port = NULL, *obj_transport = NULL; + bool is_tuple = false; if (nest_level != 0) { new_nest_level = nest_level - 1; @@ -647,6 +651,20 @@ cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx, REQUIRE(target != NULL); REQUIRE(*target == NULL || DNS_ACL_VALID(*target)); + REQUIRE(acl_data != NULL); + if (cfg_obj_islist(acl_data)) { + caml = acl_data; + } else { + INSIST(cfg_obj_istuple(acl_data)); + caml = cfg_tuple_get(acl_data, "aml"); + INSIST(caml != NULL); + obj_acl_tuple = cfg_tuple_get(acl_data, "port-transport"); + INSIST(obj_acl_tuple != NULL); + obj_port = cfg_tuple_get(obj_acl_tuple, "port"); + obj_transport = cfg_tuple_get(obj_acl_tuple, "transport"); + is_tuple = true; + } + if (*target != NULL) { /* * If target already points to an ACL, then we're being @@ -681,6 +699,54 @@ cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx, } } + if (is_tuple) { + uint16_t port = 0; + uint32_t transports = 0; + bool encrypted = false; + + if (obj_port != NULL && cfg_obj_isuint32(obj_port)) { + port = (uint16_t)cfg_obj_asuint32(obj_port); + } + + if (obj_transport != NULL && cfg_obj_isstring(obj_transport)) { + if (strcasecmp(cfg_obj_asstring(obj_transport), + "udp") == 0) { + transports = isc_nm_udpsocket; + encrypted = false; + } else if (strcasecmp(cfg_obj_asstring(obj_transport), + "tcp") == 0) { + transports = isc_nm_tcpdnssocket; + encrypted = false; + } else if (strcasecmp(cfg_obj_asstring(obj_transport), + "udp-tcp") == 0) { + /* Good ol' DNS over port 53 */ + transports = isc_nm_tcpdnssocket | + isc_nm_udpsocket; + encrypted = false; + } else if (strcasecmp(cfg_obj_asstring(obj_transport), + "tls") == 0) { + transports = isc_nm_tlsdnssocket; + encrypted = true; + } else if (strcasecmp(cfg_obj_asstring(obj_transport), + "http") == 0) { + transports = isc_nm_httpsocket; + encrypted = true; + } else if (strcasecmp(cfg_obj_asstring(obj_transport), + "http-plain") == 0) { + transports = isc_nm_httpsocket; + encrypted = false; + } else { + result = ISC_R_FAILURE; + goto cleanup; + } + } + + if (port != 0 || transports != 0) { + dns_acl_add_port_transports(dacl, port, transports, + encrypted, false); + } + } + de = dacl->elements; for (elt = cfg_list_first(caml); elt != NULL; elt = cfg_list_next(elt)) { @@ -787,6 +853,12 @@ cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx, if (de->nestedacl != NULL) { dns_acl_detach(&de->nestedacl); } + /* + * Merge the port-transports entries from the + * nested ACL into its parent. + */ + dns_acl_merge_ports_transports(dacl, inneracl, + !neg); dns_acl_attach(inneracl, &de->nestedacl); dns_acl_detach(&inneracl); /* Fall through. */ diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 4ba4b0a17c..86d1bf16d9 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -185,6 +185,39 @@ static cfg_type_t cfg_type_listenon = { "listenon", cfg_parse_tuple, /*% acl */ +/* + * Encrypted transfer related definitions + */ + +static cfg_tuplefielddef_t cfg_transport_acl_tuple_fields[] = { + { "port", &cfg_type_optional_port, 0 }, + { "transport", &cfg_type_astring, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_transport_acl_tuple = { + "transport-acl tuple", cfg_parse_kv_tuple, + cfg_print_kv_tuple, cfg_doc_kv_tuple, + &cfg_rep_tuple, cfg_transport_acl_tuple_fields +}; + +static cfg_tuplefielddef_t cfg_transport_acl_fields[] = { + { "port-transport", &cfg_transport_acl_tuple, 0 }, + { "aml", &cfg_type_bracketed_aml, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_transport_acl = { + "transport-acl", cfg_parse_tuple, cfg_print_tuple, + cfg_doc_tuple, &cfg_rep_tuple, cfg_transport_acl_fields +}; + +/* + * NOTE: To enable syntax which allows specifying port and protocol, + * replace 'cfg_type_bracketed_aml' with + * 'cfg_type_transport_acl'. + * + * Example: acl port 853 protocol tls { ... }; + */ static cfg_tuplefielddef_t acl_fields[] = { { "name", &cfg_type_astring, 0 }, { "value", &cfg_type_bracketed_aml, 0 }, @@ -2174,6 +2207,13 @@ static cfg_clausedef_t dnssecpolicy_clauses[] = { * Note: CFG_ZONE_* options indicate in which zone types this clause is * legal. */ +/* + * NOTE: To enable syntax which allows specifying port and protocol + * within 'allow-*' clauses, replace 'cfg_type_bracketed_aml' with + * 'cfg_type_transport_acl'. + * + * Example: allow-transfer port 853 protocol tls { ... }; + */ static cfg_clausedef_t zone_clauses[] = { { "allow-notify", &cfg_type_bracketed_aml, CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, @@ -2183,7 +2223,7 @@ static cfg_clausedef_t zone_clauses[] = { { "allow-query-on", &cfg_type_bracketed_aml, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB | CFG_ZONE_REDIRECT | CFG_ZONE_STATICSTUB }, - { "allow-transfer", &cfg_type_bracketed_aml, + { "allow-transfer", &cfg_type_transport_acl, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, { "allow-update", &cfg_type_bracketed_aml, CFG_ZONE_PRIMARY }, { "allow-update-forwarding", &cfg_type_bracketed_aml, diff --git a/lib/ns/client.c b/lib/ns/client.c index 0825fc3e30..f598387729 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -2553,6 +2553,7 @@ ns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr, dns_aclenv_t *env = client->manager->aclenv; isc_netaddr_t tmpnetaddr; int match; + isc_sockaddr_t local; if (acl == NULL) { if (default_allow) { @@ -2567,7 +2568,13 @@ ns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr, netaddr = &tmpnetaddr; } - result = dns_acl_match(netaddr, client->signer, acl, env, &match, NULL); + local = isc_nmhandle_localaddr(client->handle); + result = dns_acl_match_port_transport( + netaddr, isc_sockaddr_getport(&local), + isc_nm_socket_type(client->handle), + isc_nm_has_encryption(client->handle), client->signer, acl, env, + &match, NULL); + if (result != ISC_R_SUCCESS) { goto deny; /* Internal error, already logged. */ } diff --git a/lib/ns/query.c b/lib/ns/query.c index bf95b49f5a..71a65d4894 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -12064,8 +12064,9 @@ ns_query_start(ns_client_t *client, isc_nmhandle_t *handle) { query_error(client, DNS_R_NOTIMP, __LINE__); return; } - if (isc_nm_is_tlsdns_handle(handle) && - !isc_nm_xfr_allowed(handle)) { + if (isc_nm_socket_type(handle) == isc_nm_tlsdnssocket && + !isc_nm_xfr_allowed(handle)) + { /* * Currently this code is here for DoT, which * has more complex requirements for zone diff --git a/util/copyrights b/util/copyrights index 1d43fbd260..1a78494769 100644 --- a/util/copyrights +++ b/util/copyrights @@ -823,6 +823,11 @@ ./bin/tests/system/tools/clean.sh SH 2017,2018,2019,2020,2021 ./bin/tests/system/tools/setup.sh SH 2019,2020,2021 ./bin/tests/system/tools/tests.sh SH 2017,2018,2019,2020,2021 +./bin/tests/system/transport-acl/clean.sh SH 2021 +./bin/tests/system/transport-acl/self-signed-cert.pem X 2021 +./bin/tests/system/transport-acl/self-signed-key.pem X 2021 +./bin/tests/system/transport-acl/setup.sh SH 2021 +./bin/tests/system/transport-acl/tests.sh SH 2021 ./bin/tests/system/tsig/ans2/ans.pl PERL 2020,2021 ./bin/tests/system/tsig/badlocation X 2020,2021 ./bin/tests/system/tsig/badtime X 2020,2021