From 39a961112ff6acef846e2361db94b4f5e679ca81 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 7 May 2021 11:31:15 +0200 Subject: [PATCH 01/20] Change primaries objects to remote-servers Change the primaries configuration objects to the more generic remote-servers, that we can reuse for other purposes (such as parental-agents). --- bin/named/config.c | 2 +- bin/named/named.conf.rst | 58 ++++++++++++++++++---------------- doc/arm/reference.rst | 4 +-- doc/man/named.conf.5in | 58 ++++++++++++++++++---------------- doc/misc/master.zoneopt | 2 +- doc/misc/master.zoneopt.rst | 2 +- doc/misc/mirror.zoneopt | 6 ++-- doc/misc/mirror.zoneopt.rst | 6 ++-- doc/misc/options | 58 ++++++++++++++++++---------------- doc/misc/options.active | 58 ++++++++++++++++++---------------- doc/misc/options.grammar.rst | 9 +++--- doc/misc/primaries.grammar.rst | 8 ++--- doc/misc/redirect.zoneopt | 4 +-- doc/misc/redirect.zoneopt.rst | 4 +-- doc/misc/slave.zoneopt | 6 ++-- doc/misc/slave.zoneopt.rst | 6 ++-- doc/misc/stub.zoneopt | 4 +-- doc/misc/stub.zoneopt.rst | 4 +-- lib/bind9/check.c | 2 +- lib/isccfg/namedconf.c | 47 +++++++++++++-------------- 20 files changed, 183 insertions(+), 165 deletions(-) diff --git a/bin/named/config.c b/bin/named/config.c index 10087b47f3..86896efc51 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -767,7 +767,7 @@ resume: const cfg_obj_t *tls; addr = cfg_tuple_get(cfg_listelt_value(element), - "primarieselement"); + "remoteselement"); key = cfg_tuple_get(cfg_listelt_value(element), "key"); tls = cfg_tuple_get(cfg_listelt_value(element), "tls"); diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst index 39407490f6..cc3aad616c 100644 --- a/bin/named/named.conf.rst +++ b/bin/named/named.conf.rst @@ -145,10 +145,10 @@ MASTERS :: masters string [ port integer ] [ dscp - integer ] { ( primaries | ipv4_address - [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls - string ]; ... }; + integer ] { ( remote-servers | + ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key + string ] [ tls string ]; ... }; OPTIONS ^^^^^^^ @@ -167,9 +167,10 @@ OPTIONS allow-transfer { address_match_element; ... }; allow-update { address_match_element; ... }; allow-update-forwarding { address_match_element; ... }; - also-notify [ port integer ] [ dscp integer ] { ( primaries | - ipv4_address [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls string ]; ... }; + also-notify [ port integer ] [ dscp integer ] { ( + remote-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ tls + string ]; ... }; alt-transfer-source ( ipv4_address | * ) [ port ( integer | * ) ] [ dscp integer ]; alt-transfer-source-v6 ( ipv6_address | * ) [ port ( integer | @@ -185,7 +186,7 @@ OPTIONS blackhole { address_match_element; ... }; cache-file quoted_string; catalog-zones { zone string [ default-masters [ port integer ] - [ dscp integer ] { ( primaries | ipv4_address [ port + [ dscp integer ] { ( remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... } ] [ zone-directory quoted_string ] [ in-memory boolean ] [ min-update-interval @@ -478,10 +479,10 @@ PRIMARIES :: primaries string [ port integer ] [ dscp - integer ] { ( primaries | ipv4_address - [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls - string ]; ... }; + integer ] { ( remote-servers | + ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key + string ] [ tls string ]; ... }; SERVER ^^^^^^ @@ -586,9 +587,10 @@ VIEW allow-transfer { address_match_element; ... }; allow-update { address_match_element; ... }; allow-update-forwarding { address_match_element; ... }; - also-notify [ port integer ] [ dscp integer ] { ( primaries | - ipv4_address [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls string ]; ... }; + also-notify [ port integer ] [ dscp integer ] { ( + remote-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ tls + string ]; ... }; alt-transfer-source ( ipv4_address | * ) [ port ( integer | * ) ] [ dscp integer ]; alt-transfer-source-v6 ( ipv6_address | * ) [ port ( integer | @@ -598,7 +600,7 @@ VIEW auto-dnssec ( allow | maintain | off ); cache-file quoted_string; catalog-zones { zone string [ default-masters [ port integer ] - [ dscp integer ] { ( primaries | ipv4_address [ port + [ dscp integer ] { ( remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... } ] [ zone-directory quoted_string ] [ in-memory boolean ] [ min-update-interval @@ -864,7 +866,7 @@ VIEW allow-update { address_match_element; ... }; allow-update-forwarding { address_match_element; ... }; also-notify [ port integer ] [ dscp integer ] { ( - primaries | ipv4_address [ port integer ] | + remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; alt-transfer-source ( ipv4_address | * ) [ port ( @@ -905,7 +907,7 @@ VIEW masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); masters [ port integer ] [ dscp integer ] { ( - primaries | ipv4_address [ port integer ] | + remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; max-ixfr-ratio ( unlimited | percentage ); @@ -929,7 +931,7 @@ VIEW | * ) ] [ dscp integer ]; notify-to-soa boolean; primaries [ port integer ] [ dscp integer ] { ( - primaries | ipv4_address [ port integer ] | + remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; request-expire boolean; @@ -974,9 +976,10 @@ ZONE allow-transfer { address_match_element; ... }; allow-update { address_match_element; ... }; allow-update-forwarding { address_match_element; ... }; - also-notify [ port integer ] [ dscp integer ] { ( primaries | - ipv4_address [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls string ]; ... }; + also-notify [ port integer ] [ dscp integer ] { ( + remote-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ tls + string ]; ... }; alt-transfer-source ( ipv4_address | * ) [ port ( integer | * ) ] [ dscp integer ]; alt-transfer-source-v6 ( ipv6_address | * ) [ port ( integer | @@ -1012,8 +1015,8 @@ ZONE key-directory quoted_string; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port integer ] [ dscp integer ] { ( primaries | - ipv4_address [ port integer ] | ipv6_address [ port + masters [ port integer ] [ dscp integer ] { ( remote-servers + | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; max-ixfr-ratio ( unlimited | percentage ); max-journal-size ( default | unlimited | sizeval ); @@ -1035,9 +1038,10 @@ ZONE notify-source-v6 ( ipv6_address | * ) [ port ( integer | * ) ] [ dscp integer ]; notify-to-soa boolean; - primaries [ port integer ] [ dscp integer ] { ( primaries | - ipv4_address [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls string ]; ... }; + primaries [ port integer ] [ dscp integer ] { ( + remote-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ tls + string ]; ... }; request-expire boolean; request-ixfr boolean; serial-update-method ( date | increment | unixtime ); diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index f8eb970f10..4f563d4ed2 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -29,8 +29,8 @@ file documentation: ``address_match_list`` A list of one or more ``ip_addr``, ``ip_prefix``, ``key_id``, or ``acl_name`` elements; see :ref:`address_match_lists`. - ``primaries_list`` - A named list of one or more ``ip_addr`` with optional ``tls_id``, ``key_id`` and/or ``ip_port``. A ``primaries_list`` may include other ``primaries_list``. + ``remoteserver_list`` + A named list of one or more ``ip_addr`` with optional ``tls_id``, ``key_id`` and/or ``ip_port``. A ``remoteserver_list`` may include other ``remoteserver_list``. ``domain_name`` A quoted string which is used as a DNS name; for example. ``my.test.domain``. diff --git a/doc/man/named.conf.5in b/doc/man/named.conf.5in index 5a311c30db..ac1a1555f9 100644 --- a/doc/man/named.conf.5in +++ b/doc/man/named.conf.5in @@ -208,10 +208,10 @@ managed\-keys { string ( static\-key .nf .ft C masters string [ port integer ] [ dscp - integer ] { ( primaries | ipv4_address - [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls - string ]; ... }; + integer ] { ( remote\-servers | + ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key + string ] [ tls string ]; ... }; .ft P .fi .UNINDENT @@ -234,9 +234,10 @@ options { allow\-transfer { address_match_element; ... }; allow\-update { address_match_element; ... }; allow\-update\-forwarding { address_match_element; ... }; - also\-notify [ port integer ] [ dscp integer ] { ( primaries | - ipv4_address [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls string ]; ... }; + also\-notify [ port integer ] [ dscp integer ] { ( + remote\-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ tls + string ]; ... }; alt\-transfer\-source ( ipv4_address | * ) [ port ( integer | * ) ] [ dscp integer ]; alt\-transfer\-source\-v6 ( ipv6_address | * ) [ port ( integer | @@ -252,7 +253,7 @@ options { blackhole { address_match_element; ... }; cache\-file quoted_string; catalog\-zones { zone string [ default\-masters [ port integer ] - [ dscp integer ] { ( primaries | ipv4_address [ port + [ dscp integer ] { ( remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... } ] [ zone\-directory quoted_string ] [ in\-memory boolean ] [ min\-update\-interval @@ -553,10 +554,10 @@ plugin ( query ) string [ { unspecified\-text .nf .ft C primaries string [ port integer ] [ dscp - integer ] { ( primaries | ipv4_address - [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls - string ]; ... }; + integer ] { ( remote\-servers | + ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key + string ] [ tls string ]; ... }; .ft P .fi .UNINDENT @@ -685,9 +686,10 @@ view string [ class ] { allow\-transfer { address_match_element; ... }; allow\-update { address_match_element; ... }; allow\-update\-forwarding { address_match_element; ... }; - also\-notify [ port integer ] [ dscp integer ] { ( primaries | - ipv4_address [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls string ]; ... }; + also\-notify [ port integer ] [ dscp integer ] { ( + remote\-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ tls + string ]; ... }; alt\-transfer\-source ( ipv4_address | * ) [ port ( integer | * ) ] [ dscp integer ]; alt\-transfer\-source\-v6 ( ipv6_address | * ) [ port ( integer | @@ -697,7 +699,7 @@ view string [ class ] { auto\-dnssec ( allow | maintain | off ); cache\-file quoted_string; catalog\-zones { zone string [ default\-masters [ port integer ] - [ dscp integer ] { ( primaries | ipv4_address [ port + [ dscp integer ] { ( remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... } ] [ zone\-directory quoted_string ] [ in\-memory boolean ] [ min\-update\-interval @@ -963,7 +965,7 @@ view string [ class ] { allow\-update { address_match_element; ... }; allow\-update\-forwarding { address_match_element; ... }; also\-notify [ port integer ] [ dscp integer ] { ( - primaries | ipv4_address [ port integer ] | + remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; alt\-transfer\-source ( ipv4_address | * ) [ port ( @@ -1004,7 +1006,7 @@ view string [ class ] { masterfile\-format ( map | raw | text ); masterfile\-style ( full | relative ); masters [ port integer ] [ dscp integer ] { ( - primaries | ipv4_address [ port integer ] | + remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; max\-ixfr\-ratio ( unlimited | percentage ); @@ -1028,7 +1030,7 @@ view string [ class ] { | * ) ] [ dscp integer ]; notify\-to\-soa boolean; primaries [ port integer ] [ dscp integer ] { ( - primaries | ipv4_address [ port integer ] | + remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; request\-expire boolean; @@ -1077,9 +1079,10 @@ zone string [ class ] { allow\-transfer { address_match_element; ... }; allow\-update { address_match_element; ... }; allow\-update\-forwarding { address_match_element; ... }; - also\-notify [ port integer ] [ dscp integer ] { ( primaries | - ipv4_address [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls string ]; ... }; + also\-notify [ port integer ] [ dscp integer ] { ( + remote\-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ tls + string ]; ... }; alt\-transfer\-source ( ipv4_address | * ) [ port ( integer | * ) ] [ dscp integer ]; alt\-transfer\-source\-v6 ( ipv6_address | * ) [ port ( integer | @@ -1115,8 +1118,8 @@ zone string [ class ] { key\-directory quoted_string; masterfile\-format ( map | raw | text ); masterfile\-style ( full | relative ); - masters [ port integer ] [ dscp integer ] { ( primaries | - ipv4_address [ port integer ] | ipv6_address [ port + masters [ port integer ] [ dscp integer ] { ( remote\-servers + | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; max\-ixfr\-ratio ( unlimited | percentage ); max\-journal\-size ( default | unlimited | sizeval ); @@ -1138,9 +1141,10 @@ zone string [ class ] { notify\-source\-v6 ( ipv6_address | * ) [ port ( integer | * ) ] [ dscp integer ]; notify\-to\-soa boolean; - primaries [ port integer ] [ dscp integer ] { ( primaries | - ipv4_address [ port integer ] | ipv6_address [ port - integer ] ) [ key string ] [ tls string ]; ... }; + primaries [ port integer ] [ dscp integer ] { ( + remote\-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ tls + string ]; ... }; request\-expire boolean; request\-ixfr boolean; serial\-update\-method ( date | increment | unixtime ); diff --git a/doc/misc/master.zoneopt b/doc/misc/master.zoneopt index ae47d96b85..45b905c545 100644 --- a/doc/misc/master.zoneopt +++ b/doc/misc/master.zoneopt @@ -4,7 +4,7 @@ zone [ ] { allow-query-on { ; ... }; allow-transfer { ; ... }; allow-update { ; ... }; - also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; auto-dnssec ( allow | maintain | off ); diff --git a/doc/misc/master.zoneopt.rst b/doc/misc/master.zoneopt.rst index 9bac436947..968e4b24b1 100644 --- a/doc/misc/master.zoneopt.rst +++ b/doc/misc/master.zoneopt.rst @@ -6,7 +6,7 @@ allow-query-on { ; ... }; allow-transfer { ; ... }; allow-update { ; ... }; - also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; auto-dnssec ( allow | maintain | off ); diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index fee1789fe0..fd72417fc0 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -5,7 +5,7 @@ zone [ ] { allow-query-on { ; ... }; allow-transfer { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; check-names ( fail | warn | ignore ); @@ -15,7 +15,7 @@ zone [ ] { journal ; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; @@ -32,7 +32,7 @@ zone [ ] { notify-delay ; notify-source ( | * ) [ port ( | * ) ] [ dscp ]; notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; - primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; request-ixfr ; transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; diff --git a/doc/misc/mirror.zoneopt.rst b/doc/misc/mirror.zoneopt.rst index ed63137f8e..efd335c0d4 100644 --- a/doc/misc/mirror.zoneopt.rst +++ b/doc/misc/mirror.zoneopt.rst @@ -7,7 +7,7 @@ allow-query-on { ; ... }; allow-transfer { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; check-names ( fail | warn | ignore ); @@ -17,7 +17,7 @@ journal ; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; @@ -34,7 +34,7 @@ notify-delay ; notify-source ( | * ) [ port ( | * ) ] [ dscp ]; notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; - primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; request-ixfr ; transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; diff --git a/doc/misc/options b/doc/misc/options index e42b004c9e..e33917a6dc 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -74,10 +74,10 @@ managed-keys { ( static-key ; ... }; // may occur multiple times, deprecated masters [ port ] [ dscp - ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls - ]; ... }; // may occur multiple times + ] { ( | + [ port ] | + [ port ] ) [ key + ] [ tls ]; ... }; // may occur multiple times options { allow-new-zones ; @@ -91,9 +91,10 @@ options { allow-transfer { ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | @@ -109,7 +110,7 @@ options { blackhole { ; ... }; cache-file ; catalog-zones { zone [ default-masters [ port ] - [ dscp ] { ( | [ port + [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... } ] [ zone-directory ] [ in-memory ] [ min-update-interval @@ -394,10 +395,10 @@ plugin ( query ) [ { } ]; // may occur multiple times primaries [ port ] [ dscp - ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls - ]; ... }; // may occur multiple times + ] { ( | + [ port ] | + [ port ] ) [ key + ] [ tls ]; ... }; // may occur multiple times server { bogus ; @@ -470,9 +471,10 @@ view [ ] { allow-transfer { ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | @@ -482,7 +484,7 @@ view [ ] { auto-dnssec ( allow | maintain | off ); cache-file ; catalog-zones { zone [ default-masters [ port ] - [ dscp ] { ( | [ port + [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... } ] [ zone-directory ] [ in-memory ] [ min-update-interval @@ -750,7 +752,7 @@ view [ ] { allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( @@ -791,7 +793,7 @@ view [ ] { masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); masters [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-ixfr-ratio ( unlimited | ); @@ -816,7 +818,7 @@ view [ ] { notify-to-soa ; nsec3-test-zone ; // test only primaries [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; @@ -856,9 +858,10 @@ zone [ ] { allow-transfer { ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | @@ -894,8 +897,8 @@ zone [ ] { key-directory ; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port ] [ dscp ] { ( | - [ port ] | [ port + masters [ port ] [ dscp ] { ( + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); @@ -918,9 +921,10 @@ zone [ ] { [ dscp ]; notify-to-soa ; nsec3-test-zone ; // test only - primaries [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + primaries [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; request-expire ; request-ixfr ; serial-update-method ( date | increment | unixtime ); diff --git a/doc/misc/options.active b/doc/misc/options.active index d5adf85a98..49fd35e230 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -73,10 +73,10 @@ managed-keys { ( static-key ; ... }; // may occur multiple times, deprecated masters [ port ] [ dscp - ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls - ]; ... }; // may occur multiple times + ] { ( | + [ port ] | + [ port ] ) [ key + ] [ tls ]; ... }; // may occur multiple times options { allow-new-zones ; @@ -90,9 +90,10 @@ options { allow-transfer { ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | @@ -108,7 +109,7 @@ options { blackhole { ; ... }; cache-file ; catalog-zones { zone [ default-masters [ port ] - [ dscp ] { ( | [ port + [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... } ] [ zone-directory ] [ in-memory ] [ min-update-interval @@ -391,10 +392,10 @@ plugin ( query ) [ { } ]; // may occur multiple times primaries [ port ] [ dscp - ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls - ]; ... }; // may occur multiple times + ] { ( | + [ port ] | + [ port ] ) [ key + ] [ tls ]; ... }; // may occur multiple times server { bogus ; @@ -467,9 +468,10 @@ view [ ] { allow-transfer { ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | @@ -479,7 +481,7 @@ view [ ] { auto-dnssec ( allow | maintain | off ); cache-file ; catalog-zones { zone [ default-masters [ port ] - [ dscp ] { ( | [ port + [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... } ] [ zone-directory ] [ in-memory ] [ min-update-interval @@ -745,7 +747,7 @@ view [ ] { allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( @@ -786,7 +788,7 @@ view [ ] { masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); masters [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-ixfr-ratio ( unlimited | ); @@ -810,7 +812,7 @@ view [ ] { | * ) ] [ dscp ]; notify-to-soa ; primaries [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; @@ -850,9 +852,10 @@ zone [ ] { allow-transfer { ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | @@ -888,8 +891,8 @@ zone [ ] { key-directory ; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port ] [ dscp ] { ( | - [ port ] | [ port + masters [ port ] [ dscp ] { ( + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); @@ -911,9 +914,10 @@ zone [ ] { notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; - primaries [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + primaries [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; request-expire ; request-ixfr ; serial-update-method ( date | increment | unixtime ); diff --git a/doc/misc/options.grammar.rst b/doc/misc/options.grammar.rst index 9ee853c6a9..fa2bac7160 100644 --- a/doc/misc/options.grammar.rst +++ b/doc/misc/options.grammar.rst @@ -12,9 +12,10 @@ allow-transfer { ; ... }; allow-update { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | @@ -30,7 +31,7 @@ blackhole { ; ... }; cache-file ; catalog-zones { zone [ default-masters [ port ] - [ dscp ] { ( | [ port + [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... } ] [ zone-directory ] [ in-memory ] [ min-update-interval diff --git a/doc/misc/primaries.grammar.rst b/doc/misc/primaries.grammar.rst index cfc7601724..53f2e36daa 100644 --- a/doc/misc/primaries.grammar.rst +++ b/doc/misc/primaries.grammar.rst @@ -1,7 +1,7 @@ :: primaries [ port ] [ dscp - ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls - ]; ... }; + ] { ( | + [ port ] | + [ port ] ) [ key + ] [ tls ]; ... }; diff --git a/doc/misc/redirect.zoneopt b/doc/misc/redirect.zoneopt index ac53d5499c..1a48cbc2c7 100644 --- a/doc/misc/redirect.zoneopt +++ b/doc/misc/redirect.zoneopt @@ -6,9 +6,9 @@ zone [ ] { file ; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-records ; max-zone-ttl ( unlimited | ); - primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; zone-statistics ( full | terse | none | ); }; diff --git a/doc/misc/redirect.zoneopt.rst b/doc/misc/redirect.zoneopt.rst index bf263cd00d..5b754bfd0c 100644 --- a/doc/misc/redirect.zoneopt.rst +++ b/doc/misc/redirect.zoneopt.rst @@ -8,9 +8,9 @@ file ; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-records ; max-zone-ttl ( unlimited | ); - primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; zone-statistics ( full | terse | none | ); }; diff --git a/doc/misc/slave.zoneopt b/doc/misc/slave.zoneopt index 169ed84810..490bb4637e 100644 --- a/doc/misc/slave.zoneopt +++ b/doc/misc/slave.zoneopt @@ -5,7 +5,7 @@ zone [ ] { allow-query-on { ; ... }; allow-transfer { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; auto-dnssec ( allow | maintain | off ); @@ -27,7 +27,7 @@ zone [ ] { key-directory ; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; @@ -45,7 +45,7 @@ zone [ ] { notify-source ( | * ) [ port ( | * ) ] [ dscp ]; notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; - primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; request-ixfr ; sig-signing-nodes ; diff --git a/doc/misc/slave.zoneopt.rst b/doc/misc/slave.zoneopt.rst index 6f2da9559d..d15501b7b5 100644 --- a/doc/misc/slave.zoneopt.rst +++ b/doc/misc/slave.zoneopt.rst @@ -7,7 +7,7 @@ allow-query-on { ; ... }; allow-transfer { ; ... }; allow-update-forwarding { ; ... }; - also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + also-notify [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; alt-transfer-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; auto-dnssec ( allow | maintain | off ); @@ -29,7 +29,7 @@ key-directory ; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; @@ -47,7 +47,7 @@ notify-source ( | * ) [ port ( | * ) ] [ dscp ]; notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; - primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; request-ixfr ; sig-signing-nodes ; diff --git a/doc/misc/stub.zoneopt b/doc/misc/stub.zoneopt index c616cc687f..0e37a789a4 100644 --- a/doc/misc/stub.zoneopt +++ b/doc/misc/stub.zoneopt @@ -11,7 +11,7 @@ zone [ ] { forwarders [ port ] [ dscp ] { ( | ) [ port ] [ dscp ]; ... }; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-records ; max-refresh-time ; max-retry-time ; @@ -20,7 +20,7 @@ zone [ ] { min-refresh-time ; min-retry-time ; multi-master ; - primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; transfer-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; use-alt-transfer-source ; diff --git a/doc/misc/stub.zoneopt.rst b/doc/misc/stub.zoneopt.rst index d00a0c1785..1db900a76b 100644 --- a/doc/misc/stub.zoneopt.rst +++ b/doc/misc/stub.zoneopt.rst @@ -13,7 +13,7 @@ forwarders [ port ] [ dscp ] { ( | ) [ port ] [ dscp ]; ... }; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); - masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-records ; max-refresh-time ; max-retry-time ; @@ -22,7 +22,7 @@ min-refresh-time ; min-retry-time ; multi-master ; - primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; transfer-source ( | * ) [ port ( | * ) ] [ dscp ]; transfer-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; use-alt-transfer-source ; diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 02ed0f4120..e19d5dc21c 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -1999,7 +1999,7 @@ resume: const cfg_obj_t *key; addr = cfg_tuple_get(cfg_listelt_value(element), - "primarieselement"); + "remoteselement"); key = cfg_tuple_get(cfg_listelt_value(element), "key"); if (cfg_obj_issockaddr(addr)) { diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index cf2b67deb7..a512e9a2cc 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -109,7 +109,7 @@ static cfg_type_t cfg_type_logging; static cfg_type_t cfg_type_logseverity; static cfg_type_t cfg_type_logsuffix; static cfg_type_t cfg_type_logversions; -static cfg_type_t cfg_type_primarieselement; +static cfg_type_t cfg_type_remoteselement; static cfg_type_t cfg_type_maxduration; static cfg_type_t cfg_type_minimal; static cfg_type_t cfg_type_nameportiplist; @@ -191,8 +191,8 @@ static cfg_type_t cfg_type_acl = { "acl", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, acl_fields }; -/*% primaries */ -static cfg_tuplefielddef_t primaries_fields[] = { +/*% remote servers, used for primaries and parental agents */ +static cfg_tuplefielddef_t remotes_fields[] = { { "name", &cfg_type_astring, 0 }, { "port", &cfg_type_optional_port, 0 }, { "dscp", &cfg_type_optional_dscp, 0 }, @@ -200,19 +200,19 @@ static cfg_tuplefielddef_t primaries_fields[] = { { NULL, NULL, 0 } }; -static cfg_type_t cfg_type_primaries = { "primaries", cfg_parse_tuple, - cfg_print_tuple, cfg_doc_tuple, - &cfg_rep_tuple, primaries_fields }; +static cfg_type_t cfg_type_remoteservers = { "remote-servers", cfg_parse_tuple, + cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, remotes_fields }; /*% * "sockaddrkeylist", a list of socket addresses with optional keys - * and an optional default port, as used in the primaries option. + * and an optional default port, as used in the remote-servers option. * E.g., - * "port 1234 { myprimaries; 10.0.0.1 key foo; 1::2 port 69; }" + * "port 1234 { myservers; 10.0.0.1 key foo; 1::2 port 69; }" */ static cfg_tuplefielddef_t namesockaddrkey_fields[] = { - { "primarieselement", &cfg_type_primarieselement, 0 }, + { "remoteselement", &cfg_type_remoteselement, 0 }, { "key", &cfg_type_optional_keyref, 0 }, { "tls", &cfg_type_optional_tls, 0 }, { NULL, NULL, 0 }, @@ -1112,9 +1112,9 @@ static cfg_clausedef_t namedconf_clauses[] = { { "http", &cfg_type_http_description, CFG_CLAUSEFLAG_MULTI }, { "logging", &cfg_type_logging, 0 }, { "lwres", NULL, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_ANCIENT }, - { "masters", &cfg_type_primaries, CFG_CLAUSEFLAG_MULTI }, + { "masters", &cfg_type_remoteservers, CFG_CLAUSEFLAG_MULTI }, { "options", &cfg_type_options, 0 }, - { "primaries", &cfg_type_primaries, CFG_CLAUSEFLAG_MULTI }, + { "primaries", &cfg_type_remoteservers, CFG_CLAUSEFLAG_MULTI }, { "statistics-channels", &cfg_type_statschannels, CFG_CLAUSEFLAG_MULTI }, { "tls", &cfg_type_tlsconf, CFG_CLAUSEFLAG_MULTI }, @@ -3668,14 +3668,14 @@ static cfg_type_t cfg_type_nameportiplist = { }; /*% - * primaries element. + * remote servers element. */ static void -doc_primarieselement(cfg_printer_t *pctx, const cfg_type_t *type) { +doc_remoteselement(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "( "); - cfg_print_cstr(pctx, ""); + cfg_print_cstr(pctx, ""); cfg_print_cstr(pctx, " | "); cfg_print_cstr(pctx, ""); cfg_print_cstr(pctx, " "); @@ -3688,8 +3688,8 @@ doc_primarieselement(cfg_printer_t *pctx, const cfg_type_t *type) { } static isc_result_t -parse_primarieselement(cfg_parser_t *pctx, const cfg_type_t *type, - cfg_obj_t **ret) { +parse_remoteselement(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; UNUSED(type); @@ -3707,7 +3707,8 @@ parse_primarieselement(cfg_parser_t *pctx, const cfg_type_t *type, } } else { cfg_parser_error(pctx, CFG_LOG_NEAR, - "expected IP address or primaries list name"); + "expected IP address or remote servers list " + "name"); return (ISC_R_UNEXPECTEDTOKEN); } cleanup: @@ -3715,12 +3716,12 @@ cleanup: return (result); } -static cfg_type_t cfg_type_primarieselement = { "primaries_element", - parse_primarieselement, - NULL, - doc_primarieselement, - NULL, - NULL }; +static cfg_type_t cfg_type_remoteselement = { "remotes_element", + parse_remoteselement, + NULL, + doc_remoteselement, + NULL, + NULL }; static int cmp_clause(const void *ap, const void *bp) { From 0311705d4b36c536dd541b0b193bd01b68fe90b3 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 7 May 2021 11:36:40 +0200 Subject: [PATCH 02/20] Add parental-agents configuration Introduce a way to configure parental agents that can be used to query DS records to be used in automatic key rollovers. --- bin/named/named.conf.rst | 19 +++++++++++++++++++ bin/tests/system/checkconf/good.conf | 11 +++++++++++ doc/arm/Makefile.am | 1 + doc/arm/reference.rst | 18 ++++++++++++++++++ doc/man/named.conf.5in | 23 +++++++++++++++++++++++ doc/misc/master.zoneopt | 1 + doc/misc/master.zoneopt.rst | 1 + doc/misc/options | 14 ++++++++++++++ doc/misc/options.active | 14 ++++++++++++++ doc/misc/parentals.grammar.rst | 7 +++++++ doc/misc/slave.zoneopt | 1 + doc/misc/slave.zoneopt.rst | 1 + lib/dns/include/dns/zone.h | 21 +++++++++++++++++++++ lib/isccfg/namedconf.c | 3 +++ 14 files changed, 135 insertions(+) create mode 100644 doc/misc/parentals.grammar.rst diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst index cc3aad616c..b4a91cd4c9 100644 --- a/bin/named/named.conf.rst +++ b/bin/named/named.conf.rst @@ -465,6 +465,17 @@ OPTIONS zone-statistics ( full | terse | none | boolean ); }; +PARENTAL-AGENTS +^^^^^^^^^^^^^^^ + +:: + + parental-agents string [ port integer ] [ + dscp integer ] { ( remote-servers | + ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key + string ] [ tls string ]; ... }; + PLUGIN ^^^^^^ @@ -930,6 +941,10 @@ VIEW notify-source-v6 ( ipv6_address | * ) [ port ( integer | * ) ] [ dscp integer ]; notify-to-soa boolean; + parental-agents [ port integer ] [ dscp integer ] { ( + remote-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ + tls string ]; ... }; primaries [ port integer ] [ dscp integer ] { ( remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ @@ -1038,6 +1053,10 @@ ZONE notify-source-v6 ( ipv6_address | * ) [ port ( integer | * ) ] [ dscp integer ]; notify-to-soa boolean; + parental-agents [ port integer ] [ dscp integer ] { ( + remote-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ tls + string ]; ... }; primaries [ port integer ] [ dscp integer ] { ( remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls diff --git a/bin/tests/system/checkconf/good.conf b/bin/tests/system/checkconf/good.conf index d3f0039766..bd6a9b083d 100644 --- a/bin/tests/system/checkconf/good.conf +++ b/bin/tests/system/checkconf/good.conf @@ -86,6 +86,10 @@ options { transfer-source 0.0.0.0 dscp 63; zone-statistics none; }; +parental-agents "parents" { + 10.10.10.11; + 10.10.10.12; +}; view "first" { match-clients { "none"; @@ -176,11 +180,18 @@ view "fourth" { zone "dnssec-test" { type master; file "dnssec-test.db"; + parental-agents { + 1.2.3.4; + 1.2.3.5; + }; dnssec-policy "test"; }; zone "dnssec-default" { type master; file "dnssec-default.db"; + parental-agents { + "parents"; + }; dnssec-policy "default"; }; zone "dnssec-inherit" { diff --git a/doc/arm/Makefile.am b/doc/arm/Makefile.am index 31a06ebd29..5a13110956 100644 --- a/doc/arm/Makefile.am +++ b/doc/arm/Makefile.am @@ -37,6 +37,7 @@ EXTRA_DIST = \ ../misc/master.zoneopt.rst \ ../misc/mirror.zoneopt.rst \ ../misc/options.grammar.rst \ + ../misc/parentals.grammar.rst \ ../misc/primaries.grammar.rst \ ../misc/redirect.zoneopt.rst \ ../misc/server.grammar.rst \ diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 4f563d4ed2..d6a45551eb 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -280,6 +280,9 @@ The following statements are supported: ``options`` Controls global server configuration options and sets defaults for other statements. + ``parental-agents`` + Defines a named list of servers for inclusion in primary and secondary zones' ``parental-agents`` lists. + ``primaries`` Defines a named list of servers for inclusion in stub and secondary zones' ``primaries`` or ``also-notify`` lists. (Note: this is a synonym for the original keyword ``masters``, which can still be used, but is no longer the preferred terminology.) @@ -844,6 +847,21 @@ At ``debug`` level 4 or higher, the detailed context information logged at ``debug`` level 2 is logged for errors other than SERVFAIL and for negative responses such as NXDOMAIN. +.. _parentals_grammar: + +``parental-agents`` Statement Grammar +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. include:: ../misc/parentals.grammar.rst + +.. _parentals_statement: + +``parental-agents`` Statement Definition and Usage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``parental-agents`` lists allow for a common set of parental agents to be easily +used by multiple primary and secondary zones in their ``parental-agents`` lists. + .. _primaries_grammar: ``primaries`` Statement Grammar diff --git a/doc/man/named.conf.5in b/doc/man/named.conf.5in index ac1a1555f9..4beb6c4604 100644 --- a/doc/man/named.conf.5in +++ b/doc/man/named.conf.5in @@ -535,6 +535,21 @@ options { .fi .UNINDENT .UNINDENT +.SS PARENTAL\-AGENTS +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +parental\-agents string [ port integer ] [ + dscp integer ] { ( remote\-servers | + ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key + string ] [ tls string ]; ... }; +.ft P +.fi +.UNINDENT +.UNINDENT .SS PLUGIN .INDENT 0.0 .INDENT 3.5 @@ -1029,6 +1044,10 @@ view string [ class ] { notify\-source\-v6 ( ipv6_address | * ) [ port ( integer | * ) ] [ dscp integer ]; notify\-to\-soa boolean; + parental\-agents [ port integer ] [ dscp integer ] { ( + remote\-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ + tls string ]; ... }; primaries [ port integer ] [ dscp integer ] { ( remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ @@ -1141,6 +1160,10 @@ zone string [ class ] { notify\-source\-v6 ( ipv6_address | * ) [ port ( integer | * ) ] [ dscp integer ]; notify\-to\-soa boolean; + parental\-agents [ port integer ] [ dscp integer ] { ( + remote\-servers | ipv4_address [ port integer ] | + ipv6_address [ port integer ] ) [ key string ] [ tls + string ]; ... }; primaries [ port integer ] [ dscp integer ] { ( remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls diff --git a/doc/misc/master.zoneopt b/doc/misc/master.zoneopt index 45b905c545..6740613e8c 100644 --- a/doc/misc/master.zoneopt +++ b/doc/misc/master.zoneopt @@ -46,6 +46,7 @@ zone [ ] { notify-source ( | * ) [ port ( | * ) ] [ dscp ]; notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; + parental-agents [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; serial-update-method ( date | increment | unixtime ); sig-signing-nodes ; sig-signing-signatures ; diff --git a/doc/misc/master.zoneopt.rst b/doc/misc/master.zoneopt.rst index 968e4b24b1..05243c40bc 100644 --- a/doc/misc/master.zoneopt.rst +++ b/doc/misc/master.zoneopt.rst @@ -48,6 +48,7 @@ notify-source ( | * ) [ port ( | * ) ] [ dscp ]; notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; + parental-agents [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; serial-update-method ( date | increment | unixtime ); sig-signing-nodes ; sig-signing-signatures ; diff --git a/doc/misc/options b/doc/misc/options index e33917a6dc..87aa9e2f47 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -391,6 +391,12 @@ options { zone-statistics ( full | terse | none | ); }; +parental-agents [ port ] [ + dscp ] { ( | + [ port ] | + [ port ] ) [ key + ] [ tls ]; ... }; // may occur multiple times + plugin ( query ) [ { } ]; // may occur multiple times @@ -817,6 +823,10 @@ view [ ] { | * ) ] [ dscp ]; notify-to-soa ; nsec3-test-zone ; // test only + parental-agents [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ + tls ]; ... }; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ @@ -921,6 +931,10 @@ zone [ ] { [ dscp ]; notify-to-soa ; nsec3-test-zone ; // test only + parental-agents [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls diff --git a/doc/misc/options.active b/doc/misc/options.active index 49fd35e230..d8bb60f930 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -388,6 +388,12 @@ options { zone-statistics ( full | terse | none | ); }; +parental-agents [ port ] [ + dscp ] { ( | + [ port ] | + [ port ] ) [ key + ] [ tls ]; ... }; // may occur multiple times + plugin ( query ) [ { } ]; // may occur multiple times @@ -811,6 +817,10 @@ view [ ] { notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; + parental-agents [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ + tls ]; ... }; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ @@ -914,6 +924,10 @@ zone [ ] { notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; + parental-agents [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ tls + ]; ... }; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls diff --git a/doc/misc/parentals.grammar.rst b/doc/misc/parentals.grammar.rst new file mode 100644 index 0000000000..32cef5ae55 --- /dev/null +++ b/doc/misc/parentals.grammar.rst @@ -0,0 +1,7 @@ +:: + + parental-agents [ port ] [ dscp + ] { ( | + [ port ] | + [ port ] ) [ key + ] [ tls ]; ... }; diff --git a/doc/misc/slave.zoneopt b/doc/misc/slave.zoneopt index 490bb4637e..a7e7c713e3 100644 --- a/doc/misc/slave.zoneopt +++ b/doc/misc/slave.zoneopt @@ -45,6 +45,7 @@ zone [ ] { notify-source ( | * ) [ port ( | * ) ] [ dscp ]; notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; + parental-agents [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; request-ixfr ; diff --git a/doc/misc/slave.zoneopt.rst b/doc/misc/slave.zoneopt.rst index d15501b7b5..48f9454c62 100644 --- a/doc/misc/slave.zoneopt.rst +++ b/doc/misc/slave.zoneopt.rst @@ -47,6 +47,7 @@ notify-source ( | * ) [ port ( | * ) ] [ dscp ]; notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; + parental-agents [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; request-ixfr ; diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index c417c25a8f..a981f0dd23 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -655,6 +655,27 @@ dns_zone_setprimaries(dns_zone_t *zone, const isc_sockaddr_t *primaries, *\li Any result dns_name_dup() can return, if keynames!=NULL */ +isc_result_t +dns_zone_setparentals(dns_zone_t *zone, const isc_sockaddr_t *parentals, + dns_name_t **keynames, dns_name_t **tlsnames, + uint32_t count); +/*%< + * Set the list of parental agents for the zone. + * + * Require: + *\li 'zone' to be a valid zone. + *\li 'parentals' array of isc_sockaddr_t with port set or NULL. + *\li 'count' the number of parentals. + *\li 'keynames' array of dns_name_t's for tsig keys or NULL. + * + *\li If 'parentals' is NULL then 'count' must be zero. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li Any result dns_name_dup() can return, if keynames!=NULL + */ + isc_result_t dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify, const isc_dscp_t *dscps, dns_name_t **keynames, diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index a512e9a2cc..f45bfe0194 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1114,6 +1114,7 @@ static cfg_clausedef_t namedconf_clauses[] = { { "lwres", NULL, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_ANCIENT }, { "masters", &cfg_type_remoteservers, CFG_CLAUSEFLAG_MULTI }, { "options", &cfg_type_options, 0 }, + { "parental-agents", &cfg_type_remoteservers, CFG_CLAUSEFLAG_MULTI }, { "primaries", &cfg_type_remoteservers, CFG_CLAUSEFLAG_MULTI }, { "statistics-channels", &cfg_type_statschannels, CFG_CLAUSEFLAG_MULTI }, @@ -2318,6 +2319,8 @@ static cfg_clausedef_t zone_only_clauses[] = { { "masters", &cfg_type_namesockaddrkeylist, CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB | CFG_ZONE_REDIRECT }, + { "parental-agents", &cfg_type_namesockaddrkeylist, + CFG_ZONE_MASTER | CFG_ZONE_SLAVE }, { "primaries", &cfg_type_namesockaddrkeylist, CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB | CFG_ZONE_REDIRECT }, From 1e763e582bf85ecdc2624e526237671a63fb76f5 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 7 May 2021 14:27:25 +0200 Subject: [PATCH 03/20] Check parental-agents config Add checks for "parental-agents" configuration, checking for the option being at wrong type of zone (only allowed for primaries and secondaries), duplicate definitions, duplicate references, and undefined parental clauses (the name referenced in the zone clause does not have a matching "parental-agent" clause). --- .../bad-parental-agents-def-options.conf | 19 +++ .../bad-parental-agents-def-view.conf | 18 +++ .../bad-parental-agents-def-view2.conf | 20 +++ .../bad-parental-agents-def-zone.conf | 16 +++ .../checkconf/bad-parental-agents-dup.conf | 17 +++ .../checkconf/bad-parental-agents-dupdef.conf | 24 ++++ .../checkconf/bad-parental-agents-empty.conf | 18 +++ .../checkconf/bad-parental-agents-empty2.conf | 16 +++ .../checkconf/bad-parental-agents-mirror.conf | 16 +++ .../bad-parental-agents-notfound.conf | 20 +++ .../checkconf/bad-primaries-notfound.conf | 19 +++ lib/bind9/check.c | 116 +++++++++++++----- 12 files changed, 290 insertions(+), 29 deletions(-) create mode 100644 bin/tests/system/checkconf/bad-parental-agents-def-options.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-def-view.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-def-view2.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-def-zone.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-dup.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-dupdef.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-empty.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-empty2.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-mirror.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-notfound.conf create mode 100644 bin/tests/system/checkconf/bad-primaries-notfound.conf diff --git a/bin/tests/system/checkconf/bad-parental-agents-def-options.conf b/bin/tests/system/checkconf/bad-parental-agents-def-options.conf new file mode 100644 index 0000000000..6bf2115662 --- /dev/null +++ b/bin/tests/system/checkconf/bad-parental-agents-def-options.conf @@ -0,0 +1,19 @@ +/* + * 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. + */ + +options { + parental-agents { 192.168.1.2; }; +}; + +zone "example.net" { + type primary; + file "example.net.db"; +}; diff --git a/bin/tests/system/checkconf/bad-parental-agents-def-view.conf b/bin/tests/system/checkconf/bad-parental-agents-def-view.conf new file mode 100644 index 0000000000..5cb0f81d88 --- /dev/null +++ b/bin/tests/system/checkconf/bad-parental-agents-def-view.conf @@ -0,0 +1,18 @@ +/* + * 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. + */ + +view "test" { + parental-agents { 192.168.1.2; }; + zone "example.net" { + type primary; + file "example.net.db"; + }; +}; diff --git a/bin/tests/system/checkconf/bad-parental-agents-def-view2.conf b/bin/tests/system/checkconf/bad-parental-agents-def-view2.conf new file mode 100644 index 0000000000..3487429a50 --- /dev/null +++ b/bin/tests/system/checkconf/bad-parental-agents-def-view2.conf @@ -0,0 +1,20 @@ +/* + * 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. + */ + +view "test" { + parental-agents "net" { + 192.168.1.2; + }; + zone "example.net" { + type primary; + file "example.net.db"; + }; +}; diff --git a/bin/tests/system/checkconf/bad-parental-agents-def-zone.conf b/bin/tests/system/checkconf/bad-parental-agents-def-zone.conf new file mode 100644 index 0000000000..48b735a055 --- /dev/null +++ b/bin/tests/system/checkconf/bad-parental-agents-def-zone.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 "example.net" { + type primary; + file "example.net.db"; + parental-agents "net" { 192.168.1.1; }; +}; diff --git a/bin/tests/system/checkconf/bad-parental-agents-dup.conf b/bin/tests/system/checkconf/bad-parental-agents-dup.conf new file mode 100644 index 0000000000..569c42baea --- /dev/null +++ b/bin/tests/system/checkconf/bad-parental-agents-dup.conf @@ -0,0 +1,17 @@ +/* + * 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 "example.net" { + type primary; + file "example.net.db"; + parental-agents { 192.168.1.1; }; + parental-agents { 192.168.1.1; }; +}; diff --git a/bin/tests/system/checkconf/bad-parental-agents-dupdef.conf b/bin/tests/system/checkconf/bad-parental-agents-dupdef.conf new file mode 100644 index 0000000000..99360745fb --- /dev/null +++ b/bin/tests/system/checkconf/bad-parental-agents-dupdef.conf @@ -0,0 +1,24 @@ +/* + * 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. + */ + +parental-agents "net" { + 192.168.1.1; +}; + +parental-agents "net" { + 192.168.1.2; +}; + +zone "example.net" { + type primary; + file "example.net.db"; + parental-agents { "net"; }; +}; diff --git a/bin/tests/system/checkconf/bad-parental-agents-empty.conf b/bin/tests/system/checkconf/bad-parental-agents-empty.conf new file mode 100644 index 0000000000..0329584132 --- /dev/null +++ b/bin/tests/system/checkconf/bad-parental-agents-empty.conf @@ -0,0 +1,18 @@ +/* + * 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. + */ + +parental-agents "net" { }; + +zone "example.net" { + type primary; + file "example.net.db"; + parental-agents { "net"; }; +}; diff --git a/bin/tests/system/checkconf/bad-parental-agents-empty2.conf b/bin/tests/system/checkconf/bad-parental-agents-empty2.conf new file mode 100644 index 0000000000..18d9d8214b --- /dev/null +++ b/bin/tests/system/checkconf/bad-parental-agents-empty2.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 "example.net" { + type primary; + file "example.net.db"; + parental-agents { }; +}; diff --git a/bin/tests/system/checkconf/bad-parental-agents-mirror.conf b/bin/tests/system/checkconf/bad-parental-agents-mirror.conf new file mode 100644 index 0000000000..d06662d7c2 --- /dev/null +++ b/bin/tests/system/checkconf/bad-parental-agents-mirror.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 "." { + type mirror; + file "root.mirror"; + parental-agents { 192.168.1.1; }; +}; diff --git a/bin/tests/system/checkconf/bad-parental-agents-notfound.conf b/bin/tests/system/checkconf/bad-parental-agents-notfound.conf new file mode 100644 index 0000000000..7639c5f383 --- /dev/null +++ b/bin/tests/system/checkconf/bad-parental-agents-notfound.conf @@ -0,0 +1,20 @@ +/* + * 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. + */ + +parental-agents "com" { + 192.168.1.2; +}; + +zone "example.net" { + type primary; + file "example.net.db"; + parental-agents { "net"; }; +}; diff --git a/bin/tests/system/checkconf/bad-primaries-notfound.conf b/bin/tests/system/checkconf/bad-primaries-notfound.conf new file mode 100644 index 0000000000..db290e82d8 --- /dev/null +++ b/bin/tests/system/checkconf/bad-primaries-notfound.conf @@ -0,0 +1,19 @@ +/* + * 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. + */ + +primaries "net" { + 192.168.1.2; +}; + +zone "example.net" { + type secondary; + primaries { "foo"; }; +}; diff --git a/lib/bind9/check.c b/lib/bind9/check.c index e19d5dc21c..8d9807c190 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -1841,12 +1841,12 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config, } /* - * Check "primaries" style list. + * Check "remote-servers" style list. */ static isc_result_t -bind9_check_primarylist(const cfg_obj_t *cctx, const char *list, - isc_log_t *logctx, isc_symtab_t *symtab, - isc_mem_t *mctx) { +bind9_check_remoteserverlist(const cfg_obj_t *cctx, const char *list, + isc_log_t *logctx, isc_symtab_t *symtab, + isc_mem_t *mctx) { isc_symvalue_t symvalue; isc_result_t result, tresult; const cfg_obj_t *obj = NULL; @@ -1883,9 +1883,9 @@ bind9_check_primarylist(const cfg_obj_t *cctx, const char *list, file = ""; } cfg_obj_log(obj, logctx, ISC_LOG_ERROR, - "primaries list '%s' is duplicated: " + "%s list '%s' is duplicated: " "also defined at %s:%u", - name, file, line); + list, name, file, line); isc_mem_free(mctx, tmp); result = tresult; break; @@ -1913,13 +1913,35 @@ bind9_check_primarylists(const cfg_obj_t *cctx, isc_log_t *logctx, if (result != ISC_R_SUCCESS) { return (result); } - tresult = bind9_check_primarylist(cctx, "primaries", logctx, symtab, - mctx); + tresult = bind9_check_remoteserverlist(cctx, "primaries", logctx, + symtab, mctx); if (tresult != ISC_R_SUCCESS) { result = tresult; } - tresult = bind9_check_primarylist(cctx, "masters", logctx, symtab, - mctx); + tresult = bind9_check_remoteserverlist(cctx, "masters", logctx, symtab, + mctx); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + isc_symtab_destroy(&symtab); + return (result); +} + +/* + * Check parental-agents lists for duplicates. + */ +static isc_result_t +bind9_check_parentalagentlists(const cfg_obj_t *cctx, isc_log_t *logctx, + isc_mem_t *mctx) { + isc_result_t result, tresult; + isc_symtab_t *symtab = NULL; + + result = isc_symtab_create(mctx, 100, freekey, mctx, false, &symtab); + if (result != ISC_R_SUCCESS) { + return (result); + } + tresult = bind9_check_remoteserverlist(cctx, "parental-agents", logctx, + symtab, mctx); if (tresult != ISC_R_SUCCESS) { result = tresult; } @@ -1928,8 +1950,8 @@ bind9_check_primarylists(const cfg_obj_t *cctx, isc_log_t *logctx, } static isc_result_t -get_primaries(const cfg_obj_t *cctx, const char *list, const char *name, - const cfg_obj_t **ret) { +get_remotes(const cfg_obj_t *cctx, const char *list, const char *name, + const cfg_obj_t **ret) { isc_result_t result; const cfg_obj_t *obj = NULL; const cfg_listelt_t *elt = NULL; @@ -1958,20 +1980,25 @@ get_primaries(const cfg_obj_t *cctx, const char *list, const char *name, } static isc_result_t -get_primaries_def(const cfg_obj_t *cctx, const char *name, - const cfg_obj_t **ret) { - isc_result_t result; +get_remoteservers_def(const char *list, const char *name, const cfg_obj_t *cctx, + const cfg_obj_t **ret) { + isc_result_t result = ISC_R_NOTFOUND; - result = get_primaries(cctx, "primaries", name, ret); - if (result != ISC_R_SUCCESS) { - result = get_primaries(cctx, "masters", name, ret); + if (strcmp(list, "primaries") == 0) { + result = get_remotes(cctx, "primaries", name, ret); + if (result != ISC_R_SUCCESS) { + result = get_remotes(cctx, "masters", name, ret); + } + } else if (strcmp(list, "parental-agents") == 0) { + result = get_remotes(cctx, "parental-agents", name, ret); } return (result); } static isc_result_t -validate_primaries(const cfg_obj_t *obj, const cfg_obj_t *config, - uint32_t *countp, isc_log_t *logctx, isc_mem_t *mctx) { +validate_remotes(const char *list, const cfg_obj_t *obj, + const cfg_obj_t *config, uint32_t *countp, isc_log_t *logctx, + isc_mem_t *mctx) { isc_result_t result = ISC_R_SUCCESS; isc_result_t tresult; uint32_t count = 0; @@ -1980,7 +2007,7 @@ validate_primaries(const cfg_obj_t *obj, const cfg_obj_t *config, const cfg_listelt_t *element; const cfg_listelt_t **stack = NULL; uint32_t stackcount = 0, pushed = 0; - const cfg_obj_t *list; + const cfg_obj_t *listobj; REQUIRE(countp != NULL); result = isc_symtab_create(mctx, 100, NULL, NULL, false, &symtab); @@ -1990,8 +2017,8 @@ validate_primaries(const cfg_obj_t *obj, const cfg_obj_t *config, } newlist: - list = cfg_tuple_get(obj, "addresses"); - element = cfg_list_first(list); + listobj = cfg_tuple_get(obj, "addresses"); + element = cfg_list_first(listobj); resume: for (; element != NULL; element = cfg_list_next(element)) { const char *listname; @@ -2021,13 +2048,13 @@ resume: if (tresult == ISC_R_EXISTS) { continue; } - tresult = get_primaries_def(config, listname, &obj); + tresult = get_remoteservers_def(list, listname, config, &obj); if (tresult != ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) { result = tresult; } cfg_obj_log(addr, logctx, ISC_LOG_ERROR, - "unable to find primaries list '%s'", + "unable to find %s list '%s'", list, listname); continue; } @@ -2764,8 +2791,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } if (tresult == ISC_R_SUCCESS && donotify) { uint32_t count; - tresult = validate_primaries(obj, config, &count, - logctx, mctx); + tresult = validate_remotes("primaries", obj, config, + &count, logctx, mctx); if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) { result = tresult; @@ -2806,8 +2833,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, result = ISC_R_FAILURE; } else { uint32_t count; - tresult = validate_primaries(obj, config, &count, - logctx, mctx); + tresult = validate_remotes("primaries", obj, config, + &count, logctx, mctx); if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) { result = tresult; @@ -2822,6 +2849,32 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } } + /* + * Primary and secondary zones that have a "parental-agents" field, + * must have a corresponding "parental-agents" clause. + */ + if (ztype == CFG_ZONE_MASTER || ztype == CFG_ZONE_SLAVE) { + obj = NULL; + (void)cfg_map_get(zoptions, "parental-agents", &obj); + if (obj != NULL) { + uint32_t count; + tresult = validate_remotes("parental-agents", obj, + config, &count, logctx, + mctx); + if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) + { + result = tresult; + } + if (tresult == ISC_R_SUCCESS && count == 0) { + cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR, + "zone '%s': " + "empty 'parental-agents' entry", + znamestr); + result = ISC_R_FAILURE; + } + } + } + /* * Configuring a mirror zone and disabling recursion at the same time * contradicts the purpose of the former. @@ -5148,6 +5201,11 @@ bind9_check_namedconf(const cfg_obj_t *config, bool check_plugins, result = ISC_R_FAILURE; } + if (bind9_check_parentalagentlists(config, logctx, mctx) != + ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } + (void)cfg_map_get(config, "view", &views); if (views != NULL && options != NULL) { From 56262db9cd8b8e8132645c2063bad30f86b4ac81 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 11 May 2021 14:40:04 +0200 Subject: [PATCH 04/20] Add checkds system test Add a Pytest based system test for the 'checkds' feature. There is one nameserver (ns9, because it should be started the latest) that has configured several zones with dnssec-policy. The zones are set in such a state that they are waiting for DS publication or DS withdrawal. Then several other name servers act as parent servers that either have the DS for these published, or not. Also one server in the mix is to test a badly configured parental-agent. There are tests for DS publication, DS publication error handling, DS withdrawal and DS withdrawal error handling. The tests ensures that the zone is DNSSEC valid, and that the DSPublish/DSRemoved key metadata is set (or not in case of the error handling). It does not test if the rollover continues, this is already tested in the kasp system test (that uses 'rndc -dnssec checkds' to set the DSPublish/DSRemoved key metadata). --- bin/tests/system/Makefile.am | 2 +- bin/tests/system/checkds/README | 19 + bin/tests/system/checkds/clean.sh | 25 ++ bin/tests/system/checkds/conftest.py | 71 ++++ bin/tests/system/checkds/ns2/named.conf.in | 43 +++ bin/tests/system/checkds/ns2/setup.sh | 42 +++ bin/tests/system/checkds/ns2/template.db.in | 36 ++ bin/tests/system/checkds/ns4/named.conf.in | 39 ++ bin/tests/system/checkds/ns5/named.conf.in | 43 +++ bin/tests/system/checkds/ns5/setup.sh | 34 ++ bin/tests/system/checkds/ns5/template.db.in | 36 ++ bin/tests/system/checkds/ns6/named.conf.in | 43 +++ bin/tests/system/checkds/ns7/named.conf.in | 44 +++ bin/tests/system/checkds/ns9/named.conf.in | 193 ++++++++++ bin/tests/system/checkds/ns9/setup.sh | 69 ++++ bin/tests/system/checkds/ns9/template.db.in | 25 ++ bin/tests/system/checkds/setup.sh | 38 ++ bin/tests/system/checkds/tests-checkds.py | 376 ++++++++++++++++++++ bin/tests/system/conf.sh.in | 1 + util/copyrights | 8 + 20 files changed, 1186 insertions(+), 1 deletion(-) create mode 100644 bin/tests/system/checkds/README create mode 100644 bin/tests/system/checkds/clean.sh create mode 100644 bin/tests/system/checkds/conftest.py create mode 100644 bin/tests/system/checkds/ns2/named.conf.in create mode 100644 bin/tests/system/checkds/ns2/setup.sh create mode 100644 bin/tests/system/checkds/ns2/template.db.in create mode 100644 bin/tests/system/checkds/ns4/named.conf.in create mode 100644 bin/tests/system/checkds/ns5/named.conf.in create mode 100644 bin/tests/system/checkds/ns5/setup.sh create mode 100644 bin/tests/system/checkds/ns5/template.db.in create mode 100644 bin/tests/system/checkds/ns6/named.conf.in create mode 100644 bin/tests/system/checkds/ns7/named.conf.in create mode 100644 bin/tests/system/checkds/ns9/named.conf.in create mode 100644 bin/tests/system/checkds/ns9/setup.sh create mode 100644 bin/tests/system/checkds/ns9/template.db.in create mode 100644 bin/tests/system/checkds/setup.sh create mode 100755 bin/tests/system/checkds/tests-checkds.py diff --git a/bin/tests/system/Makefile.am b/bin/tests/system/Makefile.am index 0a3bfc090b..d849ced799 100644 --- a/bin/tests/system/Makefile.am +++ b/bin/tests/system/Makefile.am @@ -210,7 +210,7 @@ if HAVE_PYTHON TESTS += kasp keymgr2kasp tcp pipelined if HAVE_PYMOD_DNS -TESTS += qmin cookie timeouts +TESTS += checkds qmin cookie timeouts if HAVE_PERLMOD_NET_DNS TESTS += dnssec diff --git a/bin/tests/system/checkds/README b/bin/tests/system/checkds/README new file mode 100644 index 0000000000..4c426cb45d --- /dev/null +++ b/bin/tests/system/checkds/README @@ -0,0 +1,19 @@ +Copyright (C) Internet Systems Consortium, Inc. ("ISC") + +See COPYRIGHT in the source root or https://isc.org/copyright.html for terms. + +The test setup for the checkds tests. + +These servers are parent servers: +- ns2 is a primary authoritative server that serves the parent zone for zones + configured in ns9. +- ns4 is the secondary server for ns2. +- ns5 is a primary authoritative server that serves the parent zone for zones + configured in ns9, but this one does not publish DS records (to test cases + where the DS is missing). +- ns6 is an authoritative server for a different zone, to test badly configured + parental agents. +- ns7 is the secondary server for ns5. + +Finally, ns9 is the authoritative server for the various DNSSEC enabled test +domains. diff --git a/bin/tests/system/checkds/clean.sh b/bin/tests/system/checkds/clean.sh new file mode 100644 index 0000000000..99bafb9829 --- /dev/null +++ b/bin/tests/system/checkds/clean.sh @@ -0,0 +1,25 @@ +#!/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. + +set -e + +rm -f dig.out* +rm -f ns*/named.conf ns*/named.memstats ns*/named.run* +rm -f ns*/*.jnl ns*/*.jbk +rm -f ns*/K*.private ns*/K*.key ns*/K*.state +rm -f ns*/dsset-* +rm -f ns*/*.db ns*/*.jnl ns*/*.jbk ns*/*.db.signed ns*/*.db.infile +rm -f ns*/keygen.out.* ns*/settime.out.* ns*/signer.out.* +rm -f ns*/managed-keys.bind* +rm -f ns*/*.mkeys +rm -f ns*/zones +rm -f tests-checkds.py.status +rm -f *.checkds.out diff --git a/bin/tests/system/checkds/conftest.py b/bin/tests/system/checkds/conftest.py new file mode 100644 index 0000000000..a491ff435a --- /dev/null +++ b/bin/tests/system/checkds/conftest.py @@ -0,0 +1,71 @@ +############################################################################ +# 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. +############################################################################ + +import os +import pytest + + +def pytest_configure(config): + config.addinivalue_line( + "markers", "dnspython: mark tests that need dnspython to function" + ) + config.addinivalue_line( + "markers", "dnspython2: mark tests that need dnspython >= 2.0.0" + ) + + +def pytest_collection_modifyitems(config, items): + # pylint: disable=unused-argument,unused-import,too-many-branches + # pylint: disable=import-outside-toplevel + + # Test for dnspython module + skip_dnspython = pytest.mark.skip( + reason="need dnspython module to run") + try: + import dns.query # noqa: F401 + except ModuleNotFoundError: + for item in items: + if "dnspython" in item.keywords: + item.add_marker(skip_dnspython) + + # Test for dnspython >= 2.0.0 module + skip_dnspython2 = pytest.mark.skip( + reason="need dnspython >= 2.0.0 module to run") + try: + from dns.query import udp_with_fallback # noqa: F401 + except ImportError: + for item in items: + if "dnspython2" in item.keywords: + item.add_marker(skip_dnspython2) + + +@pytest.fixture +def named_port(request): + # pylint: disable=unused-argument + port = os.getenv("PORT") + if port is None: + port = 5301 + else: + port = int(port) + + return port + + +@pytest.fixture +def control_port(request): + # pylint: disable=unused-argument + port = os.getenv("CONTROLPORT") + if port is None: + port = 5301 + else: + port = int(port) + + return port diff --git a/bin/tests/system/checkds/ns2/named.conf.in b/bin/tests/system/checkds/ns2/named.conf.in new file mode 100644 index 0000000000..c22e27987b --- /dev/null +++ b/bin/tests/system/checkds/ns2/named.conf.in @@ -0,0 +1,43 @@ +/* + * 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. + */ + +// 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; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +zone "checkds" { + type primary; + file "checkds.db"; +}; diff --git a/bin/tests/system/checkds/ns2/setup.sh b/bin/tests/system/checkds/ns2/setup.sh new file mode 100644 index 0000000000..7fb586afee --- /dev/null +++ b/bin/tests/system/checkds/ns2/setup.sh @@ -0,0 +1,42 @@ +#!/bin/sh -e +# +# 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 source=conf.sh +. ../../conf.sh + +echo_i "ns2/setup.sh" + +for subdomain in dspublished reference missing-dspublished bad-dspublished \ + multiple-dspublished incomplete-dspublished bad2-dspublished \ + dswithdrawn missing-dswithdrawn bad-dswithdrawn \ + multiple-dswithdrawn incomplete-dswithdrawn bad2-dswithdrawn +do + cp "../ns9/dsset-$subdomain.checkds$TP" . +done + +private_type_record() { + _zone=$1 + _algorithm=$2 + _keyfile=$3 + + _id=$(keyfile_to_key_id "$_keyfile") + + printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" +} + +zone="checkds" +infile="checkds.db.infile" +zonefile="checkds.db" + +CSK=$($KEYGEN -k default $zone 2> keygen.out.$zone) +cat template.db.in "${CSK}.key" > "$infile" +private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >> "$infile" +$SIGNER -S -g -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone 2>&1 diff --git a/bin/tests/system/checkds/ns2/template.db.in b/bin/tests/system/checkds/ns2/template.db.in new file mode 100644 index 0000000000..e9e66f20ca --- /dev/null +++ b/bin/tests/system/checkds/ns2/template.db.in @@ -0,0 +1,36 @@ +; 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. + +$TTL 300 +@ IN SOA secondary.example. hostmaster.example. ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns2 +ns2 A 10.53.0.2 + +dspublished NS ns9.dspublished +reference NS ns9.reference +missing-dspublished NS ns9.missing-dspublished +bad-dspublished NS ns9.bad-dspublished +multiple-dspublished NS ns9.multiple-dspublished +incomplete-dspublished NS ns9.incomplete-dspublished +bad2-dspublished NS ns9.bad2-dspublished + +dswithdrawn NS ns9.dswithdrawn +missing-dswithdrawn NS ns9.missing-dswithdrawn +bad-dswithdrawn NS ns9.bad-dswithdrawn +multiple-dswithdrawn NS ns9.multiple-dswithdrawn +incomplete-dswithdrawn NS ns9.incomplete-dswithdrawn +bad2-dswithdrawn NS ns9.bad2-dswithdrawn + diff --git a/bin/tests/system/checkds/ns4/named.conf.in b/bin/tests/system/checkds/ns4/named.conf.in new file mode 100644 index 0000000000..6de6692597 --- /dev/null +++ b/bin/tests/system/checkds/ns4/named.conf.in @@ -0,0 +1,39 @@ +/* + * 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. + */ + +// 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; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "checkds" { + type secondary; + file "checkds.db"; + primaries { 10.53.0.2 port @PORT@; }; +}; diff --git a/bin/tests/system/checkds/ns5/named.conf.in b/bin/tests/system/checkds/ns5/named.conf.in new file mode 100644 index 0000000000..8b2e4b15ed --- /dev/null +++ b/bin/tests/system/checkds/ns5/named.conf.in @@ -0,0 +1,43 @@ +/* + * 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. + */ + +// NS5 + +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 rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.5 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +zone "checkds" { + type primary; + file "checkds.db"; +}; diff --git a/bin/tests/system/checkds/ns5/setup.sh b/bin/tests/system/checkds/ns5/setup.sh new file mode 100644 index 0000000000..100cd5dbec --- /dev/null +++ b/bin/tests/system/checkds/ns5/setup.sh @@ -0,0 +1,34 @@ +#!/bin/sh -e +# +# 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 source=conf.sh +. ../../conf.sh + +echo_i "ns5/setup.sh" + +private_type_record() { + _zone=$1 + _algorithm=$2 + _keyfile=$3 + + _id=$(keyfile_to_key_id "$_keyfile") + + printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" +} + +zone="checkds" +infile="checkds.db.infile" +zonefile="checkds.db" + +CSK=$($KEYGEN -k default $zone 2> keygen.out.$zone) +cat template.db.in "${CSK}.key" > "$infile" +private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >> "$infile" +$SIGNER -S -g -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone 2>&1 diff --git a/bin/tests/system/checkds/ns5/template.db.in b/bin/tests/system/checkds/ns5/template.db.in new file mode 100644 index 0000000000..5ff796a102 --- /dev/null +++ b/bin/tests/system/checkds/ns5/template.db.in @@ -0,0 +1,36 @@ +; 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. + +$TTL 300 +@ IN SOA secondary.example. hostmaster.example. ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns5 +ns5 A 10.53.0.5 + +dspublished NS ns9.dspublished +reference NS ns9.reference +missing-dspublished NS ns9.missing-dspublished +bad-dspublished NS ns9.bad-dspublished +multiple-dspublished NS ns9.multiple-dspublished +incomplete-dspublished NS ns9.incomplete-dspublished +bad2-dspublished NS ns9.bad2-dspublished + +dswithdrawn NS ns9.dswithdrawn +missing-dswithdrawn NS ns9.missing-dswithdrawn +bad-dswithdrawn NS ns9.bad-dswithdrawn +multiple-dswithdrawn NS ns9.multiple-dswithdrawn +incomplete-dswithdrawn NS ns9.incomplete-dswithdrawn +bad2-dswithdrawn NS ns9.bad2-dswithdrawn + diff --git a/bin/tests/system/checkds/ns6/named.conf.in b/bin/tests/system/checkds/ns6/named.conf.in new file mode 100644 index 0000000000..d9d1a1d8dd --- /dev/null +++ b/bin/tests/system/checkds/ns6/named.conf.in @@ -0,0 +1,43 @@ +/* + * 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. + */ + +// NS2 + +options { + query-source address 10.53.0.6; + notify-source 10.53.0.6; + transfer-source 10.53.0.6; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.6; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.6 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +zone "foo" { + type primary; + file "foo.db"; +}; diff --git a/bin/tests/system/checkds/ns7/named.conf.in b/bin/tests/system/checkds/ns7/named.conf.in new file mode 100644 index 0000000000..3bd74690cd --- /dev/null +++ b/bin/tests/system/checkds/ns7/named.conf.in @@ -0,0 +1,44 @@ +/* + * 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. + */ + +// NS7 + +options { + query-source address 10.53.0.7; + notify-source 10.53.0.7; + transfer-source 10.53.0.7; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.7; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.7 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +zone "checkds" { + type secondary; + file "checkds.db"; + primaries { 10.53.0.5 port @PORT@; }; +}; diff --git a/bin/tests/system/checkds/ns9/named.conf.in b/bin/tests/system/checkds/ns9/named.conf.in new file mode 100644 index 0000000000..7252d466c8 --- /dev/null +++ b/bin/tests/system/checkds/ns9/named.conf.in @@ -0,0 +1,193 @@ + +// NS9 + +options { + query-source address 10.53.0.9; + notify-source 10.53.0.9; + transfer-source 10.53.0.9; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.9; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.9 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +parental-agents "ns2" port @PORT@ { + 10.53.0.2; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +/* + * Zone with parental agent configured, due for DS checking. + */ +zone "dspublished.checkds" { + type primary; + file "dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { 10.53.0.2 port @PORT@; }; +}; + +/* + * Zone with parental agent configured, due for DS checking. + * Same as above, but now with a reference to parental-agents. + */ +zone "reference.checkds" { + type primary; + file "reference.checkds.db"; + dnssec-policy "default"; + parental-agents { "ns2"; }; +}; + +/* + * Zone with parental agent configured, due for DS checking. + * The parental agent does not have the DS yet. + */ +zone "missing-dspublished.checkds" { + type primary; + file "missing-dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { + 10.53.0.5 port @PORT@; // missing + }; +}; + + +/* + * Zone with parental agent configured, due for DS checking. + * This case, the server is badly configured. + */ +zone "bad-dspublished.checkds" { + type primary; + file "bad-dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { + 10.53.0.6 port @PORT@; // bad + }; +}; + +/* + * Zone with multiple parental agents configured, due for DS checking. + * All need to have the DS before the rollover may continue. + */ +zone "multiple-dspublished.checkds" { + type primary; + file "multiple-dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { + 10.53.0.2 port @PORT@; + 10.53.0.4 port @PORT@; + }; +}; + +/* + * Zone with multiple parental agents configured, due for DS checking. + * All need to have the DS before the rollover may continue. + * This case, one server is still missing the DS. + */ +zone "incomplete-dspublished.checkds" { + type primary; + file "incomplete-dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { + 10.53.0.2 port @PORT@; + 10.53.0.4 port @PORT@; + 10.53.0.5 port @PORT@; // missing + }; +}; + + +/* + * Zone with multiple parental agents configured, due for DS checking. + * All need to have the DS before the rollover may continue. + * This case, one server is badly configured. + */ +zone "bad2-dspublished.checkds" { + type primary; + file "bad2-dspublished.checkds.db"; + dnssec-policy "default"; + parental-agents { + 10.53.0.2 port @PORT@; + 10.53.0.4 port @PORT@; + 10.53.0.6 port @PORT@; // bad + }; +}; + +// TODO: Other test cases: +// - Test with bogus response +// - check with TSIG +// - check with TLS + + +/* + * Zones that are going insecure (test DS withdrawn polling). + */ +zone "dswithdrawn.checkds" { + type primary; + file "dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { 10.53.0.5 port @PORT@; }; +}; + +zone "missing-dswithdrawn.checkds" { + type primary; + file "missing-dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { + 10.53.0.2 port @PORT@; // still published + }; +}; + +zone "bad-dswithdrawn.checkds" { + type primary; + file "bad-dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { + 10.53.0.6 port @PORT@; // bad + }; +}; + +zone "multiple-dswithdrawn.checkds" { + type primary; + file "multiple-dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { + 10.53.0.5 port @PORT@; + 10.53.0.7 port @PORT@; + }; +}; + +zone "incomplete-dswithdrawn.checkds" { + type primary; + file "incomplete-dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { + 10.53.0.2 port @PORT@; // still published + 10.53.0.5 port @PORT@; + 10.53.0.7 port @PORT@; + }; +}; + +zone "bad2-dswithdrawn.checkds" { + type primary; + file "bad2-dswithdrawn.checkds.db"; + dnssec-policy "insecure"; + parental-agents { + 10.53.0.5 port @PORT@; + 10.53.0.7 port @PORT@; + 10.53.0.6 port @PORT@; // bad + }; +}; diff --git a/bin/tests/system/checkds/ns9/setup.sh b/bin/tests/system/checkds/ns9/setup.sh new file mode 100644 index 0000000000..efb4a1e2c2 --- /dev/null +++ b/bin/tests/system/checkds/ns9/setup.sh @@ -0,0 +1,69 @@ +#!/bin/sh -e +# +# 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 source=conf.sh +. ../../conf.sh + +echo_i "ns9/setup.sh" + +setup() { + zone="$1" + echo_i "setting up zone: $zone" + zonefile="${zone}.db" + infile="${zone}.db.infile" + echo "$zone" >> zones +} + +private_type_record() { + _zone=$1 + _algorithm=$2 + _keyfile=$3 + + _id=$(keyfile_to_key_id "$_keyfile") + + printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" +} + +# Short environment variable names for key states and times. +H="HIDDEN" +R="RUMOURED" +O="OMNIPRESENT" +U="UNRETENTIVE" +T="now-30d" +Y="now-1y" + +# DS Publication. +for zn in dspublished reference missing-dspublished bad-dspublished \ + multiple-dspublished incomplete-dspublished bad2-dspublished +do + setup "${zn}.checkds" + cp template.db.in "$zonefile" + keytimes="-P $T -P sync $T -A $T" + CSK=$($KEYGEN -k default $keytimes $zone 2> keygen.out.$zone) + $SETTIME -s -g $O -k $O $T -r $O $T -z $O $T -d $R $T "$CSK" > settime.out.$zone 2>&1 + cat template.db.in "${CSK}.key" > "$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >> "$infile" + $SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 +done + +# DS Withdrawal. +for zn in dswithdrawn missing-dswithdrawn bad-dswithdrawn multiple-dswithdrawn \ + incomplete-dswithdrawn bad2-dswithdrawn +do + setup "${zn}.checkds" + cp template.db.in "$zonefile" + keytimes="-P $Y -P sync $Y -A $Y" + CSK=$($KEYGEN -k default $keytimes $zone 2> keygen.out.$zone) + $SETTIME -s -g $H -k $O $T -r $O $T -z $O $T -d $U $T "$CSK" > settime.out.$zone 2>&1 + cat template.db.in "${CSK}.key" > "$infile" + private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >> "$infile" + $SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 +done diff --git a/bin/tests/system/checkds/ns9/template.db.in b/bin/tests/system/checkds/ns9/template.db.in new file mode 100644 index 0000000000..36404a903c --- /dev/null +++ b/bin/tests/system/checkds/ns9/template.db.in @@ -0,0 +1,25 @@ +; 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. + +$TTL 300 +@ IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns9 +ns9 A 10.53.0.9 + +a A 10.0.0.1 +b A 10.0.0.2 +c A 10.0.0.3 + diff --git a/bin/tests/system/checkds/setup.sh b/bin/tests/system/checkds/setup.sh new file mode 100644 index 0000000000..2261e15745 --- /dev/null +++ b/bin/tests/system/checkds/setup.sh @@ -0,0 +1,38 @@ +#!/bin/sh -e +# +# 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 source=conf.sh +. ../conf.sh + +set -e + +$SHELL clean.sh + +copy_setports ns2/named.conf.in ns2/named.conf +copy_setports ns4/named.conf.in ns4/named.conf +copy_setports ns5/named.conf.in ns5/named.conf +copy_setports ns6/named.conf.in ns6/named.conf +copy_setports ns7/named.conf.in ns7/named.conf +copy_setports ns9/named.conf.in ns9/named.conf + +# Setup zones +( + cd ns9 + $SHELL setup.sh +) +( + cd ns5 + $SHELL setup.sh +) +( + cd ns2 + $SHELL setup.sh +) diff --git a/bin/tests/system/checkds/tests-checkds.py b/bin/tests/system/checkds/tests-checkds.py new file mode 100755 index 0000000000..1c60c93309 --- /dev/null +++ b/bin/tests/system/checkds/tests-checkds.py @@ -0,0 +1,376 @@ +#!/usr/bin/python3 +############################################################################ +# 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. +############################################################################ + +import mmap +import os +import subprocess +import sys +import time + +import dns.resolver +import pytest + + +def has_signed_apex_nsec(zone, response): + has_nsec = False + has_rrsig = False + + ttl = 300 + nextname = "a." + types = "NS SOA RRSIG NSEC DNSKEY CDS CDNSKEY" + match = "{0} {1} IN NSEC {2}{0} {3}".format(zone, ttl, nextname, types) + sig = "{0} {1} IN RRSIG NSEC 13 2 300".format(zone, ttl) + + for rr in response.answer: + if match in rr.to_text(): + has_nsec = True + if sig in rr.to_text(): + has_rrsig = True + + if not has_nsec: + print("error: missing apex NSEC record in response") + if not has_rrsig: + print("error: missing NSEC signature in response") + + return has_nsec and has_rrsig + + +def do_query(server, qname, qtype, tcp=False): + query = dns.message.make_query(qname, qtype, use_edns=True, + want_dnssec=True) + try: + if tcp: + response = dns.query.tcp(query, server.nameservers[0], timeout=3, + port=server.port) + else: + response = dns.query.udp(query, server.nameservers[0], timeout=3, + port=server.port) + except dns.exception.Timeout: + print("error: query timeout for query {} {} to {}".format( + qname, qtype, server.nameservers[0])) + return None + + return response + + +def verify_zone(zone, transfer): + verify = os.getenv("VERIFY") + assert verify is not None + + filename = "{}out".format(zone) + with open(filename, 'w') as file: + for rr in transfer.answer: + file.write(rr.to_text()) + file.write('\n') + + # dnssec-verify command with default arguments. + verify_cmd = [verify, "-z", "-o", zone, filename] + + verifier = subprocess.run(verify_cmd, capture_output=True, check=True) + + if verifier.returncode != 0: + print("error: dnssec-verify {} failed".format(zone)) + sys.stderr.buffer.write(verifier.stderr) + + return verifier.returncode == 0 + + +def read_statefile(server, zone): + addr = server.nameservers[0] + count = 0 + keyid = 0 + state = {} + + response = do_query(server, zone, "DS", tcp=True) + if not isinstance(response, dns.message.Message): + print("error: no response for {} DS from {}".format(zone, addr)) + return {} + + if response.rcode() == dns.rcode.NOERROR: + # fetch key id from response. + for rr in response.answer: + if rr.match(dns.name.from_text(zone), dns.rdataclass.IN, + dns.rdatatype.DS, dns.rdatatype.NONE): + if count == 0: + keyid = list(dict(rr.items).items())[0][0].key_tag + count += 1 + + if count != 1: + print("error: expected a single DS in response for {} from {}," + "got {}".format(zone, addr, count)) + return {} + else: + print("error: {} response for {} DNSKEY from {}".format( + dns.rcode.to_text(response.rcode()), zone, addr)) + return {} + + filename = "ns9/K{}+013+{:05d}.state".format(zone, keyid) + print("read state file {}".format(filename)) + + try: + with open(filename, 'r') as file: + for line in file: + if line.startswith(';'): + continue + key, val = line.strip().split(':', 1) + state[key.strip()] = val.strip() + + except FileNotFoundError: + # file may not be written just yet. + return {} + + return state + + +def zone_check(server, zone): + addr = server.nameservers[0] + + # wait until zone is fully signed. + signed = False + for _ in range(10): + response = do_query(server, zone, 'NSEC') + if not isinstance(response, dns.message.Message): + print("error: no response for {} NSEC from {}".format(zone, addr)) + elif response.rcode() == dns.rcode.NOERROR: + signed = has_signed_apex_nsec(zone, response) + else: + print("error: {} response for {} NSEC from {}".format( + dns.rcode.to_text(response.rcode()), zone, addr)) + + if signed: + break + + time.sleep(1) + + assert signed + + # check if zone if DNSSEC valid. + verified = False + transfer = do_query(server, zone, 'AXFR', tcp=True) + if not isinstance(transfer, dns.message.Message): + print("error: no response for {} AXFR from {}".format(zone, addr)) + elif transfer.rcode() == dns.rcode.NOERROR: + verified = verify_zone(zone, transfer) + else: + print("error: {} response for {} AXFR from {}".format( + dns.rcode.to_text(transfer.rcode()), zone, addr)) + + assert verified + + +def keystate_check(server, zone, key): + val = 0 + deny = False + + search = key + if key.startswith('!'): + deny = True + search = key[1:] + + for _ in range(10): + state = read_statefile(server, zone) + try: + val = state[search] + except KeyError: + pass + + if not deny and val != 0: + break + if deny and val == 0: + break + + time.sleep(1) + + if deny: + assert val == 0 + else: + assert val != 0 + + +def wait_for_log(filename, log): + found = False + + for _ in range(10): + print("read log file {}".format(filename)) + + try: + with open(filename, 'r') as file: + s = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) + if s.find(bytes(log, "ascii")) != -1: + found = True + except FileNotFoundError: + print("file not found {}".format(filename)) + + if found: + break + + print("sleep") + time.sleep(1) + + assert found + + +@pytest.mark.dnspython +@pytest.mark.dnspython2 +def test_checkds_dspublished(named_port): + # We create resolver instances that will be used to send queries. + server = dns.resolver.Resolver() + server.nameservers = ["10.53.0.9"] + server.port = named_port + + parent = dns.resolver.Resolver() + parent.nameservers = ["10.53.0.2"] + parent.port = named_port + + # DS correctly published in parent. + zone_check(server, "dspublished.checkds.") + wait_for_log("ns9/named.run", + "zone dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + keystate_check(parent, "dspublished.checkds.", "DSPublish") + + # DS correctly published in parent (reference to parental-agent). + zone_check(server, "reference.checkds.") + wait_for_log("ns9/named.run", + "zone reference.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + keystate_check(parent, "reference.checkds.", "DSPublish") + + # DS not published in parent. + zone_check(server, "missing-dspublished.checkds.") + wait_for_log("ns9/named.run", + "zone missing-dspublished.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + keystate_check(parent, "missing-dspublished.checkds.", "!DSPublish") + + # Badly configured parent. + zone_check(server, "bad-dspublished.checkds.") + wait_for_log("ns9/named.run", + "zone bad-dspublished.checkds/IN (signed): checkds: " + "bad DS response from 10.53.0.6") + keystate_check(parent, "bad-dspublished.checkds.", "!DSPublish") + + # TBD: DS published in parent, but bogus signature. + + # DS correctly published in all parents. + zone_check(server, "multiple-dspublished.checkds.") + wait_for_log("ns9/named.run", + "zone multiple-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + wait_for_log("ns9/named.run", + "zone multiple-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.4") + keystate_check(parent, "multiple-dspublished.checkds.", "DSPublish") + + # DS published in only one of multiple parents. + zone_check(server, "incomplete-dspublished.checkds.") + wait_for_log("ns9/named.run", + "zone incomplete-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + wait_for_log("ns9/named.run", + "zone incomplete-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.4") + wait_for_log("ns9/named.run", + "zone incomplete-dspublished.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + keystate_check(parent, "incomplete-dspublished.checkds.", "!DSPublish") + + # One of the parents is badly configured. + wait_for_log("ns9/named.run", + "zone bad2-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + wait_for_log("ns9/named.run", + "zone bad2-dspublished.checkds/IN (signed): checkds: " + "DS response from 10.53.0.4") + wait_for_log("ns9/named.run", + "zone bad2-dspublished.checkds/IN (signed): checkds: " + "bad DS response from 10.53.0.6") + keystate_check(parent, "bad2-dspublished.checkds.", "!DSPublish") + + # TBD: DS published in all parents, but one has bogus signature. + + # TBD: Check with TSIG + + # TBD: Check with TLS + + +@pytest.mark.dnspython +@pytest.mark.dnspython2 +def test_checkds_dswithdrawn(named_port): + # We create resolver instances that will be used to send queries. + server = dns.resolver.Resolver() + server.nameservers = ["10.53.0.9"] + server.port = named_port + + parent = dns.resolver.Resolver() + parent.nameservers = ["10.53.0.2"] + parent.port = named_port + + # DS correctly published in single parent. + zone_check(server, "dswithdrawn.checkds.") + wait_for_log("ns9/named.run", + "zone dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + keystate_check(parent, "dswithdrawn.checkds.", "DSRemoved") + + # DS not withdrawn from parent. + zone_check(server, "missing-dswithdrawn.checkds.") + wait_for_log("ns9/named.run", + "zone missing-dswithdrawn.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + keystate_check(parent, "missing-dswithdrawn.checkds.", "!DSRemoved") + + # Badly configured parent. + zone_check(server, "bad-dswithdrawn.checkds.") + wait_for_log("ns9/named.run", + "zone bad-dswithdrawn.checkds/IN (signed): checkds: " + "bad DS response from 10.53.0.6") + keystate_check(parent, "bad-dswithdrawn.checkds.", "!DSRemoved") + + # TBD: DS published in parent, but bogus signature. + + # DS correctly withdrawn from all parents. + zone_check(server, "multiple-dswithdrawn.checkds.") + wait_for_log("ns9/named.run", + "zone multiple-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + wait_for_log("ns9/named.run", + "zone multiple-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.7") + keystate_check(parent, "multiple-dswithdrawn.checkds.", "DSRemoved") + + # DS withdrawn from only one of multiple parents. + zone_check(server, "incomplete-dswithdrawn.checkds.") + wait_for_log("ns9/named.run", + "zone incomplete-dswithdrawn.checkds/IN (signed): checkds: " + "DS response from 10.53.0.2") + wait_for_log("ns9/named.run", + "zone incomplete-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + wait_for_log("ns9/named.run", + "zone incomplete-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.7") + keystate_check(parent, "incomplete-dswithdrawn.checkds.", "!DSRemoved") + + # One of the parents is badly configured. + wait_for_log("ns9/named.run", + "zone bad2-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.5") + wait_for_log("ns9/named.run", + "zone bad2-dswithdrawn.checkds/IN (signed): checkds: " + "empty DS response from 10.53.0.7") + wait_for_log("ns9/named.run", + "zone bad2-dswithdrawn.checkds/IN (signed): checkds: " + "bad DS response from 10.53.0.6") + keystate_check(parent, "bad2-dswithdrawn.checkds.", "!DSRemoved") + + # TBD: DS withdrawn from all parents, but one has bogus signature. diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index 699edba607..d6773c174d 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -82,6 +82,7 @@ SEQUENTIALDIRS="$SEQUENTIAL_COMMON $SEQUENTIAL_UNIX" PARALLEL_UNIX="@DNSTAP@ chain +checkds cookie dlzexternal dnssec diff --git a/util/copyrights b/util/copyrights index fe9802ea3c..f2aae63f10 100644 --- a/util/copyrights +++ b/util/copyrights @@ -188,6 +188,14 @@ ./bin/tests/system/checkconf/dnssec.2 X 2011,2016,2018,2019,2020,2021 ./bin/tests/system/checkconf/good.zonelist X 2016,2017,2018,2019,2020,2021 ./bin/tests/system/checkconf/tests.sh SH 2005,2007,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 +./bin/tests/system/checkds/README TXT.BRIEF 2021 +./bin/tests/system/checkds/clean.sh SH 2021 +./bin/tests/system/checkds/conftest.py PYTHON 2021 +./bin/tests/system/checkds/ns2/setup.sh SH 2021 +./bin/tests/system/checkds/ns5/setup.sh SH 2021 +./bin/tests/system/checkds/ns9/setup.sh SH 2021 +./bin/tests/system/checkds/setup.sh SH 2021 +./bin/tests/system/checkds/tests-checkds.py PYTHON-BIN 2021 ./bin/tests/system/checknames/clean.sh SH 2004,2007,2012,2014,2015,2016,2018,2019,2020,2021 ./bin/tests/system/checknames/setup.sh SH 2004,2007,2012,2014,2016,2018,2019,2020,2021 ./bin/tests/system/checknames/tests.sh SH 2004,2007,2012,2013,2014,2015,2016,2018,2019,2020,2021 From 8327cb7839c282b9c6bd2cf1d4529bcd09220c23 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 11 May 2021 14:40:23 +0200 Subject: [PATCH 05/20] Remove stray "setup zone" in kasp system setup --- bin/tests/system/kasp/ns3/setup.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index b70fce7cdd..d487619f3b 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -293,7 +293,6 @@ $SETTIME -s -g $O -k $O $TcotN -r $O $TcotN -d $H $TpubN -z $R $TpubN "$CSK" > s cat template.db.in "${CSK}.key" > "$infile" private_type_record $zone $DEFAULT_ALGORITHM_NUMBER "$CSK" >> "$infile" $SIGNER -S -z -x -s now-1h -e now+30d -o $zone -O full -f $zonefile $infile > signer.out.$zone.1 2>&1 -setup step3.enable-dnssec.autosign # Step 4: # The DS has been submitted long enough ago to become OMNIPRESENT. From 6040c71478557823385432eb959611f68e04c64c Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 11 May 2021 14:43:02 +0200 Subject: [PATCH 06/20] Make "primaries" config parsing generic Make the code to parse "primaries" configuration more generic so it can be reused for "parental-agents". --- bin/named/config.c | 32 ++++++++++++++----------- bin/named/include/named/config.h | 9 +++---- bin/named/server.c | 4 ++-- bin/named/zoneconf.c | 12 +++++----- lib/dns/zone.c | 40 ++++++++++++++++---------------- 5 files changed, 52 insertions(+), 45 deletions(-) diff --git a/bin/named/config.c b/bin/named/config.c index 86896efc51..f4b2d4e7bd 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -570,8 +570,8 @@ named_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp, } static isc_result_t -getprimariesdef(const cfg_obj_t *cctx, const char *list, const char *name, - const cfg_obj_t **ret) { +getremotesdef(const cfg_obj_t *cctx, const char *list, const char *name, + const cfg_obj_t **ret) { isc_result_t result; const cfg_obj_t *obj = NULL; const cfg_listelt_t *elt; @@ -598,15 +598,20 @@ getprimariesdef(const cfg_obj_t *cctx, const char *list, const char *name, } isc_result_t -named_config_getprimariesdef(const cfg_obj_t *cctx, const char *name, - const cfg_obj_t **ret) { +named_config_getremotesdef(const cfg_obj_t *cctx, const char *list, + const char *name, const cfg_obj_t **ret) { isc_result_t result; - result = getprimariesdef(cctx, "primaries", name, ret); - if (result != ISC_R_SUCCESS) { - result = getprimariesdef(cctx, "masters", name, ret); + if (strcmp(list, "parental-agents") == 0) { + return (getremotesdef(cctx, list, name, ret)); + } else if (strcmp(list, "primaries") == 0) { + result = getremotesdef(cctx, list, name, ret); + if (result != ISC_R_SUCCESS) { + result = getremotesdef(cctx, "masters", name, ret); + } + return (result); } - return (result); + return (ISC_R_NOTFOUND); } static isc_result_t @@ -675,8 +680,9 @@ named_config_getname(isc_mem_t *mctx, const cfg_obj_t *obj, } isc_result_t -named_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list, - isc_mem_t *mctx, dns_ipkeylist_t *ipkl) { +named_config_getipandkeylist(const cfg_obj_t *config, const char *listtype, + const cfg_obj_t *list, isc_mem_t *mctx, + dns_ipkeylist_t *ipkl) { uint32_t addrcount = 0, dscpcount = 0, keycount = 0, tlscount = 0, i = 0; uint32_t listcount = 0, l = 0, j; @@ -788,11 +794,11 @@ resume: continue; } list = NULL; - tresult = named_config_getprimariesdef(config, listname, - &list); + tresult = named_config_getremotesdef(config, listtype, + listname, &list); if (tresult == ISC_R_NOTFOUND) { cfg_obj_log(addr, named_g_lctx, ISC_LOG_ERROR, - "primaries \"%s\" not found", + "%s \"%s\" not found", listtype, listname); result = tresult; diff --git a/bin/named/include/named/config.h b/bin/named/include/named/config.h index 7e005a29a1..27dc30aac6 100644 --- a/bin/named/include/named/config.h +++ b/bin/named/include/named/config.h @@ -59,12 +59,13 @@ named_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp, isc_dscp_t **dscpsp, uint32_t count); isc_result_t -named_config_getprimariesdef(const cfg_obj_t *cctx, const char *name, - const cfg_obj_t **ret); +named_config_getremotesdef(const cfg_obj_t *cctx, const char *list, + const char *name, const cfg_obj_t **ret); isc_result_t -named_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list, - isc_mem_t *mctx, dns_ipkeylist_t *ipkl); +named_config_getipandkeylist(const cfg_obj_t *config, const char *listtype, + const cfg_obj_t *list, isc_mem_t *mctx, + dns_ipkeylist_t *ipkl); isc_result_t named_config_getport(const cfg_obj_t *config, const char *type, diff --git a/bin/named/server.c b/bin/named/server.c index 517bbac9d6..42f82ba3aa 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -3088,8 +3088,8 @@ configure_catz_zone(dns_view_t *view, const cfg_obj_t *config, obj = cfg_tuple_get(catz_obj, "default-masters"); if (obj != NULL && cfg_obj_istuple(obj)) { - result = named_config_getipandkeylist(config, obj, view->mctx, - &opts->masters); + result = named_config_getipandkeylist( + config, "primaries", obj, view->mctx, &opts->masters); } obj = cfg_tuple_get(catz_obj, "in-memory"); diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 2e05cee6d4..9956e351fa 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1308,8 +1308,8 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, dns_ipkeylist_t ipkl; dns_ipkeylist_init(&ipkl); - RETERR(named_config_getipandkeylist(config, obj, mctx, - &ipkl)); + RETERR(named_config_getipandkeylist(config, "primaries", + obj, mctx, &ipkl)); result = dns_zone_setalsonotify(zone, ipkl.addrs, ipkl.dscps, ipkl.keys, ipkl.tlss, ipkl.count); @@ -1904,8 +1904,8 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, if (obj == NULL && ztype == dns_zone_mirror && dns_name_equal(dns_zone_getorigin(zone), dns_rootname)) { - result = named_config_getprimariesdef( - named_g_config, + result = named_config_getremotesdef( + named_g_config, "primaries", DEFAULT_IANA_ROOT_ZONE_PRIMARIES, &obj); RETERR(result); } @@ -1913,8 +1913,8 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, dns_ipkeylist_t ipkl; dns_ipkeylist_init(&ipkl); - RETERR(named_config_getipandkeylist(config, obj, mctx, - &ipkl)); + RETERR(named_config_getipandkeylist(config, "primaries", + obj, mctx, &ipkl)); result = dns_zone_setprimaries(mayberaw, ipkl.addrs, ipkl.keys, ipkl.tlss, ipkl.count); diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 3d24967577..46a0aff8f9 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -6012,9 +6012,9 @@ same_names(dns_name_t *const *oldlist, dns_name_t *const *newlist, } static void -clear_primarieslist(isc_sockaddr_t **addrsp, isc_dscp_t **dscpsp, - dns_name_t ***keynamesp, dns_name_t ***tlsnamesp, - unsigned int *countp, isc_mem_t *mctx) { +clear_serverslist(isc_sockaddr_t **addrsp, isc_dscp_t **dscpsp, + dns_name_t ***keynamesp, dns_name_t ***tlsnamesp, + unsigned int *countp, isc_mem_t *mctx) { unsigned int count; isc_sockaddr_t *addrs; isc_dscp_t *dscps; @@ -6074,11 +6074,11 @@ clear_primarieslist(isc_sockaddr_t **addrsp, isc_dscp_t **dscpsp, } static isc_result_t -set_primarieslist(unsigned int count, const isc_sockaddr_t *addrs, - isc_sockaddr_t **newaddrsp, const isc_dscp_t *dscp, - isc_dscp_t **newdscpp, dns_name_t **keynames, - dns_name_t ***newkeynamesp, dns_name_t **tlsnames, - dns_name_t ***newtlsnamesp, isc_mem_t *mctx) { +set_serverslist(unsigned int count, const isc_sockaddr_t *addrs, + isc_sockaddr_t **newaddrsp, const isc_dscp_t *dscp, + isc_dscp_t **newdscpp, dns_name_t **keynames, + dns_name_t ***newkeynamesp, dns_name_t **tlsnames, + dns_name_t ***newtlsnamesp, isc_mem_t *mctx) { isc_sockaddr_t *newaddrs = NULL; isc_dscp_t *newdscp = NULL; dns_name_t **newkeynames = NULL; @@ -6180,9 +6180,9 @@ dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify, goto unlock; } - clear_primarieslist(&zone->notify, &zone->notifydscp, - &zone->notifykeynames, &zone->notifytlsnames, - &zone->notifycnt, zone->mctx); + clear_serverslist(&zone->notify, &zone->notifydscp, + &zone->notifykeynames, &zone->notifytlsnames, + &zone->notifycnt, zone->mctx); if (count == 0) { goto unlock; @@ -6191,9 +6191,9 @@ dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify, /* * Set up the notify and notifykey lists */ - result = set_primarieslist(count, notify, &newaddrs, dscps, &newdscps, - keynames, &newkeynames, tlsnames, - &newtlsnames, zone->mctx); + result = set_serverslist(count, notify, &newaddrs, dscps, &newdscps, + keynames, &newkeynames, tlsnames, &newtlsnames, + zone->mctx); if (result != ISC_R_SUCCESS) { goto unlock; } @@ -6257,9 +6257,9 @@ dns_zone_setprimaries(dns_zone_t *zone, const isc_sockaddr_t *masters, zone->masterscnt * sizeof(bool)); zone->mastersok = NULL; } - clear_primarieslist(&zone->masters, &zone->masterdscps, - &zone->masterkeynames, &zone->mastertlsnames, - &zone->masterscnt, zone->mctx); + clear_serverslist(&zone->masters, &zone->masterdscps, + &zone->masterkeynames, &zone->mastertlsnames, + &zone->masterscnt, zone->mctx); /* * If count == 0, don't allocate any space for masters, mastersok or * keynames so internally, those pointers are NULL if count == 0 @@ -6279,9 +6279,9 @@ dns_zone_setprimaries(dns_zone_t *zone, const isc_sockaddr_t *masters, /* * Now set up the primaries and primary key lists */ - result = set_primarieslist(count, masters, &newaddrs, NULL, &newdscps, - keynames, &newkeynames, tlsnames, - &newtlsnames, zone->mctx); + result = set_serverslist(count, masters, &newaddrs, NULL, &newdscps, + keynames, &newkeynames, tlsnames, &newtlsnames, + zone->mctx); INSIST(newdscps == NULL); if (result != ISC_R_SUCCESS) { isc_mem_put(zone->mctx, newok, count * sizeof(*newok)); From 6f92d4b9a5abc0473781554a6d8b4ab735d31c70 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 11 May 2021 14:46:38 +0200 Subject: [PATCH 07/20] Parse "parental-agents" configuration Parse the new "parental-agents" configuration and store it in the zone structure. --- bin/named/zoneconf.c | 22 +++++++++ lib/dns/include/dns/zone.h | 26 +++++++++-- lib/dns/zone.c | 91 +++++++++++++++++++++++++++++++++----- 3 files changed, 125 insertions(+), 14 deletions(-) diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 9956e351fa..873da27849 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1710,6 +1710,28 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, dns_zone_clearforwardacl)); } + /*% + * Configure parental agents, applies to primary and secondary zones. + */ + if (ztype == dns_zone_master || ztype == dns_zone_slave) { + obj = NULL; + (void)cfg_map_get(zoptions, "parental-agents", &obj); + if (obj != NULL) { + dns_ipkeylist_t ipkl; + dns_ipkeylist_init(&ipkl); + RETERR(named_config_getipandkeylist( + config, "parental-agents", obj, mctx, &ipkl)); + result = dns_zone_setparentals(zone, ipkl.addrs, + ipkl.keys, ipkl.tlss, + ipkl.count); + dns_ipkeylist_clear(mctx, &ipkl); + RETERR(result); + } else { + RETERR(dns_zone_setparentals(zone, NULL, NULL, NULL, + 0)); + } + } + /*% * Primary master functionality. */ diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index a981f0dd23..dc8350c6e3 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -642,12 +642,30 @@ dns_zone_setprimaries(dns_zone_t *zone, const isc_sockaddr_t *primaries, *\li 'zone' to be a valid zone. *\li 'primaries' array of isc_sockaddr_t with port set or NULL. *\li 'count' the number of primaries. - *\li 'keynames' array of dns_name_t's for tsig keys or NULL. + *\li 'keynames' array of dns_name_t's for tsig keys or NULL. * - * \li dns_zone_setprimaries() is just a wrapper to setprimarieswithkeys(), - * passing NULL in the keynames field. + *\li If 'primaries' is NULL then 'count' must be zero. * - * \li If 'primaries' is NULL then 'count' must be zero. + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li Any result dns_name_dup() can return, if keynames!=NULL + */ + +isc_result_t +dns_zone_setparentals(dns_zone_t *zone, const isc_sockaddr_t *parentals, + dns_name_t **keynames, dns_name_t **tlsnames, + uint32_t count); +/*%< + * Set the list of parental agents for the zone. + * + * Require: + *\li 'zone' to be a valid zone. + *\li 'parentals' array of isc_sockaddr_t with port set or NULL. + *\li 'count' the number of primaries. + *\li 'keynames' array of dns_name_t's for tsig keys or NULL. + * + *\li If 'parentals' is NULL then 'count' must be zero. * * Returns: *\li #ISC_R_SUCCESS diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 46a0aff8f9..3211c54de8 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -281,6 +281,15 @@ struct dns_zone { unsigned int masterscnt; unsigned int curmaster; isc_sockaddr_t masteraddr; + + isc_sockaddr_t *parentals; + isc_dscp_t *parentaldscps; + dns_name_t **parentalkeynames; + dns_name_t **parentaltlsnames; + dns_dnsseckeylist_t checkds_ok; + unsigned int parentalscnt; + isc_sockaddr_t parentaladdr; + dns_notifytype_t notifytype; isc_sockaddr_t *notify; dns_name_t **notifykeynames; @@ -1129,6 +1138,16 @@ free_refs: return (result); } +static void +clear_keylist(dns_dnsseckeylist_t *list, isc_mem_t *mctx) { + dns_dnsseckey_t *key; + while (!ISC_LIST_EMPTY(*list)) { + key = ISC_LIST_HEAD(*list); + ISC_LIST_UNLINK(*list, key, link); + dns_dnsseckey_destroy(mctx, &key); + } +} + /* * Free a zone. Because we require that there be no more * outstanding events or references, no locking is necessary. @@ -1222,6 +1241,10 @@ zone_free(dns_zone_t *zone) { if (zone->kasp != NULL) { dns_kasp_detach(&zone->kasp); } + if (!ISC_LIST_EMPTY(zone->checkds_ok)) { + clear_keylist(&zone->checkds_ok, zone->mctx); + } + zone->journalsize = -1; if (zone->journal != NULL) { isc_mem_free(zone->mctx, zone->journal); @@ -1251,6 +1274,8 @@ zone_free(dns_zone_t *zone) { dns_catz_catzs_detach(&zone->catzs); } zone_freedbargs(zone); + RUNTIME_CHECK(dns_zone_setparentals(zone, NULL, NULL, NULL, 0) == + ISC_R_SUCCESS); RUNTIME_CHECK(dns_zone_setprimaries(zone, NULL, NULL, NULL, 0) == ISC_R_SUCCESS); RUNTIME_CHECK(dns_zone_setalsonotify(zone, NULL, NULL, NULL, NULL, 0) == @@ -6305,6 +6330,62 @@ unlock: return (result); } +isc_result_t +dns_zone_setparentals(dns_zone_t *zone, const isc_sockaddr_t *parentals, + dns_name_t **keynames, dns_name_t **tlsnames, + uint32_t count) { + isc_result_t result = ISC_R_SUCCESS; + isc_sockaddr_t *newaddrs = NULL; + isc_dscp_t *newdscps = NULL; + dns_name_t **newkeynames = NULL; + dns_name_t **newtlsnames = NULL; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(count == 0 || parentals != NULL); + if (keynames != NULL || tlsnames != NULL) { + REQUIRE(count != 0); + } + + LOCK_ZONE(zone); + + clear_serverslist(&zone->parentals, &zone->parentaldscps, + &zone->parentalkeynames, &zone->parentaltlsnames, + &zone->parentalscnt, zone->mctx); + /* + * If count == 0, don't allocate any space for parentals, or keynames + * so internally, those pointers are NULL if count == 0 + */ + if (count == 0) { + goto unlock; + } + + /* + * Now set up the parentals and parental key lists + */ + result = set_serverslist(count, parentals, &newaddrs, NULL, &newdscps, + keynames, &newkeynames, tlsnames, &newtlsnames, + zone->mctx); + INSIST(newdscps == NULL); + if (result != ISC_R_SUCCESS) { + goto unlock; + } + + /* + * Everything is ok so attach to the zone. + */ + zone->parentals = newaddrs; + zone->parentaldscps = newdscps; + zone->parentalkeynames = newkeynames; + zone->parentaltlsnames = newtlsnames; + zone->parentalscnt = count; + + dns_zone_log(zone, ISC_LOG_NOTICE, "checkds: set %u parentals", count); + +unlock: + UNLOCK_ZONE(zone); + return (result); +} + isc_result_t dns_zone_getdb(dns_zone_t *zone, dns_db_t **dpb) { isc_result_t result = ISC_R_SUCCESS; @@ -19617,16 +19698,6 @@ cleanup: return (result); } -static void -clear_keylist(dns_dnsseckeylist_t *list, isc_mem_t *mctx) { - dns_dnsseckey_t *key; - while (!ISC_LIST_EMPTY(*list)) { - key = ISC_LIST_HEAD(*list); - ISC_LIST_UNLINK(*list, key, link); - dns_dnsseckey_destroy(mctx, &key); - } -} - /* Called once; *timep should be set to the current time. */ static isc_result_t next_keyevent(dst_key_t *key, isc_stdtime_t *timep) { From c9b7f62767ef157f905b64e6fc45c5545a4e6491 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 12 May 2021 11:09:33 +0200 Subject: [PATCH 08/20] Add dst_key_role function Change the static function 'get_ksk_zsk' to a library function that can be used to determine the role of a dst_key. Add checks if the boolean parameters to store the role are not NULL. Rename to 'dst_key_role'. --- lib/dns/dst_api.c | 35 +++++++++++++++++++++++------------ lib/dns/include/dst/dst.h | 9 +++++++++ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index 6a2e018517..884537c2e4 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -2372,20 +2372,31 @@ dst_key_is_unused(dst_key_t *key) { return (true); } -static void -get_ksk_zsk(dst_key_t *key, bool *ksk, bool *zsk) { +isc_result_t +dst_key_role(dst_key_t *key, bool *ksk, bool *zsk) { bool k = false, z = false; + isc_result_t result, ret = ISC_R_SUCCESS; - if (dst_key_getbool(key, DST_BOOL_KSK, &k) == ISC_R_SUCCESS) { - *ksk = k; - } else { - *ksk = ((dst_key_flags(key) & DNS_KEYFLAG_KSK) != 0); + if (ksk != NULL) { + result = dst_key_getbool(key, DST_BOOL_KSK, &k); + if (result == ISC_R_SUCCESS) { + *ksk = k; + } else { + *ksk = ((dst_key_flags(key) & DNS_KEYFLAG_KSK) != 0); + ret = result; + } } - if (dst_key_getbool(key, DST_BOOL_ZSK, &z) == ISC_R_SUCCESS) { - *zsk = z; - } else { - *zsk = ((dst_key_flags(key) & DNS_KEYFLAG_KSK) == 0); + + if (zsk != NULL) { + result = dst_key_getbool(key, DST_BOOL_ZSK, &z); + if (result == ISC_R_SUCCESS) { + *zsk = z; + } else { + *zsk = ((dst_key_flags(key) & DNS_KEYFLAG_KSK) == 0); + ret = result; + } } + return (ret); } /* Hints on key whether it can be published and/or used for signing. */ @@ -2444,7 +2455,7 @@ dst_key_is_active(dst_key_t *key, isc_stdtime_t now) { time_ok = (when <= now); } - get_ksk_zsk(key, &ksk, &zsk); + (void)dst_key_role(key, &ksk, &zsk); /* Check key states: * KSK: If the DS is RUMOURED or OMNIPRESENT the key is considered @@ -2505,7 +2516,7 @@ dst_key_is_signing(dst_key_t *key, int role, isc_stdtime_t now, time_ok = (when <= now); } - get_ksk_zsk(key, &ksk, &zsk); + (void)dst_key_role(key, &ksk, &zsk); /* Check key states: * If the RRSIG state is RUMOURED or OMNIPRESENT, it means the key diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h index f219aa8e31..2f9877be43 100644 --- a/lib/dns/include/dst/dst.h +++ b/lib/dns/include/dst/dst.h @@ -1180,6 +1180,15 @@ dst_key_goal(dst_key_t *key); * 'key' to be valid. */ +isc_result_t +dst_key_role(dst_key_t *key, bool *ksk, bool *zsk); +/*%< + * Get the key role. A key can have the KSK or the ZSK role, or both. + * + * Requires: + * 'key' to be valid. + */ + void dst_key_copy_metadata(dst_key_t *to, dst_key_t *from); /*%< From 2872d6a12efe578360a641c1ba90884ea9a7dd01 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 14 May 2021 11:33:51 +0200 Subject: [PATCH 09/20] Add "parental-source[-v6]" config option Similar to "notify-source" and "transfer-source", add options to set the source address when querying parental agents for DS records. --- bin/named/config.c | 2 + bin/named/named.conf.rst | 16 ++++ bin/named/zoneconf.c | 24 +++++ bin/tests/system/checkconf/good.conf | 1 + doc/arm/reference.rst | 28 +++++- doc/man/named.conf.5in | 16 ++++ doc/misc/master.zoneopt | 2 + doc/misc/master.zoneopt.rst | 2 + doc/misc/options | 16 ++++ doc/misc/options.active | 16 ++++ doc/misc/options.grammar.rst | 4 + doc/misc/slave.zoneopt | 2 + doc/misc/slave.zoneopt.rst | 2 + lib/bind9/check.c | 1 + lib/dns/include/dns/zone.h | 97 +++++++++++++++++++ lib/dns/zone.c | 136 +++++++++++++++++++++++---- lib/isccfg/namedconf.c | 4 + 17 files changed, 349 insertions(+), 20 deletions(-) diff --git a/bin/named/config.c b/bin/named/config.c index f4b2d4e7bd..4f7c7fe0b6 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -179,6 +179,8 @@ options {\n\ notify-source *;\n\ notify-source-v6 *;\n\ nsec3-test-zone no;\n\ + parental-source *;\n\ + parental-source-v6 *;\n\ provide-ixfr true;\n\ qname-minimization relaxed;\n\ query-source address *;\n\ diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst index b4a91cd4c9..46a2ca1fe3 100644 --- a/bin/named/named.conf.rst +++ b/bin/named/named.conf.rst @@ -343,6 +343,10 @@ OPTIONS nta-lifetime duration; nta-recheck duration; nxdomain-redirect string; + parental-source ( ipv4_address | * ) [ port ( integer | * ) ] [ + dscp integer ]; + parental-source-v6 ( ipv6_address | * ) [ port ( integer | * ) + ] [ dscp integer ]; pid-file ( quoted_string | none ); port integer; preferred-glue string; @@ -744,6 +748,10 @@ VIEW nta-lifetime duration; nta-recheck duration; nxdomain-redirect string; + parental-source ( ipv4_address | * ) [ port ( integer | * ) ] [ + dscp integer ]; + parental-source-v6 ( ipv6_address | * ) [ port ( integer | * ) + ] [ dscp integer ]; plugin ( query ) string [ { unspecified-text } ]; preferred-glue string; @@ -945,6 +953,10 @@ VIEW remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; + parental-source ( ipv4_address | * ) [ port ( integer | + * ) ] [ dscp integer ]; + parental-source-v6 ( ipv6_address | * ) [ port ( + integer | * ) ] [ dscp integer ]; primaries [ port integer ] [ dscp integer ] { ( remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ @@ -1057,6 +1069,10 @@ ZONE remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; + parental-source ( ipv4_address | * ) [ port ( integer | * ) ] [ + dscp integer ]; + parental-source-v6 ( ipv6_address | * ) [ port ( integer | * ) + ] [ dscp integer ]; primaries [ port integer ] [ dscp integer ] { ( remote-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 873da27849..9680e12874 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1320,6 +1320,30 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, NULL, 0)); } + obj = NULL; + result = named_config_get(maps, "parental-source", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + RETERR(dns_zone_setparentalsrc4(zone, cfg_obj_assockaddr(obj))); + dscp = cfg_obj_getdscp(obj); + if (dscp == -1) { + dscp = named_g_dscp; + } + RETERR(dns_zone_setparentalsrc4dscp(zone, dscp)); + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + + obj = NULL; + result = named_config_get(maps, "parental-source-v6", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + RETERR(dns_zone_setparentalsrc6(zone, cfg_obj_assockaddr(obj))); + dscp = cfg_obj_getdscp(obj); + if (dscp == -1) { + dscp = named_g_dscp; + } + RETERR(dns_zone_setparentalsrc6dscp(zone, dscp)); + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + obj = NULL; result = named_config_get(maps, "notify-source", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); diff --git a/bin/tests/system/checkconf/good.conf b/bin/tests/system/checkconf/good.conf index bd6a9b083d..616a5441d4 100644 --- a/bin/tests/system/checkconf/good.conf +++ b/bin/tests/system/checkconf/good.conf @@ -185,6 +185,7 @@ view "fourth" { 1.2.3.5; }; dnssec-policy "test"; + parental-source 10.10.10.10 port 53 dscp 55; }; zone "dnssec-default" { type master; diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index d6a45551eb..da6d3d6020 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -2627,7 +2627,7 @@ options are: .. note:: Solaris 2.5.1 and earlier does not support setting the source address for TCP sockets. -.. note:: See also ``transfer-source`` and ``notify-source``. +.. note:: See also ``transfer-source``, ``notify-source`` and ``parental-source``. .. _zone_transfers: @@ -5136,6 +5136,32 @@ The following options can be specified in a ``dnssec-policy`` statement: zone is updated to the time when the new version is served by all of the parent zone's name servers. The default is ``PT1H`` (1 hour). +Automated KSK Rollovers +^^^^^^^^^^^^^^^^^^^^^^^ + +BIND has mechanisms in place to facilitate automated KSK rollovers. It +publishes CDS and CDNSKEY records that can be used by the parent zone to +publish or withdraw the zone's DS records. BIND will query the parental +agents to see if the new DS is actually published before withdrawing the +old DNSSEC key. The following options apply to DS queries sent to +``parental-agents``: + +``parental-source`` + ``parental-source`` determines which local source address, and + optionally UDP port, is used to send parental DS queries. This + address must appear in the secondary server's ``parental-agents`` zone + clause. This statement sets the ``parental-source`` for all zones, but can + be overridden on a per-zone or per-view basis by including a + ``parental-source`` statement within the ``zone`` or ``view`` block in the + configuration file. + + .. note:: Solaris 2.5.1 and earlier does not support setting the source + address for TCP sockets. + +``parental-source-v6`` + This option acts like ``parental-source``, but applies to parental DS + queries sent to IPv6 addresses. + .. _managed-keys: ``managed-keys`` Statement Grammar diff --git a/doc/man/named.conf.5in b/doc/man/named.conf.5in index 4beb6c4604..deec1ffb88 100644 --- a/doc/man/named.conf.5in +++ b/doc/man/named.conf.5in @@ -410,6 +410,10 @@ options { nta\-lifetime duration; nta\-recheck duration; nxdomain\-redirect string; + parental\-source ( ipv4_address | * ) [ port ( integer | * ) ] [ + dscp integer ]; + parental\-source\-v6 ( ipv6_address | * ) [ port ( integer | * ) + ] [ dscp integer ]; pid\-file ( quoted_string | none ); port integer; preferred\-glue string; @@ -847,6 +851,10 @@ view string [ class ] { nta\-lifetime duration; nta\-recheck duration; nxdomain\-redirect string; + parental\-source ( ipv4_address | * ) [ port ( integer | * ) ] [ + dscp integer ]; + parental\-source\-v6 ( ipv6_address | * ) [ port ( integer | * ) + ] [ dscp integer ]; plugin ( query ) string [ { unspecified\-text } ]; preferred\-glue string; @@ -1048,6 +1056,10 @@ view string [ class ] { remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; + parental\-source ( ipv4_address | * ) [ port ( integer | + * ) ] [ dscp integer ]; + parental\-source\-v6 ( ipv6_address | * ) [ port ( + integer | * ) ] [ dscp integer ]; primaries [ port integer ] [ dscp integer ] { ( remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ @@ -1164,6 +1176,10 @@ zone string [ class ] { remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls string ]; ... }; + parental\-source ( ipv4_address | * ) [ port ( integer | * ) ] [ + dscp integer ]; + parental\-source\-v6 ( ipv6_address | * ) [ port ( integer | * ) + ] [ dscp integer ]; primaries [ port integer ] [ dscp integer ] { ( remote\-servers | ipv4_address [ port integer ] | ipv6_address [ port integer ] ) [ key string ] [ tls diff --git a/doc/misc/master.zoneopt b/doc/misc/master.zoneopt index 6740613e8c..a34d512726 100644 --- a/doc/misc/master.zoneopt +++ b/doc/misc/master.zoneopt @@ -47,6 +47,8 @@ zone [ ] { notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; parental-agents [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + parental-source ( | * ) [ port ( | * ) ] [ dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; serial-update-method ( date | increment | unixtime ); sig-signing-nodes ; sig-signing-signatures ; diff --git a/doc/misc/master.zoneopt.rst b/doc/misc/master.zoneopt.rst index 05243c40bc..ad85f5f17f 100644 --- a/doc/misc/master.zoneopt.rst +++ b/doc/misc/master.zoneopt.rst @@ -49,6 +49,8 @@ notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; parental-agents [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + parental-source ( | * ) [ port ( | * ) ] [ dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; serial-update-method ( date | increment | unixtime ); sig-signing-nodes ; sig-signing-signatures ; diff --git a/doc/misc/options b/doc/misc/options index 87aa9e2f47..416326e6da 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -268,6 +268,10 @@ options { nta-lifetime ; nta-recheck ; nxdomain-redirect ; + parental-source ( | * ) [ port ( | * ) ] [ + dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) + ] [ dscp ]; pid-file ( | none ); port ; preferred-glue ; @@ -624,6 +628,10 @@ view [ ] { nta-lifetime ; nta-recheck ; nxdomain-redirect ; + parental-source ( | * ) [ port ( | * ) ] [ + dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) + ] [ dscp ]; plugin ( query ) [ { } ]; // may occur multiple times preferred-glue ; @@ -827,6 +835,10 @@ view [ ] { | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + parental-source ( | * ) [ port ( | + * ) ] [ dscp ]; + parental-source-v6 ( | * ) [ port ( + | * ) ] [ dscp ]; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ @@ -935,6 +947,10 @@ zone [ ] { | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + parental-source ( | * ) [ port ( | * ) ] [ + dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) + ] [ dscp ]; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls diff --git a/doc/misc/options.active b/doc/misc/options.active index d8bb60f930..b05e36838d 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -266,6 +266,10 @@ options { nta-lifetime ; nta-recheck ; nxdomain-redirect ; + parental-source ( | * ) [ port ( | * ) ] [ + dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) + ] [ dscp ]; pid-file ( | none ); port ; preferred-glue ; @@ -620,6 +624,10 @@ view [ ] { nta-lifetime ; nta-recheck ; nxdomain-redirect ; + parental-source ( | * ) [ port ( | * ) ] [ + dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) + ] [ dscp ]; plugin ( query ) [ { } ]; // may occur multiple times preferred-glue ; @@ -821,6 +829,10 @@ view [ ] { | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + parental-source ( | * ) [ port ( | + * ) ] [ dscp ]; + parental-source-v6 ( | * ) [ port ( + | * ) ] [ dscp ]; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ @@ -928,6 +940,10 @@ zone [ ] { | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + parental-source ( | * ) [ port ( | * ) ] [ + dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) + ] [ dscp ]; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls diff --git a/doc/misc/options.grammar.rst b/doc/misc/options.grammar.rst index fa2bac7160..392e9c1a42 100644 --- a/doc/misc/options.grammar.rst +++ b/doc/misc/options.grammar.rst @@ -188,6 +188,10 @@ nta-lifetime ; nta-recheck ; nxdomain-redirect ; + parental-source ( | * ) [ port ( | * ) ] [ + dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) + ] [ dscp ]; pid-file ( | none ); port ; preferred-glue ; diff --git a/doc/misc/slave.zoneopt b/doc/misc/slave.zoneopt index a7e7c713e3..977e2618dd 100644 --- a/doc/misc/slave.zoneopt +++ b/doc/misc/slave.zoneopt @@ -46,6 +46,8 @@ zone [ ] { notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; parental-agents [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + parental-source ( | * ) [ port ( | * ) ] [ dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; request-ixfr ; diff --git a/doc/misc/slave.zoneopt.rst b/doc/misc/slave.zoneopt.rst index 48f9454c62..77ad700f53 100644 --- a/doc/misc/slave.zoneopt.rst +++ b/doc/misc/slave.zoneopt.rst @@ -48,6 +48,8 @@ notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; parental-agents [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + parental-source ( | * ) [ port ( | * ) ] [ dscp ]; + parental-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; request-ixfr ; diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 8d9807c190..50ed64b48e 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -3637,6 +3637,7 @@ static struct { const char *v6; } sources[] = { { "transfer-source", "transfer-source-v6" }, { "notify-source", "notify-source-v6" }, + { "parental-source", "parental-source-v6" }, { "query-source", "query-source-v6" }, { NULL, NULL } }; diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index dc8350c6e3..2002ea2e77 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -924,6 +924,94 @@ dns_zone_setaltxfrsource6dscp(dns_zone_t *zone, isc_dscp_t dscp); *\li #ISC_R_SUCCESS */ +isc_result_t +dns_zone_setparentalsrc4(dns_zone_t *zone, const isc_sockaddr_t *parentalsrc); +/*%< + * Set the source address to be used with IPv4 parental DS queries. + * + * Require: + *\li 'zone' to be a valid zone. + *\li 'parentalsrc' to contain the address. + * + * Returns: + *\li #ISC_R_SUCCESS + */ + +isc_sockaddr_t * +dns_zone_getparentalsrc4(dns_zone_t *zone); +/*%< + * Returns the source address set by a previous dns_zone_setparentalsrc4 + * call, or the default of inaddr_any, port 0. + * + * Require: + *\li 'zone' to be a valid zone. + */ + +isc_dscp_t +dns_zone_getparentalsrc4dscp(dns_zone_t *zone); +/*%/ + * Get the DSCP value associated with the IPv4 parental source. + * + * Require: + *\li 'zone' to be a valid zone. + */ + +isc_result_t +dns_zone_setparentalsrc4dscp(dns_zone_t *zone, isc_dscp_t dscp); +/*%< + * Set the DSCP value associated with the IPv4 parental source. + * + * Require: + *\li 'zone' to be a valid zone. + * + * Returns: + *\li #ISC_R_SUCCESS + */ + +isc_result_t +dns_zone_setparentalsrc6(dns_zone_t *zone, const isc_sockaddr_t *parentalsrc); +/*%< + * Set the source address to be used with IPv6 parental DS queries. + * + * Require: + *\li 'zone' to be a valid zone. + *\li 'parentalsrc' to contain the address. + * + * Returns: + *\li #ISC_R_SUCCESS + */ + +isc_sockaddr_t * +dns_zone_getparentalsrc6(dns_zone_t *zone); +/*%< + * Returns the source address set by a previous dns_zone_setparentalsrc6 + * call, or the default of in6addr_any, port 0. + * + * Require: + *\li 'zone' to be a valid zone. + */ + +isc_dscp_t +dns_zone_getparentalsrc6dscp(dns_zone_t *zone); +/*%/ + * Get the DSCP value associated with the IPv6 parental source. + * + * Require: + *\li 'zone' to be a valid zone. + */ + +isc_result_t +dns_zone_setparentalsrc6dscp(dns_zone_t *zone, isc_dscp_t dscp); +/*%< + * Set the DSCP value associated with the IPv6 parental source. + * + * Require: + *\li 'zone' to be a valid zone. + * + * Returns: + *\li #ISC_R_SUCCESS + */ + isc_result_t dns_zone_setnotifysrc4(dns_zone_t *zone, const isc_sockaddr_t *notifysrc); /*%< @@ -1846,6 +1934,15 @@ dns_zonemgr_getiolimit(dns_zonemgr_t *zmgr); *\li 'zmgr' to be a valid zone manager. */ +void +dns_zonemgr_setcheckdsrate(dns_zonemgr_t *zmgr, unsigned int value); +/*%< + * Set the number of parental DS queries sent per second. + * + * Requires: + *\li 'zmgr' to be a valid zone manager + */ + void dns_zonemgr_setnotifyrate(dns_zonemgr_t *zmgr, unsigned int value); /*%< diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 3211c54de8..f3acf7364c 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -301,6 +301,8 @@ struct dns_zone { isc_task_t *loadtask; isc_sockaddr_t notifysrc4; isc_sockaddr_t notifysrc6; + isc_sockaddr_t parentalsrc4; + isc_sockaddr_t parentalsrc6; isc_sockaddr_t xfrsource4; isc_sockaddr_t xfrsource6; isc_sockaddr_t altxfrsource4; @@ -308,6 +310,8 @@ struct dns_zone { isc_sockaddr_t sourceaddr; isc_dscp_t notifysrc4dscp; isc_dscp_t notifysrc6dscp; + isc_dscp_t parentalsrc4dscp; + isc_dscp_t parentalsrc6dscp; isc_dscp_t xfrsource4dscp; isc_dscp_t xfrsource6dscp; isc_dscp_t altxfrsource4dscp; @@ -586,6 +590,7 @@ struct dns_zonemgr { isc_taskpool_t *loadtasks; isc_task_t *task; isc_pool_t *mctxpool; + isc_ratelimiter_t *checkdsrl; isc_ratelimiter_t *notifyrl; isc_ratelimiter_t *refreshrl; isc_ratelimiter_t *startupnotifyrl; @@ -602,6 +607,7 @@ struct dns_zonemgr { /* Configuration data. */ uint32_t transfersin; uint32_t transfersperns; + unsigned int checkdsrate; unsigned int notifyrate; unsigned int startupnotifyrate; unsigned int serialqueryrate; @@ -1040,6 +1046,8 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { .idleout = DNS_DEFAULT_IDLEOUT, .notifysrc4dscp = -1, .notifysrc6dscp = -1, + .parentalsrc4dscp = -1, + .parentalsrc6dscp = -1, .xfrsource4dscp = -1, .xfrsource6dscp = -1, .altxfrsource4dscp = -1, @@ -1100,6 +1108,8 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { ISC_LIST_INIT(zone->notifies); isc_sockaddr_any(&zone->notifysrc4); isc_sockaddr_any6(&zone->notifysrc6); + isc_sockaddr_any(&zone->parentalsrc4); + isc_sockaddr_any6(&zone->parentalsrc6); isc_sockaddr_any(&zone->xfrsource4); isc_sockaddr_any6(&zone->xfrsource6); isc_sockaddr_any(&zone->altxfrsource4); @@ -5947,6 +5957,75 @@ dns_zone_getaltxfrsource6dscp(dns_zone_t *zone) { return (zone->altxfrsource6dscp); } + +isc_result_t +dns_zone_setparentalsrc4(dns_zone_t *zone, const isc_sockaddr_t *parentalsrc) { + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + zone->parentalsrc4 = *parentalsrc; + UNLOCK_ZONE(zone); + + return (ISC_R_SUCCESS); +} + +isc_sockaddr_t * +dns_zone_getparentalsrc4(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (&zone->parentalsrc4); +} + +isc_result_t +dns_zone_setparentalsrc4dscp(dns_zone_t *zone, isc_dscp_t dscp) { + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + zone->parentalsrc4dscp = dscp; + UNLOCK_ZONE(zone); + + return (ISC_R_SUCCESS); +} + +isc_dscp_t +dns_zone_getparentalsrc4dscp(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (zone->parentalsrc4dscp); +} + +isc_result_t +dns_zone_setparentalsrc6(dns_zone_t *zone, const isc_sockaddr_t *parentalsrc) { + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + zone->parentalsrc6 = *parentalsrc; + UNLOCK_ZONE(zone); + + return (ISC_R_SUCCESS); +} + +isc_sockaddr_t * +dns_zone_getparentalsrc6(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (&zone->parentalsrc6); +} + +isc_result_t +dns_zone_setparentalsrc6dscp(dns_zone_t *zone, isc_dscp_t dscp) { + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + zone->parentalsrc6dscp = dscp; + UNLOCK_ZONE(zone); + + return (ISC_R_SUCCESS); +} + +isc_dscp_t +dns_zone_getparentalsrc6dscp(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (zone->parentalsrc6dscp); +} + isc_result_t dns_zone_setnotifysrc4(dns_zone_t *zone, const isc_sockaddr_t *notifysrc) { REQUIRE(DNS_ZONE_VALID(zone)); @@ -5998,6 +6077,23 @@ dns_zone_getnotifysrc6(dns_zone_t *zone) { return (&zone->notifysrc6); } +isc_result_t +dns_zone_setnotifysrc6dscp(dns_zone_t *zone, isc_dscp_t dscp) { + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + zone->notifysrc6dscp = dscp; + UNLOCK_ZONE(zone); + + return (ISC_R_SUCCESS); +} + +isc_dscp_t +dns_zone_getnotifysrc6dscp(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (zone->notifysrc6dscp); +} + static bool same_addrs(isc_sockaddr_t const *oldlist, isc_sockaddr_t const *newlist, uint32_t count) { @@ -6162,23 +6258,6 @@ set_serverslist(unsigned int count, const isc_sockaddr_t *addrs, return (ISC_R_SUCCESS); } -isc_result_t -dns_zone_setnotifysrc6dscp(dns_zone_t *zone, isc_dscp_t dscp) { - REQUIRE(DNS_ZONE_VALID(zone)); - - LOCK_ZONE(zone); - zone->notifysrc6dscp = dscp; - UNLOCK_ZONE(zone); - - return (ISC_R_SUCCESS); -} - -isc_dscp_t -dns_zone_getnotifysrc6dscp(dns_zone_t *zone) { - REQUIRE(DNS_ZONE_VALID(zone)); - return (zone->notifysrc6dscp); -} - isc_result_t dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify, const isc_dscp_t *dscps, dns_name_t **keynames, @@ -18221,6 +18300,7 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, zmgr->loadtasks = NULL; zmgr->mctxpool = NULL; zmgr->task = NULL; + zmgr->checkdsrl = NULL; zmgr->notifyrl = NULL; zmgr->refreshrl = NULL; zmgr->startupnotifyrl = NULL; @@ -18248,11 +18328,17 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, isc_task_setname(zmgr->task, "zmgr", zmgr); result = isc_ratelimiter_create(mctx, timermgr, zmgr->task, - &zmgr->notifyrl); + &zmgr->checkdsrl); if (result != ISC_R_SUCCESS) { goto free_task; } + result = isc_ratelimiter_create(mctx, timermgr, zmgr->task, + &zmgr->notifyrl); + if (result != ISC_R_SUCCESS) { + goto free_checkdsrl; + } + result = isc_ratelimiter_create(mctx, timermgr, zmgr->task, &zmgr->refreshrl); if (result != ISC_R_SUCCESS) { @@ -18271,7 +18357,8 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, goto free_startupnotifyrl; } - /* default to 20 refresh queries / notifies per second. */ + /* default to 20 refresh queries / notifies / checkds per second. */ + setrl(zmgr->checkdsrl, &zmgr->checkdsrate, 20); setrl(zmgr->notifyrl, &zmgr->notifyrate, 20); setrl(zmgr->startupnotifyrl, &zmgr->startupnotifyrate, 20); setrl(zmgr->refreshrl, &zmgr->serialqueryrate, 20); @@ -18301,6 +18388,8 @@ free_refreshrl: isc_ratelimiter_detach(&zmgr->refreshrl); free_notifyrl: isc_ratelimiter_detach(&zmgr->notifyrl); +free_checkdsrl: + isc_ratelimiter_detach(&zmgr->checkdsrl); free_task: isc_task_detach(&zmgr->task); free_urlock: @@ -18489,6 +18578,7 @@ dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + isc_ratelimiter_shutdown(zmgr->checkdsrl); isc_ratelimiter_shutdown(zmgr->notifyrl); isc_ratelimiter_shutdown(zmgr->refreshrl); isc_ratelimiter_shutdown(zmgr->startupnotifyrl); @@ -18623,6 +18713,7 @@ zonemgr_free(dns_zonemgr_t *zmgr) { isc_refcount_destroy(&zmgr->refs); isc_mutex_destroy(&zmgr->iolock); + isc_ratelimiter_detach(&zmgr->checkdsrl); isc_ratelimiter_detach(&zmgr->notifyrl); isc_ratelimiter_detach(&zmgr->refreshrl); isc_ratelimiter_detach(&zmgr->startupnotifyrl); @@ -19013,6 +19104,13 @@ setrl(isc_ratelimiter_t *rl, unsigned int *rate, unsigned int value) { *rate = value; } +void +dns_zonemgr_setcheckdsrate(dns_zonemgr_t *zmgr, unsigned int value) { + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + + setrl(zmgr->checkdsrl, &zmgr->checkdsrate, value); +} + void dns_zonemgr_setnotifyrate(dns_zonemgr_t *zmgr, unsigned int value) { REQUIRE(DNS_ZONEMGR_VALID(zmgr)); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index f45bfe0194..2a93bcc124 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2248,6 +2248,10 @@ static cfg_clausedef_t zone_clauses[] = { CFG_ZONE_MASTER | CFG_ZONE_SLAVE }, { "nsec3-test-zone", &cfg_type_boolean, CFG_CLAUSEFLAG_TESTONLY | CFG_ZONE_MASTER | CFG_ZONE_SLAVE }, + { "parental-source", &cfg_type_sockaddr4wild, + CFG_ZONE_MASTER | CFG_ZONE_SLAVE }, + { "parental-source-v6", &cfg_type_sockaddr6wild, + CFG_ZONE_MASTER | CFG_ZONE_SLAVE }, { "request-expire", &cfg_type_boolean, CFG_ZONE_SLAVE | CFG_ZONE_MIRROR }, { "request-ixfr", &cfg_type_boolean, CFG_ZONE_SLAVE | CFG_ZONE_MIRROR }, From 40331a20c4703dce5f5a260950d48e9b8ce98d30 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 19 May 2021 15:32:56 +0200 Subject: [PATCH 10/20] Add helpful function 'dns_zone_getdnsseckeys' This code gathers DNSSEC keys from key files and from the DNSKEY RRset. It is used for the 'rndc dnssec -status' command, but will also be needed for "checkds". Turn it into a function. --- bin/named/server.c | 62 ++++-------------------------- lib/dns/include/dns/zone.h | 16 ++++++++ lib/dns/zone.c | 79 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 54 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index 42f82ba3aa..10925c3c1c 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -15043,8 +15043,8 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, isc_result_t result = ISC_R_SUCCESS; dns_zone_t *zone = NULL; dns_kasp_t *kasp = NULL; - dns_dnsseckeylist_t keys, dnskeys; - dns_dnsseckey_t *key, *key_next = NULL; + dns_dnsseckeylist_t keys; + dns_dnsseckey_t *key; char *ptr, *zonetext = NULL; const char *msg = NULL; /* variables for -checkds */ @@ -15061,11 +15061,8 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, isc_stdtime_t now, when; isc_time_t timenow, timewhen; const char *dir; - dns_name_t *origin; dns_db_t *db = NULL; - dns_dbnode_t *node = NULL; dns_dbversion_t *version = NULL; - dns_rdataset_t keyset; /* Skip the command name. */ ptr = next_token(lex, text); @@ -15084,9 +15081,7 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, now = isc_time_seconds(&timenow); when = now; - ISC_LIST_INIT(dnskeys); ISC_LIST_INIT(keys); - dns_rdataset_init(&keyset); if (strcasecmp(ptr, "-status") == 0) { status = true; @@ -15199,44 +15194,14 @@ named_server_dnssec(named_server_t *server, isc_lex_t *lex, /* Get DNSSEC keys. */ dir = dns_zone_getkeydirectory(zone); - origin = dns_zone_getorigin(zone); CHECK(dns_zone_getdb(zone, &db)); - CHECK(dns_db_findnode(db, origin, false, &node)); dns_db_currentversion(db, &version); - /* Get keys from private key files. */ - dns_zone_lock_keyfiles(zone); - result = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone), dir, now, - dns_zone_getmctx(zone), &keys); - dns_zone_unlock_keyfiles(zone); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - goto cleanup; - } - /* Get public keys (dnskeys). */ - result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey, - dns_rdatatype_none, 0, &keyset, NULL); - if (result == ISC_R_SUCCESS) { - CHECK(dns_dnssec_keylistfromrdataset( - origin, dir, dns_zone_getmctx(zone), &keyset, NULL, - NULL, false, false, &dnskeys)); - } else if (result != ISC_R_NOTFOUND) { - CHECK(result); - } - /* Add new 'dnskeys' to 'keys'. */ - for (dns_dnsseckey_t *k1 = ISC_LIST_HEAD(dnskeys); k1 != NULL; - k1 = key_next) { - dns_dnsseckey_t *k2 = NULL; - key_next = ISC_LIST_NEXT(k1, link); - - for (k2 = ISC_LIST_HEAD(keys); k2 != NULL; - k2 = ISC_LIST_NEXT(k2, link)) { - if (dst_key_compare(k1->key, k2->key)) { - break; - } - } - /* No match found, add the new key. */ - if (k2 == NULL) { - ISC_LIST_UNLINK(dnskeys, k1, link); - ISC_LIST_APPEND(keys, k1, link); + LOCK(&kasp->lock); + result = dns_zone_getdnsseckeys(zone, db, version, now, &keys); + UNLOCK(&kasp->lock); + if (result != ISC_R_SUCCESS) { + if (result != ISC_R_NOTFOUND) { + goto cleanup; } } @@ -15358,12 +15323,6 @@ cleanup: (void)putnull(text); } - if (dns_rdataset_isassociated(&keyset)) { - dns_rdataset_disassociate(&keyset); - } - if (node != NULL) { - dns_db_detachnode(db, &node); - } if (version != NULL) { dns_db_closeversion(db, &version, false); } @@ -15371,11 +15330,6 @@ cleanup: dns_db_detach(&db); } - while (!ISC_LIST_EMPTY(dnskeys)) { - key = ISC_LIST_HEAD(dnskeys); - ISC_LIST_UNLINK(dnskeys, key, link); - dns_dnsseckey_destroy(dns_zone_getmctx(zone), &key); - } while (!ISC_LIST_EMPTY(keys)) { key = ISC_LIST_HEAD(keys); ISC_LIST_UNLINK(keys, key, link); diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 2002ea2e77..82d9e87150 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -1754,6 +1754,22 @@ dns_zone_getkeydirectory(dns_zone_t *zone); * Pointer to null-terminated file name, or NULL. */ +isc_result_t +dns_zone_getdnsseckeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, + isc_stdtime_t now, dns_dnsseckeylist_t *keys); +/*% + * Find DNSSEC keys used for signing with dnssec-policy. Load these keys + * into 'keys'. + * + * Requires: + *\li 'zone' to be valid initialised zone. + *\li 'keys' to be an initialised DNSSEC keylist. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li Error + */ + isc_result_t dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, diff --git a/lib/dns/zone.c b/lib/dns/zone.c index f3acf7364c..1619b60d55 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -6624,6 +6624,85 @@ failure: return (result); } +/*% + * Find DNSSEC keys used for signing zone with dnssec-policy. Load these keys + * into 'keys'. Requires KASP to be locked. + */ +isc_result_t +dns_zone_getdnsseckeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, + isc_stdtime_t now, dns_dnsseckeylist_t *keys) { + isc_result_t result; + const char *dir = dns_zone_getkeydirectory(zone); + dns_dbnode_t *node = NULL; + 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_rdataset_t keyset; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(kasp != NULL); + + ISC_LIST_INIT(dnskeys); + + CHECK(dns_db_findnode(db, origin, false, &node)); + + /* Get keys from private key files. */ + dns_zone_lock_keyfiles(zone); + result = dns_dnssec_findmatchingkeys(origin, dir, now, + dns_zone_getmctx(zone), keys); + dns_zone_unlock_keyfiles(zone); + + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { + goto failure; + } + + /* Get public keys (dnskeys). */ + dns_rdataset_init(&keyset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, + dns_rdatatype_none, 0, &keyset, NULL); + if (result == ISC_R_SUCCESS) { + CHECK(dns_dnssec_keylistfromrdataset( + origin, dir, dns_zone_getmctx(zone), &keyset, NULL, + NULL, false, false, &dnskeys)); + } else if (result != ISC_R_NOTFOUND) { + CHECK(result); + } + + /* Add new 'dnskeys' to 'keys'. */ + for (dns_dnsseckey_t *k1 = ISC_LIST_HEAD(dnskeys); k1 != NULL; + k1 = key_next) { + dns_dnsseckey_t *k2 = NULL; + key_next = ISC_LIST_NEXT(k1, link); + + for (k2 = ISC_LIST_HEAD(*keys); k2 != NULL; + k2 = ISC_LIST_NEXT(k2, link)) { + if (dst_key_compare(k1->key, k2->key)) { + break; + } + } + /* No match found, add the new key. */ + if (k2 == NULL) { + ISC_LIST_UNLINK(dnskeys, k1, link); + ISC_LIST_APPEND(*keys, k1, link); + } + } + +failure: + if (dns_rdataset_isassociated(&keyset)) { + dns_rdataset_disassociate(&keyset); + } + if (node != NULL) { + dns_db_detachnode(db, &node); + } + while (!ISC_LIST_EMPTY(dnskeys)) { + key = ISC_LIST_HEAD(dnskeys); + ISC_LIST_UNLINK(dnskeys, key, link); + dns_dnsseckey_destroy(dns_zone_getmctx(zone), &key); + } + return (result); +} + static isc_result_t offline(dns_db_t *db, dns_dbversion_t *ver, dns__zonediff_t *zonediff, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata) { From 71d5932a1439cebf4ef758e3e7ea10a1fd5ca1d3 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 24 Jun 2021 16:15:38 +0200 Subject: [PATCH 11/20] Slightly improved dnssec tools fatal message Return the offending key state identifier. --- bin/dnssec/dnssectool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/dnssec/dnssectool.c b/bin/dnssec/dnssectool.c index c01a640755..7239b5e4c7 100644 --- a/bin/dnssec/dnssectool.c +++ b/bin/dnssec/dnssectool.c @@ -272,7 +272,7 @@ strtokeystate(const char *str) { return ((dst_key_state_t)i); } } - fatal("unknown key state"); + fatal("unknown key state %s", str); } isc_stdtime_t From 4c337a8e72bf2d3675a7f42c5e1fd0e30dae48bb Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 24 Jun 2021 16:17:00 +0200 Subject: [PATCH 12/20] Add missing VERIFY export This makes the 'dnssec-verify' tool visible to the test environment. --- bin/tests/system/conf.sh.common | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/tests/system/conf.sh.common b/bin/tests/system/conf.sh.common index cd0838a893..b68c93dd93 100644 --- a/bin/tests/system/conf.sh.common +++ b/bin/tests/system/conf.sh.common @@ -727,4 +727,5 @@ export SIGNER export SUBDIRS export TMPDIR export TSIGKEYGEN +export VERIFY export WIRETEST From 6e2c24be7cd476240879e22367f08fc25b7d882d Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 24 Jun 2021 16:22:22 +0200 Subject: [PATCH 13/20] Add key metadata for DS published/withdrawn In order to keep track of how many parents have the DS for a given key published or withdrawn, keep a counter. --- lib/dns/dst_api.c | 6 +++++- lib/dns/dst_parse.c | 2 +- lib/dns/include/dst/dst.h | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index 884537c2e4..f0af50f6d2 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -97,7 +97,8 @@ #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1) static const char *numerictags[NUMERIC_NTAGS] = { - "Predecessor:", "Successor:", "MaxTTL:", "RollPeriod:", "Lifetime:" + "Predecessor:", "Successor:", "MaxTTL:", "RollPeriod:", + "Lifetime:", "DSPubCount:", "DSRemCount:" }; #define BOOLEAN_NTAGS (DST_MAX_BOOLEAN + 1) @@ -2014,6 +2015,9 @@ write_key_state(const dst_key_t *key, int type, const char *directory) { printtime(key, DST_TIME_SYNCPUBLISH, "PublishCDS", fp); printtime(key, DST_TIME_SYNCDELETE, "DeleteCDS", fp); + printnum(key, DST_NUM_DSPUBCOUNT, "DSPubCount", fp); + printnum(key, DST_NUM_DSDELCOUNT, "DSDelCount", fp); + printtime(key, DST_TIME_DNSKEY, "DNSKEYChange", fp); printtime(key, DST_TIME_ZRRSIG, "ZRRSIGChange", fp); printtime(key, DST_TIME_KRRSIG, "KRRSIGChange", fp); diff --git a/lib/dns/dst_parse.c b/lib/dns/dst_parse.c index 751180f462..74dbd85303 100644 --- a/lib/dns/dst_parse.c +++ b/lib/dns/dst_parse.c @@ -61,7 +61,7 @@ static const char *timetags[TIMING_NTAGS] = { #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1) static const char *numerictags[NUMERIC_NTAGS] = { - "Predecessor:", "Successor:", "MaxTTL:", "RollPeriod:", NULL + "Predecessor:", "Successor:", "MaxTTL:", "RollPeriod:", NULL, NULL, NULL }; struct parse_map { diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h index 2f9877be43..f454ebbf78 100644 --- a/lib/dns/include/dst/dst.h +++ b/lib/dns/include/dst/dst.h @@ -142,7 +142,9 @@ typedef enum dst_key_state { #define DST_NUM_MAXTTL 2 #define DST_NUM_ROLLPERIOD 3 #define DST_NUM_LIFETIME 4 -#define DST_MAX_NUMERIC 4 +#define DST_NUM_DSPUBCOUNT 5 +#define DST_NUM_DSDELCOUNT 6 +#define DST_MAX_NUMERIC 6 /* Boolean metadata definitions */ #define DST_BOOL_KSK 0 From 1a505549635444dfc3afed636803c7d7af05773e Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 24 Jun 2021 16:26:06 +0200 Subject: [PATCH 14/20] Add checkds log notice When the checkds published/withdrawn is activated, log a notice. Can be used for testing, but also operationally useful. --- lib/dns/keymgr.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c index 1d47da8c33..b0a6f96c8f 100644 --- a/lib/dns/keymgr.c +++ b/lib/dns/keymgr.c @@ -2245,6 +2245,19 @@ keymgr_checkds(dns_kasp_t *kasp, dns_dnsseckeylist_t *keyring, dst_key_settime(ksk_key->key, DST_TIME_DSDELETE, when); } + if (isc_log_wouldlog(dns_lctx, ISC_LOG_NOTICE)) { + char keystr[DST_KEY_FORMATSIZE]; + char timestr[26]; /* Minimal buf as per ctime_r() spec. */ + + dst_key_format(ksk_key->key, keystr, sizeof(keystr)); + isc_stdtime_tostring(when, timestr, sizeof(timestr)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, ISC_LOG_NOTICE, + "keymgr: checkds DS for key %s seen %s at %s", + keystr, dspublish ? "published" : "withdrawn", + timestr); + } + /* Store key state and update hints. */ isc_dir_init(&dir); if (directory == NULL) { From f7872dbd206c0727e284780022bd356ca4ccdf0d Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Thu, 24 Jun 2021 17:01:42 +0200 Subject: [PATCH 15/20] Add checkds code Similar to notify, add code to send and keep track of checkds requests. On every zone_rekey event, we will check the DS at parental agents (but we will only actually query parental agents if theree is a DS scheduled to be published/withdrawn). On a zone_rekey event, we will first clear the ongoing checkds requests. Reset the counter, to avoid continuing KSK rollover premature. This has the risk that if zone_rekey events happen too soon after each other, there are redundant DS queries to the parental agents. But if TTLs and the configured durations in the dnssec-policy are sane (as in not ridiculous short) the chance of this happening is low. --- lib/dns/include/dns/events.h | 1 + lib/dns/zone.c | 860 ++++++++++++++++++++++++++++++++++- 2 files changed, 857 insertions(+), 4 deletions(-) diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h index 894c4dd211..7d93637268 100644 --- a/lib/dns/include/dns/events.h +++ b/lib/dns/include/dns/events.h @@ -80,6 +80,7 @@ #define DNS_EVENT_STARTUPDATE (ISC_EVENTCLASS_DNS + 58) #define DNS_EVENT_TRYSTALE (ISC_EVENTCLASS_DNS + 59) #define DNS_EVENT_ZONEFLUSH (ISC_EVENTCLASS_DNS + 60) +#define DNS_EVENT_CHECKDSSENDTOADDR (ISC_EVENTCLASS_DNS + 61) #define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0) #define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535) diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 1619b60d55..e850e5de6e 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -93,6 +93,9 @@ #define NOTIFY_MAGIC ISC_MAGIC('N', 't', 'f', 'y') #define DNS_NOTIFY_VALID(notify) ISC_MAGIC_VALID(notify, NOTIFY_MAGIC) +#define CHECKDS_MAGIC ISC_MAGIC('C', 'h', 'D', 'S') +#define DNS_CHECKDS_VALID(checkds) ISC_MAGIC_VALID(checkds, CHECKDS_MAGIC) + #define STUB_MAGIC ISC_MAGIC('S', 't', 'u', 'b') #define DNS_STUB_VALID(stub) ISC_MAGIC_VALID(stub, STUB_MAGIC) @@ -153,6 +156,7 @@ #endif /* ifndef DNS_DUMP_DELAY */ typedef struct dns_notify dns_notify_t; +typedef struct dns_checkds dns_checkds_t; typedef struct dns_stub dns_stub_t; typedef struct dns_load dns_load_t; typedef struct dns_forward dns_forward_t; @@ -330,6 +334,7 @@ struct dns_zone { bool zero_no_soa_ttl; dns_severity_t check_names; ISC_LIST(dns_notify_t) notifies; + ISC_LIST(dns_checkds_t) checkds_requests; dns_request_t *request; dns_loadctx_t *lctx; dns_io_t *readio; @@ -646,6 +651,23 @@ struct dns_notify { #define DNS_NOTIFY_NOSOA 0x0001U #define DNS_NOTIFY_STARTUP 0x0002U +/*% + * Hold checkds state. + */ +struct dns_checkds { + unsigned int magic; + unsigned int flags; + isc_mem_t *mctx; + dns_zone_t *zone; + dns_request_t *request; + isc_sockaddr_t dst; + dns_tsigkey_t *key; + dns_transport_t *transport; + isc_dscp_t dscp; + ISC_LINK(dns_checkds_t) link; + isc_event_t *event; +}; + /*% * dns_stub holds state while performing a 'stub' transfer. * 'db' is the zone's 'db' or a new one if this is the initial @@ -867,6 +889,16 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub); static int message_count(dns_message_t *msg, dns_section_t section, dns_rdatatype_t type); static void +checkds_cancel(dns_zone_t *zone); +static void +checkds_send(dns_zone_t *zone); +static isc_result_t +checkds_createmessage(dns_zone_t *zone, dns_message_t **messagep); +static void +checkds_done(isc_task_t *task, isc_event_t *event); +static void +checkds_send_toaddr(isc_task_t *task, isc_event_t *event); +static void notify_cancel(dns_zone_t *zone); static void notify_find_address(dns_notify_t *notify); @@ -1106,6 +1138,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { isc_time_settoepoch(&zone->nsec3chaintime); isc_time_settoepoch(&zone->refreshkeytime); ISC_LIST_INIT(zone->notifies); + ISC_LIST_INIT(zone->checkds_requests); isc_sockaddr_any(&zone->notifysrc4); isc_sockaddr_any6(&zone->notifysrc6); isc_sockaddr_any(&zone->parentalsrc4); @@ -5957,7 +5990,6 @@ dns_zone_getaltxfrsource6dscp(dns_zone_t *zone) { return (zone->altxfrsource6dscp); } - isc_result_t dns_zone_setparentalsrc4(dns_zone_t *zone, const isc_sockaddr_t *parentalsrc) { REQUIRE(DNS_ZONE_VALID(zone)); @@ -12141,6 +12173,25 @@ notify_cancel(dns_zone_t *zone) { } } +static void +checkds_cancel(dns_zone_t *zone) { + dns_checkds_t *checkds; + + /* + * 'zone' locked by caller. + */ + + REQUIRE(LOCKED_ZONE(zone)); + + for (checkds = ISC_LIST_HEAD(zone->checkds_requests); checkds != NULL; + checkds = ISC_LIST_NEXT(checkds, link)) + { + if (checkds->request != NULL) { + dns_request_cancel(checkds->request); + } + } +} + static void forward_cancel(dns_zone_t *zone) { dns_forward_t *forward; @@ -14940,6 +14991,8 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) { } } + checkds_cancel(zone); + notify_cancel(zone); forward_cancel(zone); @@ -20284,6 +20337,781 @@ dnssec_report(const char *format, ...) { va_end(args); } +static void +checkds_destroy(dns_checkds_t *checkds, bool locked) { + isc_mem_t *mctx; + + REQUIRE(DNS_CHECKDS_VALID(checkds)); + + dns_zone_log(checkds->zone, ISC_LOG_DEBUG(3), + "checkds: destroy DS query"); + + if (checkds->zone != NULL) { + if (!locked) { + LOCK_ZONE(checkds->zone); + } + REQUIRE(LOCKED_ZONE(checkds->zone)); + if (ISC_LINK_LINKED(checkds, link)) { + ISC_LIST_UNLINK(checkds->zone->checkds_requests, + checkds, link); + } + if (!locked) { + UNLOCK_ZONE(checkds->zone); + } + if (locked) { + zone_idetach(&checkds->zone); + } else { + dns_zone_idetach(&checkds->zone); + } + } + if (checkds->request != NULL) { + dns_request_destroy(&checkds->request); + } + if (checkds->key != NULL) { + dns_tsigkey_detach(&checkds->key); + } + if (checkds->transport != NULL) { + dns_transport_detach(&checkds->transport); + } + mctx = checkds->mctx; + isc_mem_put(checkds->mctx, checkds, sizeof(*checkds)); + isc_mem_detach(&mctx); +} + +static isc_result_t +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; + + isc_buffer_init(&b, buf, bufsize); + result = dst_key_todns(key, &b); + if (result != ISC_R_SUCCESS) { + return (result); + } + + dns_rdata_reset(target); + isc_buffer_usedregion(&b, &r); + dns_rdata_fromregion(target, dst_key_class(key), dns_rdatatype_dnskey, + &r); + return (ISC_R_SUCCESS); +} + +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); + const char *dir = dns_zone_getkeydirectory(zone); + isc_result_t result; + uint32_t count = 0; + + if (dspublish) { + (void)dst_key_getnum(key, DST_NUM_DSPUBCOUNT, &count); + count += 1; + dst_key_setnum(key, DST_NUM_DSPUBCOUNT, count); + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: %u DS published " + "for key %u", + count, dst_key_id(key)); + + if (count != zone->parentalscnt) { + return false; + } + } else { + (void)dst_key_getnum(key, DST_NUM_DSDELCOUNT, &count); + count += 1; + dst_key_setnum(key, DST_NUM_DSDELCOUNT, count); + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: %u DS withdrawn " + "for key %u", + count, dst_key_id(key)); + + if (count != zone->parentalscnt) { + return false; + } + } + + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: checkds %s for key " + "%u", + dspublish ? "published" : "withdrawn", dst_key_id(key)); + + dns_zone_lock_keyfiles(zone); + result = dns_keymgr_checkds_id(kasp, &zone->checkds_ok, dir, now, now, + dspublish, dst_key_id(key), + dst_key_alg(key)); + dns_zone_unlock_keyfiles(zone); + + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_WARNING, + "checkds: checkds for key %u failed: %s", + dst_key_id(key), isc_result_totext(result)); + return false; + } + + return true; +} + +static isc_result_t +validate_ds(dns_zone_t *zone, dns_message_t *message) { + UNUSED(zone); + UNUSED(message); + + /* Get closest trust anchor */ + + /* Check that trust anchor is (grand)parent of zone. */ + + /* Find the DNSKEY signing the message. */ + + /* Check that DNSKEY is in chain of trust. */ + + /* Validate DS RRset. */ + + return (ISC_R_SUCCESS); +} + +static void +checkds_done(isc_task_t *task, isc_event_t *event) { + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + char rcode[128]; + dns_checkds_t *checkds; + dns_zone_t *zone; + dns_db_t *db = NULL; + dns_dbversion_t *version = NULL; + dns_dnsseckey_t *key; + dns_dnsseckeylist_t keys; + dns_kasp_t *kasp = NULL; + dns_message_t *message = NULL; + dns_rdataset_t *ds_rrset = NULL; + dns_requestevent_t *revent = (dns_requestevent_t *)event; + isc_buffer_t buf; + isc_result_t result; + isc_stdtime_t now; + isc_time_t timenow; + bool rekey = false; + bool empty = false; + + UNUSED(task); + + checkds = event->ev_arg; + REQUIRE(DNS_CHECKDS_VALID(checkds)); + + zone = checkds->zone; + INSIST(task == zone->task); + + ISC_LIST_INIT(keys); + + kasp = zone->kasp; + INSIST(kasp != NULL); + + isc_buffer_init(&buf, rcode, sizeof(rcode)); + isc_sockaddr_format(&checkds->dst, addrbuf, sizeof(addrbuf)); + + dns_zone_log(zone, ISC_LOG_DEBUG(1), "checkds: DS query to %s: done", + addrbuf); + + dns_message_create(zone->mctx, DNS_MESSAGE_INTENTPARSE, &message); + INSIST(message != NULL); + + CHECK(revent->result); + CHECK(dns_request_getresponse(revent->request, message, + DNS_MESSAGEPARSE_PRESERVEORDER)); + CHECK(dns_rcode_totext(message->rcode, &buf)); + + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: DS response from %s: %.*s", addrbuf, + (int)buf.used, rcode); + + /* Validate response. */ + CHECK(validate_ds(zone, message)); + + if (message->rcode != dns_rcode_noerror) { + dns_zone_log(zone, ISC_LOG_NOTICE, + "checkds: bad DS response from %s: %.*s", addrbuf, + (int)buf.used, rcode); + goto failure; + } + + /* Lookup DS RRset. */ + result = dns_message_firstname(message, DNS_SECTION_ANSWER); + while (result == ISC_R_SUCCESS) { + dns_name_t *name = NULL; + dns_rdataset_t *rdataset; + + dns_message_currentname(message, DNS_SECTION_ANSWER, &name); + if (dns_name_compare(&zone->origin, name) != 0) { + goto next; + } + + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (rdataset->type != dns_rdatatype_ds) { + goto next; + } + + ds_rrset = rdataset; + break; + } + + if (ds_rrset != NULL) { + break; + } + + next: + result = dns_message_nextname(message, DNS_SECTION_ANSWER); + } + + if (ds_rrset == NULL) { + empty = true; + dns_zone_log(zone, ISC_LOG_NOTICE, + "checkds: empty DS response from %s", addrbuf); + } + + TIME_NOW(&timenow); + now = isc_time_seconds(&timenow); + + CHECK(dns_zone_getdb(zone, &db)); + dns_db_currentversion(db, &version); + + KASP_LOCK(kasp); + LOCK_ZONE(zone); + for (key = ISC_LIST_HEAD(zone->checkds_ok); key != NULL; + key = ISC_LIST_NEXT(key, link)) + { + bool alldone = false, found = false; + bool checkdspub = false, checkdsdel = false, ksk = false; + dst_key_state_t ds_state = DST_KEY_STATE_NA; + isc_stdtime_t published = 0, withdrawn = 0; + isc_result_t ret = ISC_R_SUCCESS; + + /* Is this key have the KSK role? */ + (void)dst_key_role(key->key, &ksk, NULL); + if (!ksk) { + continue; + } + + /* Do we need to check the DS RRset for this key? */ + (void)dst_key_getstate(key->key, DST_KEY_DS, &ds_state); + (void)dst_key_gettime(key->key, DST_TIME_DSPUBLISH, &published); + (void)dst_key_gettime(key->key, DST_TIME_DSDELETE, &withdrawn); + + if (ds_state == DST_KEY_STATE_RUMOURED && published == 0) { + checkdspub = true; + } else if (ds_state == DST_KEY_STATE_UNRETENTIVE && + withdrawn == 0) { + checkdsdel = true; + } + if (!checkdspub && !checkdsdel) { + continue; + } + + if (empty) { + goto dswithdrawn; + } + + /* Find the appropriate DS record. */ + ret = dns_rdataset_first(ds_rrset); + while (ret == ISC_R_SUCCESS) { + dns_rdata_ds_t ds; + dns_rdata_t dnskey = DNS_RDATA_INIT; + dns_rdata_t dsrdata = DNS_RDATA_INIT; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_result_t r; + unsigned char dsbuf[DNS_DS_BUFFERSIZE]; + unsigned char keybuf[DST_KEY_MAXSIZE]; + + dns_rdataset_current(ds_rrset, &rdata); + r = dns_rdata_tostruct(&rdata, &ds, NULL); + if (r != ISC_R_SUCCESS) { + goto nextds; + } + /* Check key tag and algorithm. */ + if (dst_key_id(key->key) != ds.key_tag) { + goto nextds; + } + if (dst_key_alg(key->key) != ds.algorithm) { + goto nextds; + } + /* Derive DS from DNSKEY, see if the rdata is equal. */ + make_dnskey(key->key, keybuf, sizeof(keybuf), &dnskey); + r = dns_ds_buildrdata(&zone->origin, &dnskey, + ds.digest_type, dsbuf, &dsrdata); + if (r != ISC_R_SUCCESS) { + goto nextds; + } + if (dns_rdata_compare(&rdata, &dsrdata) == 0) { + found = true; + if (checkdspub) { + /* DS Published. */ + alldone = do_checkds(zone, key->key, + now, true); + if (alldone) { + rekey = true; + } + } + } + + nextds: + ret = dns_rdataset_next(ds_rrset); + } + + dswithdrawn: + /* DS withdrawn. */ + if (checkdsdel && !found) { + alldone = do_checkds(zone, key->key, now, false); + if (alldone) { + rekey = true; + } + } + } + UNLOCK_ZONE(zone); + KASP_UNLOCK(kasp); + + /* Rekey after checkds. */ + if (rekey) { + dns_zone_rekey(zone, false); + } + +failure: + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: DS request failed: %s", + isc_result_totext(result)); + } + + if (version != NULL) { + dns_db_closeversion(db, &version, false); + } + if (db != NULL) { + dns_db_detach(&db); + } + + while (!ISC_LIST_EMPTY(keys)) { + key = ISC_LIST_HEAD(keys); + ISC_LIST_UNLINK(keys, key, link); + dns_dnsseckey_destroy(dns_zone_getmctx(zone), &key); + } + + isc_event_free(&event); + checkds_destroy(checkds, false); + dns_message_detach(&message); +} + +static bool +checkds_isqueued(dns_zone_t *zone, isc_sockaddr_t *addr, dns_tsigkey_t *key, + dns_transport_t *transport) { + dns_checkds_t *checkds; + + for (checkds = ISC_LIST_HEAD(zone->checkds_requests); checkds != NULL; + checkds = ISC_LIST_NEXT(checkds, link)) + { + if (checkds->request != NULL) { + continue; + } + if (addr != NULL && isc_sockaddr_equal(addr, &checkds->dst) && + checkds->key == key && checkds->transport == transport) + { + return (true); + } + } + return (false); +} + +static isc_result_t +checkds_create(isc_mem_t *mctx, unsigned int flags, dns_checkds_t **checkdsp) { + dns_checkds_t *checkds; + + REQUIRE(checkdsp != NULL && *checkdsp == NULL); + + checkds = isc_mem_get(mctx, sizeof(*checkds)); + *checkds = (dns_checkds_t){ + .flags = flags, + }; + + isc_mem_attach(mctx, &checkds->mctx); + isc_sockaddr_any(&checkds->dst); + ISC_LINK_INIT(checkds, link); + checkds->magic = CHECKDS_MAGIC; + *checkdsp = checkds; + return (ISC_R_SUCCESS); +} + +static isc_result_t +checkds_createmessage(dns_zone_t *zone, dns_message_t **messagep) { + dns_message_t *message = NULL; + + dns_name_t *tempname = NULL; + dns_rdataset_t *temprdataset = NULL; + + isc_result_t result; + + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(messagep != NULL && *messagep == NULL); + + dns_message_create(zone->mctx, DNS_MESSAGE_INTENTRENDER, &message); + + message->opcode = dns_opcode_query; + message->rdclass = zone->rdclass; + + result = dns_message_gettempname(message, &tempname); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = dns_message_gettemprdataset(message, &temprdataset); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + /* + * Make question. + */ + dns_name_init(tempname, NULL); + dns_name_clone(&zone->origin, tempname); + dns_rdataset_makequestion(temprdataset, zone->rdclass, + dns_rdatatype_ds); + ISC_LIST_APPEND(tempname->list, temprdataset, link); + dns_message_addname(message, tempname, DNS_SECTION_QUESTION); + tempname = NULL; + temprdataset = NULL; + + *messagep = message; + return (ISC_R_SUCCESS); + +cleanup: + if (tempname != NULL) { + dns_message_puttempname(message, &tempname); + } + if (temprdataset != NULL) { + dns_message_puttemprdataset(message, &temprdataset); + } + dns_message_detach(&message); + return (result); +} + +static void +checkds_send_toaddr(isc_task_t *task, isc_event_t *event) { + dns_checkds_t *checkds; + isc_result_t result; + dns_message_t *message = NULL; + isc_netaddr_t dstip; + dns_tsigkey_t *key = NULL; + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t src; + unsigned int options, timeout; + bool have_checkdssource = false; + bool have_checkdsdscp = false; + isc_dscp_t dscp = -1; + + checkds = event->ev_arg; + REQUIRE(DNS_CHECKDS_VALID(checkds)); + + UNUSED(task); + + LOCK_ZONE(checkds->zone); + + checkds->event = NULL; + + if (DNS_ZONE_FLAG(checkds->zone, DNS_ZONEFLG_LOADED) == 0) { + result = ISC_R_CANCELED; + goto cleanup; + } + + if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0 || + DNS_ZONE_FLAG(checkds->zone, DNS_ZONEFLG_EXITING) || + checkds->zone->view->requestmgr == NULL || + checkds->zone->db == NULL) + { + result = ISC_R_CANCELED; + goto cleanup; + } + + /* + * The raw IPv4 address should also exist. Don't send to the + * mapped form. + */ + if (isc_sockaddr_pf(&checkds->dst) == PF_INET6 && + IN6_IS_ADDR_V4MAPPED(&checkds->dst.type.sin6.sin6_addr)) + { + isc_sockaddr_format(&checkds->dst, addrbuf, sizeof(addrbuf)); + dns_zone_log(checkds->zone, ISC_LOG_DEBUG(3), + "checkds: ignoring IPv6 mapped IPV4 address: %s", + addrbuf); + result = ISC_R_CANCELED; + goto cleanup; + } + + result = checkds_createmessage(checkds->zone, &message); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + isc_sockaddr_format(&checkds->dst, addrbuf, sizeof(addrbuf)); + if (checkds->key != NULL) { + /* Transfer ownership of key */ + key = checkds->key; + checkds->key = NULL; + } else { + isc_netaddr_fromsockaddr(&dstip, &checkds->dst); + result = dns_view_getpeertsig(checkds->zone->view, &dstip, + &key); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { + dns_zone_log(checkds->zone, ISC_LOG_ERROR, + "checkds: DS query to %s not sent. " + "Peer TSIG key lookup failure.", + addrbuf); + goto cleanup_message; + } + } + + if (key != NULL) { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(&key->name, namebuf, sizeof(namebuf)); + dns_zone_log(checkds->zone, ISC_LOG_DEBUG(3), + "checkds: sending DS query to %s : TSIG (%s)", + addrbuf, namebuf); + } else { + dns_zone_log(checkds->zone, ISC_LOG_DEBUG(3), + "checkds: sending DS query to %s", addrbuf); + } + options = 0; + if (checkds->zone->view->peers != NULL) { + dns_peer_t *peer = NULL; + bool usetcp = false; + result = dns_peerlist_peerbyaddr(checkds->zone->view->peers, + &dstip, &peer); + if (result == ISC_R_SUCCESS) { + result = dns_peer_getquerysource(peer, &src); + if (result == ISC_R_SUCCESS) { + have_checkdssource = true; + } + dns_peer_getquerydscp(peer, &dscp); + if (dscp != -1) { + have_checkdsdscp = true; + } + result = dns_peer_getforcetcp(peer, &usetcp); + if (result == ISC_R_SUCCESS && usetcp) { + options |= DNS_FETCHOPT_TCP; + } + } + } + switch (isc_sockaddr_pf(&checkds->dst)) { + case PF_INET: + if (!have_checkdssource) { + src = checkds->zone->parentalsrc4; + } + if (!have_checkdsdscp) { + dscp = checkds->zone->parentalsrc4dscp; + } + break; + case PF_INET6: + if (!have_checkdssource) { + src = checkds->zone->parentalsrc6; + } + if (!have_checkdsdscp) { + dscp = checkds->zone->parentalsrc6dscp; + } + break; + default: + result = ISC_R_NOTIMPLEMENTED; + goto cleanup_key; + } + + dns_zone_log(checkds->zone, ISC_LOG_DEBUG(3), + "checkds: create request for DS query to %s", addrbuf); + + timeout = 15; + options |= DNS_REQUESTOPT_TCP; + result = dns_request_createvia( + checkds->zone->view->requestmgr, message, &src, &checkds->dst, + dscp, options, key, timeout * 3, timeout, 0, + checkds->zone->task, checkds_done, checkds, &checkds->request); + if (result != ISC_R_SUCCESS) { + dns_zone_log( + checkds->zone, ISC_LOG_DEBUG(3), + "checkds: dns_request_createvia() to %s failed: %s", + addrbuf, dns_result_totext(result)); + goto cleanup; + } + +cleanup_key: + if (key != NULL) { + dns_tsigkey_detach(&key); + } +cleanup_message: + dns_message_detach(&message); +cleanup: + UNLOCK_ZONE(checkds->zone); + isc_event_free(&event); + if (result != ISC_R_SUCCESS) { + checkds_destroy(checkds, false); + } +} + +static isc_result_t +checkds_send_queue(dns_checkds_t *checkds) { + isc_event_t *e; + isc_result_t result; + + INSIST(checkds->event == NULL); + e = isc_event_allocate(checkds->mctx, NULL, DNS_EVENT_CHECKDSSENDTOADDR, + checkds_send_toaddr, checkds, + sizeof(isc_event_t)); + e->ev_arg = checkds; + e->ev_sender = NULL; + result = isc_ratelimiter_enqueue(checkds->zone->zmgr->checkdsrl, + checkds->zone->task, &e); + if (result != ISC_R_SUCCESS) { + isc_event_free(&e); + checkds->event = NULL; + } + return (result); +} + +static void +checkds_send(dns_zone_t *zone) { + dns_view_t *view = dns_zone_getview(zone); + isc_result_t result; + unsigned int flags = 0; + + /* + * Zone lock held by caller. + */ + REQUIRE(LOCKED_ZONE(zone)); + + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: start sending DS queries to %u parentals", + zone->parentalscnt); + + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) { + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: abort, named exiting"); + return; + } + + for (unsigned int i = 0; i < zone->parentalscnt; i++) { + dns_tsigkey_t *key = NULL; + dns_transport_t *transport = NULL; + isc_sockaddr_t dst; + dns_checkds_t *checkds = NULL; + + if ((zone->parentalkeynames != NULL) && + (zone->parentalkeynames[i] != NULL)) { + dns_name_t *keyname = zone->parentalkeynames[i]; + (void)dns_view_gettsig(view, keyname, &key); + } + + if ((zone->parentaltlsnames != NULL) && + (zone->parentaltlsnames[i] != NULL)) { + dns_name_t *tlsname = zone->parentaltlsnames[i]; + (void)dns_view_gettransport(view, DNS_TRANSPORT_TLS, + tlsname, &transport); + dns_zone_logc( + zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_ERROR, + "got TLS configuration for zone transfer"); + } + + dst = zone->parentals[i]; + + /* TODO: glue the transport to the checkds request */ + + if (checkds_isqueued(zone, &dst, key, transport)) { + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: DS query to parent " + "%d is queued", + i); + if (key != NULL) { + dns_tsigkey_detach(&key); + } + if (transport != NULL) { + dns_transport_detach(&transport); + } + continue; + } + + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: create DS query for " + "parent %d", + i); + + result = checkds_create(zone->mctx, flags, &checkds); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: create DS query for " + "parent %d failed", + i); + continue; + } + zone_iattach(zone, &checkds->zone); + checkds->dst = dst; + + INSIST(checkds->key == NULL); + if (key != NULL) { + checkds->key = key; + key = NULL; + } + + INSIST(checkds->transport == NULL); + if (transport != NULL) { + checkds->transport = transport; + transport = NULL; + } + + ISC_LIST_APPEND(zone->checkds_requests, checkds, link); + result = checkds_send_queue(checkds); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "checkds: send DS query to " + "parent %d failed", + i); + checkds_destroy(checkds, true); + } + } +} + +static void +zone_checkds(dns_zone_t *zone) { + bool cdscheck = false; + + for (dns_dnsseckey_t *key = ISC_LIST_HEAD(zone->checkds_ok); + key != NULL; key = ISC_LIST_NEXT(key, link)) + { + dst_key_state_t ds_state = DST_KEY_STATE_NA; + bool ksk = false; + isc_stdtime_t published = 0, withdrawn = 0; + + /* Is this key have the KSK role? */ + (void)dst_key_role(key->key, &ksk, NULL); + if (!ksk) { + continue; + } + + /* Do we need to check the DS RRset? */ + (void)dst_key_getstate(key->key, DST_KEY_DS, &ds_state); + (void)dst_key_gettime(key->key, DST_TIME_DSPUBLISH, &published); + (void)dst_key_gettime(key->key, DST_TIME_DSDELETE, &withdrawn); + + if (ds_state == DST_KEY_STATE_RUMOURED && published == 0) { + dst_key_setnum(key->key, DST_NUM_DSPUBCOUNT, 0); + cdscheck = true; + } else if (ds_state == DST_KEY_STATE_UNRETENTIVE && + withdrawn == 0) { + dst_key_setnum(key->key, DST_NUM_DSDELCOUNT, 0); + cdscheck = true; + } + } + + if (cdscheck) { + /* Request the DS RRset. */ + LOCK_ZONE(zone); + checkds_send(zone); + UNLOCK_ZONE(zone); + } +} + static void zone_rekey(dns_zone_t *zone) { isc_result_t result; @@ -20383,10 +21211,12 @@ zone_rekey(dns_zone_t *zone) { fullsign = DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_FULLSIGN); KASP_LOCK(kasp); - dns_zone_lock_keyfiles(zone); + dns_zone_lock_keyfiles(zone); result = dns_dnssec_findmatchingkeys(&zone->origin, dir, now, mctx, &keys); + dns_zone_unlock_keyfiles(zone); + if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_DEBUG(1), "zone_rekey:dns_dnssec_findmatchingkeys failed: %s", @@ -20394,23 +21224,45 @@ zone_rekey(dns_zone_t *zone) { } if (kasp != NULL) { + /* + * Check DS at parental agents. Clear ongoing checks. + */ + LOCK_ZONE(zone); + checkds_cancel(zone); + clear_keylist(&zone->checkds_ok, zone->mctx); + ISC_LIST_INIT(zone->checkds_ok); + UNLOCK_ZONE(zone); + + result = dns_zone_getdnsseckeys(zone, db, ver, now, + &zone->checkds_ok); + + if (result != ISC_R_SUCCESS) { + dnssec_log(zone, ISC_LOG_ERROR, + "zone_rekey:dns_zone_getdnsseckeys failed: " + "%s", + isc_result_totext(result)); + } else { + zone_checkds(zone); + } + if (result == ISC_R_SUCCESS || result == ISC_R_NOTFOUND) { + dns_zone_lock_keyfiles(zone); result = dns_keymgr_run(&zone->origin, zone->rdclass, dir, mctx, &keys, &dnskeys, kasp, now, &nexttime); + dns_zone_unlock_keyfiles(zone); + if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, "zone_rekey:dns_dnssec_keymgr " "failed: %s", isc_result_totext(result)); - dns_zone_unlock_keyfiles(zone); KASP_UNLOCK(kasp); goto failure; } } } - dns_zone_unlock_keyfiles(zone); KASP_UNLOCK(kasp); if (result == ISC_R_SUCCESS) { From 28c5179904aa1163bb7749129d04e10c835fa2af Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 22 Jun 2021 15:43:42 +0200 Subject: [PATCH 16/20] Replace zone keyflock with zonemgr keymgmt The old approach where each zone structure has its own mutex that a thread needs to obtain multiple locks to do safe keyfile I/O operations lead to a race condition ending in a possible deadlock. Consider a zone in two views. Each such zone is stored in a separate zone structure. A thread that needs to read or write the key files for this zone needs to obtain both mutexes in seperate structures. If another thread is working on the same zone in a different view, they race to get the locks. It would be possible that thread1 grabs the lock of the zone in view1, while thread2 wins the race for the lock of the zone in view2. Now both threads try to get the other lock, both of them are already locked. Ideally, when a thread wants to do key file operations, it only needs to lock a single mutex. This commit introduces a key management hash table, stored in the zonemgr structure. Each time a zone is being managed, an object is added to the hash table (and removed when the zone is being released). This object is identified by the zone name and contains a mutex that needs to be locked prior to reading or writing key files. (cherry-picked from commit ef4619366d49efd46f9fae5f75c4a67c246ba2e6) --- lib/dns/zone.c | 437 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 366 insertions(+), 71 deletions(-) diff --git a/lib/dns/zone.c b/lib/dns/zone.c index e850e5de6e..ef52ac18f9 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -111,6 +111,9 @@ #define IO_MAGIC ISC_MAGIC('Z', 'm', 'I', 'O') #define DNS_IO_VALID(load) ISC_MAGIC_VALID(load, IO_MAGIC) +#define KEYMGMT_MAGIC ISC_MAGIC('M', 'g', 'm', 't') +#define DNS_KEYMGMT_VALID(load) ISC_MAGIC_VALID(load, KEYMGMT_MAGIC) + /*% * Ensure 'a' is at least 'min' but not more than 'max'. */ @@ -163,6 +166,7 @@ typedef struct dns_forward dns_forward_t; typedef ISC_LIST(dns_forward_t) dns_forwardlist_t; typedef struct dns_io dns_io_t; typedef ISC_LIST(dns_io_t) dns_iolist_t; +typedef struct dns_keymgmt dns_keymgmt_t; typedef struct dns_signing dns_signing_t; typedef ISC_LIST(dns_signing_t) dns_signinglist_t; typedef struct dns_nsec3chain dns_nsec3chain_t; @@ -208,9 +212,6 @@ typedef struct dns_include dns_include_t; #define ZONEDB_LOCK(l, t) RWLOCK((l), (t)) #define ZONEDB_UNLOCK(l, t) RWUNLOCK((l), (t)) -#define LOCK_KEYFILES(z) LOCK(&(z)->keyflock) -#define UNLOCK_KEYFILES(z) UNLOCK(&(z)->keyflock) - #ifdef ENABLE_AFL extern bool dns_fuzzing_resolver; #endif /* ifdef ENABLE_AFL */ @@ -219,7 +220,6 @@ struct dns_zone { /* Unlocked */ unsigned int magic; isc_mutex_t lock; - isc_mutex_t keyflock; #ifdef DNS_ZONE_CHECKLOCK bool locked; #endif /* ifdef DNS_ZONE_CHECKLOCK */ @@ -627,6 +627,8 @@ struct dns_zonemgr { /* Locked by urlock. */ /* LRU cache */ struct dns_unreachable unreachable[UNREACH_CACHE_SIZE]; + + dns_keymgmt_t *keymgmt; }; /*% @@ -751,6 +753,31 @@ struct dns_nsec3chain { bool save_delete_nsec; ISC_LINK(dns_nsec3chain_t) link; }; + +/*% + * Hold key file IO locks. + */ +typedef struct dns_keyfileio { + struct dns_keyfileio *next; + uint32_t hashval; + dns_fixedname_t fname; + dns_name_t *name; + atomic_uint_fast32_t count; + isc_mutex_t lock; +} dns_keyfileio_t; + +struct dns_keymgmt { + unsigned int magic; + isc_rwlock_t lock; + isc_mem_t *mctx; + + dns_keyfileio_t **table; + + atomic_uint_fast32_t count; + + uint32_t bits; +}; + /*%< * 'dbiterator' contains a iterator for the database. If we are creating * a NSEC3 chain only the non-NSEC3 nodes will be iterated. If we are @@ -1110,7 +1137,6 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->mctx = NULL; isc_mem_attach(mctx, &zone->mctx); isc_mutex_init(&zone->lock); - isc_mutex_init(&zone->keyflock); ZONEDB_INITLOCK(&zone->dblock); /* XXX MPA check that all elements are initialised */ #ifdef DNS_ZONE_CHECKLOCK @@ -1175,7 +1201,6 @@ free_refs: isc_refcount_destroy(&zone->erefs); isc_refcount_destroy(&zone->irefs); ZONEDB_DESTROYLOCK(&zone->dblock); - isc_mutex_destroy(&zone->keyflock); isc_mutex_destroy(&zone->lock); isc_mem_putanddetach(&zone->mctx, zone, sizeof(*zone)); return (result); @@ -1366,7 +1391,6 @@ zone_free(dns_zone_t *zone) { /* last stuff */ ZONEDB_DESTROYLOCK(&zone->dblock); - isc_mutex_destroy(&zone->keyflock); isc_mutex_destroy(&zone->lock); zone->magic = 0; isc_mem_putanddetach(&zone->mctx, zone, sizeof(*zone)); @@ -6556,69 +6580,6 @@ was_dumping(dns_zone_t *zone) { return (false); } -static void -dns__zone_lockunlock_keyfiles(dns_zone_t *zone, bool lock) { - dns_viewlist_t *vlist = NULL; - dns_view_t *v = NULL; - - REQUIRE(DNS_ZONE_VALID(zone)); - - if (zone->kasp == NULL) { - /* No need to lock, nothing is writing key files. */ - return; - } - - if (zone->view == NULL || zone->view->viewlist == NULL) { - if (lock) { - LOCK_KEYFILES(zone); - } else { - UNLOCK_KEYFILES(zone); - } - return; - } - - /* - * Also lock keyfiles for zones with the same name in a different view. - */ - vlist = zone->view->viewlist; - for (v = ISC_LIST_HEAD(*vlist); v != NULL; v = ISC_LIST_NEXT(v, link)) { - dns_zone_t *z = NULL; - isc_result_t ret = dns_view_findzone(v, &zone->origin, &z); - if (ret == ISC_R_SUCCESS) { - INSIST(DNS_ZONE_VALID(z)); - - /* - * Skip in-view zones, in other words if the view - * pointer is not the same as the zone view pointer: - * 'in-view' zones can be part of another view, - * while they also have their own home view. - */ - if (v != z->view) { - dns_zone_detach(&z); - continue; - } - - /* WMM check if policy is the same? */ - if (lock) { - LOCK_KEYFILES(z); - } else { - UNLOCK_KEYFILES(z); - } - dns_zone_detach(&z); - } - } -} - -void -dns_zone_lock_keyfiles(dns_zone_t *zone) { - dns__zone_lockunlock_keyfiles(zone, true); -} - -void -dns_zone_unlock_keyfiles(dns_zone_t *zone) { - dns__zone_lockunlock_keyfiles(zone, false); -} - /*% * Find up to 'maxkeys' DNSSEC keys used for signing version 'ver' of database * 'db' for zone 'zone' in its key directory, then load these keys into 'keys'. @@ -18413,6 +18374,292 @@ dns_zone_first(dns_zonemgr_t *zmgr, dns_zone_t **first) { *** Zone manager. ***/ +#define KEYMGMT_OVERCOMMIT 3 +#define KEYMGMT_BITS_MIN 2U +#define KEYMGMT_BITS_MAX 32U + +/* + * WMM: Static hash functions copied from lib/dns/rbtdb.c. Should be moved to + * lib/isc/hash.c when we refactor the hash table code. + */ +#define GOLDEN_RATIO_32 0x61C88647 +#define HASHSIZE(bits) (UINT64_C(1) << (bits)) + +static inline uint32_t +hash_index(uint32_t val, uint32_t bits) { + return (val * GOLDEN_RATIO_32 >> (32 - bits)); +} + +static uint32_t +hash_bits_grow(uint32_t bits, uint32_t count) { + uint32_t newbits = bits; + while (count >= HASHSIZE(newbits) && newbits < KEYMGMT_BITS_MAX) { + newbits++; + } + return (newbits); +} + +static uint32_t +hash_bits_shrink(uint32_t bits, uint32_t count) { + uint32_t newbits = bits; + while (count <= HASHSIZE(newbits) && newbits > KEYMGMT_BITS_MIN) { + newbits--; + } + return (newbits); +} + +static void +zonemgr_keymgmt_init(dns_zonemgr_t *zmgr) { + dns_keymgmt_t *mgmt = isc_mem_get(zmgr->mctx, sizeof(*mgmt)); + uint32_t size; + + *mgmt = (dns_keymgmt_t){ + .bits = KEYMGMT_BITS_MIN, + }; + isc_mem_attach(zmgr->mctx, &mgmt->mctx); + isc_rwlock_init(&mgmt->lock, 0, 0); + + size = HASHSIZE(mgmt->bits); + mgmt->table = isc_mem_get(mgmt->mctx, sizeof(*mgmt->table) * size); + memset(mgmt->table, 0, size * sizeof(mgmt->table[0])); + + atomic_init(&mgmt->count, 0); + mgmt->magic = KEYMGMT_MAGIC; + + zmgr->keymgmt = mgmt; +} + +static void +zonemgr_keymgmt_destroy(dns_zonemgr_t *zmgr) { + dns_keymgmt_t *mgmt = zmgr->keymgmt; + dns_keyfileio_t *curr, *next; + uint32_t size; + + REQUIRE(DNS_KEYMGMT_VALID(mgmt)); + + RWLOCK(&mgmt->lock, isc_rwlocktype_write); + size = HASHSIZE(mgmt->bits); + for (unsigned int i = 0; + atomic_load_relaxed(&mgmt->count) > 0 && i < size; i++) { + for (curr = mgmt->table[i]; curr != NULL; curr = next) { + next = curr->next; + isc_mutex_destroy(&curr->lock); + isc_mem_put(mgmt->mctx, curr, sizeof(*curr)); + atomic_fetch_sub_relaxed(&mgmt->count, 1); + } + mgmt->table[i] = NULL; + } + RWUNLOCK(&mgmt->lock, isc_rwlocktype_write); + + mgmt->magic = 0; + isc_rwlock_destroy(&mgmt->lock); + isc_mem_put(mgmt->mctx, mgmt->table, size * sizeof(mgmt->table[0])); + isc_mem_putanddetach(&mgmt->mctx, mgmt, sizeof(dns_keymgmt_t)); +} + +static void +zonemgr_keymgmt_resize(dns_zonemgr_t *zmgr) { + dns_keyfileio_t **newtable; + dns_keymgmt_t *mgmt = zmgr->keymgmt; + uint32_t bits, newbits, count, size, newsize; + bool grow; + + REQUIRE(DNS_KEYMGMT_VALID(mgmt)); + + RWLOCK(&mgmt->lock, isc_rwlocktype_read); + count = atomic_load_relaxed(&mgmt->count); + bits = mgmt->bits; + RWUNLOCK(&mgmt->lock, isc_rwlocktype_read); + + size = HASHSIZE(bits); + INSIST(size > 0); + + if (count >= (size * KEYMGMT_OVERCOMMIT)) { + grow = true; + } else if (count < (size / 2)) { + grow = false; + } else { + /* No need to resize. */ + return; + } + + if (grow) { + newbits = hash_bits_grow(bits, count); + } else { + newbits = hash_bits_shrink(bits, count); + } + + if (newbits == bits) { + /* + * Bit values may stay the same if maximum or minimum is + * reached. + */ + return; + } + + newsize = HASHSIZE(newbits); + INSIST(newsize > 0); + + RWLOCK(&mgmt->lock, isc_rwlocktype_write); + + newtable = isc_mem_get(mgmt->mctx, sizeof(dns_keyfileio_t *) * newsize); + memset(newtable, 0, sizeof(dns_keyfileio_t *) * newsize); + + for (unsigned int i = 0; i < size; i++) { + dns_keyfileio_t *kfio, *next; + for (kfio = mgmt->table[i]; kfio != NULL; kfio = next) { + uint32_t hash = hash_index(kfio->hashval, newbits); + next = kfio->next; + kfio->next = newtable[hash]; + newtable[hash] = kfio; + } + mgmt->table[i] = NULL; + } + + isc_mem_put(mgmt->mctx, mgmt->table, sizeof(*mgmt->table) * size); + mgmt->bits = newbits; + mgmt->table = newtable; + + RWUNLOCK(&mgmt->lock, isc_rwlocktype_write); +} + +static void +zonemgr_keymgmt_add(dns_zonemgr_t *zmgr, dns_zone_t *zone, + dns_keyfileio_t **added) { + dns_keymgmt_t *mgmt = zmgr->keymgmt; + uint32_t hashval, hash; + dns_keyfileio_t *kfio, *next; + + REQUIRE(DNS_KEYMGMT_VALID(mgmt)); + + RWLOCK(&mgmt->lock, isc_rwlocktype_write); + + hashval = dns_name_hash(&zone->origin, false); + hash = hash_index(hashval, mgmt->bits); + + for (kfio = mgmt->table[hash]; kfio != NULL; kfio = next) { + next = kfio->next; + if (dns_name_equal(kfio->name, &zone->origin)) { + /* Already in table, increment the counter. */ + atomic_fetch_add_relaxed(&kfio->count, 1); + break; + } + } + + if (kfio == NULL) { + isc_buffer_t buffer; + + /* No entry found, add it. */ + kfio = isc_mem_get(mgmt->mctx, sizeof(*kfio)); + *kfio = (dns_keyfileio_t){ .hashval = hashval, + .count = 1, + .next = mgmt->table[hash] }; + + isc_buffer_init(&buffer, kfio + 1, zone->origin.length); + kfio->name = dns_fixedname_initname(&kfio->fname); + dns_name_copy(&zone->origin, kfio->name); + + isc_mutex_init(&kfio->lock); + + mgmt->table[hash] = kfio; + if (added != NULL) { + *added = kfio; + } + + atomic_fetch_add_relaxed(&mgmt->count, 1); + } + + RWUNLOCK(&mgmt->lock, isc_rwlocktype_write); + + /* + * Call resize, that function will also check if resize is necessary. + */ + zonemgr_keymgmt_resize(zmgr); +} + +static void +zonemgr_keymgmt_delete(dns_zonemgr_t *zmgr, dns_zone_t *zone) { + dns_keymgmt_t *mgmt = zmgr->keymgmt; + uint32_t hashval, hash; + dns_keyfileio_t *kfio, *prev, *next; + + REQUIRE(DNS_KEYMGMT_VALID(mgmt)); + + RWLOCK(&mgmt->lock, isc_rwlocktype_write); + + hashval = dns_name_hash(&zone->origin, false); + hash = hash_index(hashval, mgmt->bits); + + prev = NULL; + for (kfio = mgmt->table[hash]; kfio != NULL; kfio = next) { + next = kfio->next; + if (dns_name_equal(kfio->name, &zone->origin)) { + unsigned int count; + + count = atomic_fetch_sub_relaxed(&kfio->count, 1); + if (count > 0) { + /* Keep the entry. */ + break; + } + + /* Delete the entry. */ + if (prev == NULL) { + mgmt->table[hash] = kfio->next; + } else { + prev->next = kfio->next; + } + + isc_mutex_destroy(&kfio->lock); + isc_mem_put(mgmt->mctx, kfio, sizeof(*kfio)); + + atomic_fetch_sub_relaxed(&mgmt->count, 1); + + break; + } + + prev = kfio; + } + + RWUNLOCK(&mgmt->lock, isc_rwlocktype_write); + + /* + * Call resize, that function will also check if resize is necessary. + */ + zonemgr_keymgmt_resize(zmgr); +} + +static void +zonemgr_keymgmt_find(dns_zonemgr_t *zmgr, dns_zone_t *zone, + dns_keyfileio_t **match) { + dns_keymgmt_t *mgmt = zmgr->keymgmt; + uint32_t hashval, hash; + dns_keyfileio_t *kfio, *next; + + REQUIRE(DNS_KEYMGMT_VALID(mgmt)); + REQUIRE(match != NULL && *match == NULL); + + RWLOCK(&mgmt->lock, isc_rwlocktype_read); + + if (atomic_load_relaxed(&mgmt->count) == 0) { + RWUNLOCK(&mgmt->lock, isc_rwlocktype_read); + return; + } + + hashval = dns_name_hash(&zone->origin, false); + hash = hash_index(hashval, mgmt->bits); + + for (kfio = mgmt->table[hash]; kfio != NULL; kfio = next) { + next = kfio->next; + + if (dns_name_equal(kfio->name, &zone->origin)) { + *match = kfio; + break; + } + } + + RWUNLOCK(&mgmt->lock, isc_rwlocktype_read); +} + isc_result_t dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, @@ -18489,7 +18736,10 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, goto free_startupnotifyrl; } - /* default to 20 refresh queries / notifies / checkds per second. */ + /* Key file I/O locks. */ + zonemgr_keymgmt_init(zmgr); + + /* Default to 20 refresh queries / notifies / checkds per second. */ setrl(zmgr->checkdsrl, &zmgr->checkdsrate, 20); setrl(zmgr->notifyrl, &zmgr->notifyrate, 20); setrl(zmgr->startupnotifyrl, &zmgr->startupnotifyrate, 20); @@ -18603,6 +18853,8 @@ dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { */ isc_refcount_increment0(&zone->irefs); + zonemgr_keymgmt_add(zmgr, zone, NULL); + ISC_LIST_APPEND(zmgr->zones, zone, link); zone->zmgr = zmgr; isc_refcount_increment(&zmgr->refs); @@ -18631,6 +18883,9 @@ dns_zonemgr_releasezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) { LOCK_ZONE(zone); ISC_LIST_UNLINK(zmgr->zones, zone, link); + + zonemgr_keymgmt_delete(zmgr, zone); + zone->zmgr = NULL; if (isc_refcount_decrement(&zmgr->refs) == 1) { @@ -18853,6 +19108,9 @@ zonemgr_free(dns_zonemgr_t *zmgr) { isc_rwlock_destroy(&zmgr->urlock); isc_rwlock_destroy(&zmgr->rwlock); + + zonemgr_keymgmt_destroy(zmgr); + mctx = zmgr->mctx; isc_mem_put(zmgr->mctx, zmgr, sizeof(*zmgr)); isc_mem_detach(&mctx); @@ -19664,6 +19922,43 @@ dns_zonemgr_getcount(dns_zonemgr_t *zmgr, int state) { return (count); } +static void +dns__zone_lockunlock_keyfiles(dns_zone_t *zone, bool lock) { + dns_keyfileio_t *kfio = NULL; + + REQUIRE(DNS_ZONE_VALID(zone)); + + if (zone->kasp == NULL) { + /* No need to lock, nothing is writing key files. */ + return; + } + + zonemgr_keymgmt_find(zone->zmgr, zone, &kfio); + if (kfio == NULL) { + /* Should not happen, but if so, add the entry now. */ + dns_zone_log(zone, ISC_LOG_WARNING, + "attempt to lock key files, but no key file lock " + "available, abort"); + return; + } + + if (lock) { + isc_mutex_lock(&kfio->lock); + } else { + isc_mutex_unlock(&kfio->lock); + } +} + +void +dns_zone_lock_keyfiles(dns_zone_t *zone) { + dns__zone_lockunlock_keyfiles(zone, true); +} + +void +dns_zone_unlock_keyfiles(dns_zone_t *zone) { + dns__zone_lockunlock_keyfiles(zone, false); +} + isc_result_t dns_zone_checknames(dns_zone_t *zone, const dns_name_t *name, dns_rdata_t *rdata) { From 39df3f0475761cf6e4432e0bf5666981432bf321 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 25 Jun 2021 10:51:21 +0200 Subject: [PATCH 17/20] Protect dst key metadata with lock The DST key metadata can be written by several threads in parralel. Protect the dst_key_get* and dst_key_set* functions with a mutex. --- lib/dns/dst_api.c | 45 ++++++++++++++++++++++++++++++++++++++++++ lib/dns/dst_internal.h | 1 + 2 files changed, 46 insertions(+) diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index f0af50f6d2..de40f929a7 100644 --- a/lib/dns/dst_api.c +++ b/lib/dns/dst_api.c @@ -1015,10 +1015,15 @@ dst_key_getbool(const dst_key_t *key, int type, bool *valuep) { REQUIRE(VALID_KEY(key)); REQUIRE(valuep != NULL); REQUIRE(type <= DST_MAX_BOOLEAN); + + isc_mutex_lock(&(((dst_key_t *)key)->mdlock)); if (!key->boolset[type]) { + isc_mutex_unlock(&(((dst_key_t *)key)->mdlock)); return (ISC_R_NOTFOUND); } *valuep = key->bools[type]; + isc_mutex_unlock(&(((dst_key_t *)key)->mdlock)); + return (ISC_R_SUCCESS); } @@ -1026,15 +1031,21 @@ void dst_key_setbool(dst_key_t *key, int type, bool value) { REQUIRE(VALID_KEY(key)); REQUIRE(type <= DST_MAX_BOOLEAN); + + isc_mutex_lock(&key->mdlock); key->bools[type] = value; key->boolset[type] = true; + isc_mutex_unlock(&key->mdlock); } void dst_key_unsetbool(dst_key_t *key, int type) { REQUIRE(VALID_KEY(key)); REQUIRE(type <= DST_MAX_BOOLEAN); + + isc_mutex_lock(&key->mdlock); key->boolset[type] = false; + isc_mutex_unlock(&key->mdlock); } isc_result_t @@ -1042,10 +1053,15 @@ dst_key_getnum(const dst_key_t *key, int type, uint32_t *valuep) { REQUIRE(VALID_KEY(key)); REQUIRE(valuep != NULL); REQUIRE(type <= DST_MAX_NUMERIC); + + isc_mutex_lock(&(((dst_key_t *)key)->mdlock)); if (!key->numset[type]) { + isc_mutex_unlock(&(((dst_key_t *)key)->mdlock)); return (ISC_R_NOTFOUND); } *valuep = key->nums[type]; + isc_mutex_unlock(&(((dst_key_t *)key)->mdlock)); + return (ISC_R_SUCCESS); } @@ -1053,15 +1069,21 @@ void dst_key_setnum(dst_key_t *key, int type, uint32_t value) { REQUIRE(VALID_KEY(key)); REQUIRE(type <= DST_MAX_NUMERIC); + + isc_mutex_lock(&key->mdlock); key->nums[type] = value; key->numset[type] = true; + isc_mutex_unlock(&key->mdlock); } void dst_key_unsetnum(dst_key_t *key, int type) { REQUIRE(VALID_KEY(key)); REQUIRE(type <= DST_MAX_NUMERIC); + + isc_mutex_lock(&key->mdlock); key->numset[type] = false; + isc_mutex_unlock(&key->mdlock); } isc_result_t @@ -1069,10 +1091,14 @@ dst_key_gettime(const dst_key_t *key, int type, isc_stdtime_t *timep) { REQUIRE(VALID_KEY(key)); REQUIRE(timep != NULL); REQUIRE(type <= DST_MAX_TIMES); + + isc_mutex_lock(&(((dst_key_t *)key)->mdlock)); if (!key->timeset[type]) { + isc_mutex_unlock(&(((dst_key_t *)key)->mdlock)); return (ISC_R_NOTFOUND); } *timep = key->times[type]; + isc_mutex_unlock(&(((dst_key_t *)key)->mdlock)); return (ISC_R_SUCCESS); } @@ -1080,15 +1106,21 @@ void dst_key_settime(dst_key_t *key, int type, isc_stdtime_t when) { REQUIRE(VALID_KEY(key)); REQUIRE(type <= DST_MAX_TIMES); + + isc_mutex_lock(&key->mdlock); key->times[type] = when; key->timeset[type] = true; + isc_mutex_unlock(&key->mdlock); } void dst_key_unsettime(dst_key_t *key, int type) { REQUIRE(VALID_KEY(key)); REQUIRE(type <= DST_MAX_TIMES); + + isc_mutex_lock(&key->mdlock); key->timeset[type] = false; + isc_mutex_unlock(&key->mdlock); } isc_result_t @@ -1096,10 +1128,15 @@ dst_key_getstate(const dst_key_t *key, int type, dst_key_state_t *statep) { REQUIRE(VALID_KEY(key)); REQUIRE(statep != NULL); REQUIRE(type <= DST_MAX_KEYSTATES); + + isc_mutex_lock(&(((dst_key_t *)key)->mdlock)); if (!key->keystateset[type]) { + isc_mutex_unlock(&(((dst_key_t *)key)->mdlock)); return (ISC_R_NOTFOUND); } *statep = key->keystates[type]; + isc_mutex_unlock(&(((dst_key_t *)key)->mdlock)); + return (ISC_R_SUCCESS); } @@ -1107,15 +1144,21 @@ void dst_key_setstate(dst_key_t *key, int type, dst_key_state_t state) { REQUIRE(VALID_KEY(key)); REQUIRE(type <= DST_MAX_KEYSTATES); + + isc_mutex_lock(&key->mdlock); key->keystates[type] = state; key->keystateset[type] = true; + isc_mutex_unlock(&key->mdlock); } void dst_key_unsetstate(dst_key_t *key, int type) { REQUIRE(VALID_KEY(key)); REQUIRE(type <= DST_MAX_KEYSTATES); + + isc_mutex_lock(&key->mdlock); key->keystateset[type] = false; + isc_mutex_unlock(&key->mdlock); } isc_result_t @@ -1287,6 +1330,7 @@ dst_key_free(dst_key_t **keyp) { if (key->key_tkeytoken) { isc_buffer_free(&key->key_tkeytoken); } + isc_mutex_destroy(&key->mdlock); isc_safe_memwipe(key, sizeof(*key)); isc_mem_putanddetach(&mctx, key, sizeof(*key)); } @@ -1482,6 +1526,7 @@ get_key_struct(const dns_name_t *name, unsigned int alg, unsigned int flags, key->times[i] = 0; key->timeset[i] = false; } + isc_mutex_init(&key->mdlock); key->inactive = false; key->magic = KEY_MAGIC; return (key); diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h index 793606e013..5955072ef3 100644 --- a/lib/dns/dst_internal.h +++ b/lib/dns/dst_internal.h @@ -79,6 +79,7 @@ typedef enum { DO_SIGN, DO_VERIFY } dst_use_t; struct dst_key { unsigned int magic; isc_refcount_t refs; + isc_mutex_t mdlock; /*%< lock for read/write metadata */ dns_name_t *key_name; /*%< name of the key */ unsigned int key_size; /*%< size of the key in bits */ unsigned int key_proto; /*%< protocols this key is used for From b4c1f3b832808f7eb8a48382691be48808630b9a Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 25 Jun 2021 11:38:37 +0200 Subject: [PATCH 18/20] Update documentation Update ARM and DNSSEC guide with the new checkds feature. --- doc/arm/reference.rst | 12 ++++++++++-- doc/dnssec-guide/recipes.rst | 4 +++- doc/dnssec-guide/signing.rst | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index da6d3d6020..0f14eeb17e 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -861,6 +861,8 @@ responses such as NXDOMAIN. ``parental-agents`` lists allow for a common set of parental agents to be easily used by multiple primary and secondary zones in their ``parental-agents`` lists. +A parental agent is the entity that the zone has a relationship with to +change its delegation information (defined in :rfc:`7344`). .. _primaries_grammar: @@ -5143,8 +5145,14 @@ BIND has mechanisms in place to facilitate automated KSK rollovers. It publishes CDS and CDNSKEY records that can be used by the parent zone to publish or withdraw the zone's DS records. BIND will query the parental agents to see if the new DS is actually published before withdrawing the -old DNSSEC key. The following options apply to DS queries sent to -``parental-agents``: +old DNSSEC key. + + .. note:: + The DS response is not validated so it is recommended to set up a + trust relationship with the parental agent. For example, use TSIG to + authenticate the parental agent, or point to a validating resolver. + +The following options apply to DS queries sent to ``parental-agents``: ``parental-source`` ``parental-source`` determines which local source address, and diff --git a/doc/dnssec-guide/recipes.rst b/doc/dnssec-guide/recipes.rst index 6fc52e06c0..91d14be07f 100644 --- a/doc/dnssec-guide/recipes.rst +++ b/doc/dnssec-guide/recipes.rst @@ -1103,7 +1103,9 @@ unsigned. When the DS records have been removed from the parent zone, use ``rndc dnssec -checkds -key withdrawn example.com`` to tell ``named`` that the DS is removed, and the remaining DNSSEC records will be removed in a timely -manner. +manner. Or if you have parental agents configured, the DNSSEC records will be +automatically removed after BIND has seen that the parental agents no longer +serves the DS RRset for this zone. After a while, your zone is reverted back to the traditional, insecure DNS format. You can verify by checking that all DNSKEY and RRSIG records have been diff --git a/doc/dnssec-guide/signing.rst b/doc/dnssec-guide/signing.rst index 616983474d..f9f65deaad 100644 --- a/doc/dnssec-guide/signing.rst +++ b/doc/dnssec-guide/signing.rst @@ -888,11 +888,36 @@ you may not even have to do that [#]_. When the time approaches for the roll of a KSK or CSK, BIND adds a CDS and a CDNSKEY record for the key in question to the apex of the zone. If your parent zone supports polling for CDS/CDNSKEY records, they -are uploaded and the DS record published in the parent - at least ideally. At -the time of this writing (mid-2020) BIND does not check for the presence of a -DS record in the parent zone before completing the KSK or CSK rollover -and withdrawing the old key. Instead, you need to use the ``rndc`` tool -to tell ``named`` that the DS record has been published. For example: +are uploaded and the DS record published in the parent - at least ideally. + +If BIND is configured with ``parental-agents``, it will check for the DS +presence. Let's look at the following configuration excerpt: + +:: + + parental-agents { + 10.53.0.11, 10.53.0.12; + }; + + zone "example.net" in { + ... + dnssec-policy standard; + parental-agents { "net"; }; + ... + }; + +BIND will check for the presence of the DS record in the parent zone by querying +its parental agents (defined in :rfc:`7344` to be the entities that the child +zone has a relationship with to change its delegation information). In the +example above, The zone `example.net` is configured with two parental agents, +at the addresses 10.53.0.11 and 10.53.0.12. These addresses are used as an +example only. Both addresses will have to respond with a DS RRset that +includes the DS record identifying the key that is being rolled. If one or +both don't have the DS included yet the rollover is paused, and the check for +DS presence is retried after an hour. The same applies for DS withdrawal. + +Alternatively, you can use the ``rndc`` tool to tell ``named`` that the DS +record has been published or withdrawn. For example: :: From 22cd63bf811fea683bc86e84b7649ce679d53122 Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Fri, 25 Jun 2021 11:43:29 +0200 Subject: [PATCH 19/20] Add change and release note for [#1126] Seems pretty newsworthy. --- CHANGES | 5 +++++ doc/notes/notes-current.rst | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 72c9a9b44a..316b029606 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +5669. [func] Add 'checkds' feature. Zones with "dnssec-policy" and + "parental-agents" configured will check for DS presence + and are able to perform automatic KSK rollover. + [GL #1126] + 5668. [bug] When a zone fails to load on startup, the setnsec3param task is rescheduled. This caused a hang on shutdown, and is now fixed. [GL #2791] diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index ddeb2d7fd7..0e92cda1b1 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -28,7 +28,9 @@ Known Issues New Features ~~~~~~~~~~~~ -- None. +- Automatic KSK rollover: A new configuration option ``parental-agents`` is + added to add a list of servers to a zone that can be used for checking DS + presence. :gl:`#1126` Removed Features ~~~~~~~~~~~~~~~~ From c92128eada7c59892d84b54462c87130685ac87d Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Wed, 30 Jun 2021 09:23:56 +0200 Subject: [PATCH 20/20] Move private_type_record() to conf.sh.common The function 'private_type_record()' is now used in multiple system setup scripts and should be moved to the common configuration script conf.sh.common. --- bin/tests/system/checkds/ns2/setup.sh | 10 ---------- bin/tests/system/checkds/ns5/setup.sh | 10 ---------- bin/tests/system/checkds/ns9/setup.sh | 10 ---------- bin/tests/system/conf.sh.common | 16 ++++++++++++++++ bin/tests/system/kasp/ns3/setup.sh | 10 ---------- bin/tests/system/kasp/ns6/setup.sh | 11 ----------- bin/tests/system/keymgr2kasp/ns3/setup.sh | 11 ----------- bin/tests/system/keymgr2kasp/ns4/setup.sh | 10 ---------- 8 files changed, 16 insertions(+), 72 deletions(-) diff --git a/bin/tests/system/checkds/ns2/setup.sh b/bin/tests/system/checkds/ns2/setup.sh index 7fb586afee..d3f178408a 100644 --- a/bin/tests/system/checkds/ns2/setup.sh +++ b/bin/tests/system/checkds/ns2/setup.sh @@ -22,16 +22,6 @@ do cp "../ns9/dsset-$subdomain.checkds$TP" . done -private_type_record() { - _zone=$1 - _algorithm=$2 - _keyfile=$3 - - _id=$(keyfile_to_key_id "$_keyfile") - - printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" -} - zone="checkds" infile="checkds.db.infile" zonefile="checkds.db" diff --git a/bin/tests/system/checkds/ns5/setup.sh b/bin/tests/system/checkds/ns5/setup.sh index 100cd5dbec..8759366780 100644 --- a/bin/tests/system/checkds/ns5/setup.sh +++ b/bin/tests/system/checkds/ns5/setup.sh @@ -14,16 +14,6 @@ echo_i "ns5/setup.sh" -private_type_record() { - _zone=$1 - _algorithm=$2 - _keyfile=$3 - - _id=$(keyfile_to_key_id "$_keyfile") - - printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" -} - zone="checkds" infile="checkds.db.infile" zonefile="checkds.db" diff --git a/bin/tests/system/checkds/ns9/setup.sh b/bin/tests/system/checkds/ns9/setup.sh index efb4a1e2c2..57554d3dd3 100644 --- a/bin/tests/system/checkds/ns9/setup.sh +++ b/bin/tests/system/checkds/ns9/setup.sh @@ -22,16 +22,6 @@ setup() { echo "$zone" >> zones } -private_type_record() { - _zone=$1 - _algorithm=$2 - _keyfile=$3 - - _id=$(keyfile_to_key_id "$_keyfile") - - printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" -} - # Short environment variable names for key states and times. H="HIDDEN" R="RUMOURED" diff --git a/bin/tests/system/conf.sh.common b/bin/tests/system/conf.sh.common index b68c93dd93..bbc6ade2ea 100644 --- a/bin/tests/system/conf.sh.common +++ b/bin/tests/system/conf.sh.common @@ -397,6 +397,22 @@ keyfile_to_key_id() { echo "$1" | sed "s/.*+0\{0,4\}//" } +# private_type_record: write a private type record recording the state of the +# signing process +# +# For a given zone ($1), algorithm number ($2) and key file ($3), print the +# private type record with default type value of 65534, indicating that the +# signing process for this key is completed. +private_type_record() { + _zone=$1 + _algorithm=$2 + _keyfile=$3 + + _id=$(keyfile_to_key_id "$_keyfile") + + printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" +} + # nextpart*() - functions for reading files incrementally # # These functions aim to facilitate looking for (or waiting for) diff --git a/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index d487619f3b..a25bc619fb 100644 --- a/bin/tests/system/kasp/ns3/setup.sh +++ b/bin/tests/system/kasp/ns3/setup.sh @@ -22,16 +22,6 @@ setup() { echo "$zone" >> zones } -private_type_record() { - _zone=$1 - _algorithm=$2 - _keyfile=$3 - - _id=$(keyfile_to_key_id "$_keyfile") - - printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" -} - # Set in the key state files the Predecessor/Successor fields. # Key $1 is the predecessor of key $2. key_successor() { diff --git a/bin/tests/system/kasp/ns6/setup.sh b/bin/tests/system/kasp/ns6/setup.sh index e8a3c21af1..6f1d07bf43 100644 --- a/bin/tests/system/kasp/ns6/setup.sh +++ b/bin/tests/system/kasp/ns6/setup.sh @@ -21,17 +21,6 @@ setup() { infile="${zone}.db.infile" } -private_type_record() { - _zone=$1 - _algorithm=$2 - _keyfile=$3 - - _id=$(keyfile_to_key_id "$_keyfile") - - printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" -} - - # Make lines shorter by storing key states in environment variables. H="HIDDEN" R="RUMOURED" diff --git a/bin/tests/system/keymgr2kasp/ns3/setup.sh b/bin/tests/system/keymgr2kasp/ns3/setup.sh index 1bb89ad37b..f2d8c3bab6 100644 --- a/bin/tests/system/keymgr2kasp/ns3/setup.sh +++ b/bin/tests/system/keymgr2kasp/ns3/setup.sh @@ -21,17 +21,6 @@ setup() { infile="${zone}.db.infile" } -private_type_record() { - _zone=$1 - _algorithm=$2 - _keyfile=$3 - - _id=$(keyfile_to_key_id "$_keyfile") - - printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" -} - - # Make lines shorter by storing key states in environment variables. H="HIDDEN" R="RUMOURED" diff --git a/bin/tests/system/keymgr2kasp/ns4/setup.sh b/bin/tests/system/keymgr2kasp/ns4/setup.sh index 148de97570..18f6f322de 100644 --- a/bin/tests/system/keymgr2kasp/ns4/setup.sh +++ b/bin/tests/system/keymgr2kasp/ns4/setup.sh @@ -14,16 +14,6 @@ echo_i "ns4/setup.sh" -private_type_record() { - _zone=$1 - _algorithm=$2 - _keyfile=$3 - - _id=$(keyfile_to_key_id "$_keyfile") - - printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" -} - # Make lines shorter by storing key states in environment variables. H="HIDDEN" R="RUMOURED"