class GroupAuditTests(AuditLogTestBase): def setUp(self): self.message_type = MSG_GROUP_LOG self.event_type = DSDB_GROUP_EVENT_NAME super(GroupAuditTests, self).setUp() self.server_ip = os.environ["SERVER_IP"] host = "ldap://%s" % os.environ["SERVER"] self.ldb = SamDB(url=host, session_info=system_session(), credentials=self.get_credentials(), lp=self.get_loadparm()) self.server = os.environ["SERVER"] # Gets back the basedn self.base_dn = self.ldb.domain_dn() # Get the old "dSHeuristics" if it was set dsheuristics = self.ldb.get_dsheuristics() # 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 self.ldb.add({ "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn, "objectclass": "user", "sAMAccountName": USER_NAME, "userPassword": USER_PASS }) self.ldb.newgroup(GROUP_NAME_01) self.ldb.newgroup(GROUP_NAME_02) def tearDown(self): super(GroupAuditTests, self).tearDown() delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn) self.ldb.deletegroup(GROUP_NAME_01) self.ldb.deletegroup(GROUP_NAME_02) def test_add_and_remove_users_from_group(self): # # Wait for the primary group change for the created user. # messages = self.waitForMessages(2) print("Received %d messages" % len(messages)) self.assertEquals(2, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("PrimaryGroup", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=domain users,cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # Check the Add message for the new users primary group audit = messages[1]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=domain users,cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) self.assertEquals(EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP, audit["eventId"]) # # Add the user to a group # self.discardMessages() self.ldb.add_remove_group_members(GROUP_NAME_01, [USER_NAME]) messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # # Add the user to another group # self.discardMessages() self.ldb.add_remove_group_members(GROUP_NAME_02, [USER_NAME]) messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_02 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # # Remove the user from a group # self.discardMessages() self.ldb.add_remove_group_members(GROUP_NAME_01, [USER_NAME], add_members_operation=False) messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Removed", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # # Re-add the user to a group # self.discardMessages() self.ldb.add_remove_group_members(GROUP_NAME_01, [USER_NAME]) messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") def test_change_primary_group(self): # # Wait for the primary group change for the created user. # messages = self.waitForMessages(2) print("Received %d messages" % len(messages)) self.assertEquals(2, len(messages), "Did not receive the expected number of messages") # Check the PrimaryGroup message audit = messages[0]["groupChange"] self.assertEqual("PrimaryGroup", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=domain users,cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # Check the Add message for the new users primary group audit = messages[1]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=domain users,cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) self.assertEquals(EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP, audit["eventId"]) # # Add the user to a group, the user needs to be a member of a group # before there primary group can be set to that group. # self.discardMessages() self.ldb.add_remove_group_members(GROUP_NAME_01, [USER_NAME]) messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") self.assertEquals(EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP, audit["eventId"]) # # Change the primary group of a user # user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn # get the primaryGroupToken of the group res = self.ldb.search(base=group_dn, attrs=["primaryGroupToken"], scope=ldb.SCOPE_BASE) group_id = res[0]["primaryGroupToken"] # set primaryGroupID attribute of the user to that group m = ldb.Message() m.dn = ldb.Dn(self.ldb, user_dn) m["primaryGroupID"] = ldb.MessageElement(group_id, FLAG_MOD_REPLACE, "primaryGroupID") self.discardMessages() self.ldb.modify(m) # # Wait for the primary group change. # Will see the user removed from the new group # the user added to their old primary group # and a new primary group event. # messages = self.waitForMessages(3) print("Received %d messages" % len(messages)) self.assertEquals(3, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Removed", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") self.assertEquals(EVT_ID_USER_REMOVED_FROM_GLOBAL_SEC_GROUP, audit["eventId"]) audit = messages[1]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=domain users,cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") self.assertEquals(EVT_ID_USER_ADDED_TO_GLOBAL_SEC_GROUP, audit["eventId"]) audit = messages[2]["groupChange"] self.assertEqual("PrimaryGroup", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP")
class AuditLogDsdbTests(AuditLogTestBase): def setUp(self): self.message_type = MSG_DSDB_LOG self.event_type = DSDB_EVENT_NAME super(AuditLogDsdbTests, self).setUp() self.server_ip = os.environ["SERVER_IP"] host = "ldap://%s" % os.environ["SERVER"] self.ldb = SamDB(url=host, session_info=system_session(), credentials=self.get_credentials(), lp=self.get_loadparm()) self.server = os.environ["SERVER"] # Gets back the basedn self.base_dn = self.ldb.domain_dn() # Get the old "dSHeuristics" if it was set dsheuristics = self.ldb.get_dsheuristics() # 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({ "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn, "objectclass": "user", "sAMAccountName": USER_NAME, "userPassword": USER_PASS }) # # Discard the messages from the setup code # def discardSetupMessages(self, dn): self.waitForMessages(2, dn=dn) self.discardMessages() def tearDown(self): self.discardMessages() super(AuditLogDsdbTests, self).tearDown() def haveExpectedTxn(self, expected): if self.context["txnMessage"] is not None: txn = self.context["txnMessage"]["dsdbTransaction"] if txn["transactionId"] == expected: return True return False def waitForTransaction(self, expected, connection=None): """Wait for a transaction message to arrive The connection is passed through to keep the connection alive until all the logging messages have been received. """ self.connection = connection start_time = time.time() while not self.haveExpectedTxn(expected): self.msg_ctx.loop_once(0.1) if time.time() - start_time > 1: self.connection = None return "" self.connection = None return self.context["txnMessage"] def test_net_change_password(self): dn = "CN=" + USER_NAME + ",CN=Users," + self.base_dn self.discardSetupMessages(dn) creds = self.insta_creds(template=self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server) password = "******" net.change_password(newpassword=password, username=USER_NAME, oldpassword=USER_PASS) messages = self.waitForMessages(1, net, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Modify", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) # We skip the check for self.get_service_description() as this # is subject to a race between smbd and the s4 rpc_server code # as to which will set the description as it is DCE/RPC over SMB self.assertTrue(self.is_guid(audit["transactionId"])) attributes = audit["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["clearTextPassword"]["actions"] self.assertEquals(1, len(actions)) self.assertTrue(actions[0]["redacted"]) self.assertEquals("replace", actions[0]["action"]) def test_net_set_password(self): dn = "CN=" + USER_NAME + ",CN=Users," + self.base_dn self.discardSetupMessages(dn) creds = self.insta_creds(template=self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server) password = "******" domain = lp.get("workgroup") net.set_password(newpassword=password, account_name=USER_NAME, domain_name=domain) messages = self.waitForMessages(1, net, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Modify", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) # We skip the check for self.get_service_description() as this # is subject to a race between smbd and the s4 rpc_server code # as to which will set the description as it is DCE/RPC over SMB self.assertTrue(self.is_guid(audit["transactionId"])) attributes = audit["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["clearTextPassword"]["actions"] self.assertEquals(1, len(actions)) self.assertTrue(actions[0]["redacted"]) self.assertEquals("replace", actions[0]["action"]) def test_ldap_change_password(self): dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) new_password = samba.generate_random_password(32, 32) dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.ldb.modify_ldif("dn: " + dn + "\n" + "changetype: modify\n" + "delete: userPassword\n" + "userPassword: "******"\n" + "add: userPassword\n" + "userPassword: "******"\n") messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Modify", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") attributes = audit["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["userPassword"]["actions"] self.assertEquals(2, len(actions)) self.assertTrue(actions[0]["redacted"]) self.assertEquals("delete", actions[0]["action"]) self.assertTrue(actions[1]["redacted"]) self.assertEquals("add", actions[1]["action"]) def test_ldap_replace_password(self): dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) new_password = samba.generate_random_password(32, 32) self.ldb.modify_ldif("dn: " + dn + "\n" + "changetype: modify\n" + "replace: userPassword\n" + "userPassword: "******"\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Modify", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") self.assertTrue(self.is_guid(audit["transactionId"])) attributes = audit["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["userPassword"]["actions"] self.assertEquals(1, len(actions)) self.assertTrue(actions[0]["redacted"]) self.assertEquals("replace", actions[0]["action"]) def test_ldap_add_user(self): # The setup code adds a user, so we check for the dsdb events # generated by it. dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn messages = self.waitForMessages(2, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(2, len(messages), "Did not receive the expected number of messages") audit = messages[1]["dsdbChange"] self.assertEquals("Add", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") self.assertTrue(self.is_guid(audit["sessionId"])) self.assertTrue(self.is_guid(audit["transactionId"])) attributes = audit["attributes"] self.assertEquals(3, len(attributes)) actions = attributes["objectclass"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) self.assertEquals(1, len(actions[0]["values"])) self.assertEquals("user", actions[0]["values"][0]["value"]) actions = attributes["sAMAccountName"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) self.assertEquals(1, len(actions[0]["values"])) self.assertEquals(USER_NAME, actions[0]["values"][0]["value"]) actions = attributes["userPassword"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) self.assertTrue(actions[0]["redacted"]) def test_samdb_delete_user(self): dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) self.ldb.deleteuser(USER_NAME) messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Delete", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) self.assertEquals(0, audit["statusCode"]) self.assertEquals("Success", audit["status"]) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") transactionId = audit["transactionId"] message = self.waitForTransaction(transactionId) audit = message["dsdbTransaction"] self.assertEquals("commit", audit["action"]) self.assertTrue(self.is_guid(audit["transactionId"])) self.assertTrue(audit["duration"] > 0) def test_samdb_delete_non_existent_dn(self): DOES_NOT_EXIST = "doesNotExist" dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) dn = "cn=" + DOES_NOT_EXIST + ",cn=users," + self.base_dn try: self.ldb.delete(dn) self.fail("Exception not thrown") except Exception: pass messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Delete", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertEquals(ERR_NO_SUCH_OBJECT, audit["statusCode"]) self.assertEquals("No such object", audit["status"]) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") transactionId = audit["transactionId"] message = self.waitForTransaction(transactionId) audit = message["dsdbTransaction"] self.assertEquals("rollback", audit["action"]) self.assertTrue(self.is_guid(audit["transactionId"])) self.assertTrue(audit["duration"] > 0) def test_create_and_delete_secret_over_lsa(self): dn = "cn=Test Secret,CN=System," + self.base_dn self.discardSetupMessages(dn) creds = self.insta_creds(template=self.get_credentials()) lsa_conn = lsa.lsarpc("ncacn_np:%s" % self.server, self.get_loadparm(), creds) lsa_handle = lsa_conn.OpenPolicy2( system_name="\\", attr=lsa.ObjectAttribute(), access_mask=security.SEC_FLAG_MAXIMUM_ALLOWED) secret_name = lsa.String() secret_name.string = "G$Test" lsa_conn.CreateSecret(handle=lsa_handle, name=secret_name, access_mask=security.SEC_FLAG_MAXIMUM_ALLOWED) messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Add", audit["operation"]) self.assertTrue(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) # We skip the check for self.get_service_description() as this # is subject to a race between smbd and the s4 rpc_server code # as to which will set the description as it is DCE/RPC over SMB attributes = audit["attributes"] self.assertEquals(2, len(attributes)) object_class = attributes["objectClass"] self.assertEquals(1, len(object_class["actions"])) action = object_class["actions"][0] self.assertEquals("add", action["action"]) values = action["values"] self.assertEquals(1, len(values)) self.assertEquals("secret", values[0]["value"]) cn = attributes["cn"] self.assertEquals(1, len(cn["actions"])) action = cn["actions"][0] self.assertEquals("add", action["action"]) values = action["values"] self.assertEquals(1, len(values)) self.assertEquals("Test Secret", values[0]["value"]) # # Now delete the secret. self.discardMessages() h = lsa_conn.OpenSecret(handle=lsa_handle, name=secret_name, access_mask=security.SEC_FLAG_MAXIMUM_ALLOWED) lsa_conn.DeleteObject(h) messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") dn = "cn=Test Secret,CN=System," + self.base_dn audit = messages[0]["dsdbChange"] self.assertEquals("Delete", audit["operation"]) self.assertTrue(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) # We skip the check for self.get_service_description() as this # is subject to a race between smbd and the s4 rpc_server code # as to which will set the description as it is DCE/RPC over SMB def test_modify(self): dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) # # Add an attribute value # self.ldb.modify_ldif("dn: " + dn + "\n" + "changetype: modify\n" + "add: carLicense\n" + "carLicense: license-01\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Modify", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") attributes = audit["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["carLicense"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) values = actions[0]["values"] self.assertEquals(1, len(values)) self.assertEquals("license-01", values[0]["value"]) # # Add an another value to the attribute # self.discardMessages() self.ldb.modify_ldif("dn: " + dn + "\n" + "changetype: modify\n" + "add: carLicense\n" + "carLicense: license-02\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") attributes = messages[0]["dsdbChange"]["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["carLicense"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) values = actions[0]["values"] self.assertEquals(1, len(values)) self.assertEquals("license-02", values[0]["value"]) # # Add an another two values to the attribute # self.discardMessages() self.ldb.modify_ldif("dn: " + dn + "\n" + "changetype: modify\n" + "add: carLicense\n" + "carLicense: license-03\n" + "carLicense: license-04\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") attributes = messages[0]["dsdbChange"]["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["carLicense"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) values = actions[0]["values"] self.assertEquals(2, len(values)) self.assertEquals("license-03", values[0]["value"]) self.assertEquals("license-04", values[1]["value"]) # # delete two values to the attribute # self.discardMessages() self.ldb.modify_ldif("dn: " + dn + "\n" + "changetype: delete\n" + "delete: carLicense\n" + "carLicense: license-03\n" + "carLicense: license-04\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") attributes = messages[0]["dsdbChange"]["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["carLicense"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("delete", actions[0]["action"]) values = actions[0]["values"] self.assertEquals(2, len(values)) self.assertEquals("license-03", values[0]["value"]) self.assertEquals("license-04", values[1]["value"]) # # replace two values to the attribute # self.discardMessages() self.ldb.modify_ldif("dn: " + dn + "\n" + "changetype: delete\n" + "replace: carLicense\n" + "carLicense: license-05\n" + "carLicense: license-06\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") attributes = messages[0]["dsdbChange"]["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["carLicense"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("replace", actions[0]["action"]) values = actions[0]["values"] self.assertEquals(2, len(values)) self.assertEquals("license-05", values[0]["value"]) self.assertEquals("license-06", values[1]["value"])
class PassWordHashTests(TestCase): def setUp(self): self.lp = samba.tests.env_loadparm() super(PassWordHashTests, self).setUp() # Add a user to ldb, this will exercise the password_hash code # and calculate the appropriate supplemental credentials def add_user(self, options=None, clear_text=False, ldb=None): # set any needed options if options is not None: for (option, value) in options: self.lp.set(option, value) if ldb is None: self.creds = Credentials() self.session = system_session() self.creds.guess(self.lp) self.session = system_session() self.ldb = SamDB(session_info=self.session, credentials=self.creds, lp=self.lp) else: self.ldb = ldb res = self.ldb.search(base=self.ldb.get_config_basedn(), expression="ncName=%s" % self.ldb.get_default_basedn(), attrs=["nETBIOSName"]) self.netbios_domain = res[0]["nETBIOSName"][0] self.dns_domain = self.ldb.domain_dns_name() # Gets back the basedn base_dn = self.ldb.domain_dn() # Gets back the configuration basedn configuration_dn = self.ldb.get_config_basedn().get_linearized() # Get the old "dSHeuristics" if it was set dsheuristics = self.ldb.get_dsheuristics() # 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) account_control = 0 if clear_text: # get the current pwdProperties pwdProperties = self.ldb.get_pwdProperties() # enable clear text properties props = int(pwdProperties) props |= DOMAIN_PASSWORD_STORE_CLEARTEXT self.ldb.set_pwdProperties(str(props)) # Restore the value on exit. self.addCleanup(self.ldb.set_pwdProperties, pwdProperties) account_control |= UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED # (Re)adds the test user USER_NAME with password USER_PASS # and userPrincipalName UPN delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn) self.ldb.add({ "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn, "objectclass": "user", "sAMAccountName": USER_NAME, "userPassword": USER_PASS, "userPrincipalName": UPN, "userAccountControl": str(account_control) }) # Get the supplemental credentials for the user under test def get_supplemental_creds(self): base = "cn=" + USER_NAME + ",cn=users," + self.base_dn res = self.ldb.search(scope=ldb.SCOPE_BASE, base=base, attrs=["supplementalCredentials"]) self.assertIs(True, len(res) > 0) obj = res[0] sc_blob = obj["supplementalCredentials"][0] sc = ndr_unpack(drsblobs.supplementalCredentialsBlob, sc_blob) return sc # Calculate and validate a Wdigest value def check_digest(self, user, realm, password, digest): expected = calc_digest(user, realm, password) actual = binascii.hexlify(bytearray(digest)) error = "Digest expected[%s], actual[%s], " \ "user[%s], realm[%s], pass[%s]" % \ (expected, actual, user, realm, password) self.assertEquals(expected, actual, error) # Check all of the 29 expected WDigest values # def check_wdigests(self, digests): self.assertEquals(29, digests.num_hashes) # Using the n-1 pattern in the array indexes to make it easier # to check the tests against the spec and the samba-tool user tests. self.check_digest(USER_NAME, self.netbios_domain, USER_PASS, digests.hashes[1 - 1].hash) self.check_digest(USER_NAME.lower(), self.netbios_domain.lower(), USER_PASS, digests.hashes[2 - 1].hash) self.check_digest(USER_NAME.upper(), self.netbios_domain.upper(), USER_PASS, digests.hashes[3 - 1].hash) self.check_digest(USER_NAME, self.netbios_domain.upper(), USER_PASS, digests.hashes[4 - 1].hash) self.check_digest(USER_NAME, self.netbios_domain.lower(), USER_PASS, digests.hashes[5 - 1].hash) self.check_digest(USER_NAME.upper(), self.netbios_domain.lower(), USER_PASS, digests.hashes[6 - 1].hash) self.check_digest(USER_NAME.lower(), self.netbios_domain.upper(), USER_PASS, digests.hashes[7 - 1].hash) self.check_digest(USER_NAME, self.dns_domain, USER_PASS, digests.hashes[8 - 1].hash) self.check_digest(USER_NAME.lower(), self.dns_domain.lower(), USER_PASS, digests.hashes[9 - 1].hash) self.check_digest(USER_NAME.upper(), self.dns_domain.upper(), USER_PASS, digests.hashes[10 - 1].hash) self.check_digest(USER_NAME, self.dns_domain.upper(), USER_PASS, digests.hashes[11 - 1].hash) self.check_digest(USER_NAME, self.dns_domain.lower(), USER_PASS, digests.hashes[12 - 1].hash) self.check_digest(USER_NAME.upper(), self.dns_domain.lower(), USER_PASS, digests.hashes[13 - 1].hash) self.check_digest(USER_NAME.lower(), self.dns_domain.upper(), USER_PASS, digests.hashes[14 - 1].hash) self.check_digest(UPN, "", USER_PASS, digests.hashes[15 - 1].hash) self.check_digest(UPN.lower(), "", USER_PASS, digests.hashes[16 - 1].hash) self.check_digest(UPN.upper(), "", USER_PASS, digests.hashes[17 - 1].hash) name = "%s\\%s" % (self.netbios_domain, USER_NAME) self.check_digest(name, "", USER_PASS, digests.hashes[18 - 1].hash) name = "%s\\%s" % (self.netbios_domain.lower(), USER_NAME.lower()) self.check_digest(name, "", USER_PASS, digests.hashes[19 - 1].hash) name = "%s\\%s" % (self.netbios_domain.upper(), USER_NAME.upper()) self.check_digest(name, "", USER_PASS, digests.hashes[20 - 1].hash) self.check_digest(USER_NAME, "Digest", USER_PASS, digests.hashes[21 - 1].hash) self.check_digest(USER_NAME.lower(), "Digest", USER_PASS, digests.hashes[22 - 1].hash) self.check_digest(USER_NAME.upper(), "Digest", USER_PASS, digests.hashes[23 - 1].hash) self.check_digest(UPN, "Digest", USER_PASS, digests.hashes[24 - 1].hash) self.check_digest(UPN.lower(), "Digest", USER_PASS, digests.hashes[25 - 1].hash) self.check_digest(UPN.upper(), "Digest", USER_PASS, digests.hashes[26 - 1].hash) name = "%s\\%s" % (self.netbios_domain, USER_NAME) self.check_digest(name, "Digest", USER_PASS, digests.hashes[27 - 1].hash) name = "%s\\%s" % (self.netbios_domain.lower(), USER_NAME.lower()) self.check_digest(name, "Digest", USER_PASS, digests.hashes[28 - 1].hash) name = "%s\\%s" % (self.netbios_domain.upper(), USER_NAME.upper()) self.check_digest(name, "Digest", USER_PASS, digests.hashes[29 - 1].hash) def checkUserPassword(self, up, expected): # Check we've received the correct number of hashes self.assertEquals(len(expected), up.num_hashes) i = 0 for (tag, alg, rounds) in expected: self.assertEquals(tag, up.hashes[i].scheme) data = up.hashes[i].value.split("$") # Check we got the expected crypt algorithm self.assertEquals(alg, data[1]) if rounds is None: cmd = "$%s$%s" % (alg, data[2]) else: cmd = "$%s$rounds=%d$%s" % (alg, rounds, data[3]) # Calculate the expected hash value expected = crypt.crypt(USER_PASS, cmd) self.assertEquals(expected, up.hashes[i].value) i += 1 # Check that the correct nt_hash was stored for userPassword def checkNtHash(self, password, nt_hash): creds = Credentials() creds.set_anonymous() creds.set_password(password) expected = creds.get_nt_hash() actual = bytearray(nt_hash) self.assertEquals(expected, actual)
class PasswordTests(samba.tests.TestCase): def setUp(self): super(PasswordTests, self).setUp() self.ldb = SamDB(url=host, session_info=system_session(lp), credentials=creds, lp=lp) # Gets back the basedn base_dn = self.ldb.domain_dn() # Gets back the configuration basedn configuration_dn = self.ldb.get_config_basedn().get_linearized() # Get the old "dSHeuristics" if it was set dsheuristics = self.ldb.get_dsheuristics() # 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 "testuser" with no password atm delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn) self.ldb.add({ "dn": "cn=testuser,cn=users," + self.base_dn, "objectclass": "user", "sAMAccountName": "testuser"}) # Tests a password change when we don't have any password yet with a # wrong old password try: self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: noPassword add: userPassword userPassword: thatsAcomplPASS2 """) self.fail() except LdbError as e: (num, msg) = e.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) # Windows (2008 at least) seems to have some small bug here: it # returns "0000056A" on longer (always wrong) previous passwords. self.assertTrue('00000056' in msg) # Sets the initial user password with a "special" password change # I think that this internally is a password set operation and it can # only be performed by someone which has password set privileges on the # account (at least in s4 we do handle it like that). self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword add: userPassword userPassword: thatsAcomplPASS1 """) # But in the other way around this special syntax doesn't work try: self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 add: userPassword """) self.fail() except LdbError as e1: (num, _) = e1.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) # Enables the user account self.ldb.enable_account("(sAMAccountName=testuser)") # Open a second LDB connection with the user credentials. Use the # command line credentials for informations like the domain, the realm # and the workstation. creds2 = Credentials() creds2.set_username("testuser") creds2.set_password("thatsAcomplPASS1") creds2.set_domain(creds.get_domain()) creds2.set_realm(creds.get_realm()) creds2.set_workstation(creds.get_workstation()) creds2.set_gensec_features(creds2.get_gensec_features() | gensec.FEATURE_SEAL) self.ldb2 = SamDB(url=host, credentials=creds2, lp=lp) def test_unicodePwd_hash_set(self): """Performs a password hash set operation on 'unicodePwd' which should be prevented""" # Notice: Direct hash password sets should never work m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["unicodePwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE, "unicodePwd") try: self.ldb.modify(m) self.fail() except LdbError as e2: (num, _) = e2.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) def test_unicodePwd_hash_change(self): """Performs a password hash change operation on 'unicodePwd' which should be prevented""" # Notice: Direct hash password changes should never work # Hash password changes should never work try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: unicodePwd unicodePwd: XXXXXXXXXXXXXXXX add: unicodePwd unicodePwd: YYYYYYYYYYYYYYYY """) self.fail() except LdbError as e3: (num, _) = e3.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) def test_unicodePwd_clear_set(self): """Performs a password cleartext set operation on 'unicodePwd'""" m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["unicodePwd"] = MessageElement("\"thatsAcomplPASS2\"".encode('utf-16-le'), FLAG_MOD_REPLACE, "unicodePwd") self.ldb.modify(m) def test_unicodePwd_clear_change(self): """Performs a password cleartext change operation on 'unicodePwd'""" self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: unicodePwd unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """ add: unicodePwd unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """ """) # Wrong old password try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: unicodePwd unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """ add: unicodePwd unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS4\"".encode('utf-16-le')) + """ """) self.fail() except LdbError as e4: (num, msg) = e4.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) self.assertTrue('00000056' in msg) # A change to the same password again will not work (password history) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: unicodePwd unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """ add: unicodePwd unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """ """) self.fail() except LdbError as e5: (num, msg) = e5.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) self.assertTrue('0000052D' in msg) def test_dBCSPwd_hash_set(self): """Performs a password hash set operation on 'dBCSPwd' which should be prevented""" # Notice: Direct hash password sets should never work m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["dBCSPwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE, "dBCSPwd") try: self.ldb.modify(m) self.fail() except LdbError as e6: (num, _) = e6.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) def test_dBCSPwd_hash_change(self): """Performs a password hash change operation on 'dBCSPwd' which should be prevented""" # Notice: Direct hash password changes should never work try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: dBCSPwd dBCSPwd: XXXXXXXXXXXXXXXX add: dBCSPwd dBCSPwd: YYYYYYYYYYYYYYYY """) self.fail() except LdbError as e7: (num, _) = e7.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) def test_userPassword_clear_set(self): """Performs a password cleartext set operation on 'userPassword'""" # Notice: This works only against Windows if "dSHeuristics" has been set # properly m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE, "userPassword") self.ldb.modify(m) def test_userPassword_clear_change(self): """Performs a password cleartext change operation on 'userPassword'""" # Notice: This works only against Windows if "dSHeuristics" has been set # properly self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 """) # Wrong old password try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS3 add: userPassword userPassword: thatsAcomplPASS4 """) self.fail() except LdbError as e8: (num, msg) = e8.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) self.assertTrue('00000056' in msg) # A change to the same password again will not work (password history) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS2 add: userPassword userPassword: thatsAcomplPASS2 """) self.fail() except LdbError as e9: (num, msg) = e9.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) self.assertTrue('0000052D' in msg) def test_clearTextPassword_clear_set(self): """Performs a password cleartext set operation on 'clearTextPassword'""" # Notice: This never works against Windows - only supported by us try: m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["clearTextPassword"] = MessageElement("thatsAcomplPASS2".encode('utf-16-le'), FLAG_MOD_REPLACE, "clearTextPassword") self.ldb.modify(m) # this passes against s4 except LdbError as e10: (num, msg) = e10.args # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it if num != ERR_NO_SUCH_ATTRIBUTE: raise LdbError(num, msg) def test_clearTextPassword_clear_change(self): """Performs a password cleartext change operation on 'clearTextPassword'""" # Notice: This never works against Windows - only supported by us try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: clearTextPassword clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS1".encode('utf-16-le')) + """ add: clearTextPassword clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """ """) # this passes against s4 except LdbError as e11: (num, msg) = e11.args # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it if num != ERR_NO_SUCH_ATTRIBUTE: raise LdbError(num, msg) # Wrong old password try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: clearTextPassword clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS3".encode('utf-16-le')) + """ add: clearTextPassword clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS4".encode('utf-16-le')) + """ """) self.fail() except LdbError as e12: (num, msg) = e12.args # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it if num != ERR_NO_SUCH_ATTRIBUTE: self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) self.assertTrue('00000056' in msg) # A change to the same password again will not work (password history) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: clearTextPassword clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """ add: clearTextPassword clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """ """) self.fail() except LdbError as e13: (num, msg) = e13.args # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it if num != ERR_NO_SUCH_ATTRIBUTE: self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) self.assertTrue('0000052D' in msg) def test_failures(self): """Performs some failure testing""" try: self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 """) self.fail() except LdbError as e14: (num, _) = e14.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 """) self.fail() except LdbError as e15: (num, _) = e15.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword """) self.fail() except LdbError as e16: (num, _) = e16.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword """) self.fail() except LdbError as e17: (num, _) = e17.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify add: userPassword userPassword: thatsAcomplPASS1 """) self.fail() except LdbError as e18: (num, _) = e18.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify add: userPassword userPassword: thatsAcomplPASS1 """) self.fail() except LdbError as e19: (num, _) = e19.args self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) try: self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 userPassword: thatsAcomplPASS2 """) self.fail() except LdbError as e20: (num, _) = e20.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 userPassword: thatsAcomplPASS2 """) self.fail() except LdbError as e21: (num, _) = e21.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 """) self.fail() except LdbError as e22: (num, _) = e22.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 """) self.fail() except LdbError as e23: (num, _) = e23.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 add: userPassword userPassword: thatsAcomplPASS2 """) self.fail() except LdbError as e24: (num, _) = e24.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 add: userPassword userPassword: thatsAcomplPASS2 """) self.fail() except LdbError as e25: (num, _) = e25.args self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) try: self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 delete: userPassword userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 """) self.fail() except LdbError as e26: (num, _) = e26.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 delete: userPassword userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 """) self.fail() except LdbError as e27: (num, _) = e27.args self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) try: self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 replace: userPassword userPassword: thatsAcomplPASS3 """) self.fail() except LdbError as e28: (num, _) = e28.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS1 add: userPassword userPassword: thatsAcomplPASS2 replace: userPassword userPassword: thatsAcomplPASS3 """) self.fail() except LdbError as e29: (num, _) = e29.args self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) # Reverse order does work self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify add: userPassword userPassword: thatsAcomplPASS2 delete: userPassword userPassword: thatsAcomplPASS1 """) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword userPassword: thatsAcomplPASS2 add: unicodePwd unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """ """) # this passes against s4 except LdbError as e30: (num, _) = e30.args self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: unicodePwd unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """ add: userPassword userPassword: thatsAcomplPASS4 """) # this passes against s4 except LdbError as e31: (num, _) = e31.args self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE) # Several password changes at once are allowed self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify replace: userPassword userPassword: thatsAcomplPASS1 userPassword: thatsAcomplPASS2 """) # Several password changes at once are allowed self.ldb.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify replace: userPassword userPassword: thatsAcomplPASS1 userPassword: thatsAcomplPASS2 replace: userPassword userPassword: thatsAcomplPASS3 replace: userPassword userPassword: thatsAcomplPASS4 """) # This surprisingly should work delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn) self.ldb.add({ "dn": "cn=testuser2,cn=users," + self.base_dn, "objectclass": "user", "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS2"] }) # This surprisingly should work delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn) self.ldb.add({ "dn": "cn=testuser2,cn=users," + self.base_dn, "objectclass": "user", "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS1"] }) def test_empty_passwords(self): print("Performs some empty passwords testing") try: self.ldb.add({ "dn": "cn=testuser2,cn=users," + self.base_dn, "objectclass": "user", "unicodePwd": [] }) self.fail() except LdbError as e32: (num, _) = e32.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb.add({ "dn": "cn=testuser2,cn=users," + self.base_dn, "objectclass": "user", "dBCSPwd": [] }) self.fail() except LdbError as e33: (num, _) = e33.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb.add({ "dn": "cn=testuser2,cn=users," + self.base_dn, "objectclass": "user", "userPassword": [] }) self.fail() except LdbError as e34: (num, _) = e34.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) try: self.ldb.add({ "dn": "cn=testuser2,cn=users," + self.base_dn, "objectclass": "user", "clearTextPassword": [] }) self.fail() except LdbError as e35: (num, _) = e35.args self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or num == ERR_NO_SUCH_ATTRIBUTE) # for Windows delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn) m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["unicodePwd"] = MessageElement([], FLAG_MOD_ADD, "unicodePwd") try: self.ldb.modify(m) self.fail() except LdbError as e36: (num, _) = e36.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["dBCSPwd"] = MessageElement([], FLAG_MOD_ADD, "dBCSPwd") try: self.ldb.modify(m) self.fail() except LdbError as e37: (num, _) = e37.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement([], FLAG_MOD_ADD, "userPassword") try: self.ldb.modify(m) self.fail() except LdbError as e38: (num, _) = e38.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["clearTextPassword"] = MessageElement([], FLAG_MOD_ADD, "clearTextPassword") try: self.ldb.modify(m) self.fail() except LdbError as e39: (num, _) = e39.args self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or num == ERR_NO_SUCH_ATTRIBUTE) # for Windows m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["unicodePwd"] = MessageElement([], FLAG_MOD_REPLACE, "unicodePwd") try: self.ldb.modify(m) self.fail() except LdbError as e40: (num, _) = e40.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["dBCSPwd"] = MessageElement([], FLAG_MOD_REPLACE, "dBCSPwd") try: self.ldb.modify(m) self.fail() except LdbError as e41: (num, _) = e41.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement([], FLAG_MOD_REPLACE, "userPassword") try: self.ldb.modify(m) self.fail() except LdbError as e42: (num, _) = e42.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["clearTextPassword"] = MessageElement([], FLAG_MOD_REPLACE, "clearTextPassword") try: self.ldb.modify(m) self.fail() except LdbError as e43: (num, _) = e43.args self.assertTrue(num == ERR_UNWILLING_TO_PERFORM or num == ERR_NO_SUCH_ATTRIBUTE) # for Windows m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["unicodePwd"] = MessageElement([], FLAG_MOD_DELETE, "unicodePwd") try: self.ldb.modify(m) self.fail() except LdbError as e44: (num, _) = e44.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["dBCSPwd"] = MessageElement([], FLAG_MOD_DELETE, "dBCSPwd") try: self.ldb.modify(m) self.fail() except LdbError as e45: (num, _) = e45.args self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement([], FLAG_MOD_DELETE, "userPassword") try: self.ldb.modify(m) self.fail() except LdbError as e46: (num, _) = e46.args self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["clearTextPassword"] = MessageElement([], FLAG_MOD_DELETE, "clearTextPassword") try: self.ldb.modify(m) self.fail() except LdbError as e47: (num, _) = e47.args self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or num == ERR_NO_SUCH_ATTRIBUTE) # for Windows def test_plain_userPassword(self): print("Performs testing about the standard 'userPassword' behaviour") # Delete the "dSHeuristics" self.ldb.set_dsheuristics(None) time.sleep(1) # This switching time is strictly needed! m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement("myPassword", FLAG_MOD_ADD, "userPassword") self.ldb.modify(m) res = self.ldb.search("cn=testuser,cn=users," + self.base_dn, scope=SCOPE_BASE, attrs=["userPassword"]) self.assertTrue(len(res) == 1) self.assertTrue("userPassword" in res[0]) self.assertEquals(res[0]["userPassword"][0], "myPassword") m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement("myPassword2", FLAG_MOD_REPLACE, "userPassword") self.ldb.modify(m) res = self.ldb.search("cn=testuser,cn=users," + self.base_dn, scope=SCOPE_BASE, attrs=["userPassword"]) self.assertTrue(len(res) == 1) self.assertTrue("userPassword" in res[0]) self.assertEquals(res[0]["userPassword"][0], "myPassword2") m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement([], FLAG_MOD_DELETE, "userPassword") self.ldb.modify(m) res = self.ldb.search("cn=testuser,cn=users," + self.base_dn, scope=SCOPE_BASE, attrs=["userPassword"]) self.assertTrue(len(res) == 1) self.assertFalse("userPassword" in res[0]) # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes self.ldb.set_dsheuristics("000000000") m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement("myPassword3", FLAG_MOD_REPLACE, "userPassword") self.ldb.modify(m) res = self.ldb.search("cn=testuser,cn=users," + self.base_dn, scope=SCOPE_BASE, attrs=["userPassword"]) self.assertTrue(len(res) == 1) self.assertTrue("userPassword" in res[0]) self.assertEquals(res[0]["userPassword"][0], "myPassword3") # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes self.ldb.set_dsheuristics("000000002") m = Message() m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement("myPassword4", FLAG_MOD_REPLACE, "userPassword") self.ldb.modify(m) res = self.ldb.search("cn=testuser,cn=users," + self.base_dn, scope=SCOPE_BASE, attrs=["userPassword"]) self.assertTrue(len(res) == 1) self.assertTrue("userPassword" in res[0]) self.assertEquals(res[0]["userPassword"][0], "myPassword4") # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes) self.ldb.set_dsheuristics("000000001") def test_modify_dsheuristics_userPassword(self): print("Performs testing about reading userPassword between dsHeuristic modifies") # Make sure userPassword cannot be read self.ldb.set_dsheuristics("000000000") # Open a new connection (with dsHeuristic=000000000) ldb1 = SamDB(url=host, session_info=system_session(lp), credentials=creds, lp=lp) # Set userPassword to be read # This setting only affects newer connections (ldb2) ldb1.set_dsheuristics("000000001") time.sleep(1) m = Message() m.dn = Dn(ldb1, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement("thatsAcomplPASS1", FLAG_MOD_REPLACE, "userPassword") ldb1.modify(m) res = ldb1.search("cn=testuser,cn=users," + self.base_dn, scope=SCOPE_BASE, attrs=["userPassword"]) # userPassword cannot be read, despite the dsHeuristic setting self.assertTrue(len(res) == 1) self.assertFalse("userPassword" in res[0]) # Open another new connection (with dsHeuristic=000000001) ldb2 = SamDB(url=host, session_info=system_session(lp), credentials=creds, lp=lp) # Set userPassword to be unreadable # This setting does not affect this connection ldb2.set_dsheuristics("000000000") time.sleep(1) res = ldb2.search("cn=testuser,cn=users," + self.base_dn, scope=SCOPE_BASE, attrs=["userPassword"]) # Check that userPassword was not stored from ldb1 self.assertTrue(len(res) == 1) self.assertFalse("userPassword" in res[0]) m = Message() m.dn = Dn(ldb2, "cn=testuser,cn=users," + self.base_dn) m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE, "userPassword") ldb2.modify(m) res = ldb2.search("cn=testuser,cn=users," + self.base_dn, scope=SCOPE_BASE, attrs=["userPassword"]) # userPassword can be read in this connection # This is regardless of the current dsHeuristics setting self.assertTrue(len(res) == 1) self.assertTrue("userPassword" in res[0]) self.assertEquals(res[0]["userPassword"][0], "thatsAcomplPASS2") # Only password from ldb1 is the user's password creds2 = Credentials() creds2.set_username("testuser") creds2.set_password("thatsAcomplPASS1") creds2.set_domain(creds.get_domain()) creds2.set_realm(creds.get_realm()) creds2.set_workstation(creds.get_workstation()) creds2.set_gensec_features(creds2.get_gensec_features() | gensec.FEATURE_SEAL) try: SamDB(url=host, credentials=creds2, lp=lp) except: self.fail("testuser used the wrong password") ldb3 = SamDB(url=host, session_info=system_session(lp), credentials=creds, lp=lp) # Check that userPassword was stored from ldb2 res = ldb3.search("cn=testuser,cn=users," + self.base_dn, scope=SCOPE_BASE, attrs=["userPassword"]) # userPassword can be read self.assertTrue(len(res) == 1) self.assertTrue("userPassword" in res[0]) self.assertEquals(res[0]["userPassword"][0], "thatsAcomplPASS2") # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes) self.ldb.set_dsheuristics("000000001") def test_zero_length(self): # Get the old "minPwdLength" minPwdLength = self.ldb.get_minPwdLength() # Set it temporarely to "0" self.ldb.set_minPwdLength("0") # Get the old "pwdProperties" pwdProperties = self.ldb.get_pwdProperties() # Set them temporarely to "0" (to deactivate eventually the complexity) self.ldb.set_pwdProperties("0") self.ldb.setpassword("(sAMAccountName=testuser)", "") # Reset the "pwdProperties" as they were before self.ldb.set_pwdProperties(pwdProperties) # Reset the "minPwdLength" as it was before self.ldb.set_minPwdLength(minPwdLength) def test_pw_change_delete_no_value_userPassword(self): """Test password change with userPassword where the delete attribute doesn't have a value""" try: self.ldb2.modify_ldif(""" dn: cn=testuser,cn=users,""" + self.base_dn + """ changetype: modify delete: userPassword add: userPassword userPassword: thatsAcomplPASS1 """) except LdbError, (num, msg): self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) else:
class GroupAuditTests(AuditLogTestBase): def setUp(self): self.message_type = MSG_GROUP_LOG self.event_type = DSDB_GROUP_EVENT_NAME super(GroupAuditTests, self).setUp() self.remoteAddress = os.environ["CLIENT_IP"] self.server_ip = os.environ["SERVER_IP"] host = "ldap://%s" % os.environ["SERVER"] self.ldb = SamDB(url=host, session_info=system_session(), credentials=self.get_credentials(), lp=self.get_loadparm()) self.server = os.environ["SERVER"] # Gets back the basedn self.base_dn = self.ldb.domain_dn() # Get the old "dSHeuristics" if it was set dsheuristics = self.ldb.get_dsheuristics() # 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 self.ldb.add({ "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn, "objectclass": "user", "sAMAccountName": USER_NAME, "userPassword": USER_PASS }) self.ldb.newgroup(GROUP_NAME_01) self.ldb.newgroup(GROUP_NAME_02) def tearDown(self): super(GroupAuditTests, self).tearDown() delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn) self.ldb.deletegroup(GROUP_NAME_01) self.ldb.deletegroup(GROUP_NAME_02) def test_add_and_remove_users_from_group(self): # # Wait for the primary group change for the created user. # messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("PrimaryGroup", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=domain users,cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # # Add the user to a group # self.discardMessages() self.ldb.add_remove_group_members(GROUP_NAME_01, [USER_NAME]) messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # # Add the user to another group # self.discardMessages() self.ldb.add_remove_group_members(GROUP_NAME_02, [USER_NAME]) messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_02 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # # Remove the user from a group # self.discardMessages() self.ldb.add_remove_group_members( GROUP_NAME_01, [USER_NAME], add_members_operation=False) messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Removed", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # # Re-add the user to a group # self.discardMessages() self.ldb.add_remove_group_members(GROUP_NAME_01, [USER_NAME]) messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") def test_change_primary_group(self): # # Wait for the primary group change for the created user. # messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("PrimaryGroup", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=domain users,cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # # Add the user to a group, the user needs to be a member of a group # before there primary group can be set to that group. # self.discardMessages() self.ldb.add_remove_group_members(GROUP_NAME_01, [USER_NAME]) messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") # # Change the primary group of a user # user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn # get the primaryGroupToken of the group res = self.ldb.search(base=group_dn, attrs=["primaryGroupToken"], scope=ldb.SCOPE_BASE) group_id = res[0]["primaryGroupToken"] # set primaryGroupID attribute of the user to that group m = ldb.Message() m.dn = ldb.Dn(self.ldb, user_dn) m["primaryGroupID"] = ldb.MessageElement( group_id, FLAG_MOD_REPLACE, "primaryGroupID") self.discardMessages() self.ldb.modify(m) # # Wait for the primary group change. # Will see the user removed from the new group # the user added to their old primary group # and a new primary group event. # messages = self.waitForMessages(3) print("Received %d messages" % len(messages)) self.assertEquals(3, len(messages), "Did not receive the expected number of messages") audit = messages[0]["groupChange"] self.assertEqual("Removed", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") audit = messages[1]["groupChange"] self.assertEqual("Added", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=domain users,cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") audit = messages[2]["groupChange"] self.assertEqual("PrimaryGroup", audit["action"]) user_dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn group_dn = "cn=" + GROUP_NAME_01 + ",cn=users," + self.base_dn self.assertTrue(user_dn.lower(), audit["user"].lower()) self.assertTrue(group_dn.lower(), audit["group"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP")
class PassWordHashTests(TestCase): def setUp(self): super(PassWordHashTests, self).setUp() # Add a user to ldb, this will exercise the password_hash code # and calculate the appropriate supplemental credentials def add_user(self, options=None, clear_text=False): self.lp = samba.tests.env_loadparm() # set any needed options if options is not None: for (option,value) in options: self.lp.set(option, value) self.creds = Credentials() self.session = system_session() self.ldb = SamDB( session_info=self.session, credentials=self.creds, lp=self.lp) # Gets back the basedn base_dn = self.ldb.domain_dn() # Gets back the configuration basedn configuration_dn = self.ldb.get_config_basedn().get_linearized() # Get the old "dSHeuristics" if it was set dsheuristics = self.ldb.get_dsheuristics() # 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) account_control = 0 if clear_text: # get the current pwdProperties pwdProperties = self.ldb.get_pwdProperties() # enable clear text properties props = int(pwdProperties) props |= DOMAIN_PASSWORD_STORE_CLEARTEXT self.ldb.set_pwdProperties(str(props)) # Restore the value on exit. self.addCleanup(self.ldb.set_pwdProperties, pwdProperties) account_control |= UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED # (Re)adds the test user USER_NAME with password USER_PASS # and userPrincipalName UPN delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn) self.ldb.add({ "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn, "objectclass": "user", "sAMAccountName": USER_NAME, "userPassword": USER_PASS, "userPrincipalName": UPN, "userAccountControl": str(account_control) }) # Get the supplemental credentials for the user under test def get_supplemental_creds(self): base = "cn=" + USER_NAME + ",cn=users," + self.base_dn res = self.ldb.search(scope=ldb.SCOPE_BASE, base=base, attrs=["supplementalCredentials"]) self.assertIs( True, len(res) > 0) obj = res[0] sc_blob = obj["supplementalCredentials"][0] sc = ndr_unpack(drsblobs.supplementalCredentialsBlob, sc_blob) return sc # Calculate and validate a Wdigest value def check_digest(self, user, realm, password, digest): expected = calc_digest( user, realm, password) actual = binascii.hexlify(bytearray(digest)) error = "Digest expected[%s], actual[%s], " \ "user[%s], realm[%s], pass[%s]" % \ (expected, actual, user, realm, password) self.assertEquals(expected, actual, error) # Check all of the 29 expected WDigest values # def check_wdigests(self, digests): self.assertEquals(29, digests.num_hashes) self.check_digest(USER_NAME, self.lp.get("workgroup"), USER_PASS, digests.hashes[0].hash) self.check_digest(USER_NAME.lower(), self.lp.get("workgroup").lower(), USER_PASS, digests.hashes[1].hash) self.check_digest(USER_NAME.upper(), self.lp.get("workgroup").upper(), USER_PASS, digests.hashes[2].hash) self.check_digest(USER_NAME, self.lp.get("workgroup").upper(), USER_PASS, digests.hashes[3].hash) self.check_digest(USER_NAME, self.lp.get("workgroup").lower(), USER_PASS, digests.hashes[4].hash) self.check_digest(USER_NAME.upper(), self.lp.get("workgroup").lower(), USER_PASS, digests.hashes[5].hash) self.check_digest(USER_NAME.lower(), self.lp.get("workgroup").upper(), USER_PASS, digests.hashes[6].hash) self.check_digest(USER_NAME, self.lp.get("realm").lower(), USER_PASS, digests.hashes[7].hash) self.check_digest(USER_NAME.lower(), self.lp.get("realm").lower(), USER_PASS, digests.hashes[8].hash) self.check_digest(USER_NAME.upper(), self.lp.get("realm"), USER_PASS, digests.hashes[9].hash) self.check_digest(USER_NAME, self.lp.get("realm"), USER_PASS, digests.hashes[10].hash) self.check_digest(USER_NAME, self.lp.get("realm").lower(), USER_PASS, digests.hashes[11].hash) self.check_digest(USER_NAME.upper(), self.lp.get("realm").lower(), USER_PASS, digests.hashes[12].hash) self.check_digest(USER_NAME.lower(), self.lp.get("realm"), USER_PASS, digests.hashes[13].hash) self.check_digest(UPN, "", USER_PASS, digests.hashes[14].hash) self.check_digest(UPN.lower(), "", USER_PASS, digests.hashes[15].hash) self.check_digest(UPN.upper(), "", USER_PASS, digests.hashes[16].hash) name = "%s\\%s" % (self.lp.get("workgroup"), USER_NAME) self.check_digest(name, "", USER_PASS, digests.hashes[17].hash) name = "%s\\%s" % (self.lp.get("workgroup").lower(), USER_NAME.lower()) self.check_digest(name, "", USER_PASS, digests.hashes[18].hash) name = "%s\\%s" % (self.lp.get("workgroup").upper(), USER_NAME.upper()) self.check_digest(name, "", USER_PASS, digests.hashes[19].hash) self.check_digest(USER_NAME, "Digest", USER_PASS, digests.hashes[20].hash) self.check_digest(USER_NAME.lower(), "Digest", USER_PASS, digests.hashes[21].hash) self.check_digest(USER_NAME.upper(), "Digest", USER_PASS, digests.hashes[22].hash) self.check_digest(UPN, "Digest", USER_PASS, digests.hashes[23].hash) self.check_digest(UPN.lower(), "Digest", USER_PASS, digests.hashes[24].hash) self.check_digest(UPN.upper(), "Digest", USER_PASS, digests.hashes[25].hash) name = "%s\\%s" % (self.lp.get("workgroup"), USER_NAME) self.check_digest(name, "Digest", USER_PASS, digests.hashes[26].hash) name = "%s\\%s" % (self.lp.get("workgroup").lower(), USER_NAME.lower()) self.check_digest(name, "Digest", USER_PASS, digests.hashes[27].hash) name = "%s\\%s" % (self.lp.get("workgroup").upper(), USER_NAME.upper()) self.check_digest(name, "Digest", USER_PASS, digests.hashes[28].hash)
class AuditLogPassChangeTests(AuditLogTestBase): def setUp(self): self.message_type = MSG_DSDB_PWD_LOG self.event_type = DSDB_PWD_EVENT_NAME super(AuditLogPassChangeTests, self).setUp() self.server_ip = os.environ["SERVER_IP"] host = "ldap://%s" % os.environ["SERVER"] self.ldb = SamDB(url=host, session_info=system_session(), credentials=self.get_credentials(), lp=self.get_loadparm()) self.server = os.environ["SERVER"] # Gets back the basedn self.base_dn = self.ldb.domain_dn() # Get the old "dSHeuristics" if it was set dsheuristics = self.ldb.get_dsheuristics() # 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) delete_force(self.ldb, "cn=" + SECOND_USER_NAME + ",cn=users," + self.base_dn) self.ldb.add({ "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn, "objectclass": "user", "sAMAccountName": USER_NAME, "userPassword": USER_PASS }) # # Discard the messages from the setup code # def discardSetupMessages(self, dn): self.waitForMessages(1, dn=dn) self.discardMessages() def tearDown(self): super(AuditLogPassChangeTests, self).tearDown() def test_net_change_password(self): dn = "CN=" + USER_NAME + ",CN=Users," + self.base_dn self.discardSetupMessages(dn) creds = self.insta_creds(template=self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server) password = "******" net.change_password(newpassword=password, username=USER_NAME, oldpassword=USER_PASS) messages = self.waitForMessages(1, net, dn) print("Received %d messages" % len(messages)) self.assertEqual(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] self.assertEqual(EVT_ID_PASSWORD_CHANGE, audit["eventId"]) self.assertEqual("Change", audit["action"]) self.assertEqual(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEqual(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEqual(service_description, "DCE/RPC") self.assertTrue(self.is_guid(audit["transactionId"])) def test_net_set_password_user_without_permission(self): dn = "CN=" + USER_NAME + ",CN=Users," + self.base_dn self.discardSetupMessages(dn) self.ldb.newuser(SECOND_USER_NAME, SECOND_USER_PASS) # # Get the password reset from the user add # dn = "CN=" + SECOND_USER_NAME + ",CN=Users," + self.base_dn messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEqual(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] self.assertEqual(EVT_ID_PASSWORD_RESET, audit["eventId"]) self.assertEqual("Reset", audit["action"]) self.assertEqual(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEqual(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEqual(service_description, "LDAP") self.assertTrue(self.is_guid(audit["transactionId"])) self.assertEqual(0, audit["statusCode"]) self.assertEqual("Success", audit["status"]) self.discardMessages() creds = self.insta_creds(template=self.get_credentials(), username=SECOND_USER_NAME, userpass=SECOND_USER_PASS, kerberos_state=None) lp = self.get_loadparm() net = Net(creds, lp, server=self.server) password = "******" domain = lp.get("workgroup") try: net.set_password(newpassword=password, account_name=USER_NAME, domain_name=domain) self.fail("Expected exception not thrown") except Exception: pass dn = "CN=" + USER_NAME + ",CN=Users," + self.base_dn messages = self.waitForMessages(1, net, dn=dn) print("Received %d messages" % len(messages)) self.assertEqual(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] self.assertEqual(EVT_ID_PASSWORD_RESET, audit["eventId"]) self.assertEqual("Reset", audit["action"]) self.assertEqual(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEqual(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEqual(service_description, "DCE/RPC") self.assertTrue(self.is_guid(audit["transactionId"])) self.assertEqual(ERR_INSUFFICIENT_ACCESS_RIGHTS, audit["statusCode"]) self.assertEqual("insufficient access rights", audit["status"]) def test_net_set_password(self): dn = "CN=" + USER_NAME + ",CN=Users," + self.base_dn self.discardSetupMessages(dn) creds = self.insta_creds(template=self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server) password = "******" domain = lp.get("workgroup") net.set_password(newpassword=password, account_name=USER_NAME, domain_name=domain) dn = "CN=" + USER_NAME + ",CN=Users," + self.base_dn messages = self.waitForMessages(1, net, dn) print("Received %d messages" % len(messages)) self.assertEqual(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] self.assertEqual(EVT_ID_PASSWORD_RESET, audit["eventId"]) self.assertEqual("Reset", audit["action"]) self.assertEqual(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEqual(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEqual(service_description, "DCE/RPC") session_id = self.get_session() self.assertEqual(session_id, audit["sessionId"]) self.assertTrue(self.is_guid(audit["transactionId"])) def test_ldap_change_password(self): dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) new_password = samba.generate_random_password(32, 32) self.ldb.modify_ldif("dn: " + dn + "\n" + "changetype: modify\n" + "delete: userPassword\n" + "userPassword: "******"\n" + "add: userPassword\n" + "userPassword: "******"\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEqual(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] self.assertEqual(EVT_ID_PASSWORD_CHANGE, audit["eventId"]) self.assertEqual("Change", audit["action"]) self.assertEqual(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEqual(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEqual(service_description, "LDAP") self.assertTrue(self.is_guid(audit["transactionId"])) def test_ldap_replace_password(self): dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) new_password = samba.generate_random_password(32, 32) self.ldb.modify_ldif("dn: " + dn + "\n" + "changetype: modify\n" + "replace: userPassword\n" + "userPassword: "******"\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEqual(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["passwordChange"] self.assertEqual(EVT_ID_PASSWORD_RESET, audit["eventId"]) self.assertEqual("Reset", audit["action"]) self.assertEqual(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEqual(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEqual(service_description, "LDAP") self.assertTrue(self.is_guid(audit["transactionId"])) def test_ldap_add_user(self): # The setup code adds a user, so we check for the password event # generated by it. dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEqual(1, len(messages), "Did not receive the expected number of messages") # # The first message should be the reset from the Setup code. # audit = messages[0]["passwordChange"] self.assertEqual(EVT_ID_PASSWORD_RESET, audit["eventId"]) self.assertEqual("Reset", audit["action"]) self.assertEqual(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEqual(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEqual(service_description, "LDAP") self.assertTrue(self.is_guid(audit["sessionId"])) self.assertTrue(self.is_guid(audit["transactionId"]))
class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase): def setUp(self): super(AuthLogPassChangeTests, self).setUp() self.remoteAddress = os.environ["CLIENT_IP"] self.server_ip = os.environ["SERVER_IP"] host = "ldap://%s" % os.environ["SERVER"] self.ldb = SamDB(url=host, session_info=system_session(), credentials=self.get_credentials(), lp=self.get_loadparm()) print "ldb %s" % type(self.ldb) # Gets back the basedn base_dn = self.ldb.domain_dn() print "base_dn %s" % base_dn # Gets back the configuration basedn configuration_dn = self.ldb.get_config_basedn().get_linearized() # Get the old "dSHeuristics" if it was set dsheuristics = self.ldb.get_dsheuristics() # 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({ "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn, "objectclass": "user", "sAMAccountName": USER_NAME, "userPassword": USER_PASS }) # discard any auth log messages for the password setup self.discardMessages() def tearDown(self): super(AuthLogPassChangeTests, self).tearDown() def test_admin_change_password(self): def isLastExpectedMessage(msg): return (msg["type"] == "Authentication" and msg["Authentication"]["status"] == "NT_STATUS_OK" and msg["Authentication"]["serviceDescription"] == "SAMR Password Change" and msg["Authentication"]["authDescription"] == "samr_ChangePasswordUser3") creds = self.insta_creds(template = self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server_ip) password = "******" net.change_password(newpassword=password.encode('utf-8'), username=USER_NAME, oldpassword=USER_PASS) messages = self.waitForMessages(isLastExpectedMessage) print "Received %d messages" % len(messages) self.assertEquals(8, len(messages), "Did not receive the expected number of messages") def test_admin_change_password_new_password_fails_restriction(self): def isLastExpectedMessage(msg): return (msg["type"] == "Authentication" and msg["Authentication"]["status"] == "NT_STATUS_PASSWORD_RESTRICTION" and msg["Authentication"]["serviceDescription"] == "SAMR Password Change" and msg["Authentication"]["authDescription"] == "samr_ChangePasswordUser3") creds = self.insta_creds(template=self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server_ip) password = "******" exception_thrown = False try: net.change_password(newpassword=password.encode('utf-8'), oldpassword=USER_PASS, username=USER_NAME) except Exception as msg: exception_thrown = True self.assertEquals(True, exception_thrown, "Expected exception not thrown") messages = self.waitForMessages(isLastExpectedMessage) self.assertEquals(8, len(messages), "Did not receive the expected number of messages") def test_admin_change_password_unknown_user(self): def isLastExpectedMessage(msg): return (msg["type"] == "Authentication" and msg["Authentication"]["status"] == "NT_STATUS_NO_SUCH_USER" and msg["Authentication"]["serviceDescription"] == "SAMR Password Change" and msg["Authentication"]["authDescription"] == "samr_ChangePasswordUser3") creds = self.insta_creds(template=self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server_ip) password = "******" exception_thrown = False try: net.change_password(newpassword=password.encode('utf-8'), oldpassword=USER_PASS, username="******") except Exception as msg: exception_thrown = True self.assertEquals(True, exception_thrown, "Expected exception not thrown") messages = self.waitForMessages(isLastExpectedMessage) self.assertEquals(8, len(messages), "Did not receive the expected number of messages") def test_admin_change_password_bad_original_password(self): def isLastExpectedMessage(msg): return (msg["type"] == "Authentication" and msg["Authentication"]["status"] == "NT_STATUS_WRONG_PASSWORD" and msg["Authentication"]["serviceDescription"] == "SAMR Password Change" and msg["Authentication"]["authDescription"] == "samr_ChangePasswordUser3") creds = self.insta_creds(template=self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server_ip) password = "******" exception_thrown = False try: net.change_password(newpassword=password.encode('utf-8'), oldpassword="******", username=USER_NAME) except Exception as msg: exception_thrown = True self.assertEquals(True, exception_thrown, "Expected exception not thrown") messages = self.waitForMessages(isLastExpectedMessage) self.assertEquals(8, len(messages), "Did not receive the expected number of messages") # net rap password changes are broken, but they trigger enough of the # server side behaviour to exercise the code paths of interest. # if we used the real password it would be too long and does not hash # correctly, so we just check it triggers the wrong password path. def test_rap_change_password(self): def isLastExpectedMessage(msg): return (msg["type"] == "Authentication" and msg["Authentication"]["serviceDescription"] == "SAMR Password Change" and msg["Authentication"]["status"] == "NT_STATUS_WRONG_PASSWORD" and msg["Authentication"]["authDescription"] == "OemChangePasswordUser2") username = os.environ["USERNAME"] server = os.environ["SERVER"] password = os.environ["PASSWORD"] server_param = "--server=%s" % server creds = "-U%s%%%s" % (username,password) call(["bin/net", "rap", server_param, "password", USER_NAME, "notMyPassword", "notGoingToBeMyPassword", server, creds, "--option=client ipc max protocol=nt1"]) messages = self.waitForMessages(isLastExpectedMessage) self.assertEquals(7, len(messages), "Did not receive the expected number of messages") def test_ldap_change_password(self): def isLastExpectedMessage(msg): return (msg["type"] == "Authentication" and msg["Authentication"]["status"] == "NT_STATUS_OK" and msg["Authentication"]["serviceDescription"] == "LDAP Password Change" and msg["Authentication"]["authDescription"] == "LDAP Modify") new_password = samba.generate_random_password(32,32) self.ldb.modify_ldif( "dn: cn=" + USER_NAME + ",cn=users," + self.base_dn + "\n" + "changetype: modify\n" + "delete: userPassword\n" + "userPassword: "******"\n" + "add: userPassword\n" + "userPassword: "******"\n" ) messages = self.waitForMessages(isLastExpectedMessage) print "Received %d messages" % len(messages) self.assertEquals(4, len(messages), "Did not receive the expected number of messages") # # Currently this does not get logged, so we expect to only see the log # entries for the underlying ldap bind. # def test_ldap_change_password_bad_user(self): def isLastExpectedMessage(msg): return (msg["type"] == "Authorization" and msg["Authorization"]["serviceDescription"] == "LDAP" and msg["Authorization"]["authType"] == "krb5") new_password = samba.generate_random_password(32,32) try: self.ldb.modify_ldif( "dn: cn=" + "badUser" + ",cn=users," + self.base_dn + "\n" + "changetype: modify\n" + "delete: userPassword\n" + "userPassword: "******"\n" + "add: userPassword\n" + "userPassword: "******"\n" ) self.fail() except LdbError, (num, msg): pass messages = self.waitForMessages(isLastExpectedMessage) print "Received %d messages" % len(messages) self.assertEquals(3, len(messages), "Did not receive the expected number of messages")
class RodcRwdcTests(password_lockout_base.BasePasswordTestCase): counter = itertools.count(1).next def force_replication(self, base=None): if base is None: base = self.base_dn # XXX feels like a horrendous way to do it. credstring = '-U%s%%%s' % (CREDS.get_username(), CREDS.get_password()) cmd = [ 'bin/samba-tool', 'drs', 'replicate', RODC, RWDC, base, credstring, '--sync-forced' ] p = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode: print "failed with code %s" % p.returncode print ' '.join(cmd) print "stdout" print stdout print "stderr" print stderr raise RodcRwdcTestException() def _check_account_initial(self, dn): self.force_replication() return super(RodcRwdcTests, self)._check_account_initial(dn) def tearDown(self): super(RodcRwdcTests, self).tearDown() self.rwdc_db.set_dsheuristics(self.rwdc_dsheuristics) CREDS.set_kerberos_state(DONT_USE_KERBEROS) set_auto_replication(RWDC, True) def setUp(self): self.rodc_db = SamDB('ldap://%s' % RODC, credentials=CREDS, session_info=system_session(LP), lp=LP) self.rwdc_db = SamDB('ldap://%s' % RWDC, credentials=CREDS, session_info=system_session(LP), lp=LP) # Define variables for BasePasswordTestCase self.lp = LP self.global_creds = CREDS self.host = RWDC self.host_url = 'ldap://%s' % RWDC self.ldb = SamDB(url='ldap://%s' % RWDC, session_info=system_session(self.lp), credentials=self.global_creds, lp=self.lp) super(RodcRwdcTests, self).setUp() self.host = RODC self.host_url = 'ldap://%s' % RODC self.ldb = SamDB(url='ldap://%s' % RODC, session_info=system_session(self.lp), credentials=self.global_creds, lp=self.lp) self.samr = samr.samr("ncacn_ip_tcp:%s[seal]" % self.host, self.lp, self.global_creds) self.samr_handle = self.samr.Connect2( None, security.SEC_FLAG_MAXIMUM_ALLOWED) self.samr_domain = self.samr.OpenDomain( self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid) self.base_dn = self.rwdc_db.domain_dn() root = self.rodc_db.search(base='', scope=ldb.SCOPE_BASE, attrs=['dsServiceName']) self.service = root[0]['dsServiceName'][0] self.tag = uuid.uuid4().hex self.rwdc_dsheuristics = self.rwdc_db.get_dsheuristics() self.rwdc_db.set_dsheuristics("000000001") set_auto_replication(RWDC, False) # make sure DCs are synchronized before the test self.force_replication() self.rwdc_dn = get_server_ref_from_samdb(self.rwdc_db) self.rodc_dn = get_server_ref_from_samdb(self.rodc_db) def assertReferral(self, fn, *args, **kwargs): try: fn(*args, **kwargs) self.fail("failed to raise ldap referral") except ldb.LdbError as (code, msg): self.assertEqual(code, ldb.ERR_REFERRAL, "expected referral, got %s %s" % (code, msg))
class AuditLogDsdbTests(AuditLogTestBase): def setUp(self): self.message_type = MSG_DSDB_LOG self.event_type = DSDB_EVENT_NAME super(AuditLogDsdbTests, self).setUp() self.remoteAddress = os.environ["CLIENT_IP"] self.server_ip = os.environ["SERVER_IP"] host = "ldap://%s" % os.environ["SERVER"] self.ldb = SamDB(url=host, session_info=system_session(), credentials=self.get_credentials(), lp=self.get_loadparm()) self.server = os.environ["SERVER"] # Gets back the basedn self.base_dn = self.ldb.domain_dn() # Get the old "dSHeuristics" if it was set dsheuristics = self.ldb.get_dsheuristics() # 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({ "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn, "objectclass": "user", "sAMAccountName": USER_NAME, "userPassword": USER_PASS }) # # Discard the messages from the setup code # def discardSetupMessages(self, dn): self.waitForMessages(2, dn=dn) self.discardMessages() def tearDown(self): self.discardMessages() super(AuditLogDsdbTests, self).tearDown() def haveExpectedTxn(self, expected): if self.context["txnMessage"] is not None: txn = self.context["txnMessage"]["dsdbTransaction"] if txn["transactionId"] == expected: return True return False def waitForTransaction(self, expected, connection=None): """Wait for a transaction message to arrive The connection is passed through to keep the connection alive until all the logging messages have been received. """ self.connection = connection start_time = time.time() while not self.haveExpectedTxn(expected): self.msg_ctx.loop_once(0.1) if time.time() - start_time > 1: self.connection = None return "" self.connection = None return self.context["txnMessage"] def test_net_change_password(self): dn = "CN=" + USER_NAME + ",CN=Users," + self.base_dn self.discardSetupMessages(dn) creds = self.insta_creds(template=self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server) password = "******" net.change_password(newpassword=password.encode('utf-8'), username=USER_NAME, oldpassword=USER_PASS) messages = self.waitForMessages(1, net, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Modify", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) # We skip the check for self.get_service_description() as this # is subject to a race between smbd and the s4 rpc_server code # as to which will set the description as it is DCE/RPC over SMB self.assertTrue(self.is_guid(audit["transactionId"])) attributes = audit["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["clearTextPassword"]["actions"] self.assertEquals(1, len(actions)) self.assertTrue(actions[0]["redacted"]) self.assertEquals("replace", actions[0]["action"]) def test_net_set_password(self): dn = "CN=" + USER_NAME + ",CN=Users," + self.base_dn self.discardSetupMessages(dn) creds = self.insta_creds(template=self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server) password = "******" domain = lp.get("workgroup") net.set_password(newpassword=password.encode('utf-8'), account_name=USER_NAME, domain_name=domain) messages = self.waitForMessages(1, net, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Modify", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) # We skip the check for self.get_service_description() as this # is subject to a race between smbd and the s4 rpc_server code # as to which will set the description as it is DCE/RPC over SMB self.assertTrue(self.is_guid(audit["transactionId"])) attributes = audit["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["clearTextPassword"]["actions"] self.assertEquals(1, len(actions)) self.assertTrue(actions[0]["redacted"]) self.assertEquals("replace", actions[0]["action"]) def test_ldap_change_password(self): dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) new_password = samba.generate_random_password(32, 32) dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.ldb.modify_ldif( "dn: " + dn + "\n" + "changetype: modify\n" + "delete: userPassword\n" + "userPassword: "******"\n" + "add: userPassword\n" + "userPassword: "******"\n") messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Modify", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") attributes = audit["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["userPassword"]["actions"] self.assertEquals(2, len(actions)) self.assertTrue(actions[0]["redacted"]) self.assertEquals("delete", actions[0]["action"]) self.assertTrue(actions[1]["redacted"]) self.assertEquals("add", actions[1]["action"]) def test_ldap_replace_password(self): dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) new_password = samba.generate_random_password(32, 32) self.ldb.modify_ldif( "dn: " + dn + "\n" + "changetype: modify\n" + "replace: userPassword\n" + "userPassword: "******"\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Modify", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") self.assertTrue(self.is_guid(audit["transactionId"])) attributes = audit["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["userPassword"]["actions"] self.assertEquals(1, len(actions)) self.assertTrue(actions[0]["redacted"]) self.assertEquals("replace", actions[0]["action"]) def test_ldap_add_user(self): # The setup code adds a user, so we check for the dsdb events # generated by it. dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn messages = self.waitForMessages(2, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(2, len(messages), "Did not receive the expected number of messages") audit = messages[1]["dsdbChange"] self.assertEquals("Add", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") self.assertTrue(self.is_guid(audit["sessionId"])) self.assertTrue(self.is_guid(audit["transactionId"])) attributes = audit["attributes"] self.assertEquals(3, len(attributes)) actions = attributes["objectclass"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) self.assertEquals(1, len(actions[0]["values"])) self.assertEquals("user", actions[0]["values"][0]["value"]) actions = attributes["sAMAccountName"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) self.assertEquals(1, len(actions[0]["values"])) self.assertEquals(USER_NAME, actions[0]["values"][0]["value"]) actions = attributes["userPassword"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) self.assertTrue(actions[0]["redacted"]) def test_samdb_delete_user(self): dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) self.ldb.deleteuser(USER_NAME) messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Delete", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) self.assertEquals(0, audit["statusCode"]) self.assertEquals("Success", audit["status"]) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") transactionId = audit["transactionId"] message = self.waitForTransaction(transactionId) audit = message["dsdbTransaction"] self.assertEquals("commit", audit["action"]) self.assertTrue(self.is_guid(audit["transactionId"])) self.assertTrue(audit["duration"] > 0) def test_samdb_delete_non_existent_dn(self): DOES_NOT_EXIST = "doesNotExist" dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) dn = "cn=" + DOES_NOT_EXIST + ",cn=users," + self.base_dn try: self.ldb.delete(dn) self.fail("Exception not thrown") except Exception: pass messages = self.waitForMessages(1) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Delete", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertEquals(ERR_NO_SUCH_OBJECT, audit["statusCode"]) self.assertEquals("No such object", audit["status"]) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") transactionId = audit["transactionId"] message = self.waitForTransaction(transactionId) audit = message["dsdbTransaction"] self.assertEquals("rollback", audit["action"]) self.assertTrue(self.is_guid(audit["transactionId"])) self.assertTrue(audit["duration"] > 0) def test_create_and_delete_secret_over_lsa(self): dn = "cn=Test Secret,CN=System," + self.base_dn self.discardSetupMessages(dn) creds = self.insta_creds(template=self.get_credentials()) lsa_conn = lsa.lsarpc( "ncacn_np:%s" % self.server, self.get_loadparm(), creds) lsa_handle = lsa_conn.OpenPolicy2( system_name="\\", attr=lsa.ObjectAttribute(), access_mask=security.SEC_FLAG_MAXIMUM_ALLOWED) secret_name = lsa.String() secret_name.string = "G$Test" lsa_conn.CreateSecret( handle=lsa_handle, name=secret_name, access_mask=security.SEC_FLAG_MAXIMUM_ALLOWED) messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Add", audit["operation"]) self.assertTrue(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) # We skip the check for self.get_service_description() as this # is subject to a race between smbd and the s4 rpc_server code # as to which will set the description as it is DCE/RPC over SMB attributes = audit["attributes"] self.assertEquals(2, len(attributes)) object_class = attributes["objectClass"] self.assertEquals(1, len(object_class["actions"])) action = object_class["actions"][0] self.assertEquals("add", action["action"]) values = action["values"] self.assertEquals(1, len(values)) self.assertEquals("secret", values[0]["value"]) cn = attributes["cn"] self.assertEquals(1, len(cn["actions"])) action = cn["actions"][0] self.assertEquals("add", action["action"]) values = action["values"] self.assertEquals(1, len(values)) self.assertEquals("Test Secret", values[0]["value"]) # # Now delete the secret. self.discardMessages() h = lsa_conn.OpenSecret( handle=lsa_handle, name=secret_name, access_mask=security.SEC_FLAG_MAXIMUM_ALLOWED) lsa_conn.DeleteObject(h) messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") dn = "cn=Test Secret,CN=System," + self.base_dn audit = messages[0]["dsdbChange"] self.assertEquals("Delete", audit["operation"]) self.assertTrue(audit["performedAsSystem"]) self.assertTrue(dn.lower(), audit["dn"].lower()) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) # We skip the check for self.get_service_description() as this # is subject to a race between smbd and the s4 rpc_server code # as to which will set the description as it is DCE/RPC over SMB def test_modify(self): dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn self.discardSetupMessages(dn) # # Add an attribute value # self.ldb.modify_ldif( "dn: " + dn + "\n" + "changetype: modify\n" + "add: carLicense\n" + "carLicense: license-01\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") audit = messages[0]["dsdbChange"] self.assertEquals("Modify", audit["operation"]) self.assertFalse(audit["performedAsSystem"]) self.assertEquals(dn, audit["dn"]) self.assertRegexpMatches(audit["remoteAddress"], self.remoteAddress) self.assertTrue(self.is_guid(audit["sessionId"])) session_id = self.get_session() self.assertEquals(session_id, audit["sessionId"]) service_description = self.get_service_description() self.assertEquals(service_description, "LDAP") attributes = audit["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["carLicense"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) values = actions[0]["values"] self.assertEquals(1, len(values)) self.assertEquals("license-01", values[0]["value"]) # # Add an another value to the attribute # self.discardMessages() self.ldb.modify_ldif( "dn: " + dn + "\n" + "changetype: modify\n" + "add: carLicense\n" + "carLicense: license-02\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") attributes = messages[0]["dsdbChange"]["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["carLicense"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) values = actions[0]["values"] self.assertEquals(1, len(values)) self.assertEquals("license-02", values[0]["value"]) # # Add an another two values to the attribute # self.discardMessages() self.ldb.modify_ldif( "dn: " + dn + "\n" + "changetype: modify\n" + "add: carLicense\n" + "carLicense: license-03\n" + "carLicense: license-04\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") attributes = messages[0]["dsdbChange"]["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["carLicense"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("add", actions[0]["action"]) values = actions[0]["values"] self.assertEquals(2, len(values)) self.assertEquals("license-03", values[0]["value"]) self.assertEquals("license-04", values[1]["value"]) # # delete two values to the attribute # self.discardMessages() self.ldb.modify_ldif( "dn: " + dn + "\n" + "changetype: delete\n" + "delete: carLicense\n" + "carLicense: license-03\n" + "carLicense: license-04\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") attributes = messages[0]["dsdbChange"]["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["carLicense"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("delete", actions[0]["action"]) values = actions[0]["values"] self.assertEquals(2, len(values)) self.assertEquals("license-03", values[0]["value"]) self.assertEquals("license-04", values[1]["value"]) # # replace two values to the attribute # self.discardMessages() self.ldb.modify_ldif( "dn: " + dn + "\n" + "changetype: delete\n" + "replace: carLicense\n" + "carLicense: license-05\n" + "carLicense: license-06\n") messages = self.waitForMessages(1, dn=dn) print("Received %d messages" % len(messages)) self.assertEquals(1, len(messages), "Did not receive the expected number of messages") attributes = messages[0]["dsdbChange"]["attributes"] self.assertEquals(1, len(attributes)) actions = attributes["carLicense"]["actions"] self.assertEquals(1, len(actions)) self.assertEquals("replace", actions[0]["action"]) values = actions[0]["values"] self.assertEquals(2, len(values)) self.assertEquals("license-05", values[0]["value"]) self.assertEquals("license-06", values[1]["value"])
class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase): def setUp(self): super(AuthLogPassChangeTests, self).setUp() self.remoteAddress = os.environ["CLIENT_IP"] self.server_ip = os.environ["SERVER_IP"] host = "ldap://%s" % os.environ["SERVER"] self.ldb = SamDB(url=host, session_info=system_session(), credentials=self.get_credentials(), lp=self.get_loadparm()) print "ldb %s" % type(self.ldb) # Gets back the basedn base_dn = self.ldb.domain_dn() print "base_dn %s" % base_dn # Gets back the configuration basedn configuration_dn = self.ldb.get_config_basedn().get_linearized() # Get the old "dSHeuristics" if it was set dsheuristics = self.ldb.get_dsheuristics() # 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({ "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn, "objectclass": "user", "sAMAccountName": USER_NAME, "userPassword": USER_PASS }) # discard any auth log messages for the password setup self.discardMessages() def tearDown(self): super(AuthLogPassChangeTests, self).tearDown() def test_admin_change_password(self): def isLastExpectedMessage(msg): return (msg["type"] == "Authentication" and msg["Authentication"]["status"] == "NT_STATUS_OK" and msg["Authentication"]["serviceDescription"] == "SAMR Password Change" and msg["Authentication"]["authDescription"] == "samr_ChangePasswordUser3") creds = self.insta_creds(template = self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server_ip) password = "******" net.change_password(newpassword=password.encode('utf-8'), username=USER_NAME, oldpassword=USER_PASS) messages = self.waitForMessages(isLastExpectedMessage) print "Received %d messages" % len(messages) self.assertEquals(8, len(messages), "Did not receive the expected number of messages") def test_admin_change_password_new_password_fails_restriction(self): def isLastExpectedMessage(msg): return (msg["type"] == "Authentication" and msg["Authentication"]["status"] == "NT_STATUS_PASSWORD_RESTRICTION" and msg["Authentication"]["serviceDescription"] == "SAMR Password Change" and msg["Authentication"]["authDescription"] == "samr_ChangePasswordUser3") creds = self.insta_creds(template=self.get_credentials()) lp = self.get_loadparm() net = Net(creds, lp, server=self.server_ip) password = "******" exception_thrown = False try: net.change_password(newpassword=password.encode('utf-8'), oldpassword=USER_PASS, username=USER_NAME) except Exception, msg: exception_thrown = True self.assertEquals(True, exception_thrown, "Expected exception not thrown") messages = self.waitForMessages(isLastExpectedMessage) self.assertEquals(8, len(messages), "Did not receive the expected number of messages")