dns: prevent self-referencing CNAME
authorAaron Haslett <aaronhaslett@catalyst.net.nz>
Mon, 22 Oct 2018 22:52:07 +0000 (11:52 +1300)
committerKarolin Seeger <kseeger@samba.org>
Wed, 28 Nov 2018 07:22:23 +0000 (08:22 +0100)
Stops the user from adding a self-referencing CNAME over RPC, which is an easy
mistake to make with samba-tool.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13600

Signed-off-by: Aaron Haslett <aaronhaslett@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
python/samba/tests/dns.py
selftest/knownfail.d/dns
source4/rpc_server/dnsserver/dcerpc_dnsserver.c

index 12cfb86c254d79072002df507cb967d888d3b69d..56609769a4f6abccf05ac378fca7a85e84a92399 100644 (file)
@@ -1481,6 +1481,50 @@ class TestRPCRoundtrip(DNSTest):
     def tearDown(self):
         super(TestRPCRoundtrip, self).tearDown()
 
+    def rpc_update(self, fqn=None, data=None, wType=None, delete=False):
+        fqn = fqn or ("rpctestrec." + self.get_dns_domain())
+
+        rec = data_to_dns_record(wType, data)
+        add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
+        add_rec_buf.rec = rec
+
+        add_arg = add_rec_buf
+        del_arg = None
+        if delete:
+            add_arg = None
+            del_arg = add_rec_buf
+
+        self.rpc_conn.DnssrvUpdateRecord2(
+            dnsserver.DNS_CLIENT_VERSION_LONGHORN,
+            0,
+            self.server_ip,
+            self.get_dns_domain(),
+            fqn,
+            add_arg,
+            del_arg)
+
+    def test_rpc_self_referencing_cname(self):
+        cname = "cnametest2_unqual_rec_loop"
+        cname_fqn = "%s.%s" % (cname, self.get_dns_domain())
+
+        try:
+            self.rpc_update(fqn=cname, data=cname_fqn,
+                            wType=dnsp.DNS_TYPE_CNAME, delete=True)
+        except WERRORError as e:
+            if e.args[0] != werror.WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST:
+                self.fail("RPC DNS gaven wrong error on pre-test cleanup "
+                          "for self referencing CNAME: %s" % e.args[0])
+
+        try:
+            self.rpc_update(fqn=cname, wType=dnsp.DNS_TYPE_CNAME, data=cname_fqn)
+        except WERRORError as e:
+            if e.args[0] != werror.WERR_DNS_ERROR_CNAME_LOOP:
+                self.fail("RPC DNS gaven wrong error on insertion of "
+                          "self referencing CNAME: %s" % e.args[0])
+            return
+
+        self.fail("RPC DNS allowed insertion of self referencing CNAME")
+
     def test_update_add_txt_rpc_to_dns(self):
         prefix, txt = 'rpctextrec', ['"This is a test"']
 
index ca18b4334c1531300d3fad6dc950b7737dcfd6b3..0f6caba8916e3bb3d55088c0778263f1f513259d 100644 (file)
@@ -13,6 +13,7 @@ samba.tests.dns.__main__.TestRPCRoundtrip.test_update_add_null_char_txt_record\(
 samba.tests.dns.__main__.TestRPCRoundtrip.test_update_add_null_padded_txt_record\(rodc:local\)
 samba.tests.dns.__main__.TestRPCRoundtrip.test_update_add_slash_txt_record\(rodc:local\)
 samba.tests.dns.__main__.TestRPCRoundtrip.test_update_add_two_txt_records\(rodc:local\)
+samba.tests.dns.__main__.TestRPCRoundtrip.test_rpc_self_referencing_cname\(rodc:local\)
 samba.tests.dns.__main__.TestDNSUpdates.test_delete_record\(vampire_dc:local\)
 samba.tests.dns.__main__.TestDNSUpdates.test_readd_record\(vampire_dc:local\)
 samba.tests.dns.__main__.TestDNSUpdates.test_update_add_mx_record\(vampire_dc:local\)
index b42d7c549d10db8170eecb6eaaed9dc3bab1f239..edea2b33f2d2fdb0354f6aa6ee2574e467a08b41 100644 (file)
@@ -25,6 +25,7 @@
 #include "dsdb/samdb/samdb.h"
 #include "lib/util/dlinklist.h"
 #include "librpc/gen_ndr/ndr_dnsserver.h"
+#include "dns_server/dnsserver_common.h"
 #include "dnsserver.h"
 
 #define DCESRV_INTERFACE_DNSSERVER_BIND(call, iface) \
@@ -1868,6 +1869,37 @@ static WERROR dnsserver_enumerate_records(struct dnsserver_state *dsstate,
        return WERR_OK;
 }
 
+/*
+ * Check str1 + '.' + str2 = name, for example:
+ * ("dc0", "example.com", "dc0.example.com") = true
+ */
+static bool cname_self_reference(const char* node_name,
+                                const char* zone_name,
+                                struct DNS_RPC_NAME name) {
+       size_t node_len, zone_len;
+
+       if (node_name == NULL || zone_name == NULL) {
+               return false;
+       }
+
+       node_len = strlen(node_name);
+       zone_len = strlen(zone_name);
+
+       if (node_len == 0 ||
+           zone_len == 0 ||
+           (name.len != node_len + zone_len + 1)) {
+               return false;
+       }
+
+       if (strncmp(node_name, name.str, node_len) == 0 &&
+           name.str[node_len] == '.' &&
+           strncmp(zone_name, name.str + node_len + 1, zone_len) == 0) {
+               return true;
+       }
+
+       return false;
+}
+
 /* dnsserver update function */
 
 static WERROR dnsserver_update_record(struct dnsserver_state *dsstate,
@@ -1895,6 +1927,13 @@ static WERROR dnsserver_update_record(struct dnsserver_state *dsstate,
        }
        W_ERROR_HAVE_NO_MEMORY_AND_FREE(name, tmp_ctx);
 
+       /* CNAMEs can't point to themselves */
+       if (add_buf != NULL && add_buf->rec.wType == DNS_TYPE_CNAME) {
+               if (cname_self_reference(node_name, z->name, add_buf->rec.data.name)) {
+                       return WERR_DNS_ERROR_CNAME_LOOP;
+               }
+       }
+
        if (add_buf != NULL) {
                if (del_buf == NULL) {
                        /* Add record */