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/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 diff --git a/bin/named/config.c b/bin/named/config.c index 10087b47f3..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\ @@ -570,8 +572,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 +600,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 +682,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; @@ -767,7 +775,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"); @@ -788,11 +796,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/named.conf.rst b/bin/named/named.conf.rst index 39407490f6..46a2ca1fe3 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 @@ -342,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; @@ -464,6 +469,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 ^^^^^^ @@ -478,10 +494,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 +602,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 +615,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 @@ -731,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; @@ -864,7 +885,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 +926,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 ); @@ -928,8 +949,16 @@ 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 ]; ... }; + parental-source ( ipv4_address | * ) [ port ( integer | + * ) ] [ dscp integer ]; + parental-source-v6 ( ipv6_address | * ) [ port ( + integer | * ) ] [ dscp integer ]; 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 +1003,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 +1042,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 +1065,18 @@ 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 ]; ... }; + parental-agents [ port integer ] [ dscp integer ] { ( + 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 + string ]; ... }; request-expire boolean; request-ixfr boolean; serial-update-method ( date | increment | unixtime ); diff --git a/bin/named/server.c b/bin/named/server.c index 517bbac9d6..10925c3c1c 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"); @@ -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/bin/named/zoneconf.c b/bin/named/zoneconf.c index 2e05cee6d4..9680e12874 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); @@ -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); @@ -1710,6 +1734,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. */ @@ -1904,8 +1950,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 +1959,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/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/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/bin/tests/system/checkconf/good.conf b/bin/tests/system/checkconf/good.conf index d3f0039766..616a5441d4 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,19 @@ view "fourth" { zone "dnssec-test" { type master; file "dnssec-test.db"; + parental-agents { + 1.2.3.4; + 1.2.3.5; + }; dnssec-policy "test"; + parental-source 10.10.10.10 port 53 dscp 55; }; zone "dnssec-default" { type master; file "dnssec-default.db"; + parental-agents { + "parents"; + }; dnssec-policy "default"; }; zone "dnssec-inherit" { 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..d3f178408a --- /dev/null +++ b/bin/tests/system/checkds/ns2/setup.sh @@ -0,0 +1,32 @@ +#!/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 + +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..8759366780 --- /dev/null +++ b/bin/tests/system/checkds/ns5/setup.sh @@ -0,0 +1,24 @@ +#!/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" + +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..57554d3dd3 --- /dev/null +++ b/bin/tests/system/checkds/ns9/setup.sh @@ -0,0 +1,59 @@ +#!/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 +} + +# 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.common b/bin/tests/system/conf.sh.common index cd0838a893..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) @@ -727,4 +743,5 @@ export SIGNER export SUBDIRS export TMPDIR export TSIGKEYGEN +export VERIFY export WIRETEST 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/bin/tests/system/kasp/ns3/setup.sh b/bin/tests/system/kasp/ns3/setup.sh index b70fce7cdd..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() { @@ -293,7 +283,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. 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" 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 f8eb970f10..0f14eeb17e 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``. @@ -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,23 @@ 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. +A parental agent is the entity that the zone has a relationship with to +change its delegation information (defined in :rfc:`7344`). + .. _primaries_grammar: ``primaries`` Statement Grammar @@ -2609,7 +2629,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: @@ -5118,6 +5138,38 @@ 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. + + .. 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 + 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/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: :: diff --git a/doc/man/named.conf.5in b/doc/man/named.conf.5in index 5a311c30db..deec1ffb88 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 @@ -409,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; @@ -534,6 +539,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 @@ -553,10 +573,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 +705,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 +718,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 @@ -830,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; @@ -963,7 +988,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 +1029,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 ); @@ -1027,8 +1052,16 @@ 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 ]; ... }; + parental\-source ( ipv4_address | * ) [ port ( integer | + * ) ] [ dscp integer ]; + parental\-source\-v6 ( ipv6_address | * ) [ port ( + integer | * ) ] [ dscp integer ]; 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 +1110,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 +1149,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 +1172,18 @@ 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 ]; ... }; + parental\-agents [ port integer ] [ dscp integer ] { ( + 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 + 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..a34d512726 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 ); @@ -46,6 +46,9 @@ zone [ ] { notify-source ( | * ) [ port ( | * ) ] [ dscp ]; 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 9bac436947..ad85f5f17f 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 ); @@ -48,6 +48,9 @@ notify-source ( | * ) [ port ( | * ) ] [ dscp ]; 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/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..416326e6da 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 @@ -267,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 ; @@ -390,14 +395,20 @@ options { zone-statistics ( full | terse | none | ); }; +parental-agents [ port ] [ + dscp ] { ( | + [ port ] | + [ port ] ) [ key + ] [ tls ]; ... }; // may occur multiple times + 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 +481,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 +494,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 @@ -616,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 ; @@ -750,7 +766,7 @@ view [ ] { allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( @@ -791,7 +807,7 @@ view [ ] { masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); masters [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-ixfr-ratio ( unlimited | ); @@ -815,8 +831,16 @@ view [ ] { | * ) ] [ dscp ]; notify-to-soa ; nsec3-test-zone ; // test only + parental-agents [ port ] [ dscp ] { ( + | [ port ] | + [ port ] ) [ key ] [ + tls ]; ... }; + parental-source ( | * ) [ port ( | + * ) ] [ dscp ]; + parental-source-v6 ( | * ) [ port ( + | * ) ] [ dscp ]; primaries [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; @@ -856,9 +880,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 +919,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 +943,18 @@ zone [ ] { [ dscp ]; notify-to-soa ; nsec3-test-zone ; // test only - primaries [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + 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 ; serial-update-method ( date | increment | unixtime ); diff --git a/doc/misc/options.active b/doc/misc/options.active index d5adf85a98..b05e36838d 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 @@ -265,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 ; @@ -387,14 +392,20 @@ options { zone-statistics ( full | terse | none | ); }; +parental-agents [ port ] [ + dscp ] { ( | + [ port ] | + [ port ] ) [ key + ] [ tls ]; ... }; // may occur multiple times + 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 +478,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 +491,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 @@ -612,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 ; @@ -745,7 +761,7 @@ view [ ] { allow-update { ; ... }; allow-update-forwarding { ; ... }; also-notify [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; alt-transfer-source ( | * ) [ port ( @@ -786,7 +802,7 @@ view [ ] { masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); masters [ port ] [ dscp ] { ( - | [ port ] | + | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; max-ixfr-ratio ( unlimited | ); @@ -809,8 +825,16 @@ view [ ] { 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 ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; @@ -850,9 +874,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 +913,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 +936,18 @@ zone [ ] { notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; - primaries [ port ] [ dscp ] { ( | - [ port ] | [ port - ] ) [ key ] [ tls ]; ... }; + 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 ; serial-update-method ( date | increment | unixtime ); diff --git a/doc/misc/options.grammar.rst b/doc/misc/options.grammar.rst index 9ee853c6a9..392e9c1a42 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 @@ -187,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/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/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..977e2618dd 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,10 @@ zone [ ] { notify-source ( | * ) [ port ( | * ) ] [ dscp ]; notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; - primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + 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 ; sig-signing-nodes ; diff --git a/doc/misc/slave.zoneopt.rst b/doc/misc/slave.zoneopt.rst index 6f2da9559d..77ad700f53 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,10 @@ notify-source ( | * ) [ port ( | * ) ] [ dscp ]; notify-source-v6 ( | * ) [ port ( | * ) ] [ dscp ]; notify-to-soa ; - primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + 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 ; 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/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 ~~~~~~~~~~~~~~~~ diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 02ed0f4120..50ed64b48e 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; @@ -1999,7 +2026,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)) { @@ -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. @@ -3584,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 } }; @@ -5148,6 +5202,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) { diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c index 6a2e018517..de40f929a7 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) @@ -1014,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); } @@ -1025,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 @@ -1041,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); } @@ -1052,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 @@ -1068,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); } @@ -1079,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 @@ -1095,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); } @@ -1106,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 @@ -1286,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)); } @@ -1481,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); @@ -2014,6 +2060,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); @@ -2372,20 +2421,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 +2504,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 +2565,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/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 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/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/include/dns/zone.h b/lib/dns/include/dns/zone.h index c417c25a8f..82d9e87150 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -642,12 +642,51 @@ 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 + *\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 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 @@ -885,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); /*%< @@ -1627,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, @@ -1807,6 +1950,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/include/dst/dst.h b/lib/dns/include/dst/dst.h index f219aa8e31..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 @@ -1180,6 +1182,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); /*%< 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) { diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 3d24967577..ef52ac18f9 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) @@ -108,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'. */ @@ -153,12 +159,14 @@ #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; 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; @@ -204,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 */ @@ -215,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 */ @@ -281,6 +285,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; @@ -292,6 +305,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; @@ -299,6 +314,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; @@ -317,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; @@ -577,6 +595,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; @@ -593,6 +612,7 @@ struct dns_zonemgr { /* Configuration data. */ uint32_t transfersin; uint32_t transfersperns; + unsigned int checkdsrate; unsigned int notifyrate; unsigned int startupnotifyrate; unsigned int serialqueryrate; @@ -607,6 +627,8 @@ struct dns_zonemgr { /* Locked by urlock. */ /* LRU cache */ struct dns_unreachable unreachable[UNREACH_CACHE_SIZE]; + + dns_keymgmt_t *keymgmt; }; /*% @@ -631,6 +653,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 @@ -714,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 @@ -852,6 +916,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); @@ -1031,6 +1105,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, @@ -1061,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 @@ -1089,8 +1164,11 @@ 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); + isc_sockaddr_any6(&zone->parentalsrc6); isc_sockaddr_any(&zone->xfrsource4); isc_sockaddr_any6(&zone->xfrsource6); isc_sockaddr_any(&zone->altxfrsource4); @@ -1123,12 +1201,21 @@ 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); } +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 +1309,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 +1342,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) == @@ -1298,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)); @@ -5922,6 +6014,74 @@ 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)); @@ -5973,6 +6133,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) { @@ -6012,9 +6189,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 +6251,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; @@ -6137,23 +6314,6 @@ set_primarieslist(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, @@ -6180,9 +6340,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 +6351,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 +6417,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 +6439,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)); @@ -6305,6 +6465,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; @@ -6364,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'. @@ -6464,6 +6617,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) { @@ -11902,6 +12134,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; @@ -14701,6 +14952,8 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) { } } + checkds_cancel(zone); + notify_cancel(zone); forward_cancel(zone); @@ -18121,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, @@ -18140,6 +18679,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; @@ -18167,11 +18707,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) { @@ -18190,7 +18736,11 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, goto free_startupnotifyrl; } - /* default to 20 refresh queries / notifies 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); setrl(zmgr->refreshrl, &zmgr->serialqueryrate, 20); @@ -18220,6 +18770,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: @@ -18301,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); @@ -18329,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) { @@ -18408,6 +18965,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); @@ -18542,6 +19100,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); @@ -18549,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); @@ -18932,6 +19494,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)); @@ -19353,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) { @@ -19617,16 +20223,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) { @@ -20036,6 +20632,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; @@ -20135,10 +21506,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", @@ -20146,23 +21519,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) { diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index cf2b67deb7..2a93bcc124 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,10 @@ 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 }, + { "parental-agents", &cfg_type_remoteservers, 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 }, @@ -2247,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 }, @@ -2318,6 +2323,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 }, @@ -3668,14 +3675,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 +3695,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 +3714,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 +3723,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) { 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