]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Correctly handle catalog zone entries containing slashes
authorEvan Hunt <each@isc.org>
Thu, 30 Jan 2020 23:30:32 +0000 (15:30 -0800)
committerEvan Hunt <each@isc.org>
Tue, 4 Feb 2020 00:08:20 +0000 (16:08 -0800)
- Add quotes before and after zone name when generating "addzone"
  input so avoid "unexpected token" errors.
- Use a hex digest for zone filenames when the zone or view name
  contains a slash.
- Test with a domain name containing a slash.
- Incidentally added 'catzhash.py' to contrib/scripts to generate
  hash labels for catalog zones, as it was needed to write the test.

bin/tests/system/catz/tests.sh
contrib/scripts/catzhash.py [new file with mode: 0644]
lib/dns/catz.c
lib/dns/include/dns/catz.h
util/copyrights

index 0d71b6b1d8f43444595b2139b64338108023bf35..7f8de47f6c39e2e2fe5c26041cbfa70753de29fc 100644 (file)
@@ -763,7 +763,7 @@ 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
+wait_for_no_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))
 
@@ -839,7 +839,7 @@ 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
+wait_for_no_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))
 
@@ -850,6 +850,99 @@ wait_for_no_zonefile "ns2/zonedir/__catz__4d70696f2335687069467f11f5d5378c480383
 if [ $ret -ne 0 ]; then echo_i "failed"; fi
 status=$((status+ret))
 
+##########################################################################
+echo_i "Testing domain with special characters in catalog"
+n=$((n+1))
+echo_i "checking that
+this.zone/domain.has.a.slash.dom10.example. is not served by master ($n)"
+ret=0
+wait_for_no_soa @10.53.0.1 this.zone/domain.has.a.slash.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 "Adding a domain this.zone/domain.has.a.slash.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.zone/domain.has.a.slash.dom10.example."' '{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 "checking that this.zone/domain.has.a.slash.dom10.example. is now served by master ($n)"
+ret=0
+wait_for_soa @10.53.0.1 this.zone/domain.has.a.slash.dom10.example. 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 this.zone/domain.has.a.slash.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 e64cc64c99bf52d0a77fb16dd7ed57cf925a36aa.zones.catalog1.example 3600 IN PTR this.zone/domain.has.a.slash.dom10.example.
+    send
+END
+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.zone/domain.has.a.slash.dom10.example' from catalog 'catalog1.example'" &&
+wait_for_message ns2/named.run  "transfer of 'this.zone/domain.has.a.slash.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 "checking that this.zone/domain.has.a.slash.dom10.example. is served by slave ($n)"
+ret=0
+wait_for_soa @10.53.0.2 this.zone/domain.has.a.slash.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 zone-directory is populated with a hashed filename ($n)"
+ret=0
+wait_for_zonefile "ns2/zonedir/__catz__46ba3e1b28d5955e5313d5fee61bedc78c71d08035aa7ea2f7bf0b8228ab3acc.db" || ret=1
+if [ $ret -ne 0 ]; then echo_i "failed"; fi
+status=$((status+ret))
+
+n=$((n+1))
+echo_i "removing domain this.zone/domain.has.a.slash.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 e64cc64c99bf52d0a77fb16dd7ed57cf925a36aa.zones.catalog1.example
+   send
+END
+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.zone/domain.has.a.slash.dom10.example/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.zone/domain.has.a.slash.dom10.example. is not served by slave ($n)"
+ret=0
+wait_for_no_soa @10.53.0.2 this.zone/domain.has.a.slash.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 zone-directory is emptied ($n)"
+ret=0
+wait_for_no_zonefile "ns2/zonedir/__catz__46ba3e1b28d5955e5313d5fee61bedc78c71d08035aa7ea2f7bf0b8228ab3acc.db" || ret=1
+if [ $ret -ne 0 ]; then echo_i "failed"; fi
+status=$((status+ret))
+
 ##########################################################################
 echo_i "Testing adding a domain and a subdomain of it"
 n=$((n+1))
@@ -954,8 +1047,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 +1104,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))
diff --git a/contrib/scripts/catzhash.py b/contrib/scripts/catzhash.py
new file mode 100644 (file)
index 0000000..4b59be4
--- /dev/null
@@ -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())
index 6e245505148b4142b798da9ef166305d31ee29c9..8c3b9fde6978d1169485fe770b196a143c622819 100644 (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);
 }
 
index cf466d25dac43db2ec9df0b2ec881edd9eb2eba3..197fcf7d55390ed57cddfb7d1f9aecfecdb312c7 100644 (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;
 };
 
 
index 40ab641f557977d7bbe09d3964e37f93ae50dfc7..bc7ffbd33f9f58e3b3c50651f90d226814b90516 100644 (file)
 ./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