-#!/usr/bin/env python
-#
# Samba4 AD database checker
#
# Copyright (C) Andrew Tridgell 2011
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):
"""check a SAM database for errors"""
- def __init__(self, samdb, samdb_schema=None, verbose=False, fix=False, yes=False,
- quiet=False, in_transaction=False):
+ def __init__(self, samdb, samdb_schema=None, verbose=False, fix=False,
+ yes=False, quiet=False, in_transaction=False):
self.samdb = samdb
self.dict_oid_name = None
self.samdb_schema = (samdb_schema or samdb)
self.fix_time_metadata = False
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
self.in_transaction = in_transaction
+ self.infrastructure_dn = ldb.Dn(samdb, "CN=Infrastructure," + samdb.domain_dn())
+ self.naming_dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
+ 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]:
+ self.write_ncs = res[0]["msDS-hasMasterNCs"]
+ else:
+ # If the Forest Level is less than 2003 then there is no
+ # msDS-hasMasterNCs, so we fall back to hasMasterNCs
+ # no need to merge as all the NCs that are in hasMasterNCs must
+ # also be in msDS-hasMasterNCs (but not the opposite)
+ if "hasMasterNCs" in res[0]:
+ self.write_ncs = res[0]["hasMasterNCs"]
+ else:
+ self.write_ncs = None
+
def check_database(self, DN=None, scope=ldb.SCOPE_SUBTREE, controls=[], attrs=['*']):
'''perform a database check, returning the number of errors found'''
if error_count != 0 and not self.fix:
self.report("Please use --fix to fix these errors")
-
self.report('Checked %u objects (%u errors)' % (len(res), error_count))
-
return error_count
-
def report(self, msg):
'''print a message unless quiet is set'''
if not self.quiet:
print(msg)
-
- ################################################################
- # a local confirm function that obeys the --fix and --yes options
def confirm(self, msg, allow_all=False, forced=False):
'''confirm a change'''
if not self.fix:
return True
if c == 'NONE':
setattr(self, all_attr, 'NONE')
- return True
+ return False
return c
-
def do_modify(self, m, controls, msg, validate=True):
'''perform a modify with optional verbose output'''
if self.verbose:
self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY))
try:
+ controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
self.samdb.modify(m, controls=controls, validate=validate)
except Exception, err:
self.report("%s : %s" % (msg, err))
return False
return True
+ def do_rename(self, from_dn, to_rdn, to_base, controls, msg):
+ '''perform a modify with optional verbose output'''
+ if self.verbose:
+ self.report("""dn: %s
+changeType: modrdn
+newrdn: %s
+deleteOldRdn: 1
+newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
+ try:
+ to_dn = to_rdn + to_base
+ controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
+ self.samdb.rename(from_dn, to_dn, controls=controls)
+ except Exception, err:
+ self.report("%s : %s" % (msg, err))
+ return False
+ return True
- ################################################################
- # handle empty attributes
def err_empty_attribute(self, dn, attrname):
'''fix empty attributes'''
self.report("ERROR: Empty attribute %s in %s" % (attrname, dn))
"Failed to remove empty attribute %s" % attrname, validate=False):
self.report("Removed empty attribute %s" % attrname)
-
- ################################################################
- # handle normalisation mismatches
def err_normalise_mismatch(self, dn, attrname, values):
'''fix attribute normalisation errors'''
self.report("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn))
mod_list = []
for val in values:
- normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, [val])
+ normalised = self.samdb.dsdb_normalise_attributes(
+ self.samdb_schema, attrname, [val])
if len(normalised) != 1:
self.report("Unable to normalise value '%s'" % val)
mod_list.append((val, ''))
(val, nval) = mod_list[i]
m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
if nval != '':
- m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD, attrname)
+ m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD,
+ attrname)
+
+ if self.do_modify(m, ["relax:0", "show_recycled:1"],
+ "Failed to normalise attribute %s" % attrname,
+ validate=False):
+ self.report("Normalised attribute %s" % attrname)
+
+ def err_normalise_mismatch_replace(self, dn, attrname, values):
+ '''fix attribute normalisation errors'''
+ normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, values)
+ self.report("ERROR: Normalisation error for attribute '%s' in '%s'" % (attrname, dn))
+ self.report("Values/Order of values do/does not match: %s/%s!" % (values, list(normalised)))
+ if list(normalised) == values:
+ return
+ if not self.confirm_all("Fix normalisation for '%s' from '%s'?" % (attrname, dn), 'fix_all_normalisation'):
+ self.report("Not fixing attribute '%s'" % attrname)
+ return
+
+ m = ldb.Message()
+ m.dn = dn
+ m[attrname] = ldb.MessageElement(normalised, ldb.FLAG_MOD_REPLACE, attrname)
if self.do_modify(m, ["relax:0", "show_recycled:1"],
"Failed to normalise attribute %s" % attrname,
'''see if a dsdb_Dn is the special Deleted Objects DN'''
return dsdb_dn.prefix == "B:32:18E2EA80684F11D2B9AA00C04F79F805:"
-
- ################################################################
- # handle a DN pointing to a deleted object
def err_deleted_dn(self, dn, attrname, val, dsdb_dn, correct_dn):
+ """handle a DN pointing to a deleted object"""
self.report("ERROR: target DN is deleted for %s in object %s - %s" % (attrname, dn, val))
self.report("Target GUID points at deleted DN %s" % correct_dn)
if not self.confirm_all('Remove DN link?', 'remove_all_deleted_DN_links'):
"Failed to remove deleted DN attribute %s" % attrname):
self.report("Removed deleted DN on attribute %s" % attrname)
- ################################################################
- # handle a missing target DN (both GUID and DN string form are missing)
def err_missing_dn_GUID(self, dn, attrname, val, dsdb_dn):
+ """handle a missing target DN (both GUID and DN string form are missing)"""
# check if its a backlink
linkID = self.samdb_schema.get_linkId_from_lDAPDisplayName(attrname)
if (linkID & 1 == 0) and str(dsdb_dn).find('DEL\\0A') == -1:
return
self.err_deleted_dn(dn, attrname, val, dsdb_dn, dsdb_dn)
-
- ################################################################
- # handle a missing GUID extended DN component
def err_incorrect_dn_GUID(self, dn, attrname, val, dsdb_dn, errstr):
+ """handle a missing GUID extended DN component"""
self.report("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val))
controls=["extended_dn:1:1", "show_recycled:1"]
try:
"Failed to fix %s on attribute %s" % (errstr, attrname)):
self.report("Fixed %s on attribute %s" % (errstr, attrname))
-
- ################################################################
- # handle a DN string being incorrect
def err_dn_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn, errstr):
+ """handle a DN string being incorrect"""
self.report("ERROR: incorrect DN string component for %s in object %s - %s" % (attrname, dn, val))
dsdb_dn.dn = correct_dn
"Failed to fix incorrect DN string on attribute %s" % attrname):
self.report("Fixed incorrect DN string on attribute %s" % (attrname))
- ################################################################
- # handle an unknown attribute error
def err_unknown_attribute(self, obj, attrname):
'''handle an unknown attribute error'''
self.report("ERROR: unknown attribute '%s' in %s" % (attrname, obj.dn))
"Failed to remove unknown attribute %s" % attrname):
self.report("Removed unknown attribute %s" % (attrname))
-
- ################################################################
- # handle a missing backlink
def err_missing_backlink(self, obj, attrname, val, backlink_name, target_dn):
'''handle a missing backlink value'''
self.report("ERROR: missing backlink attribute '%s' in %s for link %s in %s" % (backlink_name, target_dn, attrname, obj.dn))
"Failed to fix missing backlink %s" % backlink_name):
self.report("Fixed missing backlink %s" % (backlink_name))
+ def err_incorrect_rmd_flags(self, obj, attrname, revealed_dn):
+ '''handle a incorrect RMD_FLAGS value'''
+ rmd_flags = int(revealed_dn.dn.get_extended_component("RMD_FLAGS"))
+ self.report("ERROR: incorrect RMD_FLAGS value %u for attribute '%s' in %s for link %s" % (rmd_flags, attrname, obj.dn, revealed_dn.dn.extended_str()))
+ if not self.confirm_all('Fix incorrect RMD_FLAGS %u' % rmd_flags, 'fix_rmd_flags'):
+ self.report("Not fixing incorrect RMD_FLAGS %u" % rmd_flags)
+ return
+ m = ldb.Message()
+ m.dn = obj.dn
+ m['old_value'] = ldb.MessageElement(str(revealed_dn), ldb.FLAG_MOD_DELETE, attrname)
+ if self.do_modify(m, ["show_recycled:1", "reveal_internals:0", "show_deleted:0"],
+ "Failed to fix incorrect RMD_FLAGS %u" % rmd_flags):
+ self.report("Fixed incorrect RMD_FLAGS %u" % (rmd_flags))
- ################################################################
- # handle a orphaned backlink
def err_orphaned_backlink(self, obj, attrname, val, link_name, target_dn):
'''handle a orphaned backlink value'''
self.report("ERROR: orphaned backlink attribute '%s' in %s for link %s in %s" % (attrname, obj.dn, link_name, target_dn))
"Failed to fix orphaned backlink %s" % link_name):
self.report("Fixed orphaned backlink %s" % (link_name))
+ def err_no_fsmoRoleOwner(self, obj):
+ '''handle a missing fSMORoleOwner'''
+ self.report("ERROR: fSMORoleOwner not found for role %s" % (obj.dn))
+ res = self.samdb.search("",
+ scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
+ assert len(res) == 1
+ serviceName = res[0]["dsServiceName"][0]
+ if not self.confirm_all('Sieze role %s onto current DC by adding fSMORoleOwner=%s' % (obj.dn, serviceName), 'seize_fsmo_role'):
+ self.report("Not Siezing role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName))
+ return
+ m = ldb.Message()
+ m.dn = obj.dn
+ m['value'] = ldb.MessageElement(serviceName, ldb.FLAG_MOD_ADD, 'fSMORoleOwner')
+ if self.do_modify(m, [],
+ "Failed to sieze role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName)):
+ self.report("Siezed role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName))
+
+ def err_missing_parent(self, obj):
+ '''handle a missing parent'''
+ self.report("ERROR: parent object not found for %s" % (obj.dn))
+ if not self.confirm_all('Move object %s into LostAndFound?' % (obj.dn), 'move_to_lost_and_found'):
+ self.report('Not moving object %s into LostAndFound' % (obj.dn))
+ return
+
+ keep_transaction = True
+ self.samdb.transaction_start()
+ try:
+ nc_root = self.samdb.get_nc_root(obj.dn);
+ lost_and_found = self.samdb.get_wellknown_dn(nc_root, dsdb.DS_GUID_LOSTANDFOUND_CONTAINER)
+ new_dn = ldb.Dn(self.samdb, str(obj.dn))
+ new_dn.remove_base_components(len(new_dn) - 1)
+ if self.do_rename(obj.dn, new_dn, lost_and_found, ["show_deleted:0", "relax:0"],
+ "Failed to rename object %s into lostAndFound at %s" % (obj.dn, new_dn + lost_and_found)):
+ self.report("Renamed object %s into lostAndFound at %s" % (obj.dn, new_dn + lost_and_found))
+
+ m = ldb.Message()
+ m.dn = obj.dn
+ m['lastKnownParent'] = ldb.MessageElement(str(obj.dn.parent()), ldb.FLAG_MOD_REPLACE, 'lastKnownParent')
+
+ if self.do_modify(m, [],
+ "Failed to set lastKnownParent on lostAndFound object at %s" % (new_dn + lost_and_found)):
+ self.report("Set lastKnownParent on lostAndFound object at %s" % (new_dn + lost_and_found))
+ keep_transaction = True
+ except:
+ self.samdb.transaction_cancel()
+ raise
+
+ if keep_transaction:
+ self.samdb.transaction_commit()
+ else:
+ self.samdb.transaction_cancel()
+
+
+ def err_wrong_instancetype(self, obj, calculated_instancetype):
+ '''handle a wrong instanceType'''
+ self.report("ERROR: wrong instanceType %s on %s, should be %d" % (obj["instanceType"], obj.dn, calculated_instancetype))
+ if not self.confirm_all('Change instanceType from %s to %d on %s?' % (obj["instanceType"], calculated_instancetype, obj.dn), 'fix_instancetype'):
+ self.report('Not changing instanceType from %s to %d on %s' % (obj["instanceType"], calculated_instancetype, obj.dn))
+ return
+
+ m = ldb.Message()
+ m.dn = obj.dn
+ m['value'] = ldb.MessageElement(str(calculated_instancetype), ldb.FLAG_MOD_REPLACE, 'instanceType')
+ if self.do_modify(m, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA],
+ "Failed to correct missing instanceType on %s by setting instanceType=%d" % (obj.dn, calculated_instancetype)):
+ self.report("Corrected instancetype on %s by setting instanceType=%d" % (obj.dn, calculated_instancetype))
+
+ def find_revealed_link(self, dn, attrname, guid):
+ '''return a revealed link in an object'''
+ res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[attrname],
+ controls=["show_deleted:0", "extended_dn:0", "reveal_internals:0"])
+ syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname)
+ for val in res[0][attrname]:
+ dsdb_dn = dsdb_Dn(self.samdb, val, syntax_oid)
+ guid2 = dsdb_dn.dn.get_extended_component("GUID")
+ if guid == guid2:
+ return dsdb_dn
+ return None
- ################################################################
- # specialised checking for a dn attribute
def check_dn(self, obj, attrname, syntax_oid):
'''check a DN attribute for correctness'''
error_count = 0
guid = dsdb_dn.dn.get_extended_component("GUID")
if guid is None:
error_count += 1
- self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn, "missing GUID")
+ self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn,
+ "missing GUID")
continue
guidstr = str(misc.GUID(guid))
- attrs=['isDeleted']
+ attrs = ['isDeleted']
linkID = self.samdb_schema.get_linkId_from_lDAPDisplayName(attrname)
reverse_link_name = self.samdb_schema.get_backlink_from_lDAPDisplayName(attrname)
if reverse_link_name is not None:
res[0].dn, "incorrect string version of DN")
continue
+ if is_deleted and not target_is_deleted and reverse_link_name is not None:
+ revealed_dn = self.find_revealed_link(obj.dn, attrname, guid)
+ rmd_flags = revealed_dn.dn.get_extended_component("RMD_FLAGS")
+ if rmd_flags is not None and (int(rmd_flags) & 1) == 0:
+ # the RMD_FLAGS for this link should be 1, as the target is deleted
+ self.err_incorrect_rmd_flags(obj, attrname, revealed_dn)
+ continue
+
# check the reverse_link is correct if there should be one
if reverse_link_name is not None:
match_count = 0
"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
+ if dn == self.infrastructure_dn:
+ return True
+ if dn == self.naming_dn:
+ return True
+ if dn == self.schema_dn:
+ return True
+ if dn == self.rid_dn:
+ return True
+
+ return False
+
+ def calculate_instancetype(self, dn):
+ instancetype = 0
+ nc_root = self.samdb.get_nc_root(dn)
+ if dn == nc_root:
+ instancetype |= dsdb.INSTANCE_TYPE_IS_NC_HEAD
+ try:
+ self.samdb.search(base=dn.parent(), scope=ldb.SCOPE_BASE, attrs=[], controls=["show_recycled:1"])
+ except ldb.LdbError, (enum, estr):
+ if enum != ldb.ERR_NO_SUCH_OBJECT:
+ raise
+ else:
+ instancetype |= dsdb.INSTANCE_TYPE_NC_ABOVE
+
+ if self.write_ncs is not None and str(nc_root) in self.write_ncs:
+ instancetype |= dsdb.INSTANCE_TYPE_WRITE
+
+ return instancetype
- ################################################################
- # check one object - calls to individual error handlers above
def check_object(self, dn, attrs=['*']):
'''check one object'''
if self.verbose:
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:
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]):
+ self.err_normalise_mismatch_replace(dn, attrname, list(obj[attrname]))
+ error_count += 1
+ continue
# check for empty attributes
for val in obj[attrname]:
error_count += 1
break
+ if str(attrname).lower() == "instancetype":
+ calculated_instancetype = self.calculate_instancetype(dn)
+ if len(obj["instanceType"]) != 1 or obj["instanceType"][0] != str(calculated_instancetype):
+ self.err_wrong_instancetype(obj, calculated_instancetype)
+
show_dn = True
if got_repl_property_meta_data:
rdn = (str(dn).split(","))[0]
continue
self.fix_metadata(dn, att)
+ if self.is_fsmo_role(dn):
+ if "fSMORoleOwner" not in obj:
+ self.err_no_fsmoRoleOwner(obj)
+ error_count += 1
+
+ try:
+ if dn != self.samdb.get_root_basedn():
+ res = self.samdb.search(base=dn.parent(), scope=ldb.SCOPE_BASE,
+ controls=["show_recycled:1", "show_deleted:1"])
+ except ldb.LdbError, (enum, estr):
+ if enum == ldb.ERR_NO_SUCH_OBJECT:
+ self.err_missing_parent(obj)
+ error_count += 1
+ else:
+ raise
+
return error_count
################################################################