python/tests: add python test for cracknames
authorBob Campbell <bobcampbell@catalyst.net.nz>
Tue, 4 Jul 2017 23:08:45 +0000 (11:08 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 24 Jul 2017 05:14:10 +0000 (07:14 +0200)
This fails due the bug, which causes the related test in
drsuapi_cracknames.c to flap. It also fails due to us not yet supporting
DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL or
DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL.

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

Signed-off-by: Bob Campbell <bobcampbell@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
selftest/knownfail
source4/selftest/tests.py
source4/torture/drs/python/cracknames.py [new file with mode: 0644]

index f41b99d0e39b320d83c5a43abaf99d98572bb9b3..ae0086895f2330b22a0a172f1340f139607db931 100644 (file)
 #ntvfs server blocks copychunk with execute access on read handle
 ^samba4.smb2.ioctl.copy_chunk_bad_access
 ^samba4.drs.getnc_exop.python.*getnc_exop.DrsReplicaPrefixMapTestCase.test_regular_prefix_map_ex_attid.*
+^samba4.drs.cracknames.python.*cracknames.DrsCracknamesTestCase.test_Cracknames.*
 # We don't support NDR64 yet, so we generate the wrong FAULT code
 ^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_no_auth_presentation_ctx_invalid4
 ^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_spnego_change_auth_type2
index 15037a2060899664ebd578cb251bdcedf8863f59..44c0b08df912be81aec3582a8d892f1c03b35a7a 100755 (executable)
@@ -839,6 +839,11 @@ for env in ['ad_dc_ntvfs']:
     planoldpythontestsuite(env, "samba.tests.join",
                            name="samba.tests.join.python(%s)" % env,
                            extra_args=['-U$DOMAIN/$DC_USERNAME%$DC_PASSWORD'])
+    planoldpythontestsuite(env, "cracknames",
+                           extra_path=[os.path.join(samba4srcdir, 'torture/drs/python')],
+                           name="samba4.drs.cracknames.python(%s)" % env,
+                           environ={'DC1': "$DC_SERVER", 'DC2': '$DC_SERVER'},
+                           extra_args=['-U$DOMAIN/$DC_USERNAME%$DC_PASSWORD'])
 
 planoldpythontestsuite("chgdcpass:local", "samba.tests.blackbox.samba_dnsupdate",
                        environ={'DNS_SERVER_IP': '$SERVER_IP'})
diff --git a/source4/torture/drs/python/cracknames.py b/source4/torture/drs/python/cracknames.py
new file mode 100644 (file)
index 0000000..d8c8ae5
--- /dev/null
@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) Catalyst .Net Ltd 2017
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import samba.tests
+import ldb
+import drs_base
+
+from samba.dcerpc import drsuapi
+
+class DrsCracknamesTestCase(drs_base.DrsBaseTestCase):
+    def setUp(self):
+        super(DrsCracknamesTestCase, self).setUp()
+        (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1)
+
+        self.ou = "ou=Cracknames_ou,%s" % self.ldb_dc1.get_default_basedn()
+        self.username = "Cracknames_user"
+        self.user = "cn=%s,%s" % (self.username, self.ou)
+
+        self.ldb_dc1.add({
+            "dn": self.ou,
+            "objectclass": "organizationalUnit"})
+
+        self.user_record = {
+            "dn": self.user,
+            "objectclass": "user",
+            "sAMAccountName" : self.username,
+            "userPrincipalName" : "test@test.com",
+            "servicePrincipalName" : "test/%s" % self.ldb_dc1.get_default_basedn(),
+            "displayName" : "test"}
+
+        self.ldb_dc1.add(self.user_record)
+        self.ldb_dc1.delete(self.user_record["dn"])
+        self.ldb_dc1.add(self.user_record)
+
+        # The formats specified in MS-DRSR 4.1.4.13; DS_NAME_FORMAT
+        # We don't support any of the ones specified in 4.1.4.1.2.
+        self.formats = {
+            drsuapi.DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+            drsuapi.DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+            drsuapi.DRSUAPI_DS_NAME_FORMAT_DISPLAY,
+            drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID,
+            drsuapi.DRSUAPI_DS_NAME_FORMAT_CANONICAL,
+            drsuapi.DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+            drsuapi.DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
+            drsuapi.DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+            # We currently don't support this
+            #drsuapi.DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+            # This format is not supported by Windows (or us)
+            #drsuapi.DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN,
+        }
+
+    def tearDown(self):
+        self.ldb_dc1.delete(self.user)
+        self.ldb_dc1.delete(self.ou)
+        super(DrsCracknamesTestCase, self).tearDown()
+
+    def test_Cracknames(self):
+        """
+        Verifies that we can cracknames any of the standard formats
+        (DS_NAME_FORMAT) to a GUID, and that we can cracknames a
+        GUID to any of the standard formats.
+
+        GUID was chosen just so that we don't have to do an n^2 loop.
+        """
+        (result, ctr) = self._do_cracknames(self.user,
+                                            drsuapi.DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+                                            drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID)
+
+        self.assertEquals(ctr.count, 1)
+        self.assertEquals(ctr.array[0].status,
+                          drsuapi.DRSUAPI_DS_NAME_STATUS_OK)
+
+        user_guid = ctr.array[0].result_name
+
+        for name_format in self.formats:
+            (result, ctr) = self._do_cracknames(user_guid,
+                                                drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID,
+                                                name_format)
+
+            self.assertEquals(ctr.count, 1)
+            self.assertEquals(ctr.array[0].status,
+                              drsuapi.DRSUAPI_DS_NAME_STATUS_OK,
+                              "Expected 0, got %s, desired format is %s"
+                              % (ctr.array[0].status, name_format))
+
+            (result, ctr) = self._do_cracknames(ctr.array[0].result_name,
+                                                name_format,
+                                                drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID)
+
+            self.assertEquals(ctr.count, 1)
+            self.assertEquals(ctr.array[0].status,
+                              drsuapi.DRSUAPI_DS_NAME_STATUS_OK,
+                              "Expected 0, got %s, offered format is %s"
+                              % (ctr.array[0].status, name_format))
+
+    def test_MultiValuedAttribute(self):
+        """
+        Verifies that, if we try and cracknames with the desired output
+        being a multi-valued attribute, it returns
+        DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE.
+        """
+        username = "Cracknames_user_MVA"
+        user = "cn=%s,%s" % (username, self.ou)
+
+        user_record = {
+            "dn": user,
+            "objectclass": "user",
+            "sAMAccountName" : username,
+            "userPrincipalName" : "test2@test.com",
+            "servicePrincipalName" : ["test2/%s" % self.ldb_dc1.get_default_basedn(),
+                                      "test3/%s" % self.ldb_dc1.get_default_basedn()],
+            "displayName" : "test2"}
+
+        self.ldb_dc1.add(user_record)
+
+        (result, ctr) = self._do_cracknames(user,
+                                            drsuapi.DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+                                            drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID)
+
+        self.assertEquals(ctr.count, 1)
+        self.assertEquals(ctr.array[0].status,
+                          drsuapi.DRSUAPI_DS_NAME_STATUS_OK)
+
+        user_guid = ctr.array[0].result_name
+
+        (result, ctr) = self._do_cracknames(user_guid,
+                                            drsuapi.DRSUAPI_DS_NAME_FORMAT_GUID,
+                                            drsuapi.DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL)
+
+        self.assertEquals(ctr.count, 1)
+        self.assertEquals(ctr.array[0].status,
+                          drsuapi.DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE)
+
+        self.ldb_dc1.delete(user)
+
+    def _do_cracknames(self, name, format_offered, format_desired):
+        req = drsuapi.DsNameRequest1()
+        names = drsuapi.DsNameString()
+        names.str = name
+
+        req.codepage = 1252 # German, but it doesn't really matter here
+        req.language = 1033
+        req.format_flags = 0
+        req.format_offered = format_offered
+        req.format_desired = format_desired
+        req.count = 1
+        req.names = [names]
+
+        (result, ctr) = self.drs.DsCrackNames(self.drs_handle, 1, req)
+        return (result, ctr)