2 # -*- coding: utf-8 -*-
4 # Copyright (C) Catalyst .Net Ltd 2017
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 from samba
.dcerpc
import drsuapi
27 class DrsCracknamesTestCase(drs_base
.DrsBaseTestCase
):
29 super(DrsCracknamesTestCase
, self
).setUp()
30 (self
.drs
, self
.drs_handle
) = self
._ds
_bind
(self
.dnsname_dc1
)
32 self
.ou
= "ou=Cracknames_ou,%s" % self
.ldb_dc1
.get_default_basedn()
33 self
.username
= "Cracknames_user"
34 self
.user
= "cn=%s,%s" % (self
.username
, self
.ou
)
38 "objectclass": "organizationalUnit"})
42 "objectclass": "user",
43 "sAMAccountName": self
.username
,
44 "userPrincipalName": "test@test.com",
45 "servicePrincipalName": "test/%s" % self
.ldb_dc1
.get_default_basedn(),
46 "displayName": "test"}
48 self
.ldb_dc1
.add(self
.user_record
)
49 self
.ldb_dc1
.delete(self
.user_record
["dn"])
50 self
.ldb_dc1
.add(self
.user_record
)
52 # The formats specified in MS-DRSR 4.1.4.13; DS_NAME_FORMAT
53 # We don't support any of the ones specified in 4.1.4.1.2.
55 drsuapi
.DRSUAPI_DS_NAME_FORMAT_FQDN_1779
,
56 drsuapi
.DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT
,
57 drsuapi
.DRSUAPI_DS_NAME_FORMAT_DISPLAY
,
58 drsuapi
.DRSUAPI_DS_NAME_FORMAT_GUID
,
59 drsuapi
.DRSUAPI_DS_NAME_FORMAT_CANONICAL
,
60 drsuapi
.DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL
,
61 drsuapi
.DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
,
62 drsuapi
.DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL
,
63 drsuapi
.DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY
,
64 # This format is not supported by Windows (or us)
65 # drsuapi.DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN,
69 self
.ldb_dc1
.delete(self
.user
)
70 self
.ldb_dc1
.delete(self
.ou
)
71 super(DrsCracknamesTestCase
, self
).tearDown()
73 def test_Cracknames(self
):
75 Verifies that we can cracknames any of the standard formats
76 (DS_NAME_FORMAT) to a GUID, and that we can cracknames a
77 GUID to any of the standard formats.
79 GUID was chosen just so that we don't have to do an n^2 loop.
81 (result
, ctr
) = self
._do
_cracknames
(self
.user
,
82 drsuapi
.DRSUAPI_DS_NAME_FORMAT_FQDN_1779
,
83 drsuapi
.DRSUAPI_DS_NAME_FORMAT_GUID
)
85 self
.assertEqual(ctr
.count
, 1)
86 self
.assertEqual(ctr
.array
[0].status
,
87 drsuapi
.DRSUAPI_DS_NAME_STATUS_OK
)
89 user_guid
= ctr
.array
[0].result_name
91 for name_format
in self
.formats
:
92 (result
, ctr
) = self
._do
_cracknames
(user_guid
,
93 drsuapi
.DRSUAPI_DS_NAME_FORMAT_GUID
,
96 self
.assertEqual(ctr
.count
, 1)
97 self
.assertEqual(ctr
.array
[0].status
,
98 drsuapi
.DRSUAPI_DS_NAME_STATUS_OK
,
99 "Expected 0, got %s, desired format is %s"
100 % (ctr
.array
[0].status
, name_format
))
102 (result
, ctr
) = self
._do
_cracknames
(ctr
.array
[0].result_name
,
104 drsuapi
.DRSUAPI_DS_NAME_FORMAT_GUID
)
106 self
.assertEqual(ctr
.count
, 1)
107 self
.assertEqual(ctr
.array
[0].status
,
108 drsuapi
.DRSUAPI_DS_NAME_STATUS_OK
,
109 "Expected 0, got %s, offered format is %s"
110 % (ctr
.array
[0].status
, name_format
))
112 def test_MultiValuedAttribute(self
):
114 Verifies that, if we try and cracknames with the desired output
115 being a multi-valued attribute, it returns
116 DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE.
118 username
= "Cracknames_user_MVA"
119 user
= "cn=%s,%s" % (username
, self
.ou
)
123 "objectclass": "user",
124 "sAMAccountName": username
,
125 "userPrincipalName": "test2@test.com",
126 "servicePrincipalName": ["test2/%s" % self
.ldb_dc1
.get_default_basedn(),
127 "test3/%s" % self
.ldb_dc1
.get_default_basedn()],
128 "displayName": "test2"}
130 self
.ldb_dc1
.add(user_record
)
132 (result
, ctr
) = self
._do
_cracknames
(user
,
133 drsuapi
.DRSUAPI_DS_NAME_FORMAT_FQDN_1779
,
134 drsuapi
.DRSUAPI_DS_NAME_FORMAT_GUID
)
136 self
.assertEqual(ctr
.count
, 1)
137 self
.assertEqual(ctr
.array
[0].status
,
138 drsuapi
.DRSUAPI_DS_NAME_STATUS_OK
)
140 user_guid
= ctr
.array
[0].result_name
142 (result
, ctr
) = self
._do
_cracknames
(user_guid
,
143 drsuapi
.DRSUAPI_DS_NAME_FORMAT_GUID
,
144 drsuapi
.DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL
)
146 self
.assertEqual(ctr
.count
, 1)
147 self
.assertEqual(ctr
.array
[0].status
,
148 drsuapi
.DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
)
150 self
.ldb_dc1
.delete(user
)
152 def test_NoSPNAttribute(self
):
154 Verifies that, if we try and cracknames with the desired output
155 being an SPN, it returns
156 DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE.
158 username
= "Cracknames_no_SPN"
159 user
= "cn=%s,%s" % (username
, self
.ou
)
163 "objectclass": "user",
164 "sAMAccountName" : username
,
165 "userPrincipalName" : "test4@test.com",
166 "displayName" : "test4"}
168 self
.ldb_dc1
.add(user_record
)
170 (result
, ctr
) = self
._do
_cracknames
(user
,
171 drsuapi
.DRSUAPI_DS_NAME_FORMAT_FQDN_1779
,
172 drsuapi
.DRSUAPI_DS_NAME_FORMAT_GUID
)
174 self
.assertEqual(ctr
.count
, 1)
175 self
.assertEqual(ctr
.array
[0].status
,
176 drsuapi
.DRSUAPI_DS_NAME_STATUS_OK
)
178 user_guid
= ctr
.array
[0].result_name
180 (result
, ctr
) = self
._do
_cracknames
(user_guid
,
181 drsuapi
.DRSUAPI_DS_NAME_FORMAT_GUID
,
182 drsuapi
.DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL
)
184 self
.assertEqual(ctr
.count
, 1)
185 self
.assertEqual(ctr
.array
[0].status
,
186 drsuapi
.DRSUAPI_DS_NAME_STATUS_NOT_FOUND
)
188 self
.ldb_dc1
.delete(user
)
190 def _do_cracknames(self
, name
, format_offered
, format_desired
):
191 req
= drsuapi
.DsNameRequest1()
192 names
= drsuapi
.DsNameString()
195 req
.codepage
= 1252 # German, but it doesn't really matter here
198 req
.format_offered
= format_offered
199 req
.format_desired
= format_desired
203 (result
, ctr
) = self
.drs
.DsCrackNames(self
.drs_handle
, 1, req
)