2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 06:25:31 +00:00

Merge branch '1592-catz-filename' into 'master'

Resolve "catalog zones fail if a zone name contains a slash"

Closes #1592

See merge request isc-projects/bind9!2980
This commit is contained in:
Evan Hunt
2020-02-04 03:18:51 +00:00
7 changed files with 223 additions and 120 deletions

View File

@@ -1,3 +1,6 @@
5352. [bug] Correctly handle catalog zone entries containing
characters that aren't legal in filenames. [GL #1592]
5351. [bug] CDS / CDNSKEY consistency checks failed to handle
removal records. [GL #1554]

View File

@@ -378,7 +378,7 @@ ret=0
$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
server 10.53.0.1 ${PORT}
update add masters.catalog1.example. 3600 IN A 10.53.0.3
update add masters.catalog1.example. 3600 IN AAAA fd92:7065:b8e:ffff::3
update add masters.catalog1.example. 3600 IN AAAA fd92:7065:b8e:ffff::3
update add 4346f565b4d63ddb99e5d2497ff22d04e878e8f8.zones.catalog1.example. 3600 IN PTR dom6.example.
send
END
@@ -759,96 +759,127 @@ if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
##########################################################################
echo_i "Testing very long domain in catalog"
n=$((n+1))
echo_i "checking that this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. is not served by master ($n)"
ret=0
wait_for_no_soa @10.53.0.1 this.is.aery.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
echo_i "Testing catalog entries that can't be represented as filenames"
# note: we need 4 backslashes in the shell to get 2 backslashes in DNS
# presentation format, which is 1 backslash on the wire.
for special in \
this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example \
this.zone/domain.has.a.slash.dom10.example \
this.zone\\\\domain.has.backslash.dom10.example \
this.zone:domain.has.a.colon.dom.10.example
do
# hashes below are generated by:
# python ${TOP}/contrib/scripts/catzhash.py "${special}"
n=$((n+1))
echo_i "Adding a domain this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. to master via RNDC ($n)"
ret=0
echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom10.example.db
echo "@ IN NS invalid." >> ns1/dom10.example.db
rndccmd 10.53.0.1 addzone this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. '{type master; file "dom10.example.db";};' || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
case "$special" in
this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example)
hash=825f48b1ce1b4cf5a041d20255a0c8e98d114858
db=__catz__4d70696f2335687069467f11f5d5378c480383f97782e553fb2d04a7bb2a23ed.db
;;
this.zone/domain.has.a.slash.dom10.example)
hash=e64cc64c99bf52d0a77fb16dd7ed57cf925a36aa
db=__catz__46ba3e1b28d5955e5313d5fee61bedc78c71d08035aa7ea2f7bf0b8228ab3acc.db
;;
this.zone\\\\domain.has.backslash.dom10.example)
hash=91e27e02153d38cf656a9b376d7747fbcd19f985
db=__catz__b667f7ff802c0895e0506699951cff9a1cab68c5ef8546aa0d07425f244ed870.db
;;
this.zone:domain.has.a.colon.dom.10.example)
hash=8b7238bf4c34045834c573ba4116557ebb24d33c
db=__catz__5c721f7872913a4e7fa8ad42589cce5dd6e551a4c9e6ab3f86e77c0bbc7c2ca6.db
;;
esac
n=$((n+1))
echo_i "checking that this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. is now served by master ($n)"
ret=0
wait_for_soa @10.53.0.1 this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "checking that ${special}. is not served by master ($n)"
ret=0
wait_for_no_soa @10.53.0.1 "${special}" dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
nextpart ns2/named.run >/dev/null
n=$((n+1))
echo_i "Adding a domain ${special}. to master via RNDC ($n)"
ret=0
echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" > ns1/dom10.example.db
echo "@ IN NS invalid." >> ns1/dom10.example.db
rndccmd 10.53.0.1 addzone '"'"${special}"'"' '{type master; file "dom10.example.db";};' || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "Adding domain this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. to catalog1 zone ($n)"
ret=0
$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
server 10.53.0.1 ${PORT}
update add 825f48b1ce1b4cf5a041d20255a0c8e98d114858.zones.catalog1.example 3600 IN PTR this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example.
send
n=$((n+1))
echo_i "checking that ${special}. is now served by master ($n)"
ret=0
wait_for_soa @10.53.0.1 "${special}." dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
nextpart ns2/named.run >/dev/null
n=$((n+1))
echo_i "Adding domain ${special}. to catalog1 zone ($n)"
ret=0
$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
server 10.53.0.1 ${PORT}
update add ${hash}.zones.catalog1.example 3600 IN PTR ${special}.
send
END
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "waiting for slave to sync up ($n)"
ret=0
wait_for_message ns2/named.run "catz: adding zone 'this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example' from catalog 'catalog1.example'" &&
wait_for_message ns2/named.run "transfer of 'this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example/IN' from 10.53.0.1#${PORT}: Transfer status: success" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "waiting for slave to sync up ($n)"
ret=0
wait_for_message ns2/named.run "catz: adding zone '$special' from catalog 'catalog1.example'" &&
wait_for_message ns2/named.run "transfer of '$special/IN' from 10.53.0.1#${PORT}: Transfer status: success" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "checking that this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. is served by slave ($n)"
ret=0
wait_for_soa @10.53.0.2 this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "checking that ${special}. is served by slave ($n)"
ret=0
wait_for_soa @10.53.0.2 "${special}." dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "checking that zone-directory is populated with a hashed filename ($n)"
ret=0
wait_for_zonefile "ns2/zonedir/__catz__4d70696f2335687069467f11f5d5378c480383f97782e553fb2d04a7bb2a23ed.db" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "checking that zone-directory is populated with a hashed filename ($n)"
ret=0
wait_for_zonefile "ns2/zonedir/$db" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "removing domain this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. from catalog1 zone ($n)"
ret=0
$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
server 10.53.0.1 ${PORT}
update delete 825f48b1ce1b4cf5a041d20255a0c8e98d114858.zones.catalog1.example
send
n=$((n+1))
echo_i "removing domain ${special}. from catalog1 zone ($n)"
ret=0
$NSUPDATE -d <<END >> nsupdate.out.test$n 2>&1 || ret=1
server 10.53.0.1 ${PORT}
update delete ${hash}.zones.catalog1.example
send
END
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "waiting for slave to sync up ($n)"
ret=0
wait_for_message ns2/named.run "zone_shutdown: zone this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example/IN: shutting down" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "waiting for slave to sync up ($n)"
ret=0
wait_for_message ns2/named.run "zone_shutdown: zone ${special}/IN: shutting down" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "checking that this.is.a.very.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. is not served by slave ($n)"
ret=0
wait_for_no_soa @10.53.0.2 this.is.aery.very.long.long.long.domain.that.will.cause.catalog.zones.to.generate.hash.instead.of.using.regular.filename.dom10.example. dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "checking that ${special}. is not served by slave ($n)"
ret=0
wait_for_no_soa @10.53.0.2 "${special}." dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "checking that zone-directory is emptied ($n)"
ret=0
wait_for_no_zonefile "ns2/zonedir/__catz__4d70696f2335687069467f11f5d5378c480383f97782e553fb2d04a7bb2a23ed.db" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "checking that zone-directory is emptied ($n)"
ret=0
wait_for_no_zonefile "ns2/zonedir/$db" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
done
##########################################################################
echo_i "Testing adding a domain and a subdomain of it"
@@ -954,8 +985,6 @@ wait_for_soa @10.53.0.2 subdomain.of.dom11.example. dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "removing domain dom11.example. from catalog1 zone ($n)"
ret=0
@@ -1013,7 +1042,6 @@ wait_for_no_soa @10.53.0.2 subdomain.of.d11.example. dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
##########################################################################
echo_i "Testing adding a catalog zone at runtime with rndc reconfig"
n=$((n+1))

View File

@@ -95,6 +95,8 @@ else
COLOR_WARN=''
fi
SYSTESTDIR="`basename $PWD`"
if type printf > /dev/null 2>&1
then
echofail () {
@@ -115,6 +117,23 @@ then
echoend () {
printf "${COLOR_END}%s${COLOR_NONE}\n" "$*"
}
echo_i() {
printf '%s\n' "$*" | while read -r __LINE ; do
echoinfo "I:$SYSTESTDIR:$__LINE"
done
}
echo_ic() {
printf '%s\n' "$*" | while read -r __LINE ; do
echoinfo "I:$SYSTESTDIR: $__LINE"
done
}
echo_d() {
printf '%s\n' "$*" | while read -r __LINE ; do
echoinfo "D:$SYSTESTDIR:$__LINE"
done
}
else
echofail () {
echo "$*"
@@ -134,36 +153,34 @@ else
echoend () {
echo "$*"
}
echo_i() {
echo "$@" | while read -r __LINE ; do
echoinfo "I:$SYSTESTDIR:$__LINE"
done
}
echo_ic() {
echo "$@" | while read -r __LINE ; do
echoinfo "I:$SYSTESTDIR: $__LINE"
done
}
echo_d() {
echo "$@" | while read -r __LINE ; do
echoinfo "D:$SYSTESTDIR:$__LINE"
done
}
fi
SYSTESTDIR="`basename $PWD`"
echo_i() {
echo "$@" | while read __LINE ; do
echoinfo "I:$SYSTESTDIR:$__LINE"
done
}
echo_ic() {
echo "$@" | while read __LINE ; do
echoinfo "I:$SYSTESTDIR: $__LINE"
done
}
cat_i() {
while read __LINE ; do
while read -r __LINE ; do
echoinfo "I:$SYSTESTDIR:$__LINE"
done
}
echo_d() {
echo "$@" | while read __LINE ; do
echoinfo "D:$SYSTESTDIR:$__LINE"
done
}
cat_d() {
while read __LINE ; do
while read -r __LINE ; do
echoinfo "D:$SYSTESTDIR:$__LINE"
done
}

View File

@@ -0,0 +1,30 @@
#!/usr/bin/python
# 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.
# catzhash.py: generate the SHA-1 hash of a domain name in wire format.
#
# This can be used to determine the label to use in a catalog zone to
# represent the specified zone. For example, the zone
# "domain.example" can be represented in a catalog zone called
# "catalog.example" by adding the following record:
#
# 5960775ba382e7a4e09263fc06e7c00569b6a05c.zones.catalog.example. IN PTR domain.example.
#
# The label "5960775ba382e7a4e09263fc06e7c00569b6a05c" is the output of
# this script when run with the argument "domain.example".
import sys
import dns.name
import hashlib
if len(sys.argv) < 2:
print("Usage: %s name" % sys.argv[0])
print (hashlib.sha1(dns.name.from_text(sys.argv[1]).to_wire()).hexdigest())

View File

@@ -522,8 +522,8 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
result = addzone(entry, target, target->catzs->view,
target->catzs->taskmgr,
target->catzs->zmm->udata);
target->catzs->taskmgr,
target->catzs->zmm->udata);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
"catz: adding zone '%s' from catalog "
@@ -1455,6 +1455,7 @@ dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
isc_region_t r;
isc_result_t result;
size_t rlen;
bool special = false;
REQUIRE(DNS_CATZ_ZONE_VALID(zone));
REQUIRE(entry != NULL);
@@ -1467,24 +1468,39 @@ dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
isc_buffer_putstr(tbuf, zone->catzs->view->name);
isc_buffer_putstr(tbuf, "_");
result = dns_name_totext(&zone->name, true, tbuf);
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
isc_buffer_putstr(tbuf, "_");
result = dns_name_totext(&entry->name, true, tbuf);
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* Search for slash and other special characters in the view and
* zone names. Add a null terminator so we can use strpbrk(), then
* remove it.
*/
isc_buffer_putuint8(tbuf, 0);
if (strpbrk(isc_buffer_base(tbuf), "\\/:") != NULL) {
special = true;
}
isc_buffer_subtract(tbuf, 1);
/* __catz__<digest>.db */
rlen = (isc_md_type_get_size(ISC_MD_SHA256) * 2 + 1) + 12;
/* optionally prepend with <zonedir>/ */
if (entry->opts.zonedir != NULL)
if (entry->opts.zonedir != NULL) {
rlen += strlen(entry->opts.zonedir) + 1;
}
result = isc_buffer_reserve(buffer, (unsigned int)rlen);
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
if (entry->opts.zonedir != NULL) {
isc_buffer_putstr(*buffer, entry->opts.zonedir);
@@ -1493,9 +1509,10 @@ dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
isc_buffer_usedregion(tbuf, &r);
isc_buffer_putstr(*buffer, "__catz__");
if (tbuf->used > ISC_SHA256_DIGESTLENGTH * 2 + 1) {
if (special || tbuf->used > ISC_SHA256_DIGESTLENGTH * 2 + 1) {
unsigned char digest[ISC_MAX_MD_SIZE];
unsigned int digestlen;
/* we can do that because digest string < 2 * DNS_NAME */
result = isc_md(ISC_MD_SHA256, r.base, r.length,
digest, &digestlen);
@@ -1526,7 +1543,7 @@ dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
{
/*
* We have to generate a text buffer with regular zone config:
* zone foo.bar {
* zone "foo.bar" {
* type slave;
* masters [ dscp X ] { ip1 port port1; ip2 port port2; };
* }
@@ -1550,9 +1567,9 @@ dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
isc_buffer_allocate(zone->catzs->mctx, &buffer, ISC_BUFFER_INCR);
isc_buffer_setautorealloc(buffer, true);
isc_buffer_putstr(buffer, "zone ");
isc_buffer_putstr(buffer, "zone \"");
dns_name_totext(&entry->name, true, buffer);
isc_buffer_putstr(buffer, " { type slave; masters");
isc_buffer_putstr(buffer, "\" { type slave; masters");
/*
* DSCP value has no default, but when it is specified, it is identical
@@ -1560,7 +1577,8 @@ dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
* use the DSCP value set for the first master
*/
if (entry->opts.masters.count > 0 &&
entry->opts.masters.dscps[0] >= 0) {
entry->opts.masters.dscps[0] >= 0)
{
isc_buffer_putstr(buffer, " dscp ");
snprintf(pbuf, sizeof(pbuf), "%hd",
entry->opts.masters.dscps[0]);
@@ -1602,8 +1620,9 @@ dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
isc_buffer_putstr(buffer, " key ");
result = dns_name_totext(entry->opts.masters.keys[i],
true, buffer);
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
}
isc_buffer_putstr(buffer, "; ");
}
@@ -1611,8 +1630,9 @@ dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
if (entry->opts.in_memory == false) {
isc_buffer_putstr(buffer, "file \"");
result = dns_catz_generate_masterfilename(zone, entry, &buffer);
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
isc_buffer_putstr(buffer, "\"; ");
}
@@ -1631,11 +1651,13 @@ dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
isc_buffer_putstr(buffer, "};");
*buf = buffer;
return (ISC_R_SUCCESS);
cleanup:
if (buffer != NULL)
if (buffer != NULL) {
isc_buffer_free(&buffer);
}
return (result);
}

View File

@@ -323,13 +323,15 @@ dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
/* Methods provided by named to dynamically modify the member zones */
/* xxxwpk TODO config! */
typedef isc_result_t (*dns_catz_zoneop_fn_t)(dns_catz_entry_t *entry,
dns_catz_zone_t *origin, dns_view_t *view,
isc_taskmgr_t *taskmgr, void *udata);
dns_catz_zone_t *origin,
dns_view_t *view,
isc_taskmgr_t *taskmgr,
void *udata);
struct dns_catz_zonemodmethods {
dns_catz_zoneop_fn_t addzone;
dns_catz_zoneop_fn_t modzone;
dns_catz_zoneop_fn_t delzone;
void * udata;
void *udata;
};

View File

@@ -1383,6 +1383,7 @@
./contrib/kasp/kasp.xml X 2016,2018,2019,2020
./contrib/kasp/kasp2policy.py X 2016,2018,2019,2020
./contrib/kasp/policy.good X 2016,2018,2019,2020
./contrib/scripts/catzhash.py X 2020
./contrib/scripts/check-secure-delegation.pl.in PERL 2010,2012,2014,2016,2018,2019,2020
./contrib/scripts/check5011.pl X 2013,2014,2017,2018,2019,2020
./contrib/scripts/dnssec-keyset.sh X 2015,2018,2019,2020