samba tool - tests: Fix shell metacharacters in generated password
[Samba.git] / python / samba / tests / samba_tool / user_wdigest.py
blobb531ad0a33c63e1a0e6809e5e146cd9af7c36744
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/>.
20 import os
21 import time
22 import base64
23 import ldb
24 import samba
25 from samba.tests.samba_tool.base import SambaToolCmdTest
26 from samba import (
27 credentials,
28 nttime2unix,
29 dsdb
31 from samba.ndr import ndr_unpack
32 from samba.dcerpc import drsblobs
33 import binascii
34 import md5
35 import re
36 import random
37 import string
39 USER_NAME = "WdigestTestUser"
40 # Create a random 32 character password, containing only letters and
41 # digits to avoid issues when used on the command line.
42 USER_PASS = ''.join(random.choice(string.ascii_uppercase +
43 string.ascii_lowercase +
44 string.digits) for _ in range(32))
46 # Calculate the MD5 password digest from the supplied user, realm and password
48 def calc_digest(user, realm, password):
49 data = "%s:%s:%s" % (user, realm, password)
50 return "%s:%s:%s" % (user, realm, binascii.hexlify(md5.new(data).digest()))
54 class UserCmdWdigestTestCase(SambaToolCmdTest):
55 """Tests for samba-tool user subcommands extraction of the wdigest values
56 Test results validated against Windows Server 2012 R2.
57 NOTE: That as at 22-05-2017 the values Documented at
58 3.1.1.8.11.3.1 WDIGEST_CREDENTIALS Construction
59 are incorrect.
60 """
61 users = []
62 samdb = None
64 def setUp(self):
65 super(UserCmdWdigestTestCase, self).setUp()
66 self.lp = samba.tests.env_loadparm()
67 self.samdb = self.getSamDB(
68 "-H", "ldap://%s" % os.environ["DC_SERVER"],
69 "-U%s%%%s" % (os.environ["DC_USERNAME"],
70 os.environ["DC_PASSWORD"]))
71 self.dns_domain = self.samdb.domain_dns_name()
72 res = self.samdb.search(
73 base=self.samdb.get_config_basedn(),
74 expression="ncName=%s" % self.samdb.get_default_basedn(),
75 attrs=["nETBIOSName"])
76 self.netbios_domain = res[0]["nETBIOSName"][0]
77 self.runsubcmd("user",
78 "create",
79 USER_NAME,
80 USER_PASS,
81 "-H",
82 "ldap://%s" % os.environ["DC_SERVER"],
83 "-U%s%%%s" % (
84 os.environ["DC_USERNAME"],
85 os.environ["DC_PASSWORD"]))
87 def tearDown(self):
88 super(UserCmdWdigestTestCase, self).tearDown()
89 self.runsubcmd("user", "delete", USER_NAME)
91 def _testWDigest(self, attribute, expected, missing=False):
93 (result, out, err) = self.runsubcmd("user",
94 "getpassword",
95 USER_NAME,
96 "--attributes",
97 attribute)
98 self.assertCmdSuccess(result,
99 out,
100 err,
101 "Ensure getpassword runs")
102 self.assertEqual(err, "", "getpassword")
103 self.assertMatch(out,
104 "Got password OK",
105 "getpassword out[%s]" % out)
107 if missing:
108 self.assertTrue(attribute not in out)
109 else:
110 result = re.sub(r"\n\s*", '', out)
111 self.assertMatch(result, "%s: %s" % (attribute, expected))
113 def test_Wdigest_no_suffix(self):
114 attribute = "virtualWDigest"
115 self._testWDigest(attribute, None, True)
117 def test_Wdigest_non_numeric_suffix(self):
118 attribute = "virtualWDigestss"
119 self._testWDigest(attribute, None, True)
121 def test_Wdigest00(self):
122 attribute = "virtualWDigest00"
123 self._testWDigest(attribute, None, True)
125 # Hash01 MD5(sAMAccountName,
126 # NETBIOSDomainName,
127 # password)
129 def test_Wdigest01(self):
130 attribute = "virtualWDigest01"
131 expected = calc_digest(USER_NAME,
132 self.netbios_domain,
133 USER_PASS)
134 self._testWDigest(attribute, expected)
136 # Hash02 MD5(LOWER(sAMAccountName),
137 # LOWER(NETBIOSDomainName),
138 # password)
140 def test_Wdigest02(self):
141 attribute = "virtualWDigest02"
142 expected = calc_digest(USER_NAME.lower(),
143 self.netbios_domain.lower(),
144 USER_PASS)
145 self._testWDigest(attribute, expected)
147 # Hash03 MD5(UPPER(sAMAccountName),
148 # UPPER(NETBIOSDomainName),
149 # password)
151 def test_Wdigest03(self):
152 attribute = "virtualWDigest03"
153 expected = calc_digest(USER_NAME.upper(),
154 self.netbios_domain.upper(),
155 USER_PASS)
156 self._testWDigest(attribute, expected)
158 # Hash04 MD5(sAMAccountName,
159 # UPPER(NETBIOSDomainName),
160 # password)
162 def test_Wdigest04(self):
163 attribute = "virtualWDigest04"
164 expected = calc_digest(USER_NAME,
165 self.netbios_domain.upper(),
166 USER_PASS)
167 self._testWDigest(attribute, expected)
169 # Hash05 MD5(sAMAccountName,
170 # LOWER(NETBIOSDomainName),
171 # password)
173 def test_Wdigest05(self):
174 attribute = "virtualWDigest05"
175 expected = calc_digest(USER_NAME,
176 self.netbios_domain.lower(),
177 USER_PASS)
178 self._testWDigest(attribute, expected)
180 # Hash06 MD5(UPPER(sAMAccountName),
181 # LOWER(NETBIOSDomainName),
182 # password)
184 def test_Wdigest06(self):
185 attribute = "virtualWDigest06"
186 expected = calc_digest(USER_NAME.upper(),
187 self.netbios_domain.lower(),
188 USER_PASS)
189 self._testWDigest(attribute, expected)
191 # Hash07 MD5(LOWER(sAMAccountName),
192 # UPPER(NETBIOSDomainName),
193 # password)
195 def test_Wdigest07(self):
196 attribute = "virtualWDigest07"
197 expected = calc_digest(USER_NAME.lower(),
198 self.netbios_domain.upper(),
199 USER_PASS)
200 self._testWDigest(attribute, expected)
202 # Hash08 MD5(sAMAccountName,
203 # DNSDomainName,
204 # password)
206 # Note: Samba lowercases the DNSDomainName at provision time,
207 # Windows preserves the case. This means that the WDigest08 values
208 # calculated byt Samba and Windows differ.
210 def test_Wdigest08(self):
211 attribute = "virtualWDigest08"
212 expected = calc_digest(USER_NAME,
213 self.dns_domain,
214 USER_PASS)
215 self._testWDigest(attribute, expected)
217 # Hash09 MD5(LOWER(sAMAccountName),
218 # LOWER(DNSDomainName),
219 # password)
221 def test_Wdigest09(self):
222 attribute = "virtualWDigest09"
223 expected = calc_digest(USER_NAME.lower(),
224 self.dns_domain.lower(),
225 USER_PASS)
226 self._testWDigest(attribute, expected)
228 # Hash10 MD5(UPPER(sAMAccountName),
229 # UPPER(DNSDomainName),
230 # password)
232 def test_Wdigest10(self):
233 attribute = "virtualWDigest10"
234 expected = calc_digest(USER_NAME.upper(),
235 self.dns_domain.upper(),
236 USER_PASS)
237 self._testWDigest(attribute, expected)
239 # Hash11 MD5(sAMAccountName,
240 # UPPER(DNSDomainName),
241 # password)
243 def test_Wdigest11(self):
244 attribute = "virtualWDigest11"
245 expected = calc_digest(USER_NAME,
246 self.dns_domain.upper(),
247 USER_PASS)
248 self._testWDigest(attribute, expected)
250 # Hash12 MD5(sAMAccountName,
251 # LOWER(DNSDomainName),
252 # password)
254 def test_Wdigest12(self):
255 attribute = "virtualWDigest12"
256 expected = calc_digest(USER_NAME,
257 self.dns_domain.lower(),
258 USER_PASS)
259 self._testWDigest(attribute, expected)
261 # Hash13 MD5(UPPER(sAMAccountName),
262 # LOWER(DNSDomainName),
263 # password)
265 def test_Wdigest13(self):
266 attribute = "virtualWDigest13"
267 expected = calc_digest(USER_NAME.upper(),
268 self.dns_domain.lower(),
269 USER_PASS)
270 self._testWDigest(attribute, expected)
273 # Hash14 MD5(LOWER(sAMAccountName),
274 # UPPER(DNSDomainName),
275 # password)
277 def test_Wdigest14(self):
278 attribute = "virtualWDigest14"
279 expected = calc_digest(USER_NAME.lower(),
280 self.dns_domain.upper(),
281 USER_PASS)
282 self._testWDigest(attribute, expected)
284 # Hash15 MD5(userPrincipalName,
285 # password)
287 def test_Wdigest15(self):
288 attribute = "virtualWDigest15"
289 name = "%s@%s" % (USER_NAME, self.dns_domain)
290 expected = calc_digest(name,
292 USER_PASS)
293 self._testWDigest(attribute, expected)
295 # Hash16 MD5(LOWER(userPrincipalName),
296 # password)
298 def test_Wdigest16(self):
299 attribute = "virtualWDigest16"
300 name = "%s@%s" % (USER_NAME.lower(), self.dns_domain.lower())
301 expected = calc_digest(name,
303 USER_PASS)
304 self._testWDigest(attribute, expected)
306 # Hash17 MD5(UPPER(userPrincipalName),
307 # password)
309 def test_Wdigest17(self):
310 attribute = "virtualWDigest17"
311 name = "%s@%s" % (USER_NAME.upper(), self.dns_domain.upper())
312 expected = calc_digest(name,
314 USER_PASS)
315 self._testWDigest(attribute, expected)
317 # Hash18 MD5(NETBIOSDomainName\sAMAccountName,
318 # password)
320 def test_Wdigest18(self):
321 attribute = "virtualWDigest18"
322 name = "%s\\%s" % (self.netbios_domain, USER_NAME)
323 expected = calc_digest(name,
325 USER_PASS)
326 self._testWDigest(attribute, expected)
328 # Hash19 MD5(LOWER(NETBIOSDomainName\sAMAccountName),
329 # password)
331 def test_Wdigest19(self):
332 attribute = "virtualWDigest19"
333 name = "%s\\%s" % (self.netbios_domain, USER_NAME)
334 expected = calc_digest(name.lower(),
336 USER_PASS)
337 self._testWDigest(attribute, expected)
339 # Hash20 MD5(UPPER(NETBIOSDomainName\sAMAccountName),
340 # password)
342 def test_Wdigest20(self):
343 attribute = "virtualWDigest20"
344 name = "%s\\%s" % (self.netbios_domain, USER_NAME)
345 expected = calc_digest(name.upper(),
347 USER_PASS)
348 self._testWDigest(attribute, expected)
350 # Hash21 MD5(sAMAccountName,
351 # "Digest",
352 # password)
354 def test_Wdigest21(self):
355 attribute = "virtualWDigest21"
356 expected = calc_digest(USER_NAME,
357 "Digest",
358 USER_PASS)
359 self._testWDigest(attribute, expected)
361 # Hash22 MD5(LOWER(sAMAccountName),
362 # "Digest",
363 # password)
365 def test_Wdigest22(self):
366 attribute = "virtualWDigest22"
367 expected = calc_digest(USER_NAME.lower(),
368 "Digest",
369 USER_PASS)
370 self._testWDigest(attribute, expected)
372 # Hash23 MD5(UPPER(sAMAccountName),
373 # "Digest",
374 # password)
376 def test_Wdigest23(self):
377 attribute = "virtualWDigest23"
378 expected = calc_digest(USER_NAME.upper(),
379 "Digest",
380 USER_PASS)
381 self._testWDigest(attribute, expected)
383 # Hash24 MD5(userPrincipalName),
384 # "Digest",
385 # password)
387 def test_Wdigest24(self):
388 attribute = "virtualWDigest24"
389 name = "%s@%s" % (USER_NAME, self.dns_domain)
390 expected = calc_digest(name,
391 "Digest",
392 USER_PASS)
393 self._testWDigest(attribute, expected)
395 # Hash25 MD5(LOWER(userPrincipalName),
396 # "Digest",
397 # password)
399 def test_Wdigest25(self):
400 attribute = "virtualWDigest25"
401 name = "%s@%s" % (USER_NAME, self.dns_domain.lower())
402 expected = calc_digest(name.lower(),
403 "Digest",
404 USER_PASS)
405 self._testWDigest(attribute, expected)
407 # Hash26 MD5(UPPER(userPrincipalName),
408 # "Digest",
409 # password)
411 def test_Wdigest26(self):
412 attribute = "virtualWDigest26"
413 name = "%s@%s" % (USER_NAME, self.dns_domain.lower())
414 expected = calc_digest(name.upper(),
415 "Digest",
416 USER_PASS)
417 self._testWDigest(attribute, expected)
418 # Hash27 MD5(NETBIOSDomainName\sAMAccountName,
419 # "Digest",
420 # password)
422 def test_Wdigest27(self):
423 attribute = "virtualWDigest27"
424 name = "%s\\%s" % (self.netbios_domain, USER_NAME)
425 expected = calc_digest(name,
426 "Digest",
427 USER_PASS)
428 self._testWDigest(attribute, expected)
430 # Hash28 MD5(LOWER(NETBIOSDomainName\sAMAccountName),
431 # "Digest",
432 # password)
434 def test_Wdigest28(self):
435 attribute = "virtualWDigest28"
436 name = "%s\\%s" % (self.netbios_domain.lower(), USER_NAME.lower())
437 expected = calc_digest(name,
438 "Digest",
439 USER_PASS)
440 self._testWDigest(attribute, expected)
442 # Hash29 MD5(UPPER(NETBIOSDomainName\sAMAccountName),
443 # "Digest",
444 # password)
446 def test_Wdigest29(self):
447 attribute = "virtualWDigest29"
448 name = "%s\\%s" % (self.netbios_domain.upper(), USER_NAME.upper())
449 expected = calc_digest(name,
450 "Digest",
451 USER_PASS)
452 self._testWDigest(attribute, expected)
454 def test_Wdigest30(self):
455 attribute = "virtualWDigest30"
456 self._testWDigest(attribute, None, True)
458 # Check digest calculation against an known htdigest value
459 def test_calc_digest(self):
460 htdigest = "gary:fred:2204fcc247cb47ded249ef2fe0013255"
461 digest = calc_digest("gary", "fred", "password")
462 self.assertEqual(htdigest, digest)