1 # Tests for Tests for source4/dsdb/samdb/ldb_modules/password_hash.c
3 # Copyright (C) Catalyst IT Ltd. 2017
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Tests for source4/dsdb/samdb/ldb_modules/password_hash.c
21 These tests need to be run in an environment in which
22 io->ac->gpg_key_ids != NULL, so that the gpg supplemental credentials
23 are generated. The functional level needs to be >= 2008 so that the
24 kerberos newer keys are generated.
28 from samba
.tests
.password_hash
import (
34 from samba
.ndr
import ndr_unpack
35 from samba
.dcerpc
import drsblobs
37 from samba
.tests
.pso
import PasswordSettings
41 class PassWordHashGpgmeTests(PassWordHashTests
):
44 super(PassWordHashGpgmeTests
, self
).setUp()
46 def test_default_supplementalCredentials(self
):
48 if not self
.lp
.get("password hash gpg key ids"):
49 self
.skipTest("No password hash gpg key ids, " +
50 "Primary:SambaGPG will not be generated")
52 sc
= self
.get_supplemental_creds()
54 # Check that we got all the expected supplemental credentials
55 # And they are in the expected order.
56 size
= len(sc
.sub
.packages
)
57 self
.assertEqual(5, size
)
58 (pos
, package
) = get_package(sc
, "Primary:Kerberos-Newer-Keys")
59 self
.assertEqual(1, pos
)
60 self
.assertEqual("Primary:Kerberos-Newer-Keys", package
.name
)
62 (pos
, package
) = get_package(sc
, "Primary:Kerberos")
63 self
.assertEqual(2, pos
)
64 self
.assertEqual("Primary:Kerberos", package
.name
)
66 (pos
, wd_package
) = get_package(sc
, "Primary:WDigest")
67 self
.assertEqual(3, pos
)
68 self
.assertEqual("Primary:WDigest", wd_package
.name
)
70 (pos
, package
) = get_package(sc
, "Packages")
71 self
.assertEqual(4, pos
)
72 self
.assertEqual("Packages", package
.name
)
74 (pos
, package
) = get_package(sc
, "Primary:SambaGPG")
75 self
.assertEqual(5, pos
)
76 self
.assertEqual("Primary:SambaGPG", package
.name
)
78 # Check that the WDigest values are correct.
80 digests
= ndr_unpack(drsblobs
.package_PrimaryWDigestBlob
,
81 binascii
.a2b_hex(wd_package
.data
))
82 self
.check_wdigests(digests
)
84 def test_supplementalCredentials_cleartext(self
):
85 self
.add_user(clear_text
=True)
86 if not self
.lp
.get("password hash gpg key ids"):
87 self
.skipTest("No password hash gpg key ids, " +
88 "Primary:SambaGPG will not be generated")
90 sc
= self
.get_supplemental_creds()
92 # Check that we got all the expected supplemental credentials
93 # And they are in the expected order.
94 size
= len(sc
.sub
.packages
)
95 self
.assertEqual(6, size
)
96 (pos
, package
) = get_package(sc
, "Primary:Kerberos-Newer-Keys")
97 self
.assertEqual(1, pos
)
98 self
.assertEqual("Primary:Kerberos-Newer-Keys", package
.name
)
100 (pos
, package
) = get_package(sc
, "Primary:Kerberos")
101 self
.assertEqual(2, pos
)
102 self
.assertEqual("Primary:Kerberos", package
.name
)
104 (pos
, wd_package
) = get_package(sc
, "Primary:WDigest")
105 self
.assertEqual(3, pos
)
106 self
.assertEqual("Primary:WDigest", wd_package
.name
)
108 (pos
, ct_package
) = get_package(sc
, "Primary:CLEARTEXT")
109 self
.assertEqual(4, pos
)
110 self
.assertEqual("Primary:CLEARTEXT", ct_package
.name
)
112 (pos
, package
) = get_package(sc
, "Packages")
113 self
.assertEqual(5, pos
)
114 self
.assertEqual("Packages", package
.name
)
116 (pos
, package
) = get_package(sc
, "Primary:SambaGPG")
117 self
.assertEqual(6, pos
)
118 self
.assertEqual("Primary:SambaGPG", package
.name
)
120 # Check that the WDigest values are correct.
122 digests
= ndr_unpack(drsblobs
.package_PrimaryWDigestBlob
,
123 binascii
.a2b_hex(wd_package
.data
))
124 self
.check_wdigests(digests
)
126 # Check the clear text value is correct.
127 ct
= ndr_unpack(drsblobs
.package_PrimaryCLEARTEXTBlob
,
128 binascii
.a2b_hex(ct_package
.data
))
129 self
.assertEqual(USER_PASS
.encode('utf-16-le'), ct
.cleartext
)
131 def assert_cleartext(self
, expect_cleartext
, password
=None):
132 """Checks cleartext is (or isn't) returned as expected"""
133 sc
= self
.get_supplemental_creds()
135 (pos
, ct_package
) = get_package(sc
, "Primary:CLEARTEXT")
136 self
.assertTrue(ct_package
is not None, "Failed to retrieve cleartext")
138 # Check the clear-text value is correct.
139 ct
= ndr_unpack(drsblobs
.package_PrimaryCLEARTEXTBlob
,
140 binascii
.a2b_hex(ct_package
.data
))
141 self
.assertEqual(password
.encode('utf-16-le'), ct
.cleartext
)
143 ct_package
= get_package(sc
, "Primary:CLEARTEXT")
144 self
.assertTrue(ct_package
is None,
145 "Got cleartext when we shouldn't have")
147 def test_supplementalCredentials_cleartext_pso(self
):
148 """Checks that a PSO's cleartext setting can override the domain's"""
150 # create a user that stores plain-text passwords
151 self
.add_user(clear_text
=True)
153 # check that clear-text is present in the supplementary-credentials
154 self
.assert_cleartext(expect_cleartext
=True, password
=USER_PASS
)
156 # create a PSO overriding the plain-text setting & apply it to the user
157 no_plaintext_pso
= PasswordSettings("no-plaintext-PSO", self
.ldb
,
159 store_plaintext
=False)
160 self
.addCleanup(self
.ldb
.delete
, no_plaintext_pso
.dn
)
161 userdn
= "cn=" + USER_NAME
+ ",cn=users," + self
.base_dn
162 no_plaintext_pso
.apply_to(userdn
)
164 # set the password to update the cleartext password stored
165 new_password
= samba
.generate_random_password(32, 32)
166 self
.ldb
.setpassword("(sAMAccountName=%s)" % USER_NAME
, new_password
)
168 # this time cleartext shouldn't be in the supplementary creds
169 self
.assert_cleartext(expect_cleartext
=False)
171 # unapply PSO, update password, and check we get the cleartext again
172 no_plaintext_pso
.unapply(userdn
)
173 new_password
= samba
.generate_random_password(32, 32)
174 self
.ldb
.setpassword("(sAMAccountName=%s)" % USER_NAME
, new_password
)
175 self
.assert_cleartext(expect_cleartext
=True, password
=new_password
)
177 # Now update the domain setting and check we no longer get cleartext
178 self
.set_store_cleartext(False)
179 new_password
= samba
.generate_random_password(32, 32)
180 self
.ldb
.setpassword("(sAMAccountName=%s)" % USER_NAME
, new_password
)
181 self
.assert_cleartext(expect_cleartext
=False)
183 # create a PSO overriding the domain setting & apply it to the user
184 plaintext_pso
= PasswordSettings("plaintext-PSO", self
.ldb
,
185 precedence
=100, store_plaintext
=True)
186 self
.addCleanup(self
.ldb
.delete
, plaintext_pso
.dn
)
187 plaintext_pso
.apply_to(userdn
)
188 new_password
= samba
.generate_random_password(32, 32)
189 self
.ldb
.setpassword("(sAMAccountName=%s)" % USER_NAME
, new_password
)
190 self
.assert_cleartext(expect_cleartext
=True, password
=new_password
)
192 def test_userPassword_multiple_hashes(self
):
193 self
.add_user(options
=[(
194 "password hash userPassword schemes",
195 "CryptSHA512 CryptSHA256 CryptSHA512")])
197 sc
= self
.get_supplemental_creds()
199 # Check that we got all the expected supplemental credentials
200 # And they are in the expected order.
201 size
= len(sc
.sub
.packages
)
202 self
.assertEqual(6, size
)
204 (pos
, package
) = get_package(sc
, "Primary:Kerberos-Newer-Keys")
205 self
.assertEqual(1, pos
)
206 self
.assertEqual("Primary:Kerberos-Newer-Keys", package
.name
)
208 (pos
, package
) = get_package(sc
, "Primary:Kerberos")
209 self
.assertEqual(2, pos
)
210 self
.assertEqual("Primary:Kerberos", package
.name
)
212 (pos
, wp_package
) = get_package(sc
, "Primary:WDigest")
213 self
.assertEqual(3, pos
)
214 self
.assertEqual("Primary:WDigest", wp_package
.name
)
216 (pos
, up_package
) = get_package(sc
, "Primary:userPassword")
217 self
.assertEqual(4, pos
)
218 self
.assertEqual("Primary:userPassword", up_package
.name
)
220 (pos
, package
) = get_package(sc
, "Packages")
221 self
.assertEqual(5, pos
)
222 self
.assertEqual("Packages", package
.name
)
224 (pos
, package
) = get_package(sc
, "Primary:SambaGPG")
225 self
.assertEqual(6, pos
)
226 self
.assertEqual("Primary:SambaGPG", package
.name
)
228 # Check that the WDigest values are correct.
230 digests
= ndr_unpack(drsblobs
.package_PrimaryWDigestBlob
,
231 binascii
.a2b_hex(wp_package
.data
))
232 self
.check_wdigests(digests
)
234 # Check that the userPassword hashes are computed correctly
235 # Expect three hashes to be calculated
236 up
= ndr_unpack(drsblobs
.package_PrimaryUserPasswordBlob
,
237 binascii
.a2b_hex(up_package
.data
))
238 self
.checkUserPassword(up
, [
239 ("{CRYPT}", "6", None),
240 ("{CRYPT}", "5", None),
241 ("{CRYPT}", "6", None)
243 self
.checkNtHash(USER_PASS
, up
.current_nt_hash
.hash)
245 def test_userPassword_multiple_hashes_rounds_specified(self
):
246 self
.add_user(options
=[(
247 "password hash userPassword schemes",
248 "CryptSHA512:rounds=5120 CryptSHA256:rounds=2560 CryptSHA512:rounds=5122")])
250 sc
= self
.get_supplemental_creds()
252 # Check that we got all the expected supplemental credentials
253 # And they are in the expected order.
254 size
= len(sc
.sub
.packages
)
255 self
.assertEqual(6, size
)
257 (pos
, package
) = get_package(sc
, "Primary:Kerberos-Newer-Keys")
258 self
.assertEqual(1, pos
)
259 self
.assertEqual("Primary:Kerberos-Newer-Keys", package
.name
)
261 (pos
, package
) = get_package(sc
, "Primary:Kerberos")
262 self
.assertEqual(2, pos
)
263 self
.assertEqual("Primary:Kerberos", package
.name
)
265 (pos
, wp_package
) = get_package(sc
, "Primary:WDigest")
266 self
.assertEqual(3, pos
)
267 self
.assertEqual("Primary:WDigest", wp_package
.name
)
269 (pos
, up_package
) = get_package(sc
, "Primary:userPassword")
270 self
.assertEqual(4, pos
)
271 self
.assertEqual("Primary:userPassword", up_package
.name
)
273 (pos
, package
) = get_package(sc
, "Packages")
274 self
.assertEqual(5, pos
)
275 self
.assertEqual("Packages", package
.name
)
277 (pos
, package
) = get_package(sc
, "Primary:SambaGPG")
278 self
.assertEqual(6, pos
)
279 self
.assertEqual("Primary:SambaGPG", package
.name
)
281 # Check that the WDigest values are correct.
283 digests
= ndr_unpack(drsblobs
.package_PrimaryWDigestBlob
,
284 binascii
.a2b_hex(wp_package
.data
))
285 self
.check_wdigests(digests
)
287 # Check that the userPassword hashes are computed correctly
288 # Expect three hashes to be calculated
289 up
= ndr_unpack(drsblobs
.package_PrimaryUserPasswordBlob
,
290 binascii
.a2b_hex(up_package
.data
))
291 self
.checkUserPassword(up
, [
292 ("{CRYPT}", "6", 5120),
293 ("{CRYPT}", "5", 2560),
294 ("{CRYPT}", "6", 5122)
296 self
.checkNtHash(USER_PASS
, up
.current_nt_hash
.hash)