[SCM] Samba Shared Repository - branch master updated

Garming Sam garming at samba.org
Fri May 11 07:07:02 UTC 2018


The branch, master has been updated
       via  569937b tests: Add tests for samba-tool passwordsettings commands
       via  7255e0c netcmd: Split 'domain passwordsettings' into a super-command
       via  0da9dbb netcmd: Small tweak to retrieving pwdProperties
       via  8a105af dsdb: Split out construct_generic_token_groups() so we can reuse it
       via  fcdb935 dsdb: Use attribute-name parameter for error message
       via  823dec9 tests: Add a test case for msDS-PasswordReversibleEncryptionEnabled
       via  17d8d47 tests: Add test for password-lockout via SAMR RPC
       via  f94f472 tests: Add PSO test case to existing password_lockout tests
       via  f5d67c1 tests: Add comments to help explain password_lockout tests
       via  78ebfcf tests: Add tests for Password Settings Objects
       via  d0a9e19 tests: Split out setUp code into separate function for reuse
       via  5974289 tests: Move repeated code into a helper function
      from  b07b4e4 loadparm: Remove unused realm_original

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 569937b8008ea8b64b4e3ead61ebc97c6c41f6b6
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Thu May 10 16:22:06 2018 +1200

    tests: Add tests for samba-tool passwordsettings commands
    
    I've added a test case for 'samba-tool domain passwordsettings set/show'
    to prove I haven't broken it. It's behaviour shouldn't have changed, but
    there was no test for it previously.
    
    We'll extend these tests in the very near future, when we add samba-tool
    support for managing PSOs.
    
    The base samba_tool test's runsubcmd() only handled commands with
    exactly one sub-command, i.e. it would handle the command 'samba-tool
    domain passwordsettings' OK, but not 'samba-tool domain passwordsettings
    set' (The command still seemed to run OK, but you wouldn't get the
    output/err back correctly). A new runsublevelcmd() function now handles
    a varying number of sub-commands.
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    
    Autobuild-User(master): Garming Sam <garming at samba.org>
    Autobuild-Date(master): Fri May 11 09:06:10 CEST 2018 on sn-devel-144

commit 7255e0ced33826d1e528c3e465105e7e194eb36e
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Thu May 3 12:12:04 2018 +1200

    netcmd: Split 'domain passwordsettings' into a super-command
    
    The show and set options are not really related to each other at all, so
    it makes sense to split the code into 2 separate commands.
    
    We also want to add separate sub-commands for PSOs in a subsequent
    patch.
    
    Because of the way the sub-command was implemented previously, it meant
    that you could specify other command-line options before the 'set' or
    'show' keyword, and the command would still be accepted. However, now
    that it's a super-command 'set'/'show' needs to be specified before any
    additional arguments, so we need to update the test code to reflect
    this.
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

commit 0da9dbbf5a762f0953f1860b8b04810d33557094
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Thu May 3 11:48:21 2018 +1200

    netcmd: Small tweak to retrieving pwdProperties
    
    Currently the 'samba-tool domain passwordsettings' command shares a
    'set' and 'show' option, but there is very little common code between
    the two. The only variable that's shared is pwd_props, but there's a
    separate API we can use to get this. This allows us to split the command
    into a super-command in a subsequent patch.
    
    Fixed up erroneous comments while I'm at it.
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

commit 8a105af76c3d432505e0a503cd32feb3808df59a
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Thu Apr 5 10:51:42 2018 +1200

    dsdb: Split out construct_generic_token_groups() so we can reuse it
    
    construct_generic_token_groups() currently works out the entire group
    membership for a user, including the primaryGroupID. We want to do the
    exact same thing for the msDS-ResultantPSO constructed attribute.
    However, construct_generic_token_groups() currently adds the resulting
    SIDs to the LDB search result, which we don't want to do for
    msDS-ResultantPSO.
    
    This patch splits the bulk of the group SID calculation work out into
    a separate function that we can reuse for msDS-ResultantPSO. basically
    this is just a straight move of the existing code. The only real change
    is the TALLOC_CTX is renamed (tmp_ctx --> mem_ctx) and now passed into
    the new function (so freeing it if an error conditions is hit is now
    done in the caller).
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

commit fcdb935e3749cab5bc7dcbb83414032c683640ef
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Thu Apr 5 10:40:03 2018 +1200

    dsdb: Use attribute-name parameter for error message
    
    We'll reuse this code for working out the msDS-ResultantPSO, so
    references to 'tokenGroups' in error messages would be misleading.
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

commit 823dec9d166b3fbe2caacdb699173601603c1101
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Mon May 7 17:33:51 2018 +1200

    tests: Add a test case for msDS-PasswordReversibleEncryptionEnabled
    
    Add a test for the 'msDS-PasswordReversibleEncryptionEnabled' attribute
    on the PSO. The Effective-PasswordReversibleEncryptionEnabled is
    based on the PSO setting (if one applies) or else the
    DOMAIN_PASSWORD_STORE_CLEARTEXT bit for the domain's pwdProperties.
    This indicates whether the user's cleartext password is to be stored
    in the supplementalCredentials attribute (as 'Primary:CLEARTEXT').
    
    The password_hash tests already text the cleartext behaviour, so I've
    added an additional test case for PSOs. Note that supplementary-
    credential information is not returned over LDAP (the password_hash
    test uses a local LDB connection), so it made more sense to extend
    the password_hash tests than to check this behaviour as part of the
    PSO tests (i.e. rather than in password_settings.py).
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

commit 17d8d475e5376a3c1a313c99cd988b0d1180c5e2
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Fri Apr 20 12:50:00 2018 +1200

    tests: Add test for password-lockout via SAMR RPC
    
    The existing password_lockout tests didn't check for changing the
    password via the SAMR password_change RPC. This patch adds a test-case
    for this, using the default domain lockout settings (which passes), and
    then repeats the same test using a PSO (which fails).
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

commit f94f472830d684683f2769d83ad1205720a3029d
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Mon Mar 19 12:56:14 2018 +1300

    tests: Add PSO test case to existing password_lockout tests
    
    This checks that the lockout settings of the PSO take effect when one is
    applied to a user. Import the password_settings code to create/apply a
    PSO with the same lockout settings that the test cases normally use.
    Then update the global settings so that the default lockout settings are
    wildly different (i.e. so the test fails if the default lockout settings
    get used instead of the PSO's).
    
    As the password-lockout tests are quite slow, I've selected test cases
    that should provide sufficient PSO coverage (rather than repeat every
    single password-lockout test case in its entirety).
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

commit f5d67c10c02b9f5023cb58914cbb1af193846178
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Wed Apr 11 12:40:59 2018 +1200

    tests: Add comments to help explain password_lockout tests
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

commit 78ebfcfa862ba05d18305be07192837681c841cb
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Mon Mar 12 15:22:24 2018 +1300

    tests: Add tests for Password Settings Objects
    
    a.k.a Fine-Grained Password Policies
    
    These tests currently all run and pass gainst Windows, but fail against
    Samba. (Actually, the permissions test case passes against Samba,
    presumably because it's enforced by the Schema permissions).
    
    Two helper classes have been added:
    - PasswordSettings: creates a PSO object and tracks its values.
    - TestUser: creates a user and tracks its password history
    This allows other existing tests (e.g. password_lockout, password_hash)
    to easily be extended to also cover PSOs.
    
    Most test cases use assert_PSO_applied(), which asserts:
    - the correct msDS-ResultantPSO attribute is returned
    - the PSO's min-password-length, complexity, and password-history
    settings are correctly enforced (this has been temporarily been hobbled
    until the basic constructed-attribute support is working).
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

commit d0a9e19114cfdaa9cd083e46d51f9e5de9e907e5
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Fri May 11 11:03:03 2018 +1200

    tests: Split out setUp code into separate function for reuse
    
    Any test that wants to change a password has to set the dSHeuristics
    and minPwdAge first in order for the password change to work. The code
    that does this is duplicated in several tests. This patch splits it out
    into a static method so that the code can be reused rather than
    duplicated.
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

commit 597428943b1b77267243dc69ecea6fda8dfc3163
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Thu Mar 15 12:44:30 2018 +1300

    tests: Move repeated code into a helper function
    
    Several tests hang all the objects they create off a unique OU.
    Having a common OU makes cleanup easier, and having a unique OU (i.e.
    adding some randomness) helps protect against one-off test failures
    (Replication between testenvs is happening in the background.
    Occasionally, when a test finishes on one testenv and moves onto the
    next testenv, that testenv may have received the replicated test
    objects from the first testenv, but has not received their deletion
    yet).
    
    Rather than copy-n-pasting this code yet again, split it out into a
    helper function.
    
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>

-----------------------------------------------------------------------

Summary of changes:
 python/samba/netcmd/domain.py                      | 355 ++++-----
 python/samba/tests/__init__.py                     |  13 +
 python/samba/tests/auth_log_pass_change.py         |  20 +-
 python/samba/tests/password_hash.py                |  40 +-
 python/samba/tests/password_hash_gpgme.py          |  67 +-
 python/samba/tests/password_test.py                |  60 ++
 python/samba/tests/pso.py                          | 271 +++++++
 python/samba/tests/samba_tool/base.py              |  17 +
 python/samba/tests/samba_tool/passwordsettings.py  |  70 ++
 selftest/knownfail.d/password_hash_gpgme           |   2 +
 selftest/knownfail.d/password_lockout              |   6 +
 selftest/knownfail.d/password_settings             |   9 +
 source4/dsdb/samdb/ldb_modules/operational.c       | 104 +--
 source4/dsdb/tests/python/acl.py                   |  16 +-
 source4/dsdb/tests/python/password_lockout.py      | 175 +++++
 source4/dsdb/tests/python/password_lockout_base.py |  60 +-
 source4/dsdb/tests/python/password_settings.py     | 802 +++++++++++++++++++++
 source4/dsdb/tests/python/passwords.py             |  22 +-
 source4/dsdb/tests/python/tombstone_reanimation.py |  16 +-
 source4/selftest/tests.py                          |   6 +
 source4/setup/tests/blackbox_setpassword.sh        |   2 +-
 source4/torture/drs/python/getncchanges.py         |   9 +-
 source4/torture/drs/python/link_conflicts.py       |   9 +-
 source4/torture/drs/python/repl_rodc.py            |  10 +-
 testprogs/blackbox/test_kinit_heimdal.sh           |   4 +-
 testprogs/blackbox/test_kinit_mit.sh               |   4 +-
 testprogs/blackbox/test_kpasswd_heimdal.sh         |   4 +-
 testprogs/blackbox/test_kpasswd_mit.sh             |   4 +-
 testprogs/blackbox/test_password_settings.sh       |  10 +-
 29 files changed, 1814 insertions(+), 373 deletions(-)
 create mode 100644 python/samba/tests/password_test.py
 create mode 100644 python/samba/tests/pso.py
 create mode 100644 python/samba/tests/samba_tool/passwordsettings.py
 create mode 100644 selftest/knownfail.d/password_hash_gpgme
 create mode 100644 selftest/knownfail.d/password_lockout
 create mode 100644 selftest/knownfail.d/password_settings
 create mode 100644 source4/dsdb/tests/python/password_settings.py


Changeset truncated at 500 lines:

diff --git a/python/samba/netcmd/domain.py b/python/samba/netcmd/domain.py
index da61711..cb2b1cc 100644
--- a/python/samba/netcmd/domain.py
+++ b/python/samba/netcmd/domain.py
@@ -1263,8 +1263,74 @@ class cmd_domain_level(Command):
         else:
             raise CommandError("invalid argument: '%s' (choose from 'show', 'raise')" % subcommand)
 
+class cmd_domain_passwordsettings_show(Command):
+    """Display current password settings for the domain."""
 
-class cmd_domain_passwordsettings(Command):
+    synopsis = "%prog [options]"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "versionopts": options.VersionOptions,
+        "credopts": options.CredentialsOptions,
+        }
+
+    takes_options = [
+        Option("-H", "--URL", help="LDB URL for database or target server", type=str,
+               metavar="URL", dest="H"),
+          ]
+
+    def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+
+        samdb = SamDB(url=H, session_info=system_session(),
+            credentials=creds, lp=lp)
+
+        domain_dn = samdb.domain_dn()
+        res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
+          attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
+                 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
+                 "lockOutObservationWindow"])
+        assert(len(res) == 1)
+        try:
+            pwd_props = int(res[0]["pwdProperties"][0])
+            pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
+            cur_min_pwd_len = int(res[0]["minPwdLength"][0])
+            # ticks -> days
+            cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
+            if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
+                cur_max_pwd_age = 0
+            else:
+                cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
+            cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
+            # ticks -> mins
+            if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
+                cur_account_lockout_duration = 0
+            else:
+                cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
+            cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
+        except Exception as e:
+            raise CommandError("Could not retrieve password properties!", e)
+
+        self.message("Password informations for domain '%s'" % domain_dn)
+        self.message("")
+        if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
+            self.message("Password complexity: on")
+        else:
+            self.message("Password complexity: off")
+        if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
+            self.message("Store plaintext passwords: on")
+        else:
+            self.message("Store plaintext passwords: off")
+        self.message("Password history length: %d" % pwd_hist_len)
+        self.message("Minimum password length: %d" % cur_min_pwd_len)
+        self.message("Minimum password age (days): %d" % cur_min_pwd_age)
+        self.message("Maximum password age (days): %d" % cur_max_pwd_age)
+        self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
+        self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
+        self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
+
+class cmd_domain_passwordsettings_set(Command):
     """Set password settings.
 
     Password complexity, password lockout policy, history length,
@@ -1274,7 +1340,7 @@ class cmd_domain_passwordsettings(Command):
     Use against a Windows DC is possible, but group policy will override it.
     """
 
-    synopsis = "%prog (show|set <options>) [options]"
+    synopsis = "%prog <options> [options]"
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -1306,9 +1372,7 @@ class cmd_domain_passwordsettings(Command):
           help="After this time is elapsed, the recorded number of attempts restarts from zero (<integer> | default).  Default is 30.", type=str),
           ]
 
-    takes_args = ["subcommand"]
-
-    def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
+    def run(self, H=None, min_pwd_age=None, max_pwd_age=None,
             quiet=False, complexity=None, store_plaintext=None, history_length=None,
             min_pwd_length=None, account_lockout_duration=None, account_lockout_threshold=None,
             reset_account_lockout_after=None, credopts=None, sambaopts=None,
@@ -1320,194 +1384,155 @@ class cmd_domain_passwordsettings(Command):
             credentials=creds, lp=lp)
 
         domain_dn = samdb.domain_dn()
-        res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
-          attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
-                 "minPwdAge", "maxPwdAge", "lockoutDuration", "lockoutThreshold",
-                 "lockOutObservationWindow"])
-        assert(len(res) == 1)
-        try:
-            pwd_props = int(res[0]["pwdProperties"][0])
-            pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
-            cur_min_pwd_len = int(res[0]["minPwdLength"][0])
-            # ticks -> days
-            cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
-            if int(res[0]["maxPwdAge"][0]) == -0x8000000000000000:
-                cur_max_pwd_age = 0
+        msgs = []
+        m = ldb.Message()
+        m.dn = ldb.Dn(samdb, domain_dn)
+        pwd_props = int(samdb.get_pwdProperties())
+
+        if complexity is not None:
+            if complexity == "on" or complexity == "default":
+                pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
+                msgs.append("Password complexity activated!")
+            elif complexity == "off":
+                pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
+                msgs.append("Password complexity deactivated!")
+
+        if store_plaintext is not None:
+            if store_plaintext == "on" or store_plaintext == "default":
+                pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
+                msgs.append("Plaintext password storage for changed passwords activated!")
+            elif store_plaintext == "off":
+                pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
+                msgs.append("Plaintext password storage for changed passwords deactivated!")
+
+        if complexity is not None or store_plaintext is not None:
+            m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
+              ldb.FLAG_MOD_REPLACE, "pwdProperties")
+
+        if history_length is not None:
+            if history_length == "default":
+                pwd_hist_len = 24
             else:
-                cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
-            cur_account_lockout_threshold = int(res[0]["lockoutThreshold"][0])
-            # ticks -> mins
-            if int(res[0]["lockoutDuration"][0]) == -0x8000000000000000:
-                cur_account_lockout_duration = 0
-            else:
-                cur_account_lockout_duration = abs(int(res[0]["lockoutDuration"][0])) / (1e7 * 60)
-            cur_reset_account_lockout_after = abs(int(res[0]["lockOutObservationWindow"][0])) / (1e7 * 60)
-        except Exception as e:
-            raise CommandError("Could not retrieve password properties!", e)
+                pwd_hist_len = int(history_length)
 
-        if subcommand == "show":
-            self.message("Password informations for domain '%s'" % domain_dn)
-            self.message("")
-            if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
-                self.message("Password complexity: on")
-            else:
-                self.message("Password complexity: off")
-            if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0:
-                self.message("Store plaintext passwords: on")
-            else:
-                self.message("Store plaintext passwords: off")
-            self.message("Password history length: %d" % pwd_hist_len)
-            self.message("Minimum password length: %d" % cur_min_pwd_len)
-            self.message("Minimum password age (days): %d" % cur_min_pwd_age)
-            self.message("Maximum password age (days): %d" % cur_max_pwd_age)
-            self.message("Account lockout duration (mins): %d" % cur_account_lockout_duration)
-            self.message("Account lockout threshold (attempts): %d" % cur_account_lockout_threshold)
-            self.message("Reset account lockout after (mins): %d" % cur_reset_account_lockout_after)
-        elif subcommand == "set":
-            msgs = []
-            m = ldb.Message()
-            m.dn = ldb.Dn(samdb, domain_dn)
-
-            if complexity is not None:
-                if complexity == "on" or complexity == "default":
-                    pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
-                    msgs.append("Password complexity activated!")
-                elif complexity == "off":
-                    pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
-                    msgs.append("Password complexity deactivated!")
-
-            if store_plaintext is not None:
-                if store_plaintext == "on" or store_plaintext == "default":
-                    pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT
-                    msgs.append("Plaintext password storage for changed passwords activated!")
-                elif store_plaintext == "off":
-                    pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT)
-                    msgs.append("Plaintext password storage for changed passwords deactivated!")
-
-            if complexity is not None or store_plaintext is not None:
-                m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
-                  ldb.FLAG_MOD_REPLACE, "pwdProperties")
-
-            if history_length is not None:
-                if history_length == "default":
-                    pwd_hist_len = 24
-                else:
-                    pwd_hist_len = int(history_length)
+            if pwd_hist_len < 0 or pwd_hist_len > 24:
+                raise CommandError("Password history length must be in the range of 0 to 24!")
 
-                if pwd_hist_len < 0 or pwd_hist_len > 24:
-                    raise CommandError("Password history length must be in the range of 0 to 24!")
+            m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
+              ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
+            msgs.append("Password history length changed!")
 
-                m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
-                  ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
-                msgs.append("Password history length changed!")
+        if min_pwd_length is not None:
+            if min_pwd_length == "default":
+                min_pwd_len = 7
+            else:
+                min_pwd_len = int(min_pwd_length)
 
-            if min_pwd_length is not None:
-                if min_pwd_length == "default":
-                    min_pwd_len = 7
-                else:
-                    min_pwd_len = int(min_pwd_length)
+            if min_pwd_len < 0 or min_pwd_len > 14:
+                raise CommandError("Minimum password length must be in the range of 0 to 14!")
 
-                if min_pwd_len < 0 or min_pwd_len > 14:
-                    raise CommandError("Minimum password length must be in the range of 0 to 14!")
+            m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
+              ldb.FLAG_MOD_REPLACE, "minPwdLength")
+            msgs.append("Minimum password length changed!")
 
-                m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
-                  ldb.FLAG_MOD_REPLACE, "minPwdLength")
-                msgs.append("Minimum password length changed!")
+        if min_pwd_age is not None:
+            if min_pwd_age == "default":
+                min_pwd_age = 1
+            else:
+                min_pwd_age = int(min_pwd_age)
 
-            if min_pwd_age is not None:
-                if min_pwd_age == "default":
-                    min_pwd_age = 1
-                else:
-                    min_pwd_age = int(min_pwd_age)
+            if min_pwd_age < 0 or min_pwd_age > 998:
+                raise CommandError("Minimum password age must be in the range of 0 to 998!")
 
-                if min_pwd_age < 0 or min_pwd_age > 998:
-                    raise CommandError("Minimum password age must be in the range of 0 to 998!")
+            # days -> ticks
+            min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
 
-                # days -> ticks
-                min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
+            m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
+              ldb.FLAG_MOD_REPLACE, "minPwdAge")
+            msgs.append("Minimum password age changed!")
 
-                m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
-                  ldb.FLAG_MOD_REPLACE, "minPwdAge")
-                msgs.append("Minimum password age changed!")
+        if max_pwd_age is not None:
+            if max_pwd_age == "default":
+                max_pwd_age = 43
+            else:
+                max_pwd_age = int(max_pwd_age)
 
-            if max_pwd_age is not None:
-                if max_pwd_age == "default":
-                    max_pwd_age = 43
-                else:
-                    max_pwd_age = int(max_pwd_age)
+            if max_pwd_age < 0 or max_pwd_age > 999:
+                raise CommandError("Maximum password age must be in the range of 0 to 999!")
 
-                if max_pwd_age < 0 or max_pwd_age > 999:
-                    raise CommandError("Maximum password age must be in the range of 0 to 999!")
+            # days -> ticks
+            if max_pwd_age == 0:
+                max_pwd_age_ticks = -0x8000000000000000
+            else:
+                max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
 
-                # days -> ticks
-                if max_pwd_age == 0:
-                    max_pwd_age_ticks = -0x8000000000000000
-                else:
-                    max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
+            m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
+              ldb.FLAG_MOD_REPLACE, "maxPwdAge")
+            msgs.append("Maximum password age changed!")
 
-                m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
-                  ldb.FLAG_MOD_REPLACE, "maxPwdAge")
-                msgs.append("Maximum password age changed!")
+        if account_lockout_duration is not None:
+            if account_lockout_duration == "default":
+                account_lockout_duration = 30
+            else:
+                account_lockout_duration = int(account_lockout_duration)
 
-            if account_lockout_duration is not None:
-                if account_lockout_duration == "default":
-                    account_lockout_duration = 30
-                else:
-                    account_lockout_duration = int(account_lockout_duration)
+            if account_lockout_duration < 0 or account_lockout_duration > 99999:
+                raise CommandError("Maximum password age must be in the range of 0 to 99999!")
 
-                if account_lockout_duration < 0 or account_lockout_duration > 99999:
-                    raise CommandError("Maximum password age must be in the range of 0 to 99999!")
+            # minutes -> ticks
+            if account_lockout_duration == 0:
+                account_lockout_duration_ticks = -0x8000000000000000
+            else:
+                account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
 
-                # days -> ticks
-                if account_lockout_duration == 0:
-                    account_lockout_duration_ticks = -0x8000000000000000
-                else:
-                    account_lockout_duration_ticks = -int(account_lockout_duration * (60 * 1e7))
+            m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
+              ldb.FLAG_MOD_REPLACE, "lockoutDuration")
+            msgs.append("Account lockout duration changed!")
 
-                m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks),
-                  ldb.FLAG_MOD_REPLACE, "lockoutDuration")
-                msgs.append("Account lockout duration changed!")
+        if account_lockout_threshold is not None:
+            if account_lockout_threshold == "default":
+                account_lockout_threshold = 0
+            else:
+                account_lockout_threshold = int(account_lockout_threshold)
 
-            if account_lockout_threshold is not None:
-                if account_lockout_threshold == "default":
-                    account_lockout_threshold = 0
-                else:
-                    account_lockout_threshold = int(account_lockout_threshold)
+            m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
+              ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
+            msgs.append("Account lockout threshold changed!")
 
-                m["lockoutThreshold"] = ldb.MessageElement(str(account_lockout_threshold),
-                  ldb.FLAG_MOD_REPLACE, "lockoutThreshold")
-                msgs.append("Account lockout threshold changed!")
+        if reset_account_lockout_after is not None:
+            if reset_account_lockout_after == "default":
+                reset_account_lockout_after = 30
+            else:
+                reset_account_lockout_after = int(reset_account_lockout_after)
 
-            if reset_account_lockout_after is not None:
-                if reset_account_lockout_after == "default":
-                    reset_account_lockout_after = 30
-                else:
-                    reset_account_lockout_after = int(reset_account_lockout_after)
+            if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
+                raise CommandError("Maximum password age must be in the range of 0 to 99999!")
 
-                if reset_account_lockout_after < 0 or reset_account_lockout_after > 99999:
-                    raise CommandError("Maximum password age must be in the range of 0 to 99999!")
+            # minutes -> ticks
+            if reset_account_lockout_after == 0:
+                reset_account_lockout_after_ticks = -0x8000000000000000
+            else:
+                reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
 
-                # days -> ticks
-                if reset_account_lockout_after == 0:
-                    reset_account_lockout_after_ticks = -0x8000000000000000
-                else:
-                    reset_account_lockout_after_ticks = -int(reset_account_lockout_after * (60 * 1e7))
+            m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
+              ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
+            msgs.append("Duration to reset account lockout after changed!")
 
-                m["lockOutObservationWindow"] = ldb.MessageElement(str(reset_account_lockout_after_ticks),
-                  ldb.FLAG_MOD_REPLACE, "lockOutObservationWindow")
-                msgs.append("Duration to reset account lockout after changed!")
+        if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
+            raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
 
-            if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
-                raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
+        if len(m) == 0:
+            raise CommandError("You must specify at least one option to set. Try --help")
+        samdb.modify(m)
+        msgs.append("All changes applied successfully!")
+        self.message("\n".join(msgs))
 
-            if len(m) == 0:
-                raise CommandError("You must specify at least one option to set. Try --help")
-            samdb.modify(m)
-            msgs.append("All changes applied successfully!")
-            self.message("\n".join(msgs))
-        else:
-            raise CommandError("Wrong argument '%s'!" % subcommand)
+class cmd_domain_passwordsettings(SuperCommand):
+    """Manage password policy settings."""
 
+    subcommands = {}
+    subcommands["show"] = cmd_domain_passwordsettings_show()
+    subcommands["set"] = cmd_domain_passwordsettings_set()
 
 class cmd_domain_classicupgrade(Command):
     """Upgrade from Samba classic (NT4-like) database to Samba AD DC database.
diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py
index bc8c185..61036b5 100644
--- a/python/samba/tests/__init__.py
+++ b/python/samba/tests/__init__.py
@@ -36,6 +36,7 @@ import re
 import samba.auth
 import samba.dcerpc.base
 from samba.compat import PY3, text_type
+from random import randint
 if not PY3:
     # Py2 only
     from samba.samdb import SamDB
@@ -475,3 +476,15 @@ def delete_force(samdb, dn, **kwargs):
     except ldb.LdbError as error:
         (num, errstr) = error.args
         assert num == ldb.ERR_NO_SUCH_OBJECT, "ldb.delete() failed: %s" % errstr
+
+def create_test_ou(samdb, name):
+    """Creates a unique OU for the test"""
+
+    # Add some randomness to the test OU. Replication between the testenvs is
+    # constantly happening in the background. Deletion of the last test's
+    # objects can be slow to replicate out. So the OU created by a previous
+    # testenv may still exist at the point that tests start on another testenv.
+    rand = randint(1, 10000000)
+    dn = "OU=%s%d,%s" %(name, rand, samdb.get_default_basedn())
+    samdb.add({ "dn": dn, "objectclass": "organizationalUnit"})
+    return dn
diff --git a/python/samba/tests/auth_log_pass_change.py b/python/samba/tests/auth_log_pass_change.py
index 8890694..1bbb0ea 100644
--- a/python/samba/tests/auth_log_pass_change.py
+++ b/python/samba/tests/auth_log_pass_change.py
@@ -29,6 +29,7 @@ from samba.net import Net
 import samba
 from subprocess import call
 from ldb import LdbError
+from samba.tests.password_test import PasswordCommon
 
 USER_NAME = "authlogtestuser"
 USER_PASS = samba.generate_random_password(32, 32)
@@ -53,26 +54,11 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
         base_dn = self.ldb.domain_dn()
         print("base_dn %s" % base_dn)
 
-        # Get the old "dSHeuristics" if it was set
-        dsheuristics = self.ldb.get_dsheuristics()
+        # permit password changes during this test
+        PasswordCommon.allow_password_changes(self, self.ldb)
 
-        # Set the "dSHeuristics" to activate the correct "userPassword"
-        # behaviour
-        self.ldb.set_dsheuristics("000000001")
-
-        # Reset the "dSHeuristics" as they were before
-        self.addCleanup(self.ldb.set_dsheuristics, dsheuristics)
-
-        # Get the old "minPwdAge"
-        minPwdAge = self.ldb.get_minPwdAge()
-
-        # Set it temporarily to "0"
-        self.ldb.set_minPwdAge("0")
         self.base_dn = self.ldb.domain_dn()
 
-        # Reset the "minPwdAge" as it was before
-        self.addCleanup(self.ldb.set_minPwdAge, minPwdAge)
-
         # (Re)adds the test user USER_NAME with password USER_PASS
         delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn)
         self.ldb.add({
diff --git a/python/samba/tests/password_hash.py b/python/samba/tests/password_hash.py
index a3a74aa..4c4dbcf 100644
--- a/python/samba/tests/password_hash.py
+++ b/python/samba/tests/password_hash.py
@@ -29,6 +29,7 @@ from samba.dcerpc import drsblobs
 from samba.dcerpc.samr import DOMAIN_PASSWORD_STORE_CLEARTEXT
 from samba.dsdb import UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
 from samba.tests import delete_force
+from samba.tests.password_test import PasswordCommon
 import ldb
 import samba
 import binascii
@@ -69,6 +70,17 @@ class PassWordHashTests(TestCase):
         self.lp = samba.tests.env_loadparm()
         super(PassWordHashTests, self).setUp()


-- 
Samba Shared Repository



More information about the samba-cvs mailing list