From de5299d1554130aef9554d2696742ceaab66daeb Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Wed, 12 Apr 2017 09:12:56 +1200 Subject: [PATCH] tests password_hash: add tests for Primary:userPassword Add tests to verify the generation and storage of sha256 and sha512 password hashes in suplementalCredentials Primary:userPassword Signed-off-by: Gary Lockyer Reviewed-by: Garming Sam Reviewed-by: Andrew Bartlett --- python/samba/tests/password_hash.py | 34 +++++++ python/samba/tests/password_hash_fl2003.py | 98 ++++++++++++++++++- python/samba/tests/password_hash_fl2008.py | 98 +++++++++++++++++++ python/samba/tests/password_hash_gpgme.py | 106 +++++++++++++++++++++ selftest/knownfail | 9 ++ 5 files changed, 343 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/password_hash.py b/python/samba/tests/password_hash.py index de110cfcb07..67c4839112d 100644 --- a/python/samba/tests/password_hash.py +++ b/python/samba/tests/password_hash.py @@ -33,6 +33,7 @@ import ldb import samba import binascii import md5 +import crypt USER_NAME = "PasswordHashTestUser" @@ -287,3 +288,36 @@ class PassWordHashTests(TestCase): "Digest", USER_PASS, digests.hashes[28].hash) + + + def checkUserPassword(self, up, expected): + + # Check we've received the correct number of hashes + self.assertEquals(len(expected), up.num_hashes) + + i = 0 + for (tag, alg, rounds) in expected: + self.assertEquals(tag, up.hashes[i].scheme) + + data = up.hashes[i].value.split("$") + # Check we got the expected crypt algorithm + self.assertEquals(alg, data[1]) + + if rounds is None: + cmd = "$%s$%s" % (alg, data[2]) + else: + cmd = "$%s$rounds=%d$%s" % (alg, rounds, data[3]) + + # Calculate the expected hash value + expected = crypt.crypt(USER_PASS, cmd) + self.assertEquals(expected, up.hashes[i].value) + i += 1 + + # Check that the correct nt_hash was stored for userPassword + def checkNtHash(self, password, nt_hash): + creds = Credentials() + creds.set_anonymous() + creds.set_password(password) + expected = creds.get_nt_hash() + actual = bytearray(nt_hash) + self.assertEquals(expected, actual) diff --git a/python/samba/tests/password_hash_fl2003.py b/python/samba/tests/password_hash_fl2003.py index f83dc3a08c8..0ac38b05f48 100644 --- a/python/samba/tests/password_hash_fl2003.py +++ b/python/samba/tests/password_hash_fl2003.py @@ -36,13 +36,14 @@ from samba.dcerpc import drsblobs import binascii + class PassWordHashFl2003Tests(PassWordHashTests): def setUp(self): super(PassWordHashFl2003Tests, self).setUp() def test_default_supplementalCredentials(self): - self.add_user() + self.add_user(options=[("password hash userPassword schemes", "")]) sc = self.get_supplemental_creds() @@ -69,8 +70,50 @@ class PassWordHashFl2003Tests(PassWordHashTests): binascii.a2b_hex(package.data)) self.check_wdigests(digests) + def test_userPassword_sha256(self): + self.add_user(options=[("password hash userPassword schemes", + "CryptSHA256")]) + + sc = self.get_supplemental_creds() + + # Check that we got all the expected supplemental credentials + # And they are in the expected order. + size = len(sc.sub.packages) + self.assertEquals(4, size) + + (pos, package) = get_package(sc, "Primary:Kerberos") + self.assertEquals(1, pos) + self.assertEquals("Primary:Kerberos", package.name) + + (pos, wd_package) = get_package(sc, "Primary:WDigest") + self.assertEquals(2, pos) + self.assertEquals("Primary:WDigest", wd_package.name) + + (pos, package) = get_package(sc, "Packages") + self.assertEquals(3, pos) + self.assertEquals("Packages", package.name) + + (pos, up_package) = get_package(sc, "Primary:userPassword") + self.assertEquals(4, pos) + self.assertEquals("Primary:userPassword", up_package.name) + + # Check that the WDigest values are correct. + # + digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, + binascii.a2b_hex(wd_package.data)) + self.check_wdigests(digests) + + # Check that the userPassword hashes are computed correctly + # + up = ndr_unpack(drsblobs.package_PrimaryUserPasswordBlob, + binascii.a2b_hex(up_package.data)) + + self.checkUserPassword(up, [("{CRYPT}", "5", None)]) + self.checkNtHash(USER_PASS, up.current_nt_hash.hash) + def test_supplementalCredentials_cleartext(self): - self.add_user(clear_text=True) + self.add_user(clear_text=True, + options=[("password hash userPassword schemes", "")]) sc = self.get_supplemental_creds() @@ -95,6 +138,7 @@ class PassWordHashFl2003Tests(PassWordHashTests): self.assertEquals(4, pos) self.assertEquals("Primary:CLEARTEXT", ct_package.name) + # Check that the WDigest values are correct. # digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, @@ -105,3 +149,53 @@ class PassWordHashFl2003Tests(PassWordHashTests): ct = ndr_unpack(drsblobs.package_PrimaryCLEARTEXTBlob, binascii.a2b_hex(ct_package.data)) self.assertEquals(USER_PASS.encode('utf-16-le'), ct.cleartext) + + def test_userPassword_cleartext_sha512(self): + self.add_user(clear_text=True, + options=[("password hash userPassword schemes", + "CryptSHA512:rounds=10000")]) + + sc = self.get_supplemental_creds() + + # Check that we got all the expected supplemental credentials + # And they are in the expected order. + size = len(sc.sub.packages) + self.assertEquals(5, size) + + (pos, package) = get_package(sc, "Primary:Kerberos") + self.assertEquals(1, pos) + self.assertEquals("Primary:Kerberos", package.name) + + (pos, wd_package) = get_package(sc, "Primary:WDigest") + self.assertEquals(2, pos) + self.assertEquals("Primary:WDigest", wd_package.name) + + (pos, ct_package) = get_package(sc, "Primary:CLEARTEXT") + self.assertEquals(3, pos) + self.assertEquals("Primary:CLEARTEXT", ct_package.name) + + (pos, package) = get_package(sc, "Packages") + self.assertEquals(4, pos) + self.assertEquals("Packages", package.name) + + (pos, up_package) = get_package(sc, "Primary:userPassword") + self.assertEquals(5, pos) + self.assertEquals("Primary:userPassword", up_package.name) + + # Check that the WDigest values are correct. + # + digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, + binascii.a2b_hex(wd_package.data)) + self.check_wdigests(digests) + + # Check the clear text value is correct. + ct = ndr_unpack(drsblobs.package_PrimaryCLEARTEXTBlob, + binascii.a2b_hex(ct_package.data)) + self.assertEquals(USER_PASS.encode('utf-16-le'), ct.cleartext) + + # Check that the userPassword hashes are computed correctly + # + up = ndr_unpack(drsblobs.package_PrimaryUserPasswordBlob, + binascii.a2b_hex(up_package.data)) + self.checkUserPassword(up, [("{CRYPT}", "6",10000 )]) + self.checkNtHash(USER_PASS, up.current_nt_hash.hash) diff --git a/python/samba/tests/password_hash_fl2008.py b/python/samba/tests/password_hash_fl2008.py index 9d296dddf1c..7904628c613 100644 --- a/python/samba/tests/password_hash_fl2008.py +++ b/python/samba/tests/password_hash_fl2008.py @@ -71,6 +71,50 @@ class PassWordHashFl2008Tests(PassWordHashTests): binascii.a2b_hex(package.data)) self.check_wdigests(digests) + def test_userPassword_sha512(self): + self.add_user(options=[("password hash userPassword schemes", + "CryptSHA512")]) + + sc = self.get_supplemental_creds() + + # Check that we got all the expected supplemental credentials + # And they are in the expected order. + size = len(sc.sub.packages) + self.assertEquals(5, size) + + (pos, package) = get_package(sc, "Primary:Kerberos-Newer-Keys") + self.assertEquals(1, pos) + self.assertEquals("Primary:Kerberos-Newer-Keys", package.name) + + (pos, package) = get_package(sc, "Primary:Kerberos") + self.assertEquals(2, pos) + self.assertEquals("Primary:Kerberos", package.name) + + (pos, wp_package) = get_package(sc, "Primary:WDigest") + self.assertEquals(3, pos) + self.assertEquals("Primary:WDigest", wp_package.name) + + (pos, package) = get_package(sc, "Packages") + self.assertEquals(4, pos) + self.assertEquals("Packages", package.name) + + (pos, up_package) = get_package(sc, "Primary:userPassword") + self.assertEquals(5, pos) + self.assertEquals("Primary:userPassword", up_package.name) + + # Check that the WDigest values are correct. + # + digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, + binascii.a2b_hex(wp_package.data)) + self.check_wdigests(digests) + + # Check that the userPassword hashes are computed correctly + # + up = ndr_unpack(drsblobs.package_PrimaryUserPasswordBlob, + binascii.a2b_hex(up_package.data)) + self.checkUserPassword(up, [("{CRYPT}", "6",None)]) + self.checkNtHash(USER_PASS, up.current_nt_hash.hash) + def test_supplementalCredentials_cleartext(self): self.add_user(clear_text=True) @@ -110,3 +154,57 @@ class PassWordHashFl2008Tests(PassWordHashTests): ct = ndr_unpack(drsblobs.package_PrimaryCLEARTEXTBlob, binascii.a2b_hex(ct_package.data)) self.assertEquals(USER_PASS.encode('utf-16-le'), ct.cleartext) + + def test_userPassword_cleartext_sha256(self): + self.add_user(clear_text=True, + options=[("password hash userPassword schemes", + "CryptSHA256:rounds=100")]) + + sc = self.get_supplemental_creds() + + # Check that we got all the expected supplemental credentials + # And they are in the expected order. + size = len(sc.sub.packages) + self.assertEquals(6, size) + + (pos, package) = get_package(sc, "Primary:Kerberos-Newer-Keys") + self.assertEquals(1, pos) + self.assertEquals("Primary:Kerberos-Newer-Keys", package.name) + + (pos, package) = get_package(sc, "Primary:Kerberos") + self.assertEquals(2, pos) + self.assertEquals("Primary:Kerberos", package.name) + + (pos, wd_package) = get_package(sc, "Primary:WDigest") + self.assertEquals(3, pos) + self.assertEquals("Primary:WDigest", wd_package.name) + + (pos, ct_package) = get_package(sc, "Primary:CLEARTEXT") + self.assertEquals(4, pos) + self.assertEquals("Primary:CLEARTEXT", ct_package.name) + + (pos, package) = get_package(sc, "Packages") + self.assertEquals(5, pos) + self.assertEquals("Packages", package.name) + + (pos, up_package) = get_package(sc, "Primary:userPassword") + self.assertEquals(6, pos) + self.assertEquals("Primary:userPassword", up_package.name) + + # Check that the WDigest values are correct. + # + digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, + binascii.a2b_hex(wd_package.data)) + self.check_wdigests(digests) + + # Check the clear text value is correct. + ct = ndr_unpack(drsblobs.package_PrimaryCLEARTEXTBlob, + binascii.a2b_hex(ct_package.data)) + self.assertEquals(USER_PASS.encode('utf-16-le'), ct.cleartext) + + # Check that the userPassword hashes are computed correctly + # + up = ndr_unpack(drsblobs.package_PrimaryUserPasswordBlob, + binascii.a2b_hex(up_package.data)) + self.checkUserPassword(up, [("{CRYPT}", "5",100 )]) + self.checkNtHash(USER_PASS, up.current_nt_hash.hash) diff --git a/python/samba/tests/password_hash_gpgme.py b/python/samba/tests/password_hash_gpgme.py index 8dba3dd0de7..25a8a0c7916 100644 --- a/python/samba/tests/password_hash_gpgme.py +++ b/python/samba/tests/password_hash_gpgme.py @@ -124,3 +124,109 @@ class PassWordHashGpgmeTests(PassWordHashTests): ct = ndr_unpack(drsblobs.package_PrimaryCLEARTEXTBlob, binascii.a2b_hex(ct_package.data)) self.assertEquals(USER_PASS.encode('utf-16-le'), ct.cleartext) + + def test_userPassword_multiple_hashes(self): + self.add_user(options=[( + "password hash userPassword schemes", + "CryptSHA512 CryptSHA256 CryptSHA512")]) + + sc = self.get_supplemental_creds() + + # Check that we got all the expected supplemental credentials + # And they are in the expected order. + size = len(sc.sub.packages) + self.assertEquals(6, size) + + (pos, package) = get_package(sc, "Primary:Kerberos-Newer-Keys") + self.assertEquals(1, pos) + self.assertEquals("Primary:Kerberos-Newer-Keys", package.name) + + (pos, package) = get_package(sc, "Primary:Kerberos") + self.assertEquals(2, pos) + self.assertEquals("Primary:Kerberos", package.name) + + (pos, wp_package) = get_package(sc, "Primary:WDigest") + self.assertEquals(3, pos) + self.assertEquals("Primary:WDigest", wp_package.name) + + (pos, up_package) = get_package(sc, "Primary:userPassword") + self.assertEquals(4, pos) + self.assertEquals("Primary:userPassword", up_package.name) + + (pos, package) = get_package(sc, "Packages") + self.assertEquals(5, pos) + self.assertEquals("Packages", package.name) + + (pos, package) = get_package(sc, "Primary:SambaGPG") + self.assertEquals(6, pos) + self.assertEquals("Primary:SambaGPG", package.name) + + # Check that the WDigest values are correct. + # + digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, + binascii.a2b_hex(wp_package.data)) + self.check_wdigests(digests) + + # Check that the userPassword hashes are computed correctly + # Expect three hashes to be calculated + up = ndr_unpack(drsblobs.package_PrimaryUserPasswordBlob, + binascii.a2b_hex(up_package.data)) + self.checkUserPassword(up, [ + ("{CRYPT}", "6", None), + ("{CRYPT}", "5", None), + ("{CRYPT}", "6", None) + ]) + self.checkNtHash(USER_PASS, up.current_nt_hash.hash) + + def test_userPassword_multiple_hashes_rounds_specified(self): + self.add_user(options=[( + "password hash userPassword schemes", + "CryptSHA512:rounds=5120 CryptSHA256:rounds=2560 CryptSHA512:rounds=5122")]) + + sc = self.get_supplemental_creds() + + # Check that we got all the expected supplemental credentials + # And they are in the expected order. + size = len(sc.sub.packages) + self.assertEquals(6, size) + + (pos, package) = get_package(sc, "Primary:Kerberos-Newer-Keys") + self.assertEquals(1, pos) + self.assertEquals("Primary:Kerberos-Newer-Keys", package.name) + + (pos, package) = get_package(sc, "Primary:Kerberos") + self.assertEquals(2, pos) + self.assertEquals("Primary:Kerberos", package.name) + + (pos, wp_package) = get_package(sc, "Primary:WDigest") + self.assertEquals(3, pos) + self.assertEquals("Primary:WDigest", wp_package.name) + + (pos, up_package) = get_package(sc, "Primary:userPassword") + self.assertEquals(4, pos) + self.assertEquals("Primary:userPassword", up_package.name) + + (pos, package) = get_package(sc, "Packages") + self.assertEquals(5, pos) + self.assertEquals("Packages", package.name) + + (pos, package) = get_package(sc, "Primary:SambaGPG") + self.assertEquals(6, pos) + self.assertEquals("Primary:SambaGPG", package.name) + + # Check that the WDigest values are correct. + # + digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, + binascii.a2b_hex(wp_package.data)) + self.check_wdigests(digests) + + # Check that the userPassword hashes are computed correctly + # Expect three hashes to be calculated + up = ndr_unpack(drsblobs.package_PrimaryUserPasswordBlob, + binascii.a2b_hex(up_package.data)) + self.checkUserPassword(up, [ + ("{CRYPT}", "6", 5120), + ("{CRYPT}", "5", 2560), + ("{CRYPT}", "6", 5122) + ]) + self.checkNtHash(USER_PASS, up.current_nt_hash.hash) diff --git a/selftest/knownfail b/selftest/knownfail index b16ff520e42..98f747249c7 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -330,3 +330,12 @@ # We currently don't send referrals for LDAP modify of non-replicated attrs ^samba4.ldap.rodc.python\(rodc\).__main__.RodcTests.test_modify_nonreplicated.* ^samba4.ldap.rodc_rwdc.python.*.__main__.RodcRwdcTests.test_change_password_reveal_on_demand_kerberos +# Tests for password hash supplemental credentials, userPassword hashes +# Will fail as the implementation has not been written +# +^samba.tests.password_hash_gpgme.samba.tests.password_hash_gpgme.PassWordHashGpgmeTests.test_userPassword_multiple_hashes\(ad_dc:local\) +^samba.tests.password_hash_gpgme.samba.tests.password_hash_gpgme.PassWordHashGpgmeTests.test_userPassword_multiple_hashes_rounds_specified\(ad_dc:local\) +^samba.tests.password_hash_fl2008.samba.tests.password_hash_fl2008.PassWordHashFl2008Tests.test_userPassword_cleartext_sha256\(ad_dc_ntvfs:local\) +^samba.tests.password_hash_fl2008.samba.tests.password_hash_fl2008.PassWordHashFl2008Tests.test_userPassword_sha512\(ad_dc_ntvfs:local\) +^samba.tests.password_hash_fl2003.samba.tests.password_hash_fl2003.PassWordHashFl2003Tests.test_userPassword_cleartext_sha512\(fl2003dc:local\) +^samba.tests.password_hash_fl2003.samba.tests.password_hash_fl2003.PassWordHashFl2003Tests.test_userPassword_sha256\(fl2003dc:local\)