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
):
43 def test_default_supplementalCredentials(self
):
45 if not self
.lp
.get("password hash gpg key ids"):
46 self
.skipTest("No password hash gpg key ids, " +
47 "Primary:SambaGPG will not be generated")
49 sc
= self
.get_supplemental_creds()
51 # Check that we got all the expected supplemental credentials
52 # And they are in the expected order.
53 size
= len(sc
.sub
.packages
)
54 self
.assertEqual(5, size
)
55 (pos
, package
) = get_package(sc
, "Primary:Kerberos-Newer-Keys")
56 self
.assertEqual(1, pos
)
57 self
.assertEqual("Primary:Kerberos-Newer-Keys", package
.name
)
59 (pos
, package
) = get_package(sc
, "Primary:Kerberos")
60 self
.assertEqual(2, pos
)
61 self
.assertEqual("Primary:Kerberos", package
.name
)
63 (pos
, wd_package
) = get_package(sc
, "Primary:WDigest")
64 self
.assertEqual(3, pos
)
65 self
.assertEqual("Primary:WDigest", wd_package
.name
)
67 (pos
, package
) = get_package(sc
, "Packages")
68 self
.assertEqual(4, pos
)
69 self
.assertEqual("Packages", package
.name
)
71 (pos
, package
) = get_package(sc
, "Primary:SambaGPG")
72 self
.assertEqual(5, pos
)
73 self
.assertEqual("Primary:SambaGPG", package
.name
)
75 # Check that the WDigest values are correct.
77 digests
= ndr_unpack(drsblobs
.package_PrimaryWDigestBlob
,
78 binascii
.a2b_hex(wd_package
.data
))
79 self
.check_wdigests(digests
)
81 def test_supplementalCredentials_cleartext(self
):
82 self
.add_user(clear_text
=True)
83 if not self
.lp
.get("password hash gpg key ids"):
84 self
.skipTest("No password hash gpg key ids, " +
85 "Primary:SambaGPG will not be generated")
87 sc
= self
.get_supplemental_creds()
89 # Check that we got all the expected supplemental credentials
90 # And they are in the expected order.
91 size
= len(sc
.sub
.packages
)
92 self
.assertEqual(6, size
)
93 (pos
, package
) = get_package(sc
, "Primary:Kerberos-Newer-Keys")
94 self
.assertEqual(1, pos
)
95 self
.assertEqual("Primary:Kerberos-Newer-Keys", package
.name
)
97 (pos
, package
) = get_package(sc
, "Primary:Kerberos")
98 self
.assertEqual(2, pos
)
99 self
.assertEqual("Primary:Kerberos", package
.name
)
101 (pos
, wd_package
) = get_package(sc
, "Primary:WDigest")
102 self
.assertEqual(3, pos
)
103 self
.assertEqual("Primary:WDigest", wd_package
.name
)
105 (pos
, ct_package
) = get_package(sc
, "Primary:CLEARTEXT")
106 self
.assertEqual(4, pos
)
107 self
.assertEqual("Primary:CLEARTEXT", ct_package
.name
)
109 (pos
, package
) = get_package(sc
, "Packages")
110 self
.assertEqual(5, pos
)
111 self
.assertEqual("Packages", package
.name
)
113 (pos
, package
) = get_package(sc
, "Primary:SambaGPG")
114 self
.assertEqual(6, pos
)
115 self
.assertEqual("Primary:SambaGPG", package
.name
)
117 # Check that the WDigest values are correct.
119 digests
= ndr_unpack(drsblobs
.package_PrimaryWDigestBlob
,
120 binascii
.a2b_hex(wd_package
.data
))
121 self
.check_wdigests(digests
)
123 # Check the clear text value is correct.
124 ct
= ndr_unpack(drsblobs
.package_PrimaryCLEARTEXTBlob
,
125 binascii
.a2b_hex(ct_package
.data
))
126 self
.assertEqual(USER_PASS
.encode('utf-16-le'), ct
.cleartext
)
128 def assert_cleartext(self
, expect_cleartext
, password
=None):
129 """Checks cleartext is (or isn't) returned as expected"""
130 sc
= self
.get_supplemental_creds()
132 (pos
, ct_package
) = get_package(sc
, "Primary:CLEARTEXT")
133 self
.assertTrue(ct_package
is not None, "Failed to retrieve cleartext")
135 # Check the clear-text value is correct.
136 ct
= ndr_unpack(drsblobs
.package_PrimaryCLEARTEXTBlob
,
137 binascii
.a2b_hex(ct_package
.data
))
138 self
.assertEqual(password
.encode('utf-16-le'), ct
.cleartext
)
140 ct_package
= get_package(sc
, "Primary:CLEARTEXT")
141 self
.assertTrue(ct_package
is None,
142 "Got cleartext when we shouldn't have")
144 def test_supplementalCredentials_cleartext_pso(self
):
145 """Checks that a PSO's cleartext setting can override the domain's"""
147 # create a user that stores plain-text passwords
148 self
.add_user(clear_text
=True)
150 # check that clear-text is present in the supplementary-credentials
151 self
.assert_cleartext(expect_cleartext
=True, password
=USER_PASS
)
153 # create a PSO overriding the plain-text setting & apply it to the user
154 no_plaintext_pso
= PasswordSettings("no-plaintext-PSO", self
.ldb
,
156 store_plaintext
=False)
157 self
.addCleanup(self
.ldb
.delete
, no_plaintext_pso
.dn
)
158 userdn
= "cn=" + USER_NAME
+ ",cn=users," + self
.base_dn
159 no_plaintext_pso
.apply_to(userdn
)
161 # set the password to update the cleartext password stored
162 new_password
= samba
.generate_random_password(32, 32)
163 self
.ldb
.setpassword("(sAMAccountName=%s)" % USER_NAME
, new_password
)
165 # this time cleartext shouldn't be in the supplementary creds
166 self
.assert_cleartext(expect_cleartext
=False)
168 # unapply PSO, update password, and check we get the cleartext again
169 no_plaintext_pso
.unapply(userdn
)
170 new_password
= samba
.generate_random_password(32, 32)
171 self
.ldb
.setpassword("(sAMAccountName=%s)" % USER_NAME
, new_password
)
172 self
.assert_cleartext(expect_cleartext
=True, password
=new_password
)
174 # Now update the domain setting and check we no longer get cleartext
175 self
.set_store_cleartext(False)
176 new_password
= samba
.generate_random_password(32, 32)
177 self
.ldb
.setpassword("(sAMAccountName=%s)" % USER_NAME
, new_password
)
178 self
.assert_cleartext(expect_cleartext
=False)
180 # create a PSO overriding the domain setting & apply it to the user
181 plaintext_pso
= PasswordSettings("plaintext-PSO", self
.ldb
,
182 precedence
=100, store_plaintext
=True)
183 self
.addCleanup(self
.ldb
.delete
, plaintext_pso
.dn
)
184 plaintext_pso
.apply_to(userdn
)
185 new_password
= samba
.generate_random_password(32, 32)
186 self
.ldb
.setpassword("(sAMAccountName=%s)" % USER_NAME
, new_password
)
187 self
.assert_cleartext(expect_cleartext
=True, password
=new_password
)
189 def test_userPassword_multiple_hashes(self
):
190 self
.add_user(options
=[(
191 "password hash userPassword schemes",
192 "CryptSHA512 CryptSHA256 CryptSHA512")])
194 sc
= self
.get_supplemental_creds()
196 # Check that we got all the expected supplemental credentials
197 # And they are in the expected order.
198 size
= len(sc
.sub
.packages
)
199 self
.assertEqual(6, size
)
201 (pos
, package
) = get_package(sc
, "Primary:Kerberos-Newer-Keys")
202 self
.assertEqual(1, pos
)
203 self
.assertEqual("Primary:Kerberos-Newer-Keys", package
.name
)
205 (pos
, package
) = get_package(sc
, "Primary:Kerberos")
206 self
.assertEqual(2, pos
)
207 self
.assertEqual("Primary:Kerberos", package
.name
)
209 (pos
, wp_package
) = get_package(sc
, "Primary:WDigest")
210 self
.assertEqual(3, pos
)
211 self
.assertEqual("Primary:WDigest", wp_package
.name
)
213 (pos
, up_package
) = get_package(sc
, "Primary:userPassword")
214 self
.assertEqual(4, pos
)
215 self
.assertEqual("Primary:userPassword", up_package
.name
)
217 (pos
, package
) = get_package(sc
, "Packages")
218 self
.assertEqual(5, pos
)
219 self
.assertEqual("Packages", package
.name
)
221 (pos
, package
) = get_package(sc
, "Primary:SambaGPG")
222 self
.assertEqual(6, pos
)
223 self
.assertEqual("Primary:SambaGPG", package
.name
)
225 # Check that the WDigest values are correct.
227 digests
= ndr_unpack(drsblobs
.package_PrimaryWDigestBlob
,
228 binascii
.a2b_hex(wp_package
.data
))
229 self
.check_wdigests(digests
)
231 # Check that the userPassword hashes are computed correctly
232 # Expect three hashes to be calculated
233 up
= ndr_unpack(drsblobs
.package_PrimaryUserPasswordBlob
,
234 binascii
.a2b_hex(up_package
.data
))
235 self
.checkUserPassword(up
, [
236 ("{CRYPT}", "6", None),
237 ("{CRYPT}", "5", None),
238 ("{CRYPT}", "6", None)
240 self
.checkNtHash(USER_PASS
, up
.current_nt_hash
.hash)
242 def test_userPassword_multiple_hashes_rounds_specified(self
):
243 self
.add_user(options
=[(
244 "password hash userPassword schemes",
245 "CryptSHA512:rounds=5120 CryptSHA256:rounds=2560 CryptSHA512:rounds=5122")])
247 sc
= self
.get_supplemental_creds()
249 # Check that we got all the expected supplemental credentials
250 # And they are in the expected order.
251 size
= len(sc
.sub
.packages
)
252 self
.assertEqual(6, size
)
254 (pos
, package
) = get_package(sc
, "Primary:Kerberos-Newer-Keys")
255 self
.assertEqual(1, pos
)
256 self
.assertEqual("Primary:Kerberos-Newer-Keys", package
.name
)
258 (pos
, package
) = get_package(sc
, "Primary:Kerberos")
259 self
.assertEqual(2, pos
)
260 self
.assertEqual("Primary:Kerberos", package
.name
)
262 (pos
, wp_package
) = get_package(sc
, "Primary:WDigest")
263 self
.assertEqual(3, pos
)
264 self
.assertEqual("Primary:WDigest", wp_package
.name
)
266 (pos
, up_package
) = get_package(sc
, "Primary:userPassword")
267 self
.assertEqual(4, pos
)
268 self
.assertEqual("Primary:userPassword", up_package
.name
)
270 (pos
, package
) = get_package(sc
, "Packages")
271 self
.assertEqual(5, pos
)
272 self
.assertEqual("Packages", package
.name
)
274 (pos
, package
) = get_package(sc
, "Primary:SambaGPG")
275 self
.assertEqual(6, pos
)
276 self
.assertEqual("Primary:SambaGPG", package
.name
)
278 # Check that the WDigest values are correct.
280 digests
= ndr_unpack(drsblobs
.package_PrimaryWDigestBlob
,
281 binascii
.a2b_hex(wp_package
.data
))
282 self
.check_wdigests(digests
)
284 # Check that the userPassword hashes are computed correctly
285 # Expect three hashes to be calculated
286 up
= ndr_unpack(drsblobs
.package_PrimaryUserPasswordBlob
,
287 binascii
.a2b_hex(up_package
.data
))
288 self
.checkUserPassword(up
, [
289 ("{CRYPT}", "6", 5120),
290 ("{CRYPT}", "5", 2560),
291 ("{CRYPT}", "6", 5122)
293 self
.checkNtHash(USER_PASS
, up
.current_nt_hash
.hash)