From ac8b8e5539b79407292a5ef19bdd2aaf86b92884 Mon Sep 17 00:00:00 2001 From: Nadezhda Ivanova Date: Tue, 4 Nov 2014 20:08:58 +0200 Subject: [PATCH] s4-dsdb: Tests for security checks on undelete operation Implemented according to MS-ADTS 3.1.1.5.3.7.1. Unfortunately it appears LC is also necessary, and it is not granted by default to anyone but System and Administrator, so tests had to be done negatively Signed-off-by: Nadezhda Ivanova Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam Change-Id: Ic03b8fc4e222e7842ec8a9645a1bb33e7df9c438 --- python/samba/sd_utils.py | 8 +-- source4/dsdb/tests/python/acl.py | 132 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 135 insertions(+), 5 deletions(-) diff --git a/python/samba/sd_utils.py b/python/samba/sd_utils.py index ded9bfc1926..7592a2982a4 100644 --- a/python/samba/sd_utils.py +++ b/python/samba/sd_utils.py @@ -62,7 +62,7 @@ class SDUtils(object): def dacl_add_ace(self, object_dn, ace): """Add an ACE to an objects security descriptor """ - desc = self.read_sd_on_dn(object_dn) + desc = self.read_sd_on_dn(object_dn,["show_deleted:1"]) desc_sddl = desc.as_sddl(self.domain_sid) if ace in desc_sddl: return @@ -71,10 +71,10 @@ class SDUtils(object): desc_sddl[desc_sddl.index("("):]) else: desc_sddl = desc_sddl + ace - self.modify_sd_on_dn(object_dn, desc_sddl) + self.modify_sd_on_dn(object_dn, desc_sddl, ["show_deleted:1"]) - def get_sd_as_sddl(self, object_dn, controls=None): + def get_sd_as_sddl(self, object_dn, controls=[]): """Return object nTSecutiryDescriptor in SDDL format """ - desc = self.read_sd_on_dn(object_dn, controls=controls) + desc = self.read_sd_on_dn(object_dn, controls + ["show_deleted:1"]) return desc.as_sddl(self.domain_sid) diff --git a/source4/dsdb/tests/python/acl.py b/source4/dsdb/tests/python/acl.py index 4acc12349f4..d8e89629939 100755 --- a/source4/dsdb/tests/python/acl.py +++ b/source4/dsdb/tests/python/acl.py @@ -20,7 +20,7 @@ from ldb import ( from ldb import ERR_CONSTRAINT_VIOLATION from ldb import ERR_OPERATIONS_ERROR from ldb import Message, MessageElement, Dn -from ldb import FLAG_MOD_REPLACE, FLAG_MOD_ADD +from ldb import FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE from samba.dcerpc import security, drsuapi, misc from samba.auth import system_session @@ -1637,6 +1637,136 @@ class AclExtendedTests(AclTests): self.assertEqual(len(res),1) self.assertTrue("nTSecurityDescriptor" in res[0].keys()) +class AclUndeleteTests(AclTests): + + def setUp(self): + super(AclUndeleteTests, self).setUp() + self.regular_user = "undeleter1" + self.ou1 = "OU=undeleted_ou," + self.testuser1 = "to_be_undeleted1" + self.testuser2 = "to_be_undeleted2" + self.testuser3 = "to_be_undeleted3" + self.testuser4 = "to_be_undeleted4" + self.testuser5 = "to_be_undeleted5" + self.testuser6 = "to_be_undeleted6" + + self.new_dn_ou = "CN="+ self.testuser4 + "," + self.ou1 + self.base_dn + + # Create regular user + self.testuser1_dn = self.get_user_dn(self.testuser1) + self.testuser2_dn = self.get_user_dn(self.testuser2) + self.testuser3_dn = self.get_user_dn(self.testuser3) + self.testuser4_dn = self.get_user_dn(self.testuser4) + self.testuser5_dn = self.get_user_dn(self.testuser5) + self.deleted_dn1 = self.create_delete_user(self.testuser1) + self.deleted_dn2 = self.create_delete_user(self.testuser2) + self.deleted_dn3 = self.create_delete_user(self.testuser3) + self.deleted_dn4 = self.create_delete_user(self.testuser4) + self.deleted_dn5 = self.create_delete_user(self.testuser5) + + self.ldb_admin.create_ou(self.ou1 + self.base_dn) + + self.ldb_admin.newuser(self.regular_user, self.user_pass) + self.ldb_admin.add_remove_group_members("Domain Admins", [self.regular_user], + add_members_operation=True) + self.ldb_user = self.get_ldb_connection(self.regular_user, self.user_pass) + self.sid = self.sd_utils.get_object_sid(self.get_user_dn(self.regular_user)) + + def tearDown(self): + super(AclUndeleteTests, self).tearDown() + delete_force(self.ldb_admin, self.get_user_dn(self.regular_user)) + delete_force(self.ldb_admin, self.get_user_dn(self.testuser1)) + delete_force(self.ldb_admin, self.get_user_dn(self.testuser2)) + delete_force(self.ldb_admin, self.get_user_dn(self.testuser3)) + delete_force(self.ldb_admin, self.get_user_dn(self.testuser4)) + delete_force(self.ldb_admin, self.get_user_dn(self.testuser5)) + delete_force(self.ldb_admin, self.new_dn_ou) + delete_force(self.ldb_admin, self.ou1 + self.base_dn) + + def GUID_string(self, guid): + return ldb.schema_format_value("objectGUID", guid) + + def create_delete_user(self, new_user): + self.ldb_admin.newuser(new_user, self.user_pass) + + res = self.ldb_admin.search(expression="(objectClass=*)", + base=self.get_user_dn(new_user), + scope=SCOPE_BASE, + controls=["show_deleted:1"]) + guid = res[0]["objectGUID"][0] + self.ldb_admin.delete(self.get_user_dn(new_user)) + res = self.ldb_admin.search(base="" % self.GUID_string(guid), + scope=SCOPE_BASE, controls=["show_deleted:1"]) + self.assertEquals(len(res), 1) + return str(res[0].dn) + + def undelete_deleted(self, olddn, newdn): + msg = Message() + msg.dn = Dn(self.ldb_user, olddn) + msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") + res = self.ldb_user.modify(msg, ["show_recycled:1"]) + + def undelete_deleted_with_mod(self, olddn, newdn): + msg = Message() + msg.dn = Dn(ldb, olddn) + msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") + msg["url"] = MessageElement(["www.samba.org"], FLAG_MOD_REPLACE, "url") + res = self.ldb_user.modify(msg, ["show_deleted:1"]) + + def test_undelete(self): + # it appears the user has to have LC on the old parent to be able to move the object + # otherwise we get no such object. Since only System can modify the SD on deleted object + # we cannot grant this permission via LDAP, and this leaves us with "negative" tests at the moment + + # deny write property on rdn, should fail + mod = "(OD;;WP;bf967a0e-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn1, mod) + try: + self.undelete_deleted(self.deleted_dn1, self.testuser1_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) + + # seems that permissions on isDeleted and distinguishedName are irrelevant + mod = "(OD;;WP;bf96798f-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn2, mod) + mod = "(OD;;WP;bf9679e4-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn2, mod) + self.undelete_deleted(self.deleted_dn2, self.testuser2_dn) + + # attempt undelete with simultanious addition of url, WP to which is denied + mod = "(OD;;WP;9a9a0221-4a5b-11d1-a9c3-0000f80367c1;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn3, mod) + try: + self.undelete_deleted_with_mod(self.deleted_dn3, self.testuser3_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) + + # undelete in an ou, in which we have no right to create children + mod = "(D;;CC;;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.ou1 + self.base_dn, mod) + try: + self.undelete_deleted(self.deleted_dn4, self.new_dn_ou) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) + + # delete is not required + mod = "(D;;SD;;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn5, mod) + self.undelete_deleted(self.deleted_dn5, self.testuser5_dn) + + # deny Reanimate-Tombstone, should fail + mod = "(OD;;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.base_dn, mod) + try: + self.undelete_deleted(self.deleted_dn4, self.testuser4_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) class AclSPNTests(AclTests): -- 2.11.4.GIT