1 # Tests for the samba-tool user sub command reading Primary:WDigest
3 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 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/>.
22 from samba
.tests
.samba_tool
.base
import SambaToolCmdTest
23 from hashlib
import md5
26 USER_NAME
= "WdigestTestUser"
28 # Calculate the MD5 password digest from the supplied user, realm and password
32 def calc_digest(user
, realm
, password
):
33 data
= "%s:%s:%s" % (user
, realm
, password
)
34 if isinstance(data
, str):
35 data
= data
.encode('utf8')
37 return "%s:%s:%s" % (user
, realm
, md5(data
).hexdigest())
40 class UserCmdWdigestTestCase(SambaToolCmdTest
):
41 """Tests for samba-tool user subcommands extraction of the wdigest values
42 Test results validated against Windows Server 2012 R2.
43 NOTE: That as at 22-05-2017 the values Documented at
44 3.1.1.8.11.3.1 WDIGEST_CREDENTIALS Construction
52 self
.lp
= samba
.tests
.env_loadparm()
53 self
.samdb
= self
.getSamDB(
54 "-H", "ldap://%s" % os
.environ
["DC_SERVER"],
55 "-U%s%%%s" % (os
.environ
["DC_USERNAME"],
56 os
.environ
["DC_PASSWORD"]))
57 self
.dns_domain
= self
.samdb
.domain_dns_name()
58 res
= self
.samdb
.search(
59 base
=self
.samdb
.get_config_basedn(),
60 expression
="ncName=%s" % self
.samdb
.get_default_basedn(),
61 attrs
=["nETBIOSName"])
62 self
.netbios_domain
= str(res
[0]["nETBIOSName"][0])
63 self
.password
= self
.random_password()
64 result
, out
, err
= self
.runsubcmd("user",
68 self
.assertCmdSuccess(result
,
71 "Ensure user is created")
75 result
, out
, err
= self
.runsubcmd("user", "delete", USER_NAME
)
76 self
.assertCmdSuccess(result
,
79 "Ensure user is deleted")
81 def _testWDigest(self
, attribute
, expected
, missing
=False):
83 (result
, out
, err
) = self
.runsubcmd("user",
88 self
.assertCmdSuccess(result
,
91 "Ensure getpassword runs")
92 self
.assertEqual(err
, "Any available password returned OK\n", "getpassword")
95 self
.assertTrue(attribute
not in out
)
97 self
.assertMatch(out
.replace('\n ', ''),
98 "%s: %s" % (attribute
, expected
))
100 def test_Wdigest_no_suffix(self
):
101 attribute
= "virtualWDigest"
102 self
._testWDigest
(attribute
, None, True)
104 def test_Wdigest_non_numeric_suffix(self
):
105 attribute
= "virtualWDigestss"
106 self
._testWDigest
(attribute
, None, True)
108 def test_Wdigest00(self
):
109 attribute
= "virtualWDigest00"
110 self
._testWDigest
(attribute
, None, True)
112 # Hash01 MD5(sAMAccountName,
116 def test_Wdigest01(self
):
117 attribute
= "virtualWDigest01"
118 expected
= calc_digest(USER_NAME
,
121 self
._testWDigest
(attribute
, expected
)
123 # Hash02 MD5(LOWER(sAMAccountName),
124 # LOWER(NETBIOSDomainName),
127 def test_Wdigest02(self
):
128 attribute
= "virtualWDigest02"
129 expected
= calc_digest(USER_NAME
.lower(),
130 self
.netbios_domain
.lower(),
132 self
._testWDigest
(attribute
, expected
)
134 # Hash03 MD5(UPPER(sAMAccountName),
135 # UPPER(NETBIOSDomainName),
138 def test_Wdigest03(self
):
139 attribute
= "virtualWDigest03"
140 expected
= calc_digest(USER_NAME
.upper(),
141 self
.netbios_domain
.upper(),
143 self
._testWDigest
(attribute
, expected
)
145 # Hash04 MD5(sAMAccountName,
146 # UPPER(NETBIOSDomainName),
149 def test_Wdigest04(self
):
150 attribute
= "virtualWDigest04"
151 expected
= calc_digest(USER_NAME
,
152 self
.netbios_domain
.upper(),
154 self
._testWDigest
(attribute
, expected
)
156 # Hash05 MD5(sAMAccountName,
157 # LOWER(NETBIOSDomainName),
160 def test_Wdigest05(self
):
161 attribute
= "virtualWDigest05"
162 expected
= calc_digest(USER_NAME
,
163 self
.netbios_domain
.lower(),
165 self
._testWDigest
(attribute
, expected
)
167 # Hash06 MD5(UPPER(sAMAccountName),
168 # LOWER(NETBIOSDomainName),
171 def test_Wdigest06(self
):
172 attribute
= "virtualWDigest06"
173 expected
= calc_digest(USER_NAME
.upper(),
174 self
.netbios_domain
.lower(),
176 self
._testWDigest
(attribute
, expected
)
178 # Hash07 MD5(LOWER(sAMAccountName),
179 # UPPER(NETBIOSDomainName),
182 def test_Wdigest07(self
):
183 attribute
= "virtualWDigest07"
184 expected
= calc_digest(USER_NAME
.lower(),
185 self
.netbios_domain
.upper(),
187 self
._testWDigest
(attribute
, expected
)
189 # Hash08 MD5(sAMAccountName,
193 # Note: Samba lowercases the DNSDomainName at provision time,
194 # Windows preserves the case. This means that the WDigest08 values
195 # calculated byt Samba and Windows differ.
197 def test_Wdigest08(self
):
198 attribute
= "virtualWDigest08"
199 expected
= calc_digest(USER_NAME
,
202 self
._testWDigest
(attribute
, expected
)
204 # Hash09 MD5(LOWER(sAMAccountName),
205 # LOWER(DNSDomainName),
208 def test_Wdigest09(self
):
209 attribute
= "virtualWDigest09"
210 expected
= calc_digest(USER_NAME
.lower(),
211 self
.dns_domain
.lower(),
213 self
._testWDigest
(attribute
, expected
)
215 # Hash10 MD5(UPPER(sAMAccountName),
216 # UPPER(DNSDomainName),
219 def test_Wdigest10(self
):
220 attribute
= "virtualWDigest10"
221 expected
= calc_digest(USER_NAME
.upper(),
222 self
.dns_domain
.upper(),
224 self
._testWDigest
(attribute
, expected
)
226 # Hash11 MD5(sAMAccountName,
227 # UPPER(DNSDomainName),
230 def test_Wdigest11(self
):
231 attribute
= "virtualWDigest11"
232 expected
= calc_digest(USER_NAME
,
233 self
.dns_domain
.upper(),
235 self
._testWDigest
(attribute
, expected
)
237 # Hash12 MD5(sAMAccountName,
238 # LOWER(DNSDomainName),
241 def test_Wdigest12(self
):
242 attribute
= "virtualWDigest12"
243 expected
= calc_digest(USER_NAME
,
244 self
.dns_domain
.lower(),
246 self
._testWDigest
(attribute
, expected
)
248 # Hash13 MD5(UPPER(sAMAccountName),
249 # LOWER(DNSDomainName),
252 def test_Wdigest13(self
):
253 attribute
= "virtualWDigest13"
254 expected
= calc_digest(USER_NAME
.upper(),
255 self
.dns_domain
.lower(),
257 self
._testWDigest
(attribute
, expected
)
259 # Hash14 MD5(LOWER(sAMAccountName),
260 # UPPER(DNSDomainName),
264 def test_Wdigest14(self
):
265 attribute
= "virtualWDigest14"
266 expected
= calc_digest(USER_NAME
.lower(),
267 self
.dns_domain
.upper(),
269 self
._testWDigest
(attribute
, expected
)
271 # Hash15 MD5(userPrincipalName,
274 def test_Wdigest15(self
):
275 attribute
= "virtualWDigest15"
276 name
= "%s@%s" % (USER_NAME
, self
.dns_domain
)
277 expected
= calc_digest(name
,
280 self
._testWDigest
(attribute
, expected
)
282 # Hash16 MD5(LOWER(userPrincipalName),
285 def test_Wdigest16(self
):
286 attribute
= "virtualWDigest16"
287 name
= "%s@%s" % (USER_NAME
.lower(), self
.dns_domain
.lower())
288 expected
= calc_digest(name
,
291 self
._testWDigest
(attribute
, expected
)
293 # Hash17 MD5(UPPER(userPrincipalName),
296 def test_Wdigest17(self
):
297 attribute
= "virtualWDigest17"
298 name
= "%s@%s" % (USER_NAME
.upper(), self
.dns_domain
.upper())
299 expected
= calc_digest(name
,
302 self
._testWDigest
(attribute
, expected
)
304 # Hash18 MD5(NETBIOSDomainName\sAMAccountName,
307 def test_Wdigest18(self
):
308 attribute
= "virtualWDigest18"
309 name
= "%s\\%s" % (self
.netbios_domain
, USER_NAME
)
310 expected
= calc_digest(name
,
313 self
._testWDigest
(attribute
, expected
)
315 # Hash19 MD5(LOWER(NETBIOSDomainName\sAMAccountName),
318 def test_Wdigest19(self
):
319 attribute
= "virtualWDigest19"
320 name
= "%s\\%s" % (self
.netbios_domain
, USER_NAME
)
321 expected
= calc_digest(name
.lower(),
324 self
._testWDigest
(attribute
, expected
)
326 # Hash20 MD5(UPPER(NETBIOSDomainName\sAMAccountName),
329 def test_Wdigest20(self
):
330 attribute
= "virtualWDigest20"
331 name
= "%s\\%s" % (self
.netbios_domain
, USER_NAME
)
332 expected
= calc_digest(name
.upper(),
335 self
._testWDigest
(attribute
, expected
)
337 # Hash21 MD5(sAMAccountName,
341 def test_Wdigest21(self
):
342 attribute
= "virtualWDigest21"
343 expected
= calc_digest(USER_NAME
,
346 self
._testWDigest
(attribute
, expected
)
348 # Hash22 MD5(LOWER(sAMAccountName),
352 def test_Wdigest22(self
):
353 attribute
= "virtualWDigest22"
354 expected
= calc_digest(USER_NAME
.lower(),
357 self
._testWDigest
(attribute
, expected
)
359 # Hash23 MD5(UPPER(sAMAccountName),
363 def test_Wdigest23(self
):
364 attribute
= "virtualWDigest23"
365 expected
= calc_digest(USER_NAME
.upper(),
368 self
._testWDigest
(attribute
, expected
)
370 # Hash24 MD5(userPrincipalName),
374 def test_Wdigest24(self
):
375 attribute
= "virtualWDigest24"
376 name
= "%s@%s" % (USER_NAME
, self
.dns_domain
)
377 expected
= calc_digest(name
,
380 self
._testWDigest
(attribute
, expected
)
382 # Hash25 MD5(LOWER(userPrincipalName),
386 def test_Wdigest25(self
):
387 attribute
= "virtualWDigest25"
388 name
= "%s@%s" % (USER_NAME
, self
.dns_domain
.lower())
389 expected
= calc_digest(name
.lower(),
392 self
._testWDigest
(attribute
, expected
)
394 # Hash26 MD5(UPPER(userPrincipalName),
398 def test_Wdigest26(self
):
399 attribute
= "virtualWDigest26"
400 name
= "%s@%s" % (USER_NAME
, self
.dns_domain
.lower())
401 expected
= calc_digest(name
.upper(),
404 self
._testWDigest
(attribute
, expected
)
405 # Hash27 MD5(NETBIOSDomainName\sAMAccountName,
410 def test_Wdigest27(self
):
411 attribute
= "virtualWDigest27"
412 name
= "%s\\%s" % (self
.netbios_domain
, USER_NAME
)
413 expected
= calc_digest(name
,
416 self
._testWDigest
(attribute
, expected
)
418 # Hash28 MD5(LOWER(NETBIOSDomainName\sAMAccountName),
422 def test_Wdigest28(self
):
423 attribute
= "virtualWDigest28"
424 name
= "%s\\%s" % (self
.netbios_domain
.lower(), USER_NAME
.lower())
425 expected
= calc_digest(name
,
428 self
._testWDigest
(attribute
, expected
)
430 # Hash29 MD5(UPPER(NETBIOSDomainName\sAMAccountName),
434 def test_Wdigest29(self
):
435 attribute
= "virtualWDigest29"
436 name
= "%s\\%s" % (self
.netbios_domain
.upper(), USER_NAME
.upper())
437 expected
= calc_digest(name
,
440 self
._testWDigest
(attribute
, expected
)
442 def test_Wdigest30(self
):
443 attribute
= "virtualWDigest30"
444 self
._testWDigest
(attribute
, None, True)
446 # Check digest calculation against an known htdigest value
447 def test_calc_digest(self
):
448 htdigest
= "gary:fred:2204fcc247cb47ded249ef2fe0013255"
449 digest
= calc_digest("gary", "fred", "password")
450 self
.assertEqual(htdigest
, digest
)