예제 #1
0
    def test_pso_invalid_location(self):
        """Tests that PSOs in an invalid location have no effect"""

        # PSOs should only be able to be created within a Password Settings
        # Container object. Trying to create one under an OU should fail
        try:
            rogue_pso = PasswordSettings("rogue-PSO",
                                         self.ldb,
                                         precedence=1,
                                         complexity=False,
                                         password_len=20,
                                         container=self.ou)
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEqual(num, ldb.ERR_NAMING_VIOLATION, msg)
            # Windows returns 2099 (Illegal superior), Samba returns 2037
            # (Naming violation - "not a valid child class")
            self.assertTrue('00002099' in msg or '00002037' in msg, msg)

        # we can't create Password Settings Containers under an OU either
        try:
            rogue_psc = "CN=Rogue-PSO-container,%s" % self.ou
            self.ldb.add({
                "dn": rogue_psc,
                "objectclass": "msDS-PasswordSettingsContainer"
            })
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEqual(num, ldb.ERR_NAMING_VIOLATION, msg)
            self.assertTrue('00002099' in msg or '00002037' in msg, msg)

        base_dn = self.ldb.get_default_basedn()
        rogue_psc = "CN=Rogue-PSO-container,CN=Computers,%s" % base_dn
        self.ldb.add({
            "dn": rogue_psc,
            "objectclass": "msDS-PasswordSettingsContainer"
        })

        rogue_pso = PasswordSettings("rogue-PSO",
                                     self.ldb,
                                     precedence=1,
                                     container=rogue_psc,
                                     password_len=20)
        self.add_obj_cleanup([rogue_pso.dn, rogue_psc])

        # apply the PSO to a group and check it has no effect on the user
        user = self.add_user("testuser")
        group = self.add_group("Group-1")
        rogue_pso.apply_to(group)
        self.set_attribute(group, "member", user.dn)
        self.assert_PSO_applied(user, self.pwd_defaults)

        # apply the PSO directly to the user and check it has no effect
        rogue_pso.apply_to(user.dn)
        self.assert_PSO_applied(user, self.pwd_defaults)
예제 #2
0
    def test_supplementalCredentials_cleartext_pso(self):
        """Checks that a PSO's cleartext setting can override the domain's"""

        # create a user that stores plain-text passwords
        self.add_user(clear_text=True)

        # check that clear-text is present in the supplementary-credentials
        self.assert_cleartext(expect_cleartext=True, password=USER_PASS)

        # create a PSO overriding the plain-text setting & apply it to the user
        no_plaintext_pso = PasswordSettings("no-plaintext-PSO",
                                            self.ldb,
                                            precedence=200,
                                            store_plaintext=False)
        self.addCleanup(self.ldb.delete, no_plaintext_pso.dn)
        userdn = "cn=" + USER_NAME + ",cn=users," + self.base_dn
        no_plaintext_pso.apply_to(userdn)

        # set the password to update the cleartext password stored
        new_password = samba.generate_random_password(32, 32)
        self.ldb.setpassword("(sAMAccountName=%s)" % USER_NAME, new_password)

        # this time cleartext shouldn't be in the supplementary creds
        self.assert_cleartext(expect_cleartext=False)

        # unapply PSO, update password, and check we get the cleartext again
        no_plaintext_pso.unapply(userdn)
        new_password = samba.generate_random_password(32, 32)
        self.ldb.setpassword("(sAMAccountName=%s)" % USER_NAME, new_password)
        self.assert_cleartext(expect_cleartext=True, password=new_password)

        # Now update the domain setting and check we no longer get cleartext
        self.set_store_cleartext(False)
        new_password = samba.generate_random_password(32, 32)
        self.ldb.setpassword("(sAMAccountName=%s)" % USER_NAME, new_password)
        self.assert_cleartext(expect_cleartext=False)

        # create a PSO overriding the domain setting & apply it to the user
        plaintext_pso = PasswordSettings("plaintext-PSO",
                                         self.ldb,
                                         precedence=100,
                                         store_plaintext=True)
        self.addCleanup(self.ldb.delete, plaintext_pso.dn)
        plaintext_pso.apply_to(userdn)
        new_password = samba.generate_random_password(32, 32)
        self.ldb.setpassword("(sAMAccountName=%s)" % USER_NAME, new_password)
        self.assert_cleartext(expect_cleartext=True, password=new_password)
예제 #3
0
    def test_pso_min_age(self):
        """Tests that a PSO's min-age is enforced"""
        pso = PasswordSettings("min-age-PSO", self.ldb, password_len=10,
                               password_age_min=2, complexity=False)
        self.add_obj_cleanup([pso.dn])

        # create a user and apply the PSO
        user = self.add_user("testuser")
        pso.apply_to(user.dn)
        self.assertTrue(user.get_resultant_PSO() == pso.dn)

        # changing the password immediately should fail, even if password is valid
        valid_password = "******"
        self.assert_password_invalid(user, valid_password)
        # then trying the same password later should succeed
        time.sleep(pso.password_age_min + 0.5)
        self.assert_password_valid(user, valid_password)
예제 #4
0
    def test_pso_min_age(self):
        """Tests that a PSO's min-age is enforced"""
        pso = PasswordSettings("min-age-PSO", self.ldb, password_len=10,
                               password_age_min=1, complexity=False)
        self.add_obj_cleanup([pso.dn])

        # create a user and apply the PSO
        user = self.add_user("testuser")
        pso.apply_to(user.dn)
        self.assertTrue(user.get_resultant_PSO() == pso.dn)

        # changing the password immediately should fail, even if password is valid
        valid_password = "******"
        self.assert_password_invalid(user, valid_password)
        # then trying the same password later (min-age=1sec) should succeed
        time.sleep(1.5)
        self.assert_password_valid(user, valid_password)
예제 #5
0
    def test_pso_none_applied(self):
        """Tests cases where no Resultant PSO should be returned"""

        # create a PSO that we will check *doesn't* get returned
        dummy_pso = PasswordSettings("dummy-PSO", self.ldb, password_len=20)
        self.add_obj_cleanup([dummy_pso.dn])

        # you can apply a PSO to other objects (like OUs), but the resultantPSO
        # attribute should only be returned for users
        dummy_pso.apply_to(str(self.ou))
        res = self.ldb.search(self.ou, attrs=['msDS-ResultantPSO'])
        self.assertFalse('msDS-ResultantPSO' in res[0])

        # create a dummy user and apply the PSO
        user = self.add_user("testuser")
        dummy_pso.apply_to(user.dn)
        self.assertTrue(user.get_resultant_PSO() == dummy_pso.dn)

        try:
            # now clear the ADS_UF_NORMAL_ACCOUNT flag for the user, which should
            # mean a resultant PSO is no longer returned (we're essentially turning
            # the user into a DC here, which is a little overkill but tests
            # behaviour as per the Windows specification)
            self.set_attribute(user.dn,
                               "userAccountControl",
                               str(dsdb.UF_WORKSTATION_TRUST_ACCOUNT),
                               operation=FLAG_MOD_REPLACE)
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.fail("Failed to change user into a workstation: {msg}")
        self.assertIsNone(user.get_resultant_PSO())

        try:
            # reset it back to a normal user account
            self.set_attribute(user.dn,
                               "userAccountControl",
                               str(dsdb.UF_NORMAL_ACCOUNT),
                               operation=FLAG_MOD_REPLACE)
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.fail("Failed to change user back into a user: {msg}")
        self.assertTrue(user.get_resultant_PSO() == dummy_pso.dn)

        # no PSO should be returned if RID is equal to DOMAIN_USER_RID_KRBTGT
        # (note this currently fails against Windows due to a Windows bug)
        krbtgt_user = "******" % self.ldb.domain_dn()
        dummy_pso.apply_to(krbtgt_user)
        res = self.ldb.search(krbtgt_user, attrs=['msDS-ResultantPSO'])
        self.assertFalse('msDS-ResultantPSO' in res[0])
예제 #6
0
    def _create_pso(self, pso_name):
        """Creates a PSO for use in other tests"""
        # the new PSO will take the current domain settings by default
        pso_settings = PasswordSettings(None, self.ldb)
        pso_settings.name = pso_name
        pso_settings.password_len = 10
        pso_settings.precedence = 200

        (result, out, err) = self.runsublevelcmd("domain", ("passwordsettings",
                                                 "pso", "create"), pso_name,
                                                 "200", "--min-pwd-length=10",
                                                 "-H", self.server,
                                                 self.user_auth)
        # make sure we clean-up after the test completes
        pso_settings.dn = "CN=%s,%s" %(pso_name, self.pso_container)
        self.obj_cleanup.append(pso_settings.dn)

        # sanity-check the cmd was successful
        self.assertCmdSuccess(result, out, err)
        self.assertEquals(err,"","Shouldn't be any error messages")
        self.assertIn("successfully created", out)
        self.check_pso(pso_name, pso_settings)

        return pso_settings
예제 #7
0
    def setUp(self):
        super(PasswordSettingsTestCase, self).setUp()

        self.host_url = "ldap://%s" % env_get_var_value("SERVER_IP")
        self.ldb = samba.tests.connect_samdb(self.host_url)

        # create a temp OU to put this test's users into
        self.ou = samba.tests.create_test_ou(self.ldb, "password_settings")

        # update DC to allow password changes for the duration of this test
        self.allow_password_changes()

        # store the current password-settings for the domain
        self.pwd_defaults = PasswordSettings(None, self.ldb)
        self.test_objs = []
예제 #8
0
    def test_pso_max_age(self):
        """Tests that a PSO's max-age is used"""

        # create PSOs that use the domain's max-age +/- 1 day
        domain_max_age = self.pwd_defaults.password_age_max
        day_in_secs = 60 * 60 * 24
        higher_max_age = domain_max_age + day_in_secs
        lower_max_age = domain_max_age - day_in_secs
        longer_pso = PasswordSettings("longer-age-PSO",
                                      self.ldb,
                                      precedence=5,
                                      password_age_max=higher_max_age)
        shorter_pso = PasswordSettings("shorter-age-PSO",
                                       self.ldb,
                                       precedence=1,
                                       password_age_max=lower_max_age)
        self.add_obj_cleanup([longer_pso.dn, shorter_pso.dn])

        user = self.add_user("testuser")

        # we can't wait around long enough for the max-age to expire, so
        # instead just check the msDS-UserPasswordExpiryTimeComputed for
        # the user
        attrs = ['msDS-UserPasswordExpiryTimeComputed']
        res = self.ldb.search(user.dn, attrs=attrs)
        domain_expiry = int(res[0]['msDS-UserPasswordExpiryTimeComputed'][0])

        # apply the longer PSO and check the expiry-time becomes longer
        longer_pso.apply_to(user.dn)
        self.assertTrue(user.get_resultant_PSO() == longer_pso.dn)
        res = self.ldb.search(user.dn, attrs=attrs)
        new_expiry = int(res[0]['msDS-UserPasswordExpiryTimeComputed'][0])

        # use timestamp diff of 1 day - 1 minute. The new expiry should still
        # be greater than this, without getting into nano-second granularity
        approx_timestamp_diff = (day_in_secs - 60) * (1e7)
        self.assertTrue(new_expiry > domain_expiry + approx_timestamp_diff)

        # apply the shorter PSO and check the expiry-time is shorter
        shorter_pso.apply_to(user.dn)
        self.assertTrue(user.get_resultant_PSO() == shorter_pso.dn)
        res = self.ldb.search(user.dn, attrs=attrs)
        new_expiry = int(res[0]['msDS-UserPasswordExpiryTimeComputed'][0])
        self.assertTrue(new_expiry < domain_expiry - approx_timestamp_diff)
예제 #9
0
    def test_supplementalCredentials_cleartext_pso(self):
        """Checks that a PSO's cleartext setting can override the domain's"""

        # create a user that stores plain-text passwords
        self.add_user(clear_text=True)

        # check that clear-text is present in the supplementary-credentials
        self.assert_cleartext(expect_cleartext=True, password=USER_PASS)

        # create a PSO overriding the plain-text setting & apply it to the user
        no_plaintext_pso = PasswordSettings("no-plaintext-PSO", self.ldb,
                                            precedence=200,
                                            store_plaintext=False)
        self.addCleanup(self.ldb.delete, no_plaintext_pso.dn)
        userdn = "cn=" + USER_NAME + ",cn=users," + self.base_dn
        no_plaintext_pso.apply_to(userdn)

        # set the password to update the cleartext password stored
        new_password = samba.generate_random_password(32, 32)
        self.ldb.setpassword("(sAMAccountName=%s)" % USER_NAME, new_password)

        # this time cleartext shouldn't be in the supplementary creds
        self.assert_cleartext(expect_cleartext=False)

        # unapply PSO, update password, and check we get the cleartext again
        no_plaintext_pso.unapply(userdn)
        new_password = samba.generate_random_password(32, 32)
        self.ldb.setpassword("(sAMAccountName=%s)" % USER_NAME, new_password)
        self.assert_cleartext(expect_cleartext=True, password=new_password)

        # Now update the domain setting and check we no longer get cleartext
        self.set_store_cleartext(False)
        new_password = samba.generate_random_password(32, 32)
        self.ldb.setpassword("(sAMAccountName=%s)" % USER_NAME, new_password)
        self.assert_cleartext(expect_cleartext=False)

        # create a PSO overriding the domain setting & apply it to the user
        plaintext_pso = PasswordSettings("plaintext-PSO", self.ldb,
                                         precedence=100, store_plaintext=True)
        self.addCleanup(self.ldb.delete, plaintext_pso.dn)
        plaintext_pso.apply_to(userdn)
        new_password = samba.generate_random_password(32, 32)
        self.ldb.setpassword("(sAMAccountName=%s)" % USER_NAME, new_password)
        self.assert_cleartext(expect_cleartext=True, password=new_password)
예제 #10
0
    def test_pso_invalid_location(self):
        """Tests that PSOs in an invalid location have no effect"""

        # PSOs should only be able to be created within a Password Settings
        # Container object. Trying to create one under an OU should fail
        try:
            rogue_pso = PasswordSettings("rogue-PSO", self.ldb, precedence=1,
                                         complexity=False, password_len=20,
                                         container=self.ou)
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEquals(num, ldb.ERR_NAMING_VIOLATION, msg)
            # Windows returns 2099 (Illegal superior), Samba returns 2037
            # (Naming violation - "not a valid child class")
            self.assertTrue('00002099' in msg or '00002037' in msg, msg)

        # we can't create Password Settings Containers under an OU either
        try:
            rogue_psc = "CN=Rogue-PSO-container,%s" % self.ou
            self.ldb.add({"dn": rogue_psc,
                          "objectclass": "msDS-PasswordSettingsContainer"})
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEquals(num, ldb.ERR_NAMING_VIOLATION, msg)
            self.assertTrue('00002099' in msg or '00002037' in msg, msg)

        base_dn = self.ldb.get_default_basedn()
        rogue_psc = "CN=Rogue-PSO-container,CN=Computers,%s" % base_dn
        self.ldb.add({"dn": rogue_psc,
                      "objectclass": "msDS-PasswordSettingsContainer"})

        rogue_pso = PasswordSettings("rogue-PSO", self.ldb, precedence=1,
                                     container=rogue_psc, password_len=20)
        self.add_obj_cleanup([rogue_pso.dn, rogue_psc])

        # apply the PSO to a group and check it has no effect on the user
        user = self.add_user("testuser")
        group = self.add_group("Group-1")
        rogue_pso.apply_to(group)
        self.set_attribute(group, "member", user.dn)
        self.assert_PSO_applied(user, self.pwd_defaults)

        # apply the PSO directly to the user and check it has no effect
        rogue_pso.apply_to(user.dn)
        self.assert_PSO_applied(user, self.pwd_defaults)
예제 #11
0
    def test_pso_none_applied(self):
        """Tests cases where no Resultant PSO should be returned"""

        # create a PSO that we will check *doesn't* get returned
        dummy_pso = PasswordSettings("dummy-PSO", self.ldb, password_len=20)
        self.add_obj_cleanup([dummy_pso.dn])

        # you can apply a PSO to other objects (like OUs), but the resultantPSO
        # attribute should only be returned for users
        dummy_pso.apply_to(str(self.ou))
        res = self.ldb.search(self.ou, attrs=['msDS-ResultantPSO'])
        self.assertFalse('msDS-ResultantPSO' in res[0])

        # create a dummy user and apply the PSO
        user = self.add_user("testuser")
        dummy_pso.apply_to(user.dn)
        self.assertTrue(user.get_resultant_PSO() == dummy_pso.dn)

        # now clear the ADS_UF_NORMAL_ACCOUNT flag for the user, which should
        # mean a resultant PSO is no longer returned (we're essentially turning
        # the user into a DC here, which is a little overkill but tests
        # behaviour as per the Windows specification)
        self.set_attribute(user.dn, "userAccountControl",
                           str(dsdb.UF_WORKSTATION_TRUST_ACCOUNT),
                           operation=FLAG_MOD_REPLACE)
        self.assertTrue(user.get_resultant_PSO() == None)

        # reset it back to a normal user account
        self.set_attribute(user.dn, "userAccountControl",
                           str(dsdb.UF_NORMAL_ACCOUNT),
                           operation=FLAG_MOD_REPLACE)
        self.assertTrue(user.get_resultant_PSO() == dummy_pso.dn)

        # no PSO should be returned if RID is equal to DOMAIN_USER_RID_KRBTGT
        # (note this currently fails against Windows due to a Windows bug)
        krbtgt_user = "******" % self.ldb.domain_dn()
        dummy_pso.apply_to(krbtgt_user)
        res = self.ldb.search(krbtgt_user, attrs=['msDS-ResultantPSO'])
        self.assertFalse('msDS-ResultantPSO' in res[0])
예제 #12
0
    def test_pso_max_age(self):
        """Tests that a PSO's max-age is used"""

        # create PSOs that use the domain's max-age +/- 1 day
        domain_max_age = self.pwd_defaults.password_age_max
        day_in_secs = 60 * 60 * 24
        higher_max_age = domain_max_age + day_in_secs
        lower_max_age = domain_max_age - day_in_secs
        longer_pso = PasswordSettings("longer-age-PSO", self.ldb, precedence=5,
                                      password_age_max=higher_max_age)
        shorter_pso = PasswordSettings("shorter-age-PSO", self.ldb,
                                       precedence=1,
                                       password_age_max=lower_max_age)
        self.add_obj_cleanup([longer_pso.dn, shorter_pso.dn])

        user = self.add_user("testuser")

        # we can't wait around long enough for the max-age to expire, so instead
        # just check the msDS-UserPasswordExpiryTimeComputed for the user
        attrs=['msDS-UserPasswordExpiryTimeComputed']
        res = self.ldb.search(user.dn, attrs=attrs)
        domain_expiry = int(res[0]['msDS-UserPasswordExpiryTimeComputed'][0])

        # apply the longer PSO and check the expiry-time becomes longer
        longer_pso.apply_to(user.dn)
        self.assertTrue(user.get_resultant_PSO() == longer_pso.dn)
        res = self.ldb.search(user.dn, attrs=attrs)
        new_expiry = int(res[0]['msDS-UserPasswordExpiryTimeComputed'][0])

        # use timestamp diff of 1 day - 1 minute. The new expiry should still
        # be greater than this, without getting into nano-second granularity
        approx_timestamp_diff = (day_in_secs - 60) * (1e7)
        self.assertTrue(new_expiry > domain_expiry + approx_timestamp_diff)

        # apply the shorter PSO and check the expiry-time is shorter
        shorter_pso.apply_to(user.dn)
        self.assertTrue(user.get_resultant_PSO() == shorter_pso.dn)
        res = self.ldb.search(user.dn, attrs=attrs)
        new_expiry = int(res[0]['msDS-UserPasswordExpiryTimeComputed'][0])
        self.assertTrue(new_expiry < domain_expiry - approx_timestamp_diff)
예제 #13
0
    def _create_pso(self, pso_name):
        """Creates a PSO for use in other tests"""
        # the new PSO will take the current domain settings by default
        pso_settings = PasswordSettings(None, self.ldb)
        pso_settings.name = pso_name
        pso_settings.password_len = 10
        pso_settings.precedence = 200

        (result, out,
         err) = self.runsublevelcmd("domain",
                                    ("passwordsettings", "pso", "create"),
                                    pso_name, "200", "--min-pwd-length=10",
                                    "-H", self.server, self.user_auth)
        # make sure we clean-up after the test completes
        pso_settings.dn = "CN=%s,%s" % (pso_name, self.pso_container)
        self.obj_cleanup.append(pso_settings.dn)

        # sanity-check the cmd was successful
        self.assertCmdSuccess(result, out, err)
        self.assertEqual(err, "", "Shouldn't be any error messages")
        self.assertIn("successfully created", out)
        self.check_pso(pso_name, pso_settings)

        return pso_settings
예제 #14
0
    def test_pso_permissions(self):
        """Checks that regular users can't modify/view PSO objects"""

        user = self.add_user("testuser")

        # get an ldb connection with the new user's privileges
        user_ldb = self.get_ldb_connection("testuser", user.get_password(),
                                           self.host_url)

        # regular users should not be able to create a PSO (at least, not in
        # the default Password Settings container)
        try:
            priv_pso = PasswordSettings("priv-PSO", user_ldb, password_len=20)
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEqual(num, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, msg)

        # create a PSO as the admin user
        priv_pso = PasswordSettings("priv-PSO", self.ldb, password_len=20)
        self.add_obj_cleanup([priv_pso.dn])

        # regular users should not be able to apply a PSO to a user
        try:
            self.set_attribute(priv_pso.dn,
                               "msDS-PSOAppliesTo",
                               user.dn,
                               samdb=user_ldb)
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEqual(num, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, msg)
            self.assertTrue('00002098' in msg, msg)

        self.set_attribute(priv_pso.dn,
                           "msDS-PSOAppliesTo",
                           user.dn,
                           samdb=self.ldb)

        # regular users should not be able to change a PSO's precedence
        try:
            priv_pso.set_precedence(100, samdb=user_ldb)
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEqual(num, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, msg)
            self.assertTrue('00002098' in msg, msg)

        priv_pso.set_precedence(100, samdb=self.ldb)

        # regular users should not be able to view a PSO's settings
        pso_attrs = [
            "msDS-PSOAppliesTo", "msDS-PasswordSettingsPrecedence",
            "msDS-PasswordHistoryLength", "msDS-LockoutThreshold",
            "msDS-PasswordComplexityEnabled"
        ]

        # users can see the PSO object's DN, but not its attributes
        res = user_ldb.search(priv_pso.dn,
                              scope=ldb.SCOPE_BASE,
                              attrs=pso_attrs)
        self.assertTrue(str(priv_pso.dn) == str(res[0].dn))
        for attr in pso_attrs:
            self.assertFalse(attr in res[0])

        # whereas admin users can see everything
        res = self.ldb.search(priv_pso.dn,
                              scope=ldb.SCOPE_BASE,
                              attrs=pso_attrs)
        for attr in pso_attrs:
            self.assertTrue(attr in res[0])

        # check replace/delete operations can't be performed by regular users
        operations = [FLAG_MOD_REPLACE, FLAG_MOD_DELETE]

        for oper in operations:
            try:
                self.set_attribute(priv_pso.dn,
                                   "msDS-PSOAppliesTo",
                                   user.dn,
                                   samdb=user_ldb,
                                   operation=oper)
                self.fail()
            except ldb.LdbError as e:
                (num, msg) = e.args
                self.assertEqual(num, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, msg)
                self.assertTrue('00002098' in msg, msg)

            # ...but can be performed by the admin user
            self.set_attribute(priv_pso.dn,
                               "msDS-PSOAppliesTo",
                               user.dn,
                               samdb=self.ldb,
                               operation=oper)
예제 #15
0
    def test_pso_basics(self):
        """Simple tests that a PSO takes effect when applied to a group or user"""

        # create some PSOs that vary in priority and basic password-len
        best_pso = PasswordSettings("highest-priority-PSO", self.ldb,
                                    precedence=5, password_len=16,
                                    history_len=6)
        medium_pso = PasswordSettings("med-priority-PSO", self.ldb,
                                      precedence=15, password_len=10,
                                      history_len=4)
        worst_pso = PasswordSettings("lowest-priority-PSO", self.ldb,
                                     precedence=100, complexity=False,
                                     password_len=4, history_len=2)

        # handle PSO clean-up (as they're outside the top-level test OU)
        self.add_obj_cleanup([worst_pso.dn, medium_pso.dn, best_pso.dn])

        # create some groups and apply the PSOs to the groups
        group1 = self.add_group("Group-1")
        group2 = self.add_group("Group-2")
        group3 = self.add_group("Group-3")
        group4 = self.add_group("Group-4")
        worst_pso.apply_to(group1)
        medium_pso.apply_to(group2)
        best_pso.apply_to(group3)
        worst_pso.apply_to(group4)

        # create a user and check the default settings apply to it
        user = self.add_user("testuser")
        self.assert_PSO_applied(user, self.pwd_defaults)

        # add user to a group. Check that the group's PSO applies to the user
        self.set_attribute(group1, "member", user.dn)
        self.assert_PSO_applied(user, worst_pso)

        # add the user to a group with a higher precedence PSO and and check
        # that now trumps the previous PSO
        self.set_attribute(group2, "member", user.dn)
        self.assert_PSO_applied(user, medium_pso)

        # add the user to the remaining groups. The highest precedence PSO
        # should now take effect
        self.set_attribute(group3, "member", user.dn)
        self.set_attribute(group4, "member", user.dn)
        self.assert_PSO_applied(user, best_pso)

        # delete a group membership and check the PSO changes
        self.set_attribute(group3, "member", user.dn, operation=FLAG_MOD_DELETE)
        self.assert_PSO_applied(user, medium_pso)

        # apply the low-precedence PSO directly to the user
        # (directly applied PSOs should trump higher precedence group PSOs)
        worst_pso.apply_to(user.dn)
        self.assert_PSO_applied(user, worst_pso)

        # remove applying the PSO directly to the user and check PSO changes
        worst_pso.unapply(user.dn)
        self.assert_PSO_applied(user, medium_pso)

        # remove all appliesTo and check we have the default settings again
        worst_pso.unapply(group1)
        medium_pso.unapply(group2)
        worst_pso.unapply(group4)
        self.assert_PSO_applied(user, self.pwd_defaults)
예제 #16
0
    def test_pso_special_groups(self):
        """Checks applying a PSO to built-in AD groups takes effect"""

        # create some PSOs that will apply to special groups
        default_pso = PasswordSettings("default-PSO",
                                       self.ldb,
                                       precedence=20,
                                       password_len=8,
                                       complexity=False)
        guest_pso = PasswordSettings("guest-PSO",
                                     self.ldb,
                                     history_len=4,
                                     precedence=5,
                                     password_len=5)
        builtin_pso = PasswordSettings("builtin-PSO",
                                       self.ldb,
                                       history_len=9,
                                       precedence=1,
                                       password_len=9)
        admin_pso = PasswordSettings("admin-PSO",
                                     self.ldb,
                                     history_len=0,
                                     precedence=2,
                                     password_len=10)
        self.add_obj_cleanup(
            [default_pso.dn, guest_pso.dn, admin_pso.dn, builtin_pso.dn])
        base_dn = self.ldb.domain_dn()
        domain_users = "CN=Domain Users,CN=Users,%s" % base_dn
        domain_guests = "CN=Domain Guests,CN=Users,%s" % base_dn
        admin_users = "CN=Domain Admins,CN=Users,%s" % base_dn

        # if we apply a PSO to Domain Users (which all users are a member of)
        # then that PSO should take effect on a new user
        default_pso.apply_to(domain_users)
        user = self.add_user("testuser")
        self.assert_PSO_applied(user, default_pso)

        # Apply a PSO to a builtin group. 'Domain Users' should be a member of
        # Builtin/Users, but builtin groups should be excluded from the PSO
        # calculation, so this should have no effect
        builtin_pso.apply_to("CN=Users,CN=Builtin,%s" % base_dn)
        builtin_pso.apply_to("CN=Administrators,CN=Builtin,%s" % base_dn)
        self.assert_PSO_applied(user, default_pso)

        # change the user's primary group to another group (the primaryGroupID
        # is a little odd in that there's no memberOf backlink for it)
        self.set_attribute(domain_guests, "member", user.dn)
        user.set_primary_group(domain_guests)
        # No PSO is applied to the Domain Guests yet, so the default PSO should
        # still apply
        self.assert_PSO_applied(user, default_pso)

        # now apply a PSO to the guests group, which should trump the default
        # PSO (because the guest PSO has a better precedence)
        guest_pso.apply_to(domain_guests)
        self.assert_PSO_applied(user, guest_pso)

        # create a new group that's a member of Admin Users
        nested_group = self.add_group("nested-group")
        self.set_attribute(admin_users, "member", nested_group)
        # set the user's primary-group to be the new group
        self.set_attribute(nested_group, "member", user.dn)
        user.set_primary_group(nested_group)
        # we've only changed group membership so far, not the PSO
        self.assert_PSO_applied(user, guest_pso)

        # now apply the best-precedence PSO to Admin Users and check it applies
        # to the user (via the nested-group's membership)
        admin_pso.apply_to(admin_users)
        self.assert_PSO_applied(user, admin_pso)

        # restore the default primaryGroupID so we can safely delete the group
        user.set_primary_group(domain_users)
예제 #17
0
    def test_pso_special_groups(self):
        """Checks applying a PSO to built-in AD groups takes effect"""

        # create some PSOs that will apply to special groups
        default_pso = PasswordSettings("default-PSO", self.ldb, precedence=20,
                                       password_len=8, complexity=False)
        guest_pso = PasswordSettings("guest-PSO", self.ldb, history_len=4,
                                     precedence=5, password_len=5)
        builtin_pso = PasswordSettings("builtin-PSO", self.ldb, history_len=9,
                                       precedence=1, password_len=9)
        admin_pso = PasswordSettings("admin-PSO", self.ldb, history_len=0,
                                     precedence=2, password_len=10)
        self.add_obj_cleanup([default_pso.dn, guest_pso.dn, admin_pso.dn,
                              builtin_pso.dn])
        domain_users = "CN=Domain Users,CN=Users,%s" % self.ldb.domain_dn()
        domain_guests = "CN=Domain Guests,CN=Users,%s" % self.ldb.domain_dn()
        admin_users = "CN=Domain Admins,CN=Users,%s" % self.ldb.domain_dn()

        # if we apply a PSO to Domain Users (which all users are a member of)
        # then that PSO should take effect on a new user
        default_pso.apply_to(domain_users)
        user = self.add_user("testuser")
        self.assert_PSO_applied(user, default_pso)

        # Apply a PSO to a builtin group. 'Domain Users' should be a member of
        # Builtin/Users, but builtin groups should be excluded from the PSO
        # calculation, so this should have no effect
        builtin_pso.apply_to("CN=Users,CN=Builtin,%s" % self.ldb.domain_dn())
        builtin_pso.apply_to("CN=Administrators,CN=Builtin,%s" % self.ldb.domain_dn())
        self.assert_PSO_applied(user, default_pso)

        # change the user's primary group to another group (the primaryGroupID
        # is a little odd in that there's no memberOf backlink for it)
        self.set_attribute(domain_guests, "member", user.dn)
        user.set_primary_group(domain_guests)
        # No PSO is applied to the Domain Guests yet, so the default PSO should
        # still apply
        self.assert_PSO_applied(user, default_pso)

        # now apply a PSO to the guests group, which should trump the default
        # PSO (because the guest PSO has a better precedence)
        guest_pso.apply_to(domain_guests)
        self.assert_PSO_applied(user, guest_pso)

        # create a new group that's a member of Admin Users
        nested_group = self.add_group("nested-group")
        self.set_attribute(admin_users, "member", nested_group)
        # set the user's primary-group to be the new group
        self.set_attribute(nested_group, "member", user.dn)
        user.set_primary_group(nested_group)
        # we've only changed group membership so far, not the PSO
        self.assert_PSO_applied(user, guest_pso)

        # now apply the best-precedence PSO to Admin Users and check it applies
        # to the user (via the nested-group's membership)
        admin_pso.apply_to(admin_users)
        self.assert_PSO_applied(user, admin_pso)

        # restore the default primaryGroupID so we can safely delete the group
        user.set_primary_group(domain_users)
예제 #18
0
    def test_pso_nested_groups(self):
        """PSOs operate correctly when applied to nested groups"""

        # create some PSOs that vary in priority and basic password-len
        group1_pso = PasswordSettings("group1-PSO", self.ldb, precedence=50,
                                      password_len=12, history_len=3)
        group2_pso = PasswordSettings("group2-PSO", self.ldb, precedence=25,
                                      password_len=10, history_len=5,
                                      complexity=False)
        group3_pso = PasswordSettings("group3-PSO", self.ldb, precedence=10,
                                      password_len=6, history_len=2)

        # create some groups and apply the PSOs to the groups
        group1 = self.add_group("Group-1")
        group2 = self.add_group("Group-2")
        group3 = self.add_group("Group-3")
        group4 = self.add_group("Group-4")
        group1_pso.apply_to(group1)
        group2_pso.apply_to(group2)
        group3_pso.apply_to(group3)

        # create a PSO and apply it to a group that the user is not a member
        # of - it should not have any effect on the user
        unused_pso = PasswordSettings("unused-PSO", self.ldb, precedence=1,
                                      password_len=20)
        unused_pso.apply_to(group4)

        # handle PSO clean-up (as they're outside the top-level test OU)
        self.add_obj_cleanup([group1_pso.dn, group2_pso.dn, group3_pso.dn,
                              unused_pso.dn])

        # create a user and check the default settings apply to it
        user = self.add_user("testuser")
        self.assert_PSO_applied(user, self.pwd_defaults)

        # add user to a group. Check that the group's PSO applies to the user
        self.set_attribute(group1, "member", user.dn)
        self.set_attribute(group2, "member", group1)
        self.assert_PSO_applied(user, group2_pso)

        # add another level to the group heirachy & check this PSO takes effect
        self.set_attribute(group3, "member", group2)
        self.assert_PSO_applied(user, group3_pso)

        # invert the PSO precedence and check the new lowest value takes effect
        group1_pso.set_precedence(3)
        group2_pso.set_precedence(13)
        group3_pso.set_precedence(33)
        self.assert_PSO_applied(user, group1_pso)

        # delete a PSO and check it no longer applies
        self.ldb.delete(group1_pso.dn)
        self.test_objs.remove(group1_pso.dn)
        self.assert_PSO_applied(user, group2_pso)
예제 #19
0
    def test_pso_equal_precedence(self):
        """Tests expected PSO wins when several have the same precedence"""

        # create some PSOs that vary in priority and basic password-len
        pso1 = PasswordSettings("PSO-1",
                                self.ldb,
                                precedence=5,
                                history_len=1,
                                password_len=11)
        pso2 = PasswordSettings("PSO-2",
                                self.ldb,
                                precedence=5,
                                history_len=2,
                                password_len=8)
        pso3 = PasswordSettings("PSO-3",
                                self.ldb,
                                precedence=5,
                                history_len=3,
                                password_len=5,
                                complexity=False)
        pso4 = PasswordSettings("PSO-4",
                                self.ldb,
                                precedence=5,
                                history_len=4,
                                password_len=13,
                                complexity=False)

        # handle PSO clean-up (as they're outside the top-level test OU)
        self.add_obj_cleanup([pso1.dn, pso2.dn, pso3.dn, pso4.dn])

        # create some groups and apply the PSOs to the groups
        group1 = self.add_group("Group-1")
        group2 = self.add_group("Group-2")
        group3 = self.add_group("Group-3")
        group4 = self.add_group("Group-4")
        pso1.apply_to(group1)
        pso2.apply_to(group2)
        pso3.apply_to(group3)
        pso4.apply_to(group4)

        # create a user and check the default settings apply to it
        user = self.add_user("testuser")
        self.assert_PSO_applied(user, self.pwd_defaults)

        # add the user to all the groups
        self.set_attribute(group1, "member", user.dn)
        self.set_attribute(group2, "member", user.dn)
        self.set_attribute(group3, "member", user.dn)
        self.set_attribute(group4, "member", user.dn)

        # precedence is equal, so the PSO with lowest GUID gets applied
        pso_list = [pso1, pso2, pso3, pso4]
        best_pso = self.PSO_with_lowest_GUID(pso_list)
        self.assert_PSO_applied(user, best_pso)

        # excluding the winning PSO, apply the other PSOs directly to the user
        pso_list.remove(best_pso)
        for pso in pso_list:
            pso.apply_to(user.dn)

        # we should now have a different PSO applied (the 2nd lowest GUID)
        next_best_pso = self.PSO_with_lowest_GUID(pso_list)
        self.assertTrue(next_best_pso is not best_pso)
        self.assert_PSO_applied(user, next_best_pso)

        # bump the precedence of another PSO and it should now win
        pso_list.remove(next_best_pso)
        best_pso = pso_list[0]
        best_pso.set_precedence(4)
        self.assert_PSO_applied(user, best_pso)
예제 #20
0
    def test_pso_nested_groups(self):
        """PSOs operate correctly when applied to nested groups"""

        # create some PSOs that vary in priority and basic password-len
        group1_pso = PasswordSettings("group1-PSO",
                                      self.ldb,
                                      precedence=50,
                                      password_len=12,
                                      history_len=3)
        group2_pso = PasswordSettings("group2-PSO",
                                      self.ldb,
                                      precedence=25,
                                      password_len=10,
                                      history_len=5,
                                      complexity=False)
        group3_pso = PasswordSettings("group3-PSO",
                                      self.ldb,
                                      precedence=10,
                                      password_len=6,
                                      history_len=2)

        # create some groups and apply the PSOs to the groups
        group1 = self.add_group("Group-1")
        group2 = self.add_group("Group-2")
        group3 = self.add_group("Group-3")
        group4 = self.add_group("Group-4")
        group1_pso.apply_to(group1)
        group2_pso.apply_to(group2)
        group3_pso.apply_to(group3)

        # create a PSO and apply it to a group that the user is not a member
        # of - it should not have any effect on the user
        unused_pso = PasswordSettings("unused-PSO",
                                      self.ldb,
                                      precedence=1,
                                      password_len=20)
        unused_pso.apply_to(group4)

        # handle PSO clean-up (as they're outside the top-level test OU)
        self.add_obj_cleanup(
            [group1_pso.dn, group2_pso.dn, group3_pso.dn, unused_pso.dn])

        # create a user and check the default settings apply to it
        user = self.add_user("testuser")
        self.assert_PSO_applied(user, self.pwd_defaults)

        # add user to a group. Check that the group's PSO applies to the user
        self.set_attribute(group1, "member", user.dn)
        self.set_attribute(group2, "member", group1)
        self.assert_PSO_applied(user, group2_pso)

        # add another level to the group heirachy & check this PSO takes effect
        self.set_attribute(group3, "member", group2)
        self.assert_PSO_applied(user, group3_pso)

        # invert the PSO precedence and check the new lowest value takes effect
        group1_pso.set_precedence(3)
        group2_pso.set_precedence(13)
        group3_pso.set_precedence(33)
        self.assert_PSO_applied(user, group1_pso)

        # delete a PSO and check it no longer applies
        self.ldb.delete(group1_pso.dn)
        self.test_objs.remove(group1_pso.dn)
        self.assert_PSO_applied(user, group2_pso)
예제 #21
0
    def test_pso_add_user(self):
        """Tests against a 'Domain Users' PSO taking effect on a new user"""

        # create a PSO that will apply to users by default
        default_pso = PasswordSettings("default-PSO",
                                       self.ldb,
                                       precedence=20,
                                       password_len=12,
                                       complexity=False)
        self.add_obj_cleanup([default_pso.dn])

        # apply the PSO to Domain Users (which all users are a member of). In
        # theory, this PSO *could* take effect on a new user (but it doesn't)
        domain_users = "CN=Domain Users,CN=Users,%s" % self.ldb.domain_dn()
        default_pso.apply_to(domain_users)

        # first try to add a user with a password that doesn't meet the domain
        # defaults, to prove that the DC will reject bad passwords during a
        # user add
        userdn = "CN=testuser,%s" % self.ou
        password = self.format_password_for_ldif('abcdef')

        # Note we use an LDIF operation to ensure that the password gets set
        # as part of the 'add' operation (whereas self.add_user() adds the user
        # first, then sets the password later in a 2nd step)
        try:
            ldif = """
dn: %s
objectClass: user
sAMAccountName: testuser
unicodePwd:: %s
""" % (userdn, password)
            self.ldb.add_ldif(ldif)
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            # error codes differ between Samba and Windows
            self.assertTrue(
                num == ldb.ERR_UNWILLING_TO_PERFORM
                or num == ldb.ERR_CONSTRAINT_VIOLATION, msg)
            self.assertTrue('0000052D' in msg, msg)

        # now use a password that meets the domain defaults, but doesn't meet
        # the PSO requirements. Note that Windows allows this, i.e. it doesn't
        # honour the PSO during the add operation
        password = self.format_password_for_ldif('abcde12#')
        ldif = """
dn: %s
objectClass: user
sAMAccountName: testuser
unicodePwd:: %s
""" % (userdn, password)
        self.ldb.add_ldif(ldif)

        # Now do essentially the same thing, but set the password in a 2nd step
        # which proves that the same password doesn't meet the PSO requirements
        userdn = "CN=testuser2,%s" % self.ou
        ldif = """
dn: %s
objectClass: user
sAMAccountName: testuser2
""" % userdn
        self.ldb.add_ldif(ldif)

        # now that the user exists, assert that the PSO is honoured
        try:
            ldif = """
dn: %s
changetype: modify
delete: unicodePwd
add: unicodePwd
unicodePwd:: %s
""" % (userdn, password)
            self.ldb.modify_ldif(ldif)
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEqual(num, ldb.ERR_CONSTRAINT_VIOLATION, msg)
            self.assertTrue('0000052D' in msg, msg)

        # check setting a password that meets the PSO settings works
        password = self.format_password_for_ldif('abcdefghijkl')
        ldif = """
dn: %s
changetype: modify
delete: unicodePwd
add: unicodePwd
unicodePwd:: %s
""" % (userdn, password)
        self.ldb.modify_ldif(ldif)
예제 #22
0
    def test_pso_create(self):
        """Tests basic PSO creation using the samba-tool"""

        # we expect the PSO to take the current domain settings by default
        # (we'll set precedence/complexity, the rest should be the defaults)
        expected_pso = PasswordSettings(None, self.ldb)
        expected_pso.complexity = False
        expected_pso.precedence = 100

        # check basic PSO creation works
        pso_name = "test-create-PSO"
        (result, out, err) = self.runsublevelcmd("domain", ("passwordsettings",
                                                 "pso", "create"), pso_name,
                                                 "100", "--complexity=off",
                                                 "-H", self.server,
                                                 self.user_auth)
        # make sure we clean-up after the test completes
        self.obj_cleanup.append("CN=%s,%s" %(pso_name, self.pso_container))

        self.assertCmdSuccess(result, out, err)
        self.assertEquals(err,"","Shouldn't be any error messages")
        self.assertIn("successfully created", out)
        self.check_pso(pso_name, expected_pso)

        # check creating a PSO with the same name fails
        (result, out, err) = self.runsublevelcmd("domain", ("passwordsettings",
                                                 "pso", "create"), pso_name,
                                                 "100", "--complexity=off",
                                                 "-H", self.server,
                                                 self.user_auth)
        self.assertCmdFail(result, "Ensure that create for existing PSO fails")
        self.assertIn("already exists", err)

        # check we need to specify at least one password policy argument
        pso_name = "test-create-PSO2"
        (result, out, err) = self.runsublevelcmd("domain", ("passwordsettings",
                                                 "pso", "create"), pso_name,
                                                 "100", "-H", self.server,
                                                 self.user_auth)
        self.assertCmdFail(result, "Ensure that create for existing PSO fails")
        self.assertIn("specify at least one password policy setting", err)

        # create a PSO with different settings and check they match
        expected_pso.complexity = True
        expected_pso.store_plaintext = True
        expected_pso.precedence = 50
        expected_pso.password_len = 12
        day_in_secs = 60 * 60 * 24
        expected_pso.password_age_min = 11 * day_in_secs
        expected_pso.password_age_max = 50 * day_in_secs

        (result, out, err) = self.runsublevelcmd("domain", ("passwordsettings",
                                                 "pso", "create"), pso_name,
                                                 "50", "--complexity=on",
                                                 "--store-plaintext=on",
                                                 "--min-pwd-length=12",
                                                 "--min-pwd-age=11",
                                                 "--max-pwd-age=50",
                                                 "-H", self.server,
                                                 self.user_auth)
        self.obj_cleanup.append("CN=%s,%s" %(pso_name, self.pso_container))
        self.assertCmdSuccess(result, out, err)
        self.assertEquals(err,"","Shouldn't be any error messages")
        self.assertIn("successfully created", out)
        self.check_pso(pso_name, expected_pso)

        # check the PSOs we created are present in the 'list' command
        (result, out, err) = self.runsublevelcmd("domain", ("passwordsettings",
                                                 "pso", "list"),
                                                 "-H", self.server,
                                                 self.user_auth)
        self.assertCmdSuccess(result, out, err)
        self.assertIn("test-create-PSO", out)
        self.assertIn("test-create-PSO2", out)
예제 #23
0
    def test_pso_equal_precedence(self):
        """Tests expected PSO wins when several have the same precedence"""

        # create some PSOs that vary in priority and basic password-len
        pso1 = PasswordSettings("PSO-1", self.ldb, precedence=5, history_len=1,
                                password_len=11)
        pso2 = PasswordSettings("PSO-2", self.ldb, precedence=5, history_len=2,
                                password_len=8)
        pso3 = PasswordSettings("PSO-3", self.ldb, precedence=5, history_len=3,
                                password_len=5, complexity=False)
        pso4 = PasswordSettings("PSO-4", self.ldb, precedence=5, history_len=4,
                                password_len=13, complexity=False)

        # handle PSO clean-up (as they're outside the top-level test OU)
        self.add_obj_cleanup([pso1.dn, pso2.dn, pso3.dn, pso4.dn])

        # create some groups and apply the PSOs to the groups
        group1 = self.add_group("Group-1")
        group2 = self.add_group("Group-2")
        group3 = self.add_group("Group-3")
        group4 = self.add_group("Group-4")
        pso1.apply_to(group1)
        pso2.apply_to(group2)
        pso3.apply_to(group3)
        pso4.apply_to(group4)

        # create a user and check the default settings apply to it
        user = self.add_user("testuser")
        self.assert_PSO_applied(user, self.pwd_defaults)

        # add the user to all the groups
        self.set_attribute(group1, "member", user.dn)
        self.set_attribute(group2, "member", user.dn)
        self.set_attribute(group3, "member", user.dn)
        self.set_attribute(group4, "member", user.dn)

        # precedence is equal, so the PSO with lowest GUID gets applied
        pso_list = [pso1, pso2, pso3, pso4]
        best_pso = self.PSO_with_lowest_GUID(pso_list)
        self.assert_PSO_applied(user, best_pso)

        # excluding the winning PSO, apply the other PSOs directly to the user
        pso_list.remove(best_pso)
        for pso in pso_list:
            pso.apply_to(user.dn)

        # we should now have a different PSO applied (the 2nd lowest GUID)
        next_best_pso = self.PSO_with_lowest_GUID(pso_list)
        self.assertTrue(next_best_pso is not best_pso)
        self.assert_PSO_applied(user, next_best_pso)

        # bump the precedence of another PSO and it should now win
        pso_list.remove(next_best_pso)
        best_pso = pso_list[0]
        best_pso.set_precedence(4)
        self.assert_PSO_applied(user, best_pso)
예제 #24
0
    def test_pso_add_user(self):
        """Tests against a 'Domain Users' PSO taking effect on a new user"""

        # create a PSO that will apply to users by default
        default_pso = PasswordSettings("default-PSO", self.ldb, precedence=20,
                                       password_len=12, complexity=False)
        self.add_obj_cleanup([default_pso.dn])

        # apply the PSO to Domain Users (which all users are a member of). In
        # theory, this PSO *could* take effect on a new user (but it doesn't)
        domain_users = "CN=Domain Users,CN=Users,%s" % self.ldb.domain_dn()
        default_pso.apply_to(domain_users)

        # first try to add a user with a password that doesn't meet the domain
        # defaults, to prove that the DC will reject bad passwords during a
        # user add
        userdn = "CN=testuser,%s" % self.ou
        password = base64.b64encode("\"abcdef\"".encode('utf-16-le'))

        # Note we use an LDIF operation to ensure that the password gets set
        # as part of the 'add' operation (whereas self.add_user() adds the user
        # first, then sets the password later in a 2nd step)
        try:
            ldif = """
dn: %s
objectClass: user
sAMAccountName: testuser
unicodePwd:: %s
""" % (userdn, password)
            self.ldb.add_ldif(ldif)
            self.fail()
        except ldb.LdbError as e:
                (num, msg) = e.args
                # error codes differ between Samba and Windows
                self.assertTrue(num == ldb.ERR_UNWILLING_TO_PERFORM or
                                num == ldb.ERR_CONSTRAINT_VIOLATION, msg)
                self.assertTrue('0000052D' in msg, msg)

        # now use a password that meets the domain defaults, but doesn't meet
        # the PSO requirements. Note that Windows allows this, i.e. it doesn't
        # honour the PSO during the add operation
        password = base64.b64encode("\"abcde12#\"".encode('utf-16-le'))
        ldif = """
dn: %s
objectClass: user
sAMAccountName: testuser
unicodePwd:: %s
""" % (userdn, password)
        self.ldb.add_ldif(ldif)

        # Now do essentially the same thing, but set the password in a 2nd step
        # which proves that the same password doesn't meet the PSO requirements
        userdn = "CN=testuser2,%s" % self.ou
        ldif = """
dn: %s
objectClass: user
sAMAccountName: testuser2
""" % userdn
        self.ldb.add_ldif(ldif)

        # now that the user exists, assert that the PSO is honoured
        try:
            ldif = """
dn: %s
changetype: modify
delete: unicodePwd
add: unicodePwd
unicodePwd:: %s
""" % (userdn, password)
            self.ldb.modify_ldif(ldif)
            self.fail()
        except ldb.LdbError as e:
                (num, msg) = e.args
                self.assertEquals(num, ldb.ERR_CONSTRAINT_VIOLATION, msg)
                self.assertTrue('0000052D' in msg, msg)

        # check setting a password that meets the PSO settings works
        password = base64.b64encode("\"abcdefghijkl\"".encode('utf-16-le'))
        ldif = """
dn: %s
changetype: modify
delete: unicodePwd
add: unicodePwd
unicodePwd:: %s
""" % (userdn, password)
        self.ldb.modify_ldif(ldif)
예제 #25
0
    def test_pso_permissions(self):
        """Checks that regular users can't modify/view PSO objects"""

        user = self.add_user("testuser")

        # get an ldb connection with the new user's privileges
        user_ldb = self.get_ldb_connection("testuser", user.get_password(),
                                           self.host_url)

        # regular users should not be able to create a PSO (at least, not in
        # the default Password Settings container)
        try:
            priv_pso = PasswordSettings("priv-PSO", user_ldb, password_len=20)
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEquals(num, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, msg)

        # create a PSO as the admin user
        priv_pso = PasswordSettings("priv-PSO", self.ldb, password_len=20)
        self.add_obj_cleanup([priv_pso.dn])

        # regular users should not be able to apply a PSO to a user
        try:
            self.set_attribute(priv_pso.dn, "msDS-PSOAppliesTo", user.dn,
                               samdb=user_ldb)
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEquals(num, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, msg)
            self.assertTrue('00002098' in msg, msg)

        self.set_attribute(priv_pso.dn, "msDS-PSOAppliesTo", user.dn,
                           samdb=self.ldb)

        # regular users should not be able to change a PSO's precedence
        try:
            priv_pso.set_precedence(100, samdb=user_ldb)
            self.fail()
        except ldb.LdbError as e:
            (num, msg) = e.args
            self.assertEquals(num, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, msg)
            self.assertTrue('00002098' in msg, msg)

        priv_pso.set_precedence(100, samdb=self.ldb)

        # regular users should not be able to view a PSO's settings
        pso_attrs = ["msDS-PSOAppliesTo", "msDS-PasswordSettingsPrecedence",
                     "msDS-PasswordHistoryLength", "msDS-LockoutThreshold",
                     "msDS-PasswordComplexityEnabled"]

        # users can see the PSO object's DN, but not its attributes
        res = user_ldb.search(priv_pso.dn, scope=ldb.SCOPE_BASE,
                              attrs=pso_attrs)
        self.assertTrue(str(priv_pso.dn) == str(res[0].dn))
        for attr in pso_attrs:
            self.assertFalse(attr in res[0])

        # whereas admin users can see everything
        res = self.ldb.search(priv_pso.dn, scope=ldb.SCOPE_BASE,
                              attrs=pso_attrs)
        for attr in pso_attrs:
            self.assertTrue(attr in res[0])

        # check replace/delete operations can't be performed by regular users
        operations = [ FLAG_MOD_REPLACE, FLAG_MOD_DELETE ]

        for oper in operations:
            try:
                self.set_attribute(priv_pso.dn, "msDS-PSOAppliesTo", user.dn,
                                   samdb=user_ldb, operation=oper)
                self.fail()
            except ldb.LdbError as e:
                (num, msg) = e.args
                self.assertEquals(num, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, msg)
                self.assertTrue('00002098' in msg, msg)

            # ...but can be performed by the admin user
            self.set_attribute(priv_pso.dn, "msDS-PSOAppliesTo", user.dn,
                               samdb=self.ldb, operation=oper)
예제 #26
0
    def test_pso_create(self):
        """Tests basic PSO creation using the samba-tool"""

        # we expect the PSO to take the current domain settings by default
        # (we'll set precedence/complexity, the rest should be the defaults)
        expected_pso = PasswordSettings(None, self.ldb)
        expected_pso.complexity = False
        expected_pso.precedence = 100

        # check basic PSO creation works
        pso_name = "test-create-PSO"
        (result, out,
         err) = self.runsublevelcmd("domain",
                                    ("passwordsettings", "pso", "create"),
                                    pso_name, "100", "--complexity=off", "-H",
                                    self.server, self.user_auth)
        # make sure we clean-up after the test completes
        self.obj_cleanup.append("CN=%s,%s" % (pso_name, self.pso_container))

        self.assertCmdSuccess(result, out, err)
        self.assertEqual(err, "", "Shouldn't be any error messages")
        self.assertIn("successfully created", out)
        self.check_pso(pso_name, expected_pso)

        # check creating a PSO with the same name fails
        (result, out,
         err) = self.runsublevelcmd("domain",
                                    ("passwordsettings", "pso", "create"),
                                    pso_name, "100", "--complexity=off", "-H",
                                    self.server, self.user_auth)
        self.assertCmdFail(result, "Ensure that create for existing PSO fails")
        self.assertIn("already exists", err)

        # check we need to specify at least one password policy argument
        pso_name = "test-create-PSO2"
        (result, out,
         err) = self.runsublevelcmd("domain",
                                    ("passwordsettings", "pso", "create"),
                                    pso_name, "100", "-H", self.server,
                                    self.user_auth)
        self.assertCmdFail(result, "Ensure that create for existing PSO fails")
        self.assertIn("specify at least one password policy setting", err)

        # create a PSO with different settings and check they match
        expected_pso.complexity = True
        expected_pso.store_plaintext = True
        expected_pso.precedence = 50
        expected_pso.password_len = 12
        day_in_secs = 60 * 60 * 24
        expected_pso.password_age_min = 11 * day_in_secs
        expected_pso.password_age_max = 50 * day_in_secs

        (result, out, err) = self.runsublevelcmd(
            "domain", ("passwordsettings", "pso", "create"), pso_name, "50",
            "--complexity=on", "--store-plaintext=on", "--min-pwd-length=12",
            "--min-pwd-age=11", "--max-pwd-age=50", "-H", self.server,
            self.user_auth)
        self.obj_cleanup.append("CN=%s,%s" % (pso_name, self.pso_container))
        self.assertCmdSuccess(result, out, err)
        self.assertEqual(err, "", "Shouldn't be any error messages")
        self.assertIn("successfully created", out)
        self.check_pso(pso_name, expected_pso)

        # check the PSOs we created are present in the 'list' command
        (result, out,
         err) = self.runsublevelcmd("domain",
                                    ("passwordsettings", "pso", "list"), "-H",
                                    self.server, self.user_auth)
        self.assertCmdSuccess(result, out, err)
        self.assertIn("test-create-PSO", out)
        self.assertIn("test-create-PSO2", out)
예제 #27
0
    def test_pso_basics(self):
        """Simple tests that a PSO takes effect when applied to a group/user"""

        # create some PSOs that vary in priority and basic password-len
        best_pso = PasswordSettings("highest-priority-PSO",
                                    self.ldb,
                                    precedence=5,
                                    password_len=16,
                                    history_len=6)
        medium_pso = PasswordSettings("med-priority-PSO",
                                      self.ldb,
                                      precedence=15,
                                      password_len=10,
                                      history_len=4)
        worst_pso = PasswordSettings("lowest-priority-PSO",
                                     self.ldb,
                                     precedence=100,
                                     complexity=False,
                                     password_len=4,
                                     history_len=2)

        # handle PSO clean-up (as they're outside the top-level test OU)
        self.add_obj_cleanup([worst_pso.dn, medium_pso.dn, best_pso.dn])

        # create some groups and apply the PSOs to the groups
        group1 = self.add_group("Group-1")
        group2 = self.add_group("Group-2")
        group3 = self.add_group("Group-3")
        group4 = self.add_group("Group-4")
        worst_pso.apply_to(group1)
        medium_pso.apply_to(group2)
        best_pso.apply_to(group3)
        worst_pso.apply_to(group4)

        # create a user and check the default settings apply to it
        user = self.add_user("testuser")
        self.assert_PSO_applied(user, self.pwd_defaults)

        # add user to a group. Check that the group's PSO applies to the user
        self.set_attribute(group1, "member", user.dn)
        self.assert_PSO_applied(user, worst_pso)

        # add the user to a group with a higher precedence PSO and and check
        # that now trumps the previous PSO
        self.set_attribute(group2, "member", user.dn)
        self.assert_PSO_applied(user, medium_pso)

        # add the user to the remaining groups. The highest precedence PSO
        # should now take effect
        self.set_attribute(group3, "member", user.dn)
        self.set_attribute(group4, "member", user.dn)
        self.assert_PSO_applied(user, best_pso)

        # delete a group membership and check the PSO changes
        self.set_attribute(group3,
                           "member",
                           user.dn,
                           operation=FLAG_MOD_DELETE)
        self.assert_PSO_applied(user, medium_pso)

        # apply the low-precedence PSO directly to the user
        # (directly applied PSOs should trump higher precedence group PSOs)
        worst_pso.apply_to(user.dn)
        self.assert_PSO_applied(user, worst_pso)

        # remove applying the PSO directly to the user and check PSO changes
        worst_pso.unapply(user.dn)
        self.assert_PSO_applied(user, medium_pso)

        # remove all appliesTo and check we have the default settings again
        worst_pso.unapply(group1)
        medium_pso.unapply(group2)
        worst_pso.unapply(group4)
        self.assert_PSO_applied(user, self.pwd_defaults)