tests password_hash: add tests for Primary:userPassword
authorGary Lockyer <gary@catalyst.net.nz>
Tue, 11 Apr 2017 21:12:56 +0000 (09:12 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 25 May 2017 00:25:12 +0000 (02:25 +0200)
    Add tests to verify the generation and storage of sha256 and sha512
    password hashes in suplementalCredentials Primary:userPassword

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
python/samba/tests/password_hash.py
python/samba/tests/password_hash_fl2003.py
python/samba/tests/password_hash_fl2008.py
python/samba/tests/password_hash_gpgme.py
selftest/knownfail

index de110cfcb07ef0a5d34ad70b6663c86b400c7286..67c4839112d6f2a7b496b306ac6209b2f8a58f7a 100644 (file)
@@ -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)
index f83dc3a08c8e96f6f15e305438aa886561ee7679..0ac38b05f48251857913c08886bf29614632e23a 100644 (file)
@@ -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,50 @@ 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,
+                             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)
+
+    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,
@@ -105,3 +192,10 @@ 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)
+
+        # 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)
index 9d296dddf1c192bff0b04d733c4b7e7c9bc5a784..7904628c61398e1e65105bff873115894eddbd79 100644 (file)
@@ -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)
index 8dba3dd0de740b10a0daf2e712bd0de239c78793..25a8a0c79162eff402b6338f6131cf33fe0be9a2 100644 (file)
@@ -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)
index b16ff520e4274f693f690e306d4645f8d9f5a62c..98f747249c7d974fabc6fc2b1cd815bfb28d396b 100644 (file)
 # 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\)