[SCM] Samba Shared Repository - branch master updated
Andrew Bartlett
abartlet at samba.org
Wed Jul 1 14:57:04 UTC 2020
The branch, master has been updated
via d3086501456 tls: Use NORMAL:-VERS-SSL3.0 as the default configuration
via cabf873b75b selftest: Run test of how userPassword / crypt() style passwords are stored in quicktest
via 2c4ecf002a3 selftest: Split samba.tests.samba_tool.user_virtualCryptSHA into GPG and not GPG parts
via 91453f110fa dsdb: Allow "password hash userPassword schemes = CryptSHA256" to work on RHEL7
from 213501163c0 share_mode_lock.c: initialize out param
https://git.samba.org/?p=samba.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit d30865014569f4b9a1261d9f0c40bc4fc98f883e
Author: Andreas Schneider <asn at samba.org>
Date: Tue Jun 30 17:12:17 2020 +0200
tls: Use NORMAL:-VERS-SSL3.0 as the default configuration
This seems to be really broken in GnuTLS and the documentation is also
not correct.
This partially reverts 53e3a959b958a3b099df6ecc5f6e294e96bd948e
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14408
Signed-off-by: Andreas Schneider <asn at samba.org>
Reviewed-by: Alexander Bokovoy <ab at samba.org>
Autobuild-User(master): Andrew Bartlett <abartlet at samba.org>
Autobuild-Date(master): Wed Jul 1 14:56:33 UTC 2020 on sn-devel-184
commit cabf873b75b1d4d456190358bc3ed051bca16978
Author: Andrew Bartlett <abartlet at samba.org>
Date: Wed Jul 1 14:31:54 2020 +1200
selftest: Run test of how userPassword / crypt() style passwords are stored in quicktest
This ensures that the crypt_r()/crypt_rn()/crypt() behaviour is tested in all
the samba-o3 builds and so is checked on RHEL7 in GitLab CI.
https://bugzilla.samba.org/show_bug.cgi?id=14424
Signed-off-by: Andrew Bartlett <abartlet at samba.org>
Reviewed-by: Alexander Bokovoy <ab at samba.org>
commit 2c4ecf002a3fbbe8be061814468529c8bd6bb7aa
Author: Andrew Bartlett <abartlet at samba.org>
Date: Wed Jul 1 14:30:24 2020 +1200
selftest: Split samba.tests.samba_tool.user_virtualCryptSHA into GPG and not GPG parts
This allows the userPassword (not GPG) part of the test to run on hosts without
python3-gpg (eg RHEL7) while still testing the userPassword handling.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14424
Signed-off-by: Andrew Bartlett <abartlet at samba.org>
Reviewed-by: Alexander Bokovoy <ab at samba.org>
commit 91453f110fa72062291eb59ad9d95fab0f423557
Author: Andrew Bartlett <abartlet at samba.org>
Date: Wed Jul 1 14:35:39 2020 +1200
dsdb: Allow "password hash userPassword schemes = CryptSHA256" to work on RHEL7
On RHEL7 crypt_r() will set errno. This is a problem because the implementation of crypt_r()
in RHEL8 and elsewhere in libcrypt will return non-NULL but set errno on failure.
The workaround is to use crypt_rn(), provided only by libcrypt, which will return NULL
on failure, and so avoid checking errno in the non-failure case.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14424
Signed-off-by: Andrew Bartlett <abartlet at samba.org>
Reviewed-by: Alexander Bokovoy <ab at samba.org>
-----------------------------------------------------------------------
Summary of changes:
docs-xml/smbdotconf/security/tlspriority.xml | 6 +-
lib/param/loadparm.c | 6 -
lib/replace/wscript | 1 +
python/samba/tests/docs.py | 21 --
.../tests/samba_tool/user_virtualCryptSHA_base.py | 118 ++++++++++
.../tests/samba_tool/user_virtualCryptSHA_gpg.py | 261 +++++++++++++++++++++
.../user_virtualCryptSHA_userPassword.py | 185 +++++++++++++++
selftest/quick | 3 +
source3/param/loadparm.c | 8 +-
source4/dsdb/samdb/ldb_modules/password_hash.c | 37 ++-
source4/selftest/tests.py | 3 +-
11 files changed, 603 insertions(+), 46 deletions(-)
create mode 100644 python/samba/tests/samba_tool/user_virtualCryptSHA_base.py
create mode 100644 python/samba/tests/samba_tool/user_virtualCryptSHA_gpg.py
create mode 100644 python/samba/tests/samba_tool/user_virtualCryptSHA_userPassword.py
Changeset truncated at 500 lines:
diff --git a/docs-xml/smbdotconf/security/tlspriority.xml b/docs-xml/smbdotconf/security/tlspriority.xml
index 6d1f0dcb912..471dc25ba3b 100644
--- a/docs-xml/smbdotconf/security/tlspriority.xml
+++ b/docs-xml/smbdotconf/security/tlspriority.xml
@@ -12,10 +12,8 @@
<ulink url="http://gnutls.org/manual/html_node/Priority-Strings.html">GNUTLS
Priority-Strings documentation at http://gnutls.org/manual/html_node/Priority-Strings.html</ulink>
</para>
- <para>By default it will try to find a config file matching "SAMBA", but if
- that does not exist will use the entry for "SYSTEM" and last fallback to
- NORMAL. In all cases the SSL3.0 protocol will be disabled.</para>
+ <para>The SSL3.0 protocol will be disabled.</para>
</description>
- <value type="default">@SAMBA,SYSTEM,NORMAL:!-VERS-SSL3.0</value>
+ <value type="default">NORMAL:-VERS-SSL3.0</value>
</samba:parameter>
diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c
index 53eedeb0cb2..da639a8b0ff 100644
--- a/lib/param/loadparm.c
+++ b/lib/param/loadparm.c
@@ -2818,15 +2818,9 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
lpcfg_do_global_parameter(lp_ctx, "tls keyfile", "tls/key.pem");
lpcfg_do_global_parameter(lp_ctx, "tls certfile", "tls/cert.pem");
lpcfg_do_global_parameter(lp_ctx, "tls cafile", "tls/ca.pem");
-#ifdef HAVE_GNUTLS_SET_DEFAULT_PRIORITY_APPEND
- lpcfg_do_global_parameter(lp_ctx,
- "tls priority",
- "@SAMBA,SYSTEM,NORMAL:!-VERS-SSL3.0");
-#else
lpcfg_do_global_parameter(lp_ctx,
"tls priority",
"NORMAL:-VERS-SSL3.0");
-#endif
lpcfg_do_global_parameter(lp_ctx, "nsupdate command", "/usr/bin/nsupdate -g");
diff --git a/lib/replace/wscript b/lib/replace/wscript
index ab2b3c043af..55c8903f1c8 100644
--- a/lib/replace/wscript
+++ b/lib/replace/wscript
@@ -661,6 +661,7 @@ def configure(conf):
conf.CHECK_FUNCS_IN('crypt', 'crypt', checklibc=True)
conf.CHECK_FUNCS_IN('crypt_r', 'crypt', checklibc=True)
+ conf.CHECK_FUNCS_IN('crypt_rn', 'crypt', checklibc=True)
conf.CHECK_VARIABLE('rl_event_hook', define='HAVE_DECL_RL_EVENT_HOOK', always=True,
headers='readline.h readline/readline.h readline/history.h')
diff --git a/python/samba/tests/docs.py b/python/samba/tests/docs.py
index 10339702a97..135be830521 100644
--- a/python/samba/tests/docs.py
+++ b/python/samba/tests/docs.py
@@ -29,22 +29,6 @@ import multiprocessing
import concurrent.futures
import tempfile
-config_h = os.path.join("bin/default/include/config.h")
-config_hash = dict()
-
-if os.path.exists(config_h):
- config_hash = dict()
- f = open(config_h, 'r')
- try:
- lines = f.readlines()
- config_hash = dict((x[0], ' '.join(x[1:]))
- for x in map(lambda line: line.strip().split(' ')[1:],
- list(filter(lambda line: (line[0:7] == '#define') and (len(line.split(' ')) > 2), lines))))
- finally:
- f.close()
-
-have_gnutls_system_config_support = ("HAVE_GNUTLS_SET_DEFAULT_PRIORITY_APPEND" in config_hash)
-
class TestCase(samba.tests.TestCaseInTempDir):
def _format_message(self, parameters, message):
@@ -234,11 +218,6 @@ class SmbDotConfTests(TestCase):
'smbd max async dosmode',
])
- # 'tls priority' has a legacy default value if we don't link against a
- # modern GnuTLS version.
- if not have_gnutls_system_config_support:
- special_cases.add('tls priority')
-
def setUp(self):
super(SmbDotConfTests, self).setUp()
# create a minimal smb.conf file for testparm
diff --git a/python/samba/tests/samba_tool/user_virtualCryptSHA_base.py b/python/samba/tests/samba_tool/user_virtualCryptSHA_base.py
new file mode 100644
index 00000000000..e32f8d7343c
--- /dev/null
+++ b/python/samba/tests/samba_tool/user_virtualCryptSHA_base.py
@@ -0,0 +1,118 @@
+# Tests for the samba-tool user sub command reading Primary:userPassword
+#
+# Copyright (C) Andrew Bartlett <abartlet at samba.org> 2017
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import time
+import base64
+import ldb
+import samba
+from samba.tests.samba_tool.base import SambaToolCmdTest
+from samba.credentials import Credentials
+from samba.samdb import SamDB
+from samba.auth import system_session
+from samba.ndr import ndr_unpack
+from samba.dcerpc import drsblobs
+from samba import dsdb
+import re
+
+USER_NAME = "CryptSHATestUser"
+HASH_OPTION = "password hash userPassword schemes"
+
+# Get the value of an attribute from the output string
+# Note: Does not correctly handle values spanning multiple lines,
+# which is acceptable for it's usage in these tests.
+
+
+def _get_attribute(out, name):
+ p = re.compile("^" + name + ":\s+(\S+)")
+ for line in out.split("\n"):
+ m = p.match(line)
+ if m:
+ return m.group(1)
+ return ""
+
+
+class UserCmdCryptShaTestCase(SambaToolCmdTest):
+ """
+ Tests for samba-tool user subcommands generation of the virtualCryptSHA256
+ and virtualCryptSHA512 attributes
+ """
+ users = []
+ samdb = None
+
+ def setUp(self):
+ super(UserCmdCryptShaTestCase, self).setUp()
+
+ def add_user(self, hashes=""):
+ self.lp = samba.tests.env_loadparm()
+
+ # set the extra hashes to be calculated
+ self.lp.set(HASH_OPTION, hashes)
+
+ self.creds = Credentials()
+ self.session = system_session()
+ self.ldb = SamDB(
+ session_info=self.session,
+ credentials=self.creds,
+ lp=self.lp)
+
+ password = self.random_password()
+ self.runsubcmd("user",
+ "create",
+ USER_NAME,
+ password)
+
+ def tearDown(self):
+ super(UserCmdCryptShaTestCase, self).tearDown()
+ self.runsubcmd("user", "delete", USER_NAME)
+
+ def _get_password(self, attributes, decrypt=False):
+ command = ["user",
+ "getpassword",
+ USER_NAME,
+ "--attributes",
+ attributes]
+ if decrypt:
+ command.append("--decrypt-samba-gpg")
+
+ (result, out, err) = self.runsubcmd(*command)
+ self.assertCmdSuccess(result,
+ out,
+ err,
+ "Ensure getpassword runs")
+ self.assertEqual(err, "", "getpassword")
+ self.assertMatch(out,
+ "Got password OK",
+ "getpassword out[%s]" % out)
+ return out
+
+ # Change the just the NT password hash, as would happen if the password
+ # was updated by Windows, the userPassword values are now obsolete.
+ #
+ def _change_nt_hash(self):
+ res = self.ldb.search(expression = "cn=%s" % USER_NAME,
+ scope = ldb.SCOPE_SUBTREE)
+ msg = ldb.Message()
+ msg.dn = res[0].dn
+ msg["unicodePwd"] = ldb.MessageElement(b"ABCDEF1234567890",
+ ldb.FLAG_MOD_REPLACE,
+ "unicodePwd")
+ self.ldb.modify(
+ msg,
+ controls=["local_oid:%s:0" %
+ dsdb.DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID])
diff --git a/python/samba/tests/samba_tool/user_virtualCryptSHA_gpg.py b/python/samba/tests/samba_tool/user_virtualCryptSHA_gpg.py
new file mode 100644
index 00000000000..25c02d9ac2a
--- /dev/null
+++ b/python/samba/tests/samba_tool/user_virtualCryptSHA_gpg.py
@@ -0,0 +1,261 @@
+# Tests for the samba-tool user sub command reading Primary:userPassword
+#
+# Copyright (C) Andrew Bartlett <abartlet at samba.org> 2017
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from samba.tests.samba_tool.user_virtualCryptSHA_base import UserCmdCryptShaTestCase, _get_attribute
+
+class UserCmdCryptShaTestCaseGPG(UserCmdCryptShaTestCase):
+ """
+ Tests for samba-tool user subcommands generation of the virtualCryptSHA256
+ and virtualCryptSHA512 attributes
+ """
+
+ # gpg decryption enabled.
+ # both virtual attributes specified, no rounds option
+ # no hashes stored in supplementalCredentials
+ # Should get values
+ def test_gpg_both_hashes_no_rounds(self):
+ self.add_user()
+ out = self._get_password("virtualCryptSHA256,virtualCryptSHA512", True)
+
+ self.assertTrue("virtualCryptSHA256:" in out)
+ self.assertTrue("virtualCryptSHA512:" in out)
+ self.assertTrue("rounds=" not in out)
+
+ # gpg decryption enabled.
+ # SHA256 specified
+ # no hashes stored in supplementalCredentials
+ # No rounds
+ #
+ # Should get values
+ def test_gpg_sha256_no_rounds(self):
+ self.add_user()
+ out = self._get_password("virtualCryptSHA256", True)
+
+ self.assertTrue("virtualCryptSHA256:" in out)
+ self.assertTrue("virtualCryptSHA512:" not in out)
+ self.assertTrue("rounds=" not in out)
+
+ # gpg decryption enabled.
+ # SHA512 specified
+ # no hashes stored in supplementalCredentials
+ # No rounds
+ #
+ # Should get values
+ def test_gpg_sha512_no_rounds(self):
+ self.add_user()
+ out = self._get_password("virtualCryptSHA512", True)
+
+ self.assertTrue("virtualCryptSHA256:" not in out)
+ self.assertTrue("virtualCryptSHA512:" in out)
+ self.assertTrue("rounds=" not in out)
+
+ # gpg decryption enabled.
+ # SHA128 specified, i.e. invalid/unknown algorithm
+ # no hashes stored in supplementalCredentials
+ # No rounds
+ #
+ # Should not get values
+ def test_gpg_invalid_alg_no_rounds(self):
+ self.add_user()
+ out = self._get_password("virtualCryptSHA128", True)
+
+ self.assertTrue("virtualCryptSHA256:" not in out)
+ self.assertTrue("virtualCryptSHA512:" not in out)
+ self.assertTrue("rounds=" not in out)
+
+ # gpg decryption enabled.
+ # both virtual attributes specified, no rounds option
+ # no hashes stored in supplementalCredentials
+ # underlying windows password changed, so plain text password is
+ # invalid.
+ # Should not get values
+ def test_gpg_both_hashes_no_rounds_pwd_changed(self):
+ self.add_user()
+ self._change_nt_hash()
+ out = self._get_password("virtualCryptSHA256,virtualCryptSHA512", True)
+
+ self.assertTrue("virtualCryptSHA256:" not in out)
+ self.assertTrue("virtualCryptSHA512:" not in out)
+ self.assertTrue("rounds=" not in out)
+
+ # gpg decryption enabled.
+ # SHA256 specified, no rounds option
+ # no hashes stored in supplementalCredentials
+ # underlying windows password changed, so plain text password is
+ # invalid.
+ # Should not get values
+ def test_gpg_sha256_no_rounds_pwd_changed(self):
+ self.add_user()
+ self._change_nt_hash()
+ out = self._get_password("virtualCryptSHA256", True)
+
+ self.assertTrue("virtualCryptSHA256:" not in out)
+ self.assertTrue("virtualCryptSHA512:" not in out)
+ self.assertTrue("rounds=" not in out)
+
+ # gpg decryption enabled.
+ # SHA512 specified, no rounds option
+ # no hashes stored in supplementalCredentials
+ # underlying windows password changed, so plain text password is
+ # invalid.
+ # Should not get values
+ def test_gpg_sha512_no_rounds_pwd_changed(self):
+ self.add_user()
+ self._change_nt_hash()
+ out = self._get_password("virtualCryptSHA256", True)
+
+ self.assertTrue("virtualCryptSHA256:" not in out)
+ self.assertTrue("virtualCryptSHA512:" not in out)
+ self.assertTrue("rounds=" not in out)
+
+ # gpg decryption enabled.
+ # both virtual attributes specified, rounds specified
+ # no hashes stored in supplementalCredentials
+ # Should get values reflecting the requested rounds
+ def test_gpg_both_hashes_both_rounds(self):
+ self.add_user()
+ out = self._get_password(
+ "virtualCryptSHA256;rounds=10123,virtualCryptSHA512;rounds=10456",
+ True)
+
+ self.assertTrue("virtualCryptSHA256:" in out)
+ self.assertTrue("virtualCryptSHA512:" in out)
+
+ sha256 = _get_attribute(out, "virtualCryptSHA256")
+ self.assertTrue(sha256.startswith("{CRYPT}$5$rounds=10123$"))
+
+ sha512 = _get_attribute(out, "virtualCryptSHA512")
+ self.assertTrue(sha512.startswith("{CRYPT}$6$rounds=10456$"))
+
+ # gpg decryption enabled.
+ # both virtual attributes specified, rounds specified
+ # invalid rounds for sha256
+ # no hashes stored in supplementalCredentials
+ # Should get values, no rounds for sha256, rounds for sha 512
+ def test_gpg_both_hashes_sha256_rounds_invalid(self):
+ self.add_user()
+ out = self._get_password(
+ "virtualCryptSHA256;rounds=invalid,virtualCryptSHA512;rounds=3125",
+ True)
+
+ self.assertTrue("virtualCryptSHA256:" in out)
+ self.assertTrue("virtualCryptSHA512:" in out)
+
+ sha256 = _get_attribute(out, "virtualCryptSHA256")
+ self.assertTrue(sha256.startswith("{CRYPT}$5$"))
+ self.assertTrue("rounds" not in sha256)
+
+ sha512 = _get_attribute(out, "virtualCryptSHA512")
+ self.assertTrue(sha512.startswith("{CRYPT}$6$rounds=3125$"))
+
+ # gpg decryption enabled.
+ # both virtual attributes specified, rounds specified
+ # both hashes stored in supplementalCredentials, with no rounds
+ # Should get calculated hashed with the correct number of rounds
+ def test_gpg_both_hashes_rounds_stored_hashes(self):
+ self.add_user("CryptSHA512 CryptSHA256")
+
+ out = self._get_password("virtualCryptSHA256;rounds=2561," +
+ "virtualCryptSHA512;rounds=5129",
+ True)
+
+ self.assertTrue("virtualCryptSHA256:" in out)
+ self.assertTrue("virtualCryptSHA512:" in out)
+ self.assertTrue("rounds=" in out)
+
+ # Should be calculating the hashes
+ # so they should change between calls.
+ sha256 = _get_attribute(out, "virtualCryptSHA256")
+ sha512 = _get_attribute(out, "virtualCryptSHA512")
+
+ out = self._get_password("virtualCryptSHA256;rounds=2561," +
+ "virtualCryptSHA512;rounds=5129",
+ True)
+ self.assertFalse(sha256 == _get_attribute(out, "virtualCryptSHA256"))
+ self.assertFalse(sha512 == _get_attribute(out, "virtualCryptSHA512"))
+
+ # The returned hashes should specify the correct number of rounds
+ self.assertTrue(sha256.startswith("{CRYPT}$5$rounds=2561"))
+ self.assertTrue(sha512.startswith("{CRYPT}$6$rounds=5129"))
+
+ # gpg decryption enabled.
+ # both virtual attributes specified, rounds specified
+ # both hashes stored in supplementalCredentials, with rounds
+ # Should get values
+ def test_gpg_both_hashes_rounds_stored_hashes_with_rounds(self):
+ self.add_user("CryptSHA512 " +
+ "CryptSHA256 " +
+ "CryptSHA512:rounds=5129 " +
+ "CryptSHA256:rounds=2561")
+
+ out = self._get_password("virtualCryptSHA256;rounds=2561," +
+ "virtualCryptSHA512;rounds=5129",
+ True)
+
+ self.assertTrue("virtualCryptSHA256:" in out)
+ self.assertTrue("virtualCryptSHA512:" in out)
+ self.assertTrue("rounds=" in out)
+
+ # Should be using the pre computed hash in supplementalCredentials
+ # so it should not change between calls.
+ sha256 = _get_attribute(out, "virtualCryptSHA256")
+ sha512 = _get_attribute(out, "virtualCryptSHA512")
+
+ out = self._get_password("virtualCryptSHA256;rounds=2561," +
+ "virtualCryptSHA512;rounds=5129",
+ True)
+ self.assertEquals(sha256, _get_attribute(out, "virtualCryptSHA256"))
+ self.assertEquals(sha512, _get_attribute(out, "virtualCryptSHA512"))
+
+ # The returned hashes should specify the correct number of rounds
+ self.assertTrue(sha256.startswith("{CRYPT}$5$rounds=2561"))
+ self.assertTrue(sha512.startswith("{CRYPT}$6$rounds=5129"))
+
+ # gpg decryption enabled.
+ # both virtual attributes specified, rounds specified
+ # both hashes stored in supplementalCredentials, with rounds
+ # number of rounds stored/requested do not match
+ # Should get calculated hashes with the correct number of rounds
+ def test_gpg_both_hashes_rounds_stored_hashes_with_rounds_no_match(self):
+ self.add_user("CryptSHA512 " +
+ "CryptSHA256 " +
+ "CryptSHA512:rounds=5129 " +
+ "CryptSHA256:rounds=2561")
+
+ out = self._get_password("virtualCryptSHA256;rounds=4000," +
+ "virtualCryptSHA512;rounds=5000",
+ True)
+
+ self.assertTrue("virtualCryptSHA256:" in out)
+ self.assertTrue("virtualCryptSHA512:" in out)
+ self.assertTrue("rounds=" in out)
+
+ # Should be calculating the hashes
+ # so they should change between calls.
+ sha256 = _get_attribute(out, "virtualCryptSHA256")
+ sha512 = _get_attribute(out, "virtualCryptSHA512")
+
+ out = self._get_password("virtualCryptSHA256;rounds=4000," +
+ "virtualCryptSHA512;rounds=5000",
+ True)
+ self.assertFalse(sha256 == _get_attribute(out, "virtualCryptSHA256"))
+ self.assertFalse(sha512 == _get_attribute(out, "virtualCryptSHA512"))
+
+ # The calculated hashes should specify the correct number of rounds
+ self.assertTrue(sha256.startswith("{CRYPT}$5$rounds=4000"))
+ self.assertTrue(sha512.startswith("{CRYPT}$6$rounds=5000"))
diff --git a/python/samba/tests/samba_tool/user_virtualCryptSHA_userPassword.py b/python/samba/tests/samba_tool/user_virtualCryptSHA_userPassword.py
new file mode 100644
index 00000000000..6c1c6295b85
--- /dev/null
+++ b/python/samba/tests/samba_tool/user_virtualCryptSHA_userPassword.py
@@ -0,0 +1,185 @@
+# Tests for the samba-tool user sub command reading Primary:userPassword
+#
+# Copyright (C) Andrew Bartlett <abartlet at samba.org> 2017
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
--
Samba Shared Repository
More information about the samba-cvs
mailing list