From ffe2dc9b5d78ccb3d2e7c9de380ea48edc9e2217 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 19 Jan 2013 09:41:00 +0100 Subject: [PATCH] dbckecker: fix nTSecurityDescriptor values from before 4.0.0rc6 (bug #9481) 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 Reviewed-by: Andrew Bartlett --- source4/scripting/python/samba/dbchecker.py | 183 +++++++++++++++++++- 1 file changed, 181 insertions(+), 2 deletions(-) diff --git a/source4/scripting/python/samba/dbchecker.py b/source4/scripting/python/samba/dbchecker.py index bc68457a05f7..06fd82752f75 100644 --- a/source4/scripting/python/samba/dbchecker.py +++ b/source4/scripting/python/samba/dbchecker.py @@ -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 @@ -58,6 +60,7 @@ class dbcheck(object): self.schema_dn = samdb.get_schema_basedn() self.rid_dn = ldb.Dn(samdb, "CN=RID Manager$,CN=System," + samdb.domain_dn()) self.ntds_dsa = samdb.get_dsServiceName() + self.class_schemaIDGUID = {} res = self.samdb.search(base=self.ntds_dsa, scope=ldb.SCOPE_BASE, attrs=['msDS-hasMasterNCs', 'hasMasterNCs']) if "msDS-hasMasterNCs" in res[0]: @@ -548,6 +551,164 @@ 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 str(ace.object.inherited_type) + + def lookup_class_schemaIDGUID(self, cls): + if cls in self.class_schemaIDGUID: + return self.class_schemaIDGUID[cls] + + flt = "(&(ldapDisplayName=%s)(objectClass=classSchema))" % cls + res = self.samdb.search(base=self.schema_dn, + expression=flt, + attrs=["schemaIDGUID"]) + t = str(ndr_unpack(misc.GUID, res[0]["schemaIDGUID"][0])) + + self.class_schemaIDGUID[cls] = t + return t + + def process_sd(self, dn, obj): + sd_attr = "nTSecurityDescriptor" + sd_val = obj[sd_attr] + + sd = ndr_unpack(security.descriptor, str(sd_val)) + + is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper() == 'TRUE' + if is_deleted: + # we don't fix deleted objects + return (sd, None) + + 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) + + if last_inherited_type is None: + # ok + return (sd, None) + + cls = None + try: + cls = obj["objectClass"][-1] + except KeyError, e: + pass + + if cls is None: + res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, + attrs=["isDeleted", "objectClass"], + controls=["show_recycled:1"]) + o = res[0] + is_deleted = 'isDeleted' in o and o['isDeleted'][0].upper() == 'TRUE' + if is_deleted: + # we don't fix deleted objects + return (sd, None) + cls = o["objectClass"][-1] + + t = self.lookup_class_schemaIDGUID(cls) + + if t != last_inherited_type: + # broken + return (sd_clean, sd) + + # ok + 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 +749,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 +788,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(dn, 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]): -- 2.34.1