dbckecker: fix nTSecurityDescriptor values from before 4.0.0rc6 (bug #9481)
authorStefan Metzmacher <metze@samba.org>
Sat, 19 Jan 2013 08:41:00 +0000 (09:41 +0100)
committerStefan Metzmacher <metze@samba.org>
Thu, 24 Jan 2013 21:59:43 +0000 (22:59 +0100)
They inherited effective ACE for the wrong object classes.

For SACL ACEs the problem was also present in 4.0.0.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
source4/scripting/python/samba/dbchecker.py

index bc68457a05f7e1dcd576d60439326f8e90c1fc2e..8d49e1807ba1465d43da3182c9a7639d4cb58380 100644 (file)
@@ -21,9 +21,10 @@ import ldb
 from samba import dsdb
 from samba import common
 from samba.dcerpc import misc
-from samba.ndr import ndr_unpack
+from samba.ndr import ndr_unpack, ndr_pack
 from samba.dcerpc import drsblobs
 from samba.common import dsdb_Dn
+from samba.dcerpc import security
 
 
 class dbcheck(object):
@@ -49,6 +50,7 @@ class dbcheck(object):
         self.fix_all_missing_backlinks = False
         self.fix_all_orphaned_backlinks = False
         self.fix_rmd_flags = False
+        self.fix_ntsecuritydescriptor = False
         self.seize_fsmo_role = False
         self.move_to_lost_and_found = False
         self.fix_instancetype = False
@@ -548,6 +550,118 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
                           "Failed to fix metadata for attribute %s" % attr):
             self.report("Fixed metadata for attribute %s" % attr)
 
+    def ace_get_effective_inherited_type(self, ace):
+        if ace.flags & security.SEC_ACE_FLAG_INHERIT_ONLY:
+            return None
+
+        check = False
+        if ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+            check = True
+        elif ace.type == security.SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+            check = True
+        elif ace.type == security.SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
+            check = True
+        elif ace.type == security.SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
+            check = True
+
+        if not check:
+            return None
+
+        if not ace.object.flags & security.SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT:
+            return None
+
+        return ace.object.inherited_type
+
+    def process_sd(self, obj):
+        sd_attr = "nTSecurityDescriptor"
+        sd_val = obj[sd_attr]
+
+        sd = ndr_unpack(security.descriptor, str(sd_val))
+
+        sd_clean = security.descriptor()
+        sd_clean.owner_sid = sd.owner_sid
+        sd_clean.group_sid = sd.group_sid
+        sd_clean.type = sd.type
+        sd_clean.revision = sd.revision
+
+        broken = False
+        last_inherited_type = None
+
+        aces = []
+        if sd.sacl is not None:
+            aces = sd.sacl.aces
+        for i in range(0, len(aces)):
+            ace = aces[i]
+
+            if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE:
+                sd_clean.sacl_add(ace)
+                continue
+
+            t = self.ace_get_effective_inherited_type(ace)
+            if t is None:
+                continue
+
+            if last_inherited_type is not None:
+                if t != last_inherited_type:
+                    # if it inherited from more than
+                    # one type it's very likely to be broken
+                    #
+                    # If not the recalculation will calculate
+                    # the same result.
+                    broken = True
+                continue
+
+            last_inherited_type = t
+
+        aces = []
+        if sd.dacl is not None:
+            aces = sd.dacl.aces
+        for i in range(0, len(aces)):
+            ace = aces[i]
+
+            if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE:
+                sd_clean.dacl_add(ace)
+                continue
+
+            t = self.ace_get_effective_inherited_type(ace)
+            if t is None:
+                continue
+
+            if last_inherited_type is not None:
+                if t != last_inherited_type:
+                    # if it inherited from more than
+                    # one type it's very likely to be broken
+                    #
+                    # If not the recalculation will calculate
+                    # the same result.
+                    broken = True
+                continue
+
+            last_inherited_type = t
+
+        if broken:
+            return (sd_clean, sd)
+
+        return (sd, None)
+
+    def err_wrong_sd(self, dn, sd, sd_broken):
+        '''re-write replPropertyMetaData elements for a single attribute for a
+        object. This is used to fix missing replPropertyMetaData elements'''
+        sd_attr = "nTSecurityDescriptor"
+        sd_val = ndr_pack(sd)
+        sd_flags = security.SECINFO_DACL | security.SECINFO_SACL
+
+        if not self.confirm_all('Fix %s on %s?' % (sd_attr, dn), 'fix_ntsecuritydescriptor'):
+            self.report('Not fixing %s on %s\n' % (sd_attr, dn))
+            return
+
+        nmsg = ldb.Message()
+        nmsg.dn = dn
+        nmsg[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
+        if self.do_modify(nmsg, ["sd_flags:1:%d" % sd_flags],
+                          "Failed to fix metadata for attribute %s" % sd_attr):
+            self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
+
     def is_fsmo_role(self, dn):
         if dn == self.samdb.domain_dn:
             return True
@@ -588,8 +702,19 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
             attrs.append("replPropertyMetaData")
 
         try:
+            sd_flags = 0
+            sd_flags |= security.SECINFO_OWNER
+            sd_flags |= security.SECINFO_GROUP
+            sd_flags |= security.SECINFO_DACL
+            sd_flags |= security.SECINFO_SACL
+
             res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE,
-                                    controls=["extended_dn:1:1", "show_recycled:1", "show_deleted:1"],
+                                    controls=[
+                                        "extended_dn:1:1",
+                                        "show_recycled:1",
+                                        "show_deleted:1",
+                                        "sd_flags:1:%d" % sd_flags,
+                                    ],
                                     attrs=attrs)
         except ldb.LdbError, (enum, estr):
             if enum == ldb.ERR_NO_SUCH_OBJECT:
@@ -616,6 +741,13 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
                 got_repl_property_meta_data = True
                 continue
 
+            if str(attrname).lower() == 'ntsecuritydescriptor':
+                (sd, sd_broken) = self.process_sd(obj)
+                if sd_broken is not None:
+                    self.err_wrong_sd(dn, sd, sd_broken)
+                    error_count += 1
+                continue
+
             if str(attrname).lower() == 'objectclass':
                 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, list(obj[attrname]))
                 if list(normalised) != list(obj[attrname]):