[SCM] Samba Shared Repository - branch master updated
Douglas Bagnall
dbagnall at samba.org
Wed Nov 4 00:20:03 UTC 2020
The branch, master has been updated
via a1b021200e3 selftest: add test for new "samba-tool user unlock" command
via 0bc93500a8b samba-tool: add new "user unlock" command
from 27480333fdc s3:vfs: Document the encryption_required flag in vfs.h
https://git.samba.org/?p=samba.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit a1b021200e3d068631798942a1f219b26afadca7
Author: Björn Baumbach <bb at sernet.de>
Date: Thu Oct 29 12:38:51 2020 +0100
selftest: add test for new "samba-tool user unlock" command
Signed-off-by: Björn Baumbach <bb at sernet.de>
Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Autobuild-User(master): Douglas Bagnall <dbagnall at samba.org>
Autobuild-Date(master): Wed Nov 4 00:19:25 UTC 2020 on sn-devel-184
commit 0bc93500a8bfb268085b02379375741903961f4e
Author: Björn Baumbach <bb at sernet.de>
Date: Thu Oct 22 17:29:56 2020 +0200
samba-tool: add new "user unlock" command
Can be used to unlock a user when the badPwdCount has been reached.
Introduces SamDB error classes, as suggested by
Douglas Bagnall <douglas.bagnall at catalyst.net.nz> - thanks!
This helps to handle expected failures.
Tracebacks of really unexpected failures will not be hidden.
Signed-off-by: Björn Baumbach <bb at sernet.de>
Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
-----------------------------------------------------------------------
Summary of changes:
docs-xml/manpages/samba-tool.8.xml | 6 ++
python/samba/netcmd/user.py | 74 +++++++++++++++++++++-
python/samba/samdb.py | 30 +++++++++
python/samba/tests/samba_tool/user.py | 41 ++++++++++++
source4/dsdb/tests/python/password_lockout.py | 20 ++++++
source4/dsdb/tests/python/password_lockout_base.py | 1 +
6 files changed, 171 insertions(+), 1 deletion(-)
Changeset truncated at 500 lines:
diff --git a/docs-xml/manpages/samba-tool.8.xml b/docs-xml/manpages/samba-tool.8.xml
index ccaaa8b432a..a7e8a7c9d1a 100644
--- a/docs-xml/manpages/samba-tool.8.xml
+++ b/docs-xml/manpages/samba-tool.8.xml
@@ -1465,6 +1465,12 @@
<para>Sets or resets the password of a user account.</para>
</refsect3>
+<refsect3>
+ <title>user unlock <replaceable>username</replaceable> [options]</title>
+ <para>This command unlocks a user account in the Active Directory
+ domain.</para>
+</refsect3>
+
<refsect3>
<title>user getpassword <replaceable>username</replaceable> [options]</title>
<para>Gets the password of a user account.</para>
diff --git a/python/samba/netcmd/user.py b/python/samba/netcmd/user.py
index f9762e761ea..b483dcf5591 100644
--- a/python/samba/netcmd/user.py
+++ b/python/samba/netcmd/user.py
@@ -33,7 +33,7 @@ import binascii
from subprocess import Popen, PIPE, STDOUT, check_call, CalledProcessError
from getpass import getpass
from samba.auth import system_session
-from samba.samdb import SamDB
+from samba.samdb import SamDB, SamDBError, SamDBNotFoundError
from samba.dcerpc import misc
from samba.dcerpc import security
from samba.dcerpc import drsblobs
@@ -3257,6 +3257,77 @@ unixHomeDirectory: {6}
self.outf.write("Modified User '{}' successfully\n"
.format(username))
+class cmd_user_unlock(Command):
+ """Unlock a user account.
+
+ This command unlocks a user account in the Active Directory domain. The
+ username specified on the command is the sAMAccountName. The username may
+ also be specified using the --filter option.
+
+ The command may be run from the root userid or another authorized userid.
+ The -H or --URL= option can be used to execute the command against a remote
+ server.
+
+ Example:
+ samba-tool user unlock user1 -H ldap://samba.samdom.example.com \\
+ --username=Administrator --password=Passw0rd
+
+ The example shows how to unlock a user account in the domain against a
+ remote LDAP server. The -H parameter is used to specify the remote target
+ server. The --username= and --password= options are used to pass the
+ username and password of a user that exists on the remote server and is
+ authorized to issue the command on that server.
+"""
+
+ synopsis = "%prog (<username>|--filter <filter>) [options]"
+
+ takes_options = [
+ Option("-H",
+ "--URL",
+ help="LDB URL for database or target server",
+ type=str,
+ metavar="URL",
+ dest="H"),
+ Option("--filter",
+ help="LDAP Filter to set password on",
+ type=str),
+ ]
+
+ takes_args = ["username?"]
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ def run(self,
+ username=None,
+ sambaopts=None,
+ credopts=None,
+ versionopts=None,
+ filter=None,
+ H=None):
+ if username is None and filter is None:
+ raise CommandError("Either the username or '--filter' must be "
+ "specified!")
+
+ if filter is None:
+ filter = ("(&(objectClass=user)(sAMAccountName=%s))" % (
+ ldb.binary_encode(username)))
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp, fallback_machine=True)
+
+ samdb = SamDB(url=H,
+ session_info=system_session(),
+ credentials=creds,
+ lp=lp)
+ try:
+ samdb.unlock_account(filter)
+ except (SamDBError, ldb.LdbError) as msg:
+ raise CommandError("Failed to unlock user '%s': %s" % (
+ username or filter, msg))
class cmd_user_sensitive(Command):
"""Set/unset or show UF_NOT_DELEGATED for an account."""
@@ -3336,5 +3407,6 @@ class cmd_user(SuperCommand):
subcommands["show"] = cmd_user_show()
subcommands["move"] = cmd_user_move()
subcommands["rename"] = cmd_user_rename()
+ subcommands["unlock"] = cmd_user_unlock()
subcommands["addunixattrs"] = cmd_user_add_unix_attrs()
subcommands["sensitive"] = cmd_user_sensitive()
diff --git a/python/samba/samdb.py b/python/samba/samdb.py
index 0ec91ed3970..e8aee496352 100644
--- a/python/samba/samdb.py
+++ b/python/samba/samdb.py
@@ -42,6 +42,11 @@ __docformat__ = "restructuredText"
def get_default_backend_store():
return "tdb"
+class SamDBError(Exception):
+ pass
+
+class SamDBNotFoundError(SamDBError):
+ pass
class SamDB(samba.Ldb):
"""The SAM database."""
@@ -179,6 +184,31 @@ dn: %s
changetype: modify
replace: pwdLastSet
pwdLastSet: 0
+""" % (user_dn)
+ self.modify_ldif(mod)
+
+ def unlock_account(self, search_filter):
+ """Unlock a user account by resetting lockoutTime to 0.
+ This does also reset the badPwdCount to 0.
+
+ :param search_filter: LDAP filter to find the user (e.g.
+ sAMAccountName=username)
+ """
+ res = self.search(base=self.domain_dn(),
+ scope=ldb.SCOPE_SUBTREE,
+ expression=search_filter,
+ attrs=[])
+ if len(res) == 0:
+ raise SamDBNotFoundError('Unable to find user "%s"' % search_filter)
+ if len(res) != 1:
+ raise SamDBError('User "%s" is not unique' % search_filter)
+ user_dn = res[0].dn
+
+ mod = """
+dn: %s
+changetype: modify
+replace: lockoutTime
+lockoutTime: 0
""" % (user_dn)
self.modify_ldif(mod)
diff --git a/python/samba/tests/samba_tool/user.py b/python/samba/tests/samba_tool/user.py
index 22f76333ae2..07eb09b24d5 100644
--- a/python/samba/tests/samba_tool/user.py
+++ b/python/samba/tests/samba_tool/user.py
@@ -800,6 +800,47 @@ sAMAccountName: %s
self._check_posix_user(user)
self.runsubcmd("user", "delete", user["name"])
+ # Test: samba-tool user unlock
+ # This test does not verify that the command unlocks the user, it just
+ # tests the command itself. The unlock test, which unlocks locked users,
+ # is located in the 'samba4.ldap.password_lockout' test in
+ # source4/dsdb/tests/python/password_lockout.py
+ def test_unlock(self):
+
+ # try to unlock a nonexistent user, this should fail
+ nonexistentusername = "userdoesnotexist"
+ (result, out, err) = self.runsubcmd(
+ "user", "unlock", nonexistentusername)
+ self.assertCmdFail(result, "Ensure that unlock nonexistent user fails")
+ self.assertIn("Failed to unlock user '%s'" % nonexistentusername, err)
+ self.assertIn("Unable to find user", err)
+
+ # try to unlock with insufficient permissions, this should fail
+ unprivileged_username = "unprivilegedunlockuser"
+ unlocktest_username = "usertounlock"
+
+ self.runsubcmd("user", "add", unprivileged_username, "Passw0rd")
+ self.runsubcmd("user", "add", unlocktest_username, "Passw0rd")
+
+ (result, out, err) = self.runsubcmd(
+ "user", "unlock", unlocktest_username,
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (unprivileged_username,
+ "Passw0rd"))
+ self.assertCmdFail(result, "Fail with LDAP_INSUFFICIENT_ACCESS_RIGHTS")
+ self.assertIn("Failed to unlock user '%s'" % unlocktest_username, err)
+ self.assertIn("LDAP error 50 LDAP_INSUFFICIENT_ACCESS_RIGHTS", err)
+
+ self.runsubcmd("user", "delete", unprivileged_username)
+ self.runsubcmd("user", "delete", unlocktest_username)
+
+ # run unlock against test users
+ for user in self.users:
+ (result, out, err) = self.runsubcmd(
+ "user", "unlock", user["name"])
+ self.assertCmdSuccess(result, out, err, "Error running user unlock")
+ self.assertEqual(err, "", "Shouldn't be any error messages")
+
def _randomUser(self, base={}):
"""create a user with random attribute values, you can specify base attributes"""
user = {
diff --git a/source4/dsdb/tests/python/password_lockout.py b/source4/dsdb/tests/python/password_lockout.py
index cbe15c33742..445944862b8 100755
--- a/source4/dsdb/tests/python/password_lockout.py
+++ b/source4/dsdb/tests/python/password_lockout.py
@@ -17,6 +17,7 @@ sys.path.insert(0, "bin/python")
import samba
from samba.tests.subunitrun import TestProgram, SubunitOptions
+from samba.netcmd.main import cmd_sambatool
import samba.getopt as options
@@ -133,6 +134,17 @@ replace: lockoutTime
lockoutTime: 0
""")
+ def _reset_samba_tool(self, res):
+ username = res[0]["sAMAccountName"][0]
+
+ cmd = cmd_sambatool.subcommands['user'].subcommands['unlock']
+ result = cmd._run("samba-tool user unlock",
+ username,
+ "-H%s" % host_url,
+ "-U%s%%%s" % (global_creds.get_username(),
+ global_creds.get_password()))
+ self.assertEqual(result, None)
+
def _reset_ldap_userAccountControl(self, res):
self.assertTrue("userAccountControl" in res[0])
self.assertTrue("msDS-User-Account-Control-Computed" in res[0])
@@ -157,6 +169,8 @@ userAccountControl: %d
self._reset_ldap_lockoutTime(res)
elif method == "samr":
self._reset_samr(res)
+ elif method == "samba-tool":
+ self._reset_samba_tool(res)
else:
self.assertTrue(False, msg="Invalid reset method[%s]" % method)
@@ -635,6 +649,12 @@ userPassword: thatsAcomplPASS2XYZ
"samr",
initial_lastlogon_relation='greater')
+ # just test "samba-tool user unlock" command once
+ def test_userPassword_lockout_with_clear_change_krb5_ldap_samba_tool(self):
+ self._test_userPassword_lockout_with_clear_change(self.lockout1krb5_creds,
+ self.lockout2krb5_ldb,
+ "samba-tool")
+
def test_multiple_logon_krb5(self):
self._test_multiple_logon(self.lockout1krb5_creds)
diff --git a/source4/dsdb/tests/python/password_lockout_base.py b/source4/dsdb/tests/python/password_lockout_base.py
index 17ae807faf6..0f9617da1e6 100644
--- a/source4/dsdb/tests/python/password_lockout_base.py
+++ b/source4/dsdb/tests/python/password_lockout_base.py
@@ -113,6 +113,7 @@ class BasePasswordTestCase(PasswordTestCase):
print("\033[01;32m %s \033[00m\n" % msg)
attrs = [
"objectSid",
+ "sAMAccountName",
"badPwdCount",
"badPasswordTime",
"lastLogon",
--
Samba Shared Repository
More information about the samba-cvs
mailing list