1 # Samba4 AD database checker
3 # Copyright (C) Andrew Tridgell 2011
4 # Copyright (C) Matthieu Patou <mat@matws.net> 2011
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 from base64 import b64decode
24 from samba import dsdb
25 from samba import common
26 from samba.dcerpc import misc
27 from samba.dcerpc import drsuapi
28 from samba.ndr import ndr_unpack, ndr_pack
29 from samba.dcerpc import drsblobs
30 from samba.common import dsdb_Dn
31 from samba.dcerpc import security
32 from samba.descriptor import get_wellknown_sds, get_diff_sds
33 from samba.auth import system_session, admin_session
34 from samba.netcmd import CommandError
35 from samba.netcmd.fsmo import get_fsmo_roleowner
38 class dbcheck(object):
39 """check a SAM database for errors"""
41 def __init__(self, samdb, samdb_schema=None, verbose=False, fix=False,
42 yes=False, quiet=False, in_transaction=False,
43 reset_well_known_acls=False):
45 self.dict_oid_name = None
46 self.samdb_schema = (samdb_schema or samdb)
47 self.verbose = verbose
51 self.remove_all_unknown_attributes = False
52 self.remove_all_empty_attributes = False
53 self.fix_all_normalisation = False
54 self.fix_all_duplicates = False
55 self.fix_all_DN_GUIDs = False
56 self.fix_all_binary_dn = False
57 self.remove_implausible_deleted_DN_links = False
58 self.remove_plausible_deleted_DN_links = False
59 self.fix_all_string_dn_component_mismatch = False
60 self.fix_all_GUID_dn_component_mismatch = False
61 self.fix_all_SID_dn_component_mismatch = False
62 self.fix_all_old_dn_string_component_mismatch = False
63 self.fix_all_metadata = False
64 self.fix_time_metadata = False
65 self.fix_undead_linked_attributes = False
66 self.fix_all_missing_backlinks = False
67 self.fix_all_orphaned_backlinks = False
68 self.fix_all_missing_forward_links = False
69 self.duplicate_link_cache = dict()
70 self.recover_all_forward_links = False
71 self.fix_rmd_flags = False
72 self.fix_ntsecuritydescriptor = False
73 self.fix_ntsecuritydescriptor_owner_group = False
74 self.seize_fsmo_role = False
75 self.move_to_lost_and_found = False
76 self.fix_instancetype = False
77 self.fix_replmetadata_zero_invocationid = False
78 self.fix_replmetadata_duplicate_attid = False
79 self.fix_replmetadata_wrong_attid = False
80 self.fix_replmetadata_unsorted_attid = False
81 self.fix_deleted_deleted_objects = False
82 self.fix_incorrect_deleted_objects = False
84 self.fix_base64_userparameters = False
85 self.fix_utf8_userparameters = False
86 self.fix_doubled_userparameters = False
87 self.fix_sid_rid_set_conflict = False
88 self.reset_well_known_acls = reset_well_known_acls
89 self.reset_all_well_known_acls = False
90 self.in_transaction = in_transaction
91 self.infrastructure_dn = ldb.Dn(samdb, "CN=Infrastructure," + samdb.domain_dn())
92 self.naming_dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
93 self.schema_dn = samdb.get_schema_basedn()
94 self.rid_dn = ldb.Dn(samdb, "CN=RID Manager$,CN=System," + samdb.domain_dn())
95 self.ntds_dsa = ldb.Dn(samdb, samdb.get_dsServiceName())
96 self.class_schemaIDGUID = {}
97 self.wellknown_sds = get_wellknown_sds(self.samdb)
98 self.fix_all_missing_objectclass = False
99 self.fix_missing_deleted_objects = False
100 self.fix_replica_locations = False
101 self.fix_missing_rid_set_master = False
104 self.link_id_cache = {}
107 res = samdb.search(base="CN=DnsAdmins,CN=Users,%s" % samdb.domain_dn(), scope=ldb.SCOPE_BASE,
109 dnsadmins_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0])
110 self.name_map['DnsAdmins'] = str(dnsadmins_sid)
111 except ldb.LdbError, (enum, estr):
112 if enum != ldb.ERR_NO_SUCH_OBJECT:
116 self.system_session_info = system_session()
117 self.admin_session_info = admin_session(None, samdb.get_domain_sid())
119 res = self.samdb.search(base=self.ntds_dsa, scope=ldb.SCOPE_BASE, attrs=['msDS-hasMasterNCs', 'hasMasterNCs'])
120 if "msDS-hasMasterNCs" in res[0]:
121 self.write_ncs = res[0]["msDS-hasMasterNCs"]
123 # If the Forest Level is less than 2003 then there is no
124 # msDS-hasMasterNCs, so we fall back to hasMasterNCs
125 # no need to merge as all the NCs that are in hasMasterNCs must
126 # also be in msDS-hasMasterNCs (but not the opposite)
127 if "hasMasterNCs" in res[0]:
128 self.write_ncs = res[0]["hasMasterNCs"]
130 self.write_ncs = None
132 res = self.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=['namingContexts'])
133 self.deleted_objects_containers = []
134 self.ncs_lacking_deleted_containers = []
135 self.dns_partitions = []
137 self.ncs = res[0]["namingContexts"]
145 dn = self.samdb.get_wellknown_dn(ldb.Dn(self.samdb, nc),
146 dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER)
147 self.deleted_objects_containers.append(dn)
149 self.ncs_lacking_deleted_containers.append(ldb.Dn(self.samdb, nc))
151 domaindns_zone = 'DC=DomainDnsZones,%s' % self.samdb.get_default_basedn()
152 forestdns_zone = 'DC=ForestDnsZones,%s' % self.samdb.get_root_basedn()
153 domain = self.samdb.search(scope=ldb.SCOPE_ONELEVEL,
154 attrs=["msDS-NC-Replica-Locations", "msDS-NC-RO-Replica-Locations"],
155 base=self.samdb.get_partitions_dn(),
156 expression="(&(objectClass=crossRef)(ncName=%s))" % domaindns_zone)
158 self.dns_partitions.append((ldb.Dn(self.samdb, forestdns_zone), domain[0]))
160 forest = self.samdb.search(scope=ldb.SCOPE_ONELEVEL,
161 attrs=["msDS-NC-Replica-Locations", "msDS-NC-RO-Replica-Locations"],
162 base=self.samdb.get_partitions_dn(),
163 expression="(&(objectClass=crossRef)(ncName=%s))" % forestdns_zone)
165 self.dns_partitions.append((ldb.Dn(self.samdb, domaindns_zone), forest[0]))
167 fsmo_dn = ldb.Dn(self.samdb, "CN=RID Manager$,CN=System," + self.samdb.domain_dn())
168 rid_master = get_fsmo_roleowner(self.samdb, fsmo_dn, "rid")
169 if ldb.Dn(self.samdb, self.samdb.get_dsServiceName()) == rid_master:
170 self.is_rid_master = True
172 self.is_rid_master = False
174 # To get your rid set
176 res = self.samdb.search(base=ldb.Dn(self.samdb, self.samdb.get_serverName()),
177 scope=ldb.SCOPE_BASE, attrs=["serverReference"])
178 # 2. Get server reference
179 self.server_ref_dn = ldb.Dn(self.samdb, res[0]['serverReference'][0])
182 res = self.samdb.search(base=self.server_ref_dn,
183 scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences'])
184 if "rIDSetReferences" in res[0]:
185 self.rid_set_dn = ldb.Dn(self.samdb, res[0]['rIDSetReferences'][0])
187 self.rid_set_dn = None
189 self.compatibleFeatures = []
190 self.requiredFeatures = []
193 res = self.samdb.search(scope=ldb.SCOPE_BASE,
195 attrs=["compatibleFeatures",
197 if "compatibleFeatures" in res[0]:
198 self.compatibleFeatures = res[0]["compatibleFeatures"]
199 if "requiredFeatures" in res[0]:
200 self.requiredFeatures = res[0]["requiredFeatures"]
201 except ldb.LdbError as (enum, estr):
202 if enum != ldb.ERR_NO_SUCH_OBJECT:
206 def check_database(self, DN=None, scope=ldb.SCOPE_SUBTREE, controls=[], attrs=['*']):
207 '''perform a database check, returning the number of errors found'''
208 res = self.samdb.search(base=DN, scope=scope, attrs=['dn'], controls=controls)
209 self.report('Checking %u objects' % len(res))
212 error_count += self.check_deleted_objects_containers()
214 self.attribute_or_class_ids = set()
217 self.dn_set.add(str(object.dn))
218 error_count += self.check_object(object.dn, attrs=attrs)
221 error_count += self.check_rootdse()
223 if error_count != 0 and not self.fix:
224 self.report("Please use --fix to fix these errors")
226 self.report('Checked %u objects (%u errors)' % (len(res), error_count))
229 def check_deleted_objects_containers(self):
230 """This function only fixes conflicts on the Deleted Objects
231 containers, not the attributes"""
233 for nc in self.ncs_lacking_deleted_containers:
234 if nc == self.schema_dn:
237 self.report("ERROR: NC %s lacks a reference to a Deleted Objects container" % nc)
238 if not self.confirm_all('Fix missing Deleted Objects container for %s?' % (nc), 'fix_missing_deleted_objects'):
241 dn = ldb.Dn(self.samdb, "CN=Deleted Objects")
246 # If something already exists here, add a conflict
247 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[],
248 controls=["show_deleted:1", "extended_dn:1:1",
249 "show_recycled:1", "reveal_internals:0"])
251 guid = res[0].dn.get_extended_component("GUID")
252 conflict_dn = ldb.Dn(self.samdb,
253 "CN=Deleted Objects\\0ACNF:%s" % str(misc.GUID(guid)))
254 conflict_dn.add_base(nc)
256 except ldb.LdbError, (enum, estr):
257 if enum == ldb.ERR_NO_SUCH_OBJECT:
260 self.report("Couldn't check for conflicting Deleted Objects container: %s" % estr)
263 if conflict_dn is not None:
265 self.samdb.rename(dn, conflict_dn, ["show_deleted:1", "relax:0", "show_recycled:1"])
266 except ldb.LdbError, (enum, estr):
267 self.report("Couldn't move old Deleted Objects placeholder: %s to %s: %s" % (dn, conflict_dn, estr))
270 # Refresh wellKnownObjects links
271 res = self.samdb.search(base=nc, scope=ldb.SCOPE_BASE,
272 attrs=['wellKnownObjects'],
273 controls=["show_deleted:1", "extended_dn:0",
274 "show_recycled:1", "reveal_internals:0"])
276 self.report("wellKnownObjects was not found for NC %s" % nc)
279 # Prevent duplicate deleted objects containers just in case
280 wko = res[0]["wellKnownObjects"]
282 proposed_objectguid = None
284 dsdb_dn = dsdb_Dn(self.samdb, o, dsdb.DSDB_SYNTAX_BINARY_DN)
285 if self.is_deleted_objects_dn(dsdb_dn):
286 self.report("wellKnownObjects had duplicate Deleted Objects value %s" % o)
287 # We really want to put this back in the same spot
288 # as the original one, so that on replication we
289 # merge, rather than conflict.
290 proposed_objectguid = dsdb_dn.dn.get_extended_component("GUID")
293 if proposed_objectguid is not None:
294 guid_suffix = "\nobjectGUID: %s" % str(misc.GUID(proposed_objectguid))
296 wko_prefix = "B:32:%s" % dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER
297 listwko.append('%s:%s' % (wko_prefix, dn))
300 # Insert a brand new Deleted Objects container
301 self.samdb.add_ldif("""dn: %s
303 objectClass: container
304 description: Container for deleted objects
306 isCriticalSystemObject: TRUE
307 showInAdvancedViewOnly: TRUE
308 systemFlags: -1946157056%s""" % (dn, guid_suffix),
309 controls=["relax:0", "provision:0"])
311 delta = ldb.Message()
312 delta.dn = ldb.Dn(self.samdb, str(res[0]["dn"]))
313 delta["wellKnownObjects"] = ldb.MessageElement(listwko,
314 ldb.FLAG_MOD_REPLACE,
317 # Insert the link to the brand new container
318 if self.do_modify(delta, ["relax:0"],
319 "NC %s lacks Deleted Objects WKGUID" % nc,
321 self.report("Added %s well known guid link" % dn)
323 self.deleted_objects_containers.append(dn)
327 def report(self, msg):
328 '''print a message unless quiet is set'''
332 def confirm(self, msg, allow_all=False, forced=False):
333 '''confirm a change'''
340 return common.confirm(msg, forced=forced, allow_all=allow_all)
342 ################################################################
343 # a local confirm function with support for 'all'
344 def confirm_all(self, msg, all_attr):
345 '''confirm a change with support for "all" '''
348 if getattr(self, all_attr) == 'NONE':
350 if getattr(self, all_attr) == 'ALL':
356 c = common.confirm(msg, forced=forced, allow_all=True)
358 setattr(self, all_attr, 'ALL')
361 setattr(self, all_attr, 'NONE')
365 def do_delete(self, dn, controls, msg):
366 '''delete dn with optional verbose output'''
368 self.report("delete DN %s" % dn)
370 controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
371 self.samdb.delete(dn, controls=controls)
372 except Exception, err:
373 if self.in_transaction:
374 raise CommandError("%s : %s" % (msg, err))
375 self.report("%s : %s" % (msg, err))
379 def do_modify(self, m, controls, msg, validate=True):
380 '''perform a modify with optional verbose output'''
382 self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY))
384 controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
385 self.samdb.modify(m, controls=controls, validate=validate)
386 except Exception, err:
387 if self.in_transaction:
388 raise CommandError("%s : %s" % (msg, err))
389 self.report("%s : %s" % (msg, err))
393 def do_rename(self, from_dn, to_rdn, to_base, controls, msg):
394 '''perform a modify with optional verbose output'''
396 self.report("""dn: %s
400 newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
402 to_dn = to_rdn + to_base
403 controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
404 self.samdb.rename(from_dn, to_dn, controls=controls)
405 except Exception, err:
406 if self.in_transaction:
407 raise CommandError("%s : %s" % (msg, err))
408 self.report("%s : %s" % (msg, err))
412 def get_attr_linkID_and_reverse_name(self, attrname):
413 if attrname in self.link_id_cache:
414 return self.link_id_cache[attrname]
415 linkID = self.samdb_schema.get_linkId_from_lDAPDisplayName(attrname)
417 revname = self.samdb_schema.get_backlink_from_lDAPDisplayName(attrname)
420 self.link_id_cache[attrname] = (linkID, revname)
421 return linkID, revname
423 def err_empty_attribute(self, dn, attrname):
424 '''fix empty attributes'''
425 self.report("ERROR: Empty attribute %s in %s" % (attrname, dn))
426 if not self.confirm_all('Remove empty attribute %s from %s?' % (attrname, dn), 'remove_all_empty_attributes'):
427 self.report("Not fixing empty attribute %s" % attrname)
432 m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname)
433 if self.do_modify(m, ["relax:0", "show_recycled:1"],
434 "Failed to remove empty attribute %s" % attrname, validate=False):
435 self.report("Removed empty attribute %s" % attrname)
437 def err_normalise_mismatch(self, dn, attrname, values):
438 '''fix attribute normalisation errors'''
439 self.report("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn))
442 normalised = self.samdb.dsdb_normalise_attributes(
443 self.samdb_schema, attrname, [val])
444 if len(normalised) != 1:
445 self.report("Unable to normalise value '%s'" % val)
446 mod_list.append((val, ''))
447 elif (normalised[0] != val):
448 self.report("value '%s' should be '%s'" % (val, normalised[0]))
449 mod_list.append((val, normalised[0]))
450 if not self.confirm_all('Fix normalisation for %s from %s?' % (attrname, dn), 'fix_all_normalisation'):
451 self.report("Not fixing attribute %s" % attrname)
456 for i in range(0, len(mod_list)):
457 (val, nval) = mod_list[i]
458 m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
460 m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD,
463 if self.do_modify(m, ["relax:0", "show_recycled:1"],
464 "Failed to normalise attribute %s" % attrname,
466 self.report("Normalised attribute %s" % attrname)
468 def err_normalise_mismatch_replace(self, dn, attrname, values):
469 '''fix attribute normalisation errors'''
470 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, values)
471 self.report("ERROR: Normalisation error for attribute '%s' in '%s'" % (attrname, dn))
472 self.report("Values/Order of values do/does not match: %s/%s!" % (values, list(normalised)))
473 if list(normalised) == values:
475 if not self.confirm_all("Fix normalisation for '%s' from '%s'?" % (attrname, dn), 'fix_all_normalisation'):
476 self.report("Not fixing attribute '%s'" % attrname)
481 m[attrname] = ldb.MessageElement(normalised, ldb.FLAG_MOD_REPLACE, attrname)
483 if self.do_modify(m, ["relax:0", "show_recycled:1"],
484 "Failed to normalise attribute %s" % attrname,
486 self.report("Normalised attribute %s" % attrname)
488 def err_duplicate_values(self, dn, attrname, dup_values, values):
489 '''fix attribute normalisation errors'''
490 self.report("ERROR: Duplicate values for attribute '%s' in '%s'" % (attrname, dn))
491 self.report("Values contain a duplicate: [%s]/[%s]!" % (','.join(dup_values), ','.join(values)))
492 if not self.confirm_all("Fix duplicates for '%s' from '%s'?" % (attrname, dn), 'fix_all_duplicates'):
493 self.report("Not fixing attribute '%s'" % attrname)
498 m[attrname] = ldb.MessageElement(values, ldb.FLAG_MOD_REPLACE, attrname)
500 if self.do_modify(m, ["relax:0", "show_recycled:1"],
501 "Failed to remove duplicate value on attribute %s" % attrname,
503 self.report("Removed duplicate value on attribute %s" % attrname)
505 def is_deleted_objects_dn(self, dsdb_dn):
506 '''see if a dsdb_Dn is the special Deleted Objects DN'''
507 return dsdb_dn.prefix == "B:32:%s:" % dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER
509 def err_missing_objectclass(self, dn):
510 """handle object without objectclass"""
511 self.report("ERROR: missing objectclass in object %s. If you have another working DC, please run 'samba-tool drs replicate --full-sync --local <destinationDC> <sourceDC> %s'" % (dn, self.samdb.get_nc_root(dn)))
512 if not self.confirm_all("If you cannot re-sync from another DC, do you wish to delete object '%s'?" % dn, 'fix_all_missing_objectclass'):
513 self.report("Not deleting object with missing objectclass '%s'" % dn)
515 if self.do_delete(dn, ["relax:0"],
516 "Failed to remove DN %s" % dn):
517 self.report("Removed DN %s" % dn)
519 def err_deleted_dn(self, dn, attrname, val, dsdb_dn, correct_dn, remove_plausible=False):
520 """handle a DN pointing to a deleted object"""
521 if not remove_plausible:
522 self.report("ERROR: target DN is deleted for %s in object %s - %s" % (attrname, dn, val))
523 self.report("Target GUID points at deleted DN %r" % str(correct_dn))
524 if not self.confirm_all('Remove DN link?', 'remove_implausible_deleted_DN_links'):
525 self.report("Not removing")
528 self.report("WARNING: target DN is deleted for %s in object %s - %s" % (attrname, dn, val))
529 self.report("Target GUID points at deleted DN %r" % str(correct_dn))
530 if not self.confirm_all('Remove stale DN link?', 'remove_plausible_deleted_DN_links'):
531 self.report("Not removing")
536 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
537 if self.do_modify(m, ["show_recycled:1",
538 "local_oid:%s:0" % dsdb.DSDB_CONTROL_REPLMD_VANISH_LINKS],
539 "Failed to remove deleted DN attribute %s" % attrname):
540 self.report("Removed deleted DN on attribute %s" % attrname)
542 def err_missing_target_dn_or_GUID(self, dn, attrname, val, dsdb_dn):
543 """handle a missing target DN (if specified, GUID form can't be found,
544 and otherwise DN string form can't be found)"""
545 # check if its a backlink
546 linkID, _ = self.get_attr_linkID_and_reverse_name(attrname)
547 if (linkID & 1 == 0) and str(dsdb_dn).find('\\0ADEL') == -1:
549 linkID, reverse_link_name \
550 = self.get_attr_linkID_and_reverse_name(attrname)
551 if reverse_link_name is not None:
552 self.report("WARNING: no target object found for GUID "
553 "component for one-way forward link "
555 "%s - %s" % (attrname, dn, val))
556 self.report("Not removing dangling forward link")
559 nc_root = self.samdb.get_nc_root(dn)
560 target_nc_root = self.samdb.get_nc_root(dsdb_dn.dn)
561 if nc_root != target_nc_root:
562 # We don't bump the error count as Samba produces these
563 # in normal operation
564 self.report("WARNING: no target object found for GUID "
565 "component for cross-partition link "
567 "%s - %s" % (attrname, dn, val))
568 self.report("Not removing dangling one-way "
569 "cross-partition link "
570 "(we might be mid-replication)")
573 # Due to our link handling one-way links pointing to
574 # missing objects are plausible.
576 # We don't bump the error count as Samba produces these
577 # in normal operation
578 self.report("WARNING: no target object found for GUID "
579 "component for DN value %s in object "
580 "%s - %s" % (attrname, dn, val))
581 self.err_deleted_dn(dn, attrname, val,
582 dsdb_dn, dsdb_dn, True)
585 # We bump the error count here, as we should have deleted this
586 self.report("ERROR: no target object found for GUID "
587 "component for link %s in object "
588 "%s - %s" % (attrname, dn, val))
589 self.err_deleted_dn(dn, attrname, val, dsdb_dn, dsdb_dn, False)
592 def err_missing_dn_GUID_component(self, dn, attrname, val, dsdb_dn, errstr):
593 """handle a missing GUID extended DN component"""
594 self.report("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val))
595 controls=["extended_dn:1:1", "show_recycled:1"]
597 res = self.samdb.search(base=str(dsdb_dn.dn), scope=ldb.SCOPE_BASE,
598 attrs=[], controls=controls)
599 except ldb.LdbError, (enum, estr):
600 self.report("unable to find object for DN %s - (%s)" % (dsdb_dn.dn, estr))
601 if enum != ldb.ERR_NO_SUCH_OBJECT:
603 self.err_missing_target_dn_or_GUID(dn, attrname, val, dsdb_dn)
606 self.report("unable to find object for DN %s" % dsdb_dn.dn)
607 self.err_missing_target_dn_or_GUID(dn, attrname, val, dsdb_dn)
609 dsdb_dn.dn = res[0].dn
611 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn), 'fix_all_DN_GUIDs'):
612 self.report("Not fixing %s" % errstr)
616 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
617 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
619 if self.do_modify(m, ["show_recycled:1"],
620 "Failed to fix %s on attribute %s" % (errstr, attrname)):
621 self.report("Fixed %s on attribute %s" % (errstr, attrname))
623 def err_incorrect_binary_dn(self, dn, attrname, val, dsdb_dn, errstr):
624 """handle an incorrect binary DN component"""
625 self.report("ERROR: %s binary component for %s in object %s - %s" % (errstr, attrname, dn, val))
626 controls=["extended_dn:1:1", "show_recycled:1"]
628 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn), 'fix_all_binary_dn'):
629 self.report("Not fixing %s" % errstr)
633 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
634 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
636 if self.do_modify(m, ["show_recycled:1"],
637 "Failed to fix %s on attribute %s" % (errstr, attrname)):
638 self.report("Fixed %s on attribute %s" % (errstr, attrname))
640 def err_dn_string_component_old(self, dn, attrname, val, dsdb_dn, correct_dn):
641 """handle a DN string being incorrect"""
642 self.report("NOTE: old (due to rename or delete) DN string component for %s in object %s - %s" % (attrname, dn, val))
643 dsdb_dn.dn = correct_dn
645 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn),
646 'fix_all_old_dn_string_component_mismatch'):
647 self.report("Not fixing old string component")
651 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
652 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
653 if self.do_modify(m, ["show_recycled:1"],
654 "Failed to fix old DN string on attribute %s" % (attrname)):
655 self.report("Fixed old DN string on attribute %s" % (attrname))
657 def err_dn_component_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn, mismatch_type):
658 """handle a DN string being incorrect"""
659 self.report("ERROR: incorrect DN %s component for %s in object %s - %s" % (mismatch_type, attrname, dn, val))
660 dsdb_dn.dn = correct_dn
662 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn),
663 'fix_all_%s_dn_component_mismatch' % mismatch_type):
664 self.report("Not fixing %s component mismatch" % mismatch_type)
668 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
669 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
670 if self.do_modify(m, ["show_recycled:1"],
671 "Failed to fix incorrect DN %s on attribute %s" % (mismatch_type, attrname)):
672 self.report("Fixed incorrect DN %s on attribute %s" % (mismatch_type, attrname))
674 def err_unknown_attribute(self, obj, attrname):
675 '''handle an unknown attribute error'''
676 self.report("ERROR: unknown attribute '%s' in %s" % (attrname, obj.dn))
677 if not self.confirm_all('Remove unknown attribute %s' % attrname, 'remove_all_unknown_attributes'):
678 self.report("Not removing %s" % attrname)
682 m['old_value'] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, attrname)
683 if self.do_modify(m, ["relax:0", "show_recycled:1"],
684 "Failed to remove unknown attribute %s" % attrname):
685 self.report("Removed unknown attribute %s" % (attrname))
687 def err_undead_linked_attribute(self, obj, attrname, val):
688 '''handle a link that should not be there on a deleted object'''
689 self.report("ERROR: linked attribute '%s' to '%s' is present on "
690 "deleted object %s" % (attrname, val, obj.dn))
691 if not self.confirm_all('Remove linked attribute %s' % attrname, 'fix_undead_linked_attributes'):
692 self.report("Not removing linked attribute %s" % attrname)
696 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
698 if self.do_modify(m, ["show_recycled:1", "show_deleted:1", "reveal_internals:0",
699 "local_oid:%s:0" % dsdb.DSDB_CONTROL_REPLMD_VANISH_LINKS],
700 "Failed to delete forward link %s" % attrname):
701 self.report("Fixed undead forward link %s" % (attrname))
703 def err_missing_backlink(self, obj, attrname, val, backlink_name, target_dn):
704 '''handle a missing backlink value'''
705 self.report("ERROR: missing backlink attribute '%s' in %s for link %s in %s" % (backlink_name, target_dn, attrname, obj.dn))
706 if not self.confirm_all('Fix missing backlink %s' % backlink_name, 'fix_all_missing_backlinks'):
707 self.report("Not fixing missing backlink %s" % backlink_name)
711 m['new_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_ADD, backlink_name)
712 if self.do_modify(m, ["show_recycled:1", "relax:0"],
713 "Failed to fix missing backlink %s" % backlink_name):
714 self.report("Fixed missing backlink %s" % (backlink_name))
716 def err_incorrect_rmd_flags(self, obj, attrname, revealed_dn):
717 '''handle a incorrect RMD_FLAGS value'''
718 rmd_flags = int(revealed_dn.dn.get_extended_component("RMD_FLAGS"))
719 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()))
720 if not self.confirm_all('Fix incorrect RMD_FLAGS %u' % rmd_flags, 'fix_rmd_flags'):
721 self.report("Not fixing incorrect RMD_FLAGS %u" % rmd_flags)
725 m['old_value'] = ldb.MessageElement(str(revealed_dn), ldb.FLAG_MOD_DELETE, attrname)
726 if self.do_modify(m, ["show_recycled:1", "reveal_internals:0", "show_deleted:0"],
727 "Failed to fix incorrect RMD_FLAGS %u" % rmd_flags):
728 self.report("Fixed incorrect RMD_FLAGS %u" % (rmd_flags))
730 def err_orphaned_backlink(self, obj_dn, backlink_attr, backlink_val,
731 target_dn, forward_attr, forward_syntax,
732 check_duplicates=True):
733 '''handle a orphaned backlink value'''
734 if check_duplicates is True and self.has_duplicate_links(target_dn, forward_attr, forward_syntax):
735 self.report("WARNING: Keep orphaned backlink attribute " + \
736 "'%s' in '%s' for link '%s' in '%s'" % (
737 backlink_attr, obj_dn, forward_attr, target_dn))
739 self.report("ERROR: orphaned backlink attribute '%s' in %s for link %s in %s" % (backlink_attr, obj_dn, forward_attr, target_dn))
740 if not self.confirm_all('Remove orphaned backlink %s' % backlink_attr, 'fix_all_orphaned_backlinks'):
741 self.report("Not removing orphaned backlink %s" % backlink_attr)
745 m['value'] = ldb.MessageElement(backlink_val, ldb.FLAG_MOD_DELETE, backlink_attr)
746 if self.do_modify(m, ["show_recycled:1", "relax:0"],
747 "Failed to fix orphaned backlink %s" % backlink_attr):
748 self.report("Fixed orphaned backlink %s" % (backlink_attr))
750 def err_recover_forward_links(self, obj, forward_attr, forward_vals):
751 '''handle a duplicate links value'''
753 self.report("RECHECK: 'Missing/Duplicate/Correct link' lines above for attribute '%s' in '%s'" % (forward_attr, obj.dn))
755 if not self.confirm_all("Commit fixes for (missing/duplicate) forward links in attribute '%s'" % forward_attr, 'recover_all_forward_links'):
756 self.report("Not fixing corrupted (missing/duplicate) forward links in attribute '%s' of '%s'" % (
757 forward_attr, obj.dn))
761 m['value'] = ldb.MessageElement(forward_vals, ldb.FLAG_MOD_REPLACE, forward_attr)
762 if self.do_modify(m, ["local_oid:1.3.6.1.4.1.7165.4.3.19.2:1"],
763 "Failed to fix duplicate links in attribute '%s'" % forward_attr):
764 self.report("Fixed duplicate links in attribute '%s'" % (forward_attr))
765 duplicate_cache_key = "%s:%s" % (str(obj.dn), forward_attr)
766 assert duplicate_cache_key in self.duplicate_link_cache
767 self.duplicate_link_cache[duplicate_cache_key] = False
769 def err_no_fsmoRoleOwner(self, obj):
770 '''handle a missing fSMORoleOwner'''
771 self.report("ERROR: fSMORoleOwner not found for role %s" % (obj.dn))
772 res = self.samdb.search("",
773 scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
775 serviceName = res[0]["dsServiceName"][0]
776 if not self.confirm_all('Sieze role %s onto current DC by adding fSMORoleOwner=%s' % (obj.dn, serviceName), 'seize_fsmo_role'):
777 self.report("Not Siezing role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName))
781 m['value'] = ldb.MessageElement(serviceName, ldb.FLAG_MOD_ADD, 'fSMORoleOwner')
782 if self.do_modify(m, [],
783 "Failed to sieze role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName)):
784 self.report("Siezed role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName))
786 def err_missing_parent(self, obj):
787 '''handle a missing parent'''
788 self.report("ERROR: parent object not found for %s" % (obj.dn))
789 if not self.confirm_all('Move object %s into LostAndFound?' % (obj.dn), 'move_to_lost_and_found'):
790 self.report('Not moving object %s into LostAndFound' % (obj.dn))
793 keep_transaction = False
794 self.samdb.transaction_start()
796 nc_root = self.samdb.get_nc_root(obj.dn);
797 lost_and_found = self.samdb.get_wellknown_dn(nc_root, dsdb.DS_GUID_LOSTANDFOUND_CONTAINER)
798 new_dn = ldb.Dn(self.samdb, str(obj.dn))
799 new_dn.remove_base_components(len(new_dn) - 1)
800 if self.do_rename(obj.dn, new_dn, lost_and_found, ["show_deleted:0", "relax:0"],
801 "Failed to rename object %s into lostAndFound at %s" % (obj.dn, new_dn + lost_and_found)):
802 self.report("Renamed object %s into lostAndFound at %s" % (obj.dn, new_dn + lost_and_found))
806 m['lastKnownParent'] = ldb.MessageElement(str(obj.dn.parent()), ldb.FLAG_MOD_REPLACE, 'lastKnownParent')
808 if self.do_modify(m, [],
809 "Failed to set lastKnownParent on lostAndFound object at %s" % (new_dn + lost_and_found)):
810 self.report("Set lastKnownParent on lostAndFound object at %s" % (new_dn + lost_and_found))
811 keep_transaction = True
813 self.samdb.transaction_cancel()
817 self.samdb.transaction_commit()
819 self.samdb.transaction_cancel()
821 def err_wrong_dn(self, obj, new_dn, rdn_attr, rdn_val, name_val):
822 '''handle a wrong dn'''
824 new_rdn = ldb.Dn(self.samdb, str(new_dn))
825 new_rdn.remove_base_components(len(new_rdn) - 1)
826 new_parent = new_dn.parent()
829 if rdn_val != name_val:
830 attributes += "%s=%r " % (rdn_attr, rdn_val)
831 attributes += "name=%r" % (name_val)
833 self.report("ERROR: wrong dn[%s] %s new_dn[%s]" % (obj.dn, attributes, new_dn))
834 if not self.confirm_all("Rename %s to %s?" % (obj.dn, new_dn), 'fix_dn'):
835 self.report("Not renaming %s to %s" % (obj.dn, new_dn))
838 if self.do_rename(obj.dn, new_rdn, new_parent, ["show_recycled:1", "relax:0"],
839 "Failed to rename object %s into %s" % (obj.dn, new_dn)):
840 self.report("Renamed %s into %s" % (obj.dn, new_dn))
842 def err_wrong_instancetype(self, obj, calculated_instancetype):
843 '''handle a wrong instanceType'''
844 self.report("ERROR: wrong instanceType %s on %s, should be %d" % (obj["instanceType"], obj.dn, calculated_instancetype))
845 if not self.confirm_all('Change instanceType from %s to %d on %s?' % (obj["instanceType"], calculated_instancetype, obj.dn), 'fix_instancetype'):
846 self.report('Not changing instanceType from %s to %d on %s' % (obj["instanceType"], calculated_instancetype, obj.dn))
851 m['value'] = ldb.MessageElement(str(calculated_instancetype), ldb.FLAG_MOD_REPLACE, 'instanceType')
852 if self.do_modify(m, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA],
853 "Failed to correct missing instanceType on %s by setting instanceType=%d" % (obj.dn, calculated_instancetype)):
854 self.report("Corrected instancetype on %s by setting instanceType=%d" % (obj.dn, calculated_instancetype))
856 def err_short_userParameters(self, obj, attrname, value):
857 # This is a truncated userParameters due to a pre 4.1 replication bug
858 self.report("ERROR: incorrect userParameters value on object %s. If you have another working DC that does not give this warning, please run 'samba-tool drs replicate --full-sync --local <destinationDC> <sourceDC> %s'" % (obj.dn, self.samdb.get_nc_root(obj.dn)))
860 def err_base64_userParameters(self, obj, attrname, value):
861 '''handle a wrong userParameters'''
862 self.report("ERROR: wrongly formatted userParameters %s on %s, should not be base64-encoded" % (value, obj.dn))
863 if not self.confirm_all('Convert userParameters from base64 encoding on %s?' % (obj.dn), 'fix_base64_userparameters'):
864 self.report('Not changing userParameters from base64 encoding on %s' % (obj.dn))
869 m['value'] = ldb.MessageElement(b64decode(obj[attrname][0]), ldb.FLAG_MOD_REPLACE, 'userParameters')
870 if self.do_modify(m, [],
871 "Failed to correct base64-encoded userParameters on %s by converting from base64" % (obj.dn)):
872 self.report("Corrected base64-encoded userParameters on %s by converting from base64" % (obj.dn))
874 def err_utf8_userParameters(self, obj, attrname, value):
875 '''handle a wrong userParameters'''
876 self.report("ERROR: wrongly formatted userParameters on %s, should not be psudo-UTF8 encoded" % (obj.dn))
877 if not self.confirm_all('Convert userParameters from UTF8 encoding on %s?' % (obj.dn), 'fix_utf8_userparameters'):
878 self.report('Not changing userParameters from UTF8 encoding on %s' % (obj.dn))
883 m['value'] = ldb.MessageElement(obj[attrname][0].decode('utf8').encode('utf-16-le'),
884 ldb.FLAG_MOD_REPLACE, 'userParameters')
885 if self.do_modify(m, [],
886 "Failed to correct psudo-UTF8 encoded userParameters on %s by converting from UTF8" % (obj.dn)):
887 self.report("Corrected psudo-UTF8 encoded userParameters on %s by converting from UTF8" % (obj.dn))
889 def err_doubled_userParameters(self, obj, attrname, value):
890 '''handle a wrong userParameters'''
891 self.report("ERROR: wrongly formatted userParameters on %s, should not be double UTF16 encoded" % (obj.dn))
892 if not self.confirm_all('Convert userParameters from doubled UTF-16 encoding on %s?' % (obj.dn), 'fix_doubled_userparameters'):
893 self.report('Not changing userParameters from doubled UTF-16 encoding on %s' % (obj.dn))
898 m['value'] = ldb.MessageElement(obj[attrname][0].decode('utf-16-le').decode('utf-16-le').encode('utf-16-le'),
899 ldb.FLAG_MOD_REPLACE, 'userParameters')
900 if self.do_modify(m, [],
901 "Failed to correct doubled-UTF16 encoded userParameters on %s by converting" % (obj.dn)):
902 self.report("Corrected doubled-UTF16 encoded userParameters on %s by converting" % (obj.dn))
904 def err_odd_userParameters(self, obj, attrname):
905 # This is a truncated userParameters due to a pre 4.1 replication bug
906 self.report("ERROR: incorrect userParameters value on object %s (odd length). If you have another working DC that does not give this warning, please run 'samba-tool drs replicate --full-sync --local <destinationDC> <sourceDC> %s'" % (obj.dn, self.samdb.get_nc_root(obj.dn)))
908 def find_revealed_link(self, dn, attrname, guid):
909 '''return a revealed link in an object'''
910 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[attrname],
911 controls=["show_deleted:0", "extended_dn:0", "reveal_internals:0"])
912 syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname)
913 for val in res[0][attrname]:
914 dsdb_dn = dsdb_Dn(self.samdb, val, syntax_oid)
915 guid2 = dsdb_dn.dn.get_extended_component("GUID")
920 def check_duplicate_links(self, obj, forward_attr, forward_syntax, forward_linkID, backlink_attr):
921 '''check a linked values for duplicate forward links'''
924 duplicate_dict = dict()
927 # Only forward links can have this problem
928 if forward_linkID & 1:
929 # If we got the reverse, skip it
930 return (error_count, duplicate_dict, unique_dict)
932 if backlink_attr is None:
933 return (error_count, duplicate_dict, unique_dict)
935 duplicate_cache_key = "%s:%s" % (str(obj.dn), forward_attr)
936 if duplicate_cache_key not in self.duplicate_link_cache:
937 self.duplicate_link_cache[duplicate_cache_key] = False
939 for val in obj[forward_attr]:
940 dsdb_dn = dsdb_Dn(self.samdb, val, forward_syntax)
942 # all DNs should have a GUID component
943 guid = dsdb_dn.dn.get_extended_component("GUID")
946 guidstr = str(misc.GUID(guid))
947 keystr = guidstr + dsdb_dn.prefix
948 if keystr not in unique_dict:
949 unique_dict[keystr] = dsdb_dn
952 if keystr not in duplicate_dict:
953 duplicate_dict[keystr] = dict()
954 duplicate_dict[keystr]["keep"] = None
955 duplicate_dict[keystr]["delete"] = list()
957 # Now check for the highest RMD_VERSION
958 v1 = int(unique_dict[keystr].dn.get_extended_component("RMD_VERSION"))
959 v2 = int(dsdb_dn.dn.get_extended_component("RMD_VERSION"))
961 duplicate_dict[keystr]["keep"] = unique_dict[keystr]
962 duplicate_dict[keystr]["delete"].append(dsdb_dn)
965 duplicate_dict[keystr]["keep"] = dsdb_dn
966 duplicate_dict[keystr]["delete"].append(unique_dict[keystr])
967 unique_dict[keystr] = dsdb_dn
969 # Fallback to the highest RMD_LOCAL_USN
970 u1 = int(unique_dict[keystr].dn.get_extended_component("RMD_LOCAL_USN"))
971 u2 = int(dsdb_dn.dn.get_extended_component("RMD_LOCAL_USN"))
973 duplicate_dict[keystr]["keep"] = unique_dict[keystr]
974 duplicate_dict[keystr]["delete"].append(dsdb_dn)
976 duplicate_dict[keystr]["keep"] = dsdb_dn
977 duplicate_dict[keystr]["delete"].append(unique_dict[keystr])
978 unique_dict[keystr] = dsdb_dn
981 self.duplicate_link_cache[duplicate_cache_key] = True
983 return (error_count, duplicate_dict, unique_dict)
985 def has_duplicate_links(self, dn, forward_attr, forward_syntax):
986 '''check a linked values for duplicate forward links'''
989 duplicate_cache_key = "%s:%s" % (str(dn), forward_attr)
990 if duplicate_cache_key in self.duplicate_link_cache:
991 return self.duplicate_link_cache[duplicate_cache_key]
993 forward_linkID, backlink_attr = self.get_attr_linkID_and_reverse_name(forward_attr)
995 attrs = [forward_attr]
996 controls = ["extended_dn:1:1", "reveal_internals:0"]
998 # check its the right GUID
1000 res = self.samdb.search(base=str(dn), scope=ldb.SCOPE_BASE,
1001 attrs=attrs, controls=controls)
1002 except ldb.LdbError, (enum, estr):
1003 if enum != ldb.ERR_NO_SUCH_OBJECT:
1009 error_count, duplicate_dict, unique_dict = \
1010 self.check_duplicate_links(obj, forward_attr, forward_syntax, forward_linkID, backlink_attr)
1012 if duplicate_cache_key in self.duplicate_link_cache:
1013 return self.duplicate_link_cache[duplicate_cache_key]
1017 def find_missing_forward_links_from_backlinks(self, obj,
1021 forward_unique_dict):
1022 '''Find all backlinks linking to obj_guid_str not already in forward_unique_dict'''
1023 missing_forward_links = []
1026 if backlink_attr is None:
1027 return (missing_forward_links, error_count)
1029 if forward_syntax != ldb.SYNTAX_DN:
1030 self.report("Not checking for missing forward links for syntax: %s",
1032 return (missing_forward_links, error_count)
1034 if "sortedLinks" in self.compatibleFeatures:
1035 self.report("Not checking for missing forward links because the db " + \
1036 "has the sortedLinks feature")
1037 return (missing_forward_links, error_count)
1040 obj_guid = obj['objectGUID'][0]
1041 obj_guid_str = str(ndr_unpack(misc.GUID, obj_guid))
1042 filter = "(%s=<GUID=%s>)" % (backlink_attr, obj_guid_str)
1044 res = self.samdb.search(expression=filter,
1045 scope=ldb.SCOPE_SUBTREE, attrs=["objectGUID"],
1046 controls=["extended_dn:1:1",
1047 "search_options:1:2",
1048 "paged_results:1:1000"])
1049 except ldb.LdbError, (enum, estr):
1053 target_dn = dsdb_Dn(self.samdb, r.dn.extended_str(), forward_syntax)
1055 guid = target_dn.dn.get_extended_component("GUID")
1056 guidstr = str(misc.GUID(guid))
1057 if guidstr in forward_unique_dict:
1060 # A valid forward link looks like this:
1062 # <GUID=9f92d30a-fc23-11e4-a5f6-30be15454808>;
1063 # <RMD_ADDTIME=131607546230000000>;
1064 # <RMD_CHANGETIME=131607546230000000>;
1066 # <RMD_INVOCID=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d>;
1067 # <RMD_LOCAL_USN=3765>;
1068 # <RMD_ORIGINATING_USN=3765>;
1070 # <SID=S-1-5-21-4177067393-1453636373-93818738-1124>;
1071 # CN=unsorted-u8,CN=Users,DC=release-4-5-0-pre1,DC=samba,DC=corp
1073 # Note that versions older than Samba 4.8 create
1074 # links with RMD_VERSION=0.
1076 # Try to get the local_usn and time from objectClass
1077 # if possible and fallback to any other one.
1078 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1079 obj['replPropertyMetadata'][0])
1080 for o in repl.ctr.array:
1081 local_usn = o.local_usn
1082 t = o.originating_change_time
1083 if o.attid == drsuapi.DRSUAPI_ATTID_objectClass:
1086 # We use a magic invocationID for restoring missing
1087 # forward links to recover from bug #13228.
1088 # This should allow some more future magic to fix the
1091 # It also means it looses the conflict resolution
1092 # against almost every real invocation, if the
1093 # version is also 0.
1094 originating_invocid = misc.GUID("ffffffff-4700-4700-4700-000000b13228")
1100 rmd_invocid = originating_invocid
1101 rmd_originating_usn = originating_usn
1102 rmd_local_usn = local_usn
1105 target_dn.dn.set_extended_component("RMD_ADDTIME", str(rmd_addtime))
1106 target_dn.dn.set_extended_component("RMD_CHANGETIME", str(rmd_changetime))
1107 target_dn.dn.set_extended_component("RMD_FLAGS", str(rmd_flags))
1108 target_dn.dn.set_extended_component("RMD_INVOCID", ndr_pack(rmd_invocid))
1109 target_dn.dn.set_extended_component("RMD_ORIGINATING_USN", str(rmd_originating_usn))
1110 target_dn.dn.set_extended_component("RMD_LOCAL_USN", str(rmd_local_usn))
1111 target_dn.dn.set_extended_component("RMD_VERSION", str(rmd_version))
1114 missing_forward_links.append(target_dn)
1116 return (missing_forward_links, error_count)
1118 def check_dn(self, obj, attrname, syntax_oid):
1119 '''check a DN attribute for correctness'''
1121 obj_guid = obj['objectGUID'][0]
1123 linkID, reverse_link_name = self.get_attr_linkID_and_reverse_name(attrname)
1124 if reverse_link_name is not None:
1125 reverse_syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(reverse_link_name)
1127 reverse_syntax_oid = None
1129 error_count, duplicate_dict, unique_dict = \
1130 self.check_duplicate_links(obj, attrname, syntax_oid, linkID, reverse_link_name)
1132 if len(duplicate_dict) != 0:
1134 missing_forward_links, missing_error_count = \
1135 self.find_missing_forward_links_from_backlinks(obj,
1136 attrname, syntax_oid,
1139 error_count += missing_error_count
1141 forward_links = [dn for dn in unique_dict.values()]
1143 if missing_error_count != 0:
1144 self.report("ERROR: Missing and duplicate forward link values for attribute '%s' in '%s'" % (
1147 self.report("ERROR: Duplicate forward link values for attribute '%s' in '%s'" % (attrname, obj.dn))
1148 for m in missing_forward_links:
1149 self.report("Missing link '%s'" % (m))
1150 if not self.confirm_all("Schedule readding missing forward link for attribute %s" % attrname,
1151 'fix_all_missing_forward_links'):
1152 self.err_orphaned_backlink(m.dn, reverse_link_name,
1153 obj.dn.extended_str(), obj.dn,
1154 attrname, syntax_oid,
1155 check_duplicates=False)
1157 forward_links += [m]
1158 for keystr in duplicate_dict.keys():
1159 d = duplicate_dict[keystr]
1160 for dd in d["delete"]:
1161 self.report("Duplicate link '%s'" % dd)
1162 self.report("Correct link '%s'" % d["keep"])
1164 # We now construct the sorted dn values.
1165 # They're sorted by the objectGUID of the target
1166 # See dsdb_Dn.__cmp__()
1167 vals = [str(dn) for dn in sorted(forward_links)]
1168 self.err_recover_forward_links(obj, attrname, vals)
1169 # We should continue with the fixed values
1170 obj[attrname] = ldb.MessageElement(vals, 0, attrname)
1172 for val in obj[attrname]:
1173 dsdb_dn = dsdb_Dn(self.samdb, val, syntax_oid)
1175 # all DNs should have a GUID component
1176 guid = dsdb_dn.dn.get_extended_component("GUID")
1179 self.err_missing_dn_GUID_component(obj.dn, attrname, val, dsdb_dn,
1183 guidstr = str(misc.GUID(guid))
1184 attrs = ['isDeleted', 'replPropertyMetaData']
1186 if (str(attrname).lower() == 'msds-hasinstantiatedncs') and (obj.dn == self.ntds_dsa):
1187 fixing_msDS_HasInstantiatedNCs = True
1188 attrs.append("instanceType")
1190 fixing_msDS_HasInstantiatedNCs = False
1192 if reverse_link_name is not None:
1193 attrs.append(reverse_link_name)
1195 # check its the right GUID
1197 res = self.samdb.search(base="<GUID=%s>" % guidstr, scope=ldb.SCOPE_BASE,
1198 attrs=attrs, controls=["extended_dn:1:1", "show_recycled:1",
1199 "reveal_internals:0"
1201 except ldb.LdbError, (enum, estr):
1202 if enum != ldb.ERR_NO_SUCH_OBJECT:
1205 # We don't always want to
1206 error_count += self.err_missing_target_dn_or_GUID(obj.dn,
1212 if fixing_msDS_HasInstantiatedNCs:
1213 dsdb_dn.prefix = "B:8:%08X:" % int(res[0]['instanceType'][0])
1214 dsdb_dn.binary = "%08X" % int(res[0]['instanceType'][0])
1216 if str(dsdb_dn) != val:
1218 self.err_incorrect_binary_dn(obj.dn, attrname, val, dsdb_dn, "incorrect instanceType part of Binary DN")
1221 # now we have two cases - the source object might or might not be deleted
1222 is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper() == 'TRUE'
1223 target_is_deleted = 'isDeleted' in res[0] and res[0]['isDeleted'][0].upper() == 'TRUE'
1226 if is_deleted and not obj.dn in self.deleted_objects_containers and linkID:
1227 # A fully deleted object should not have any linked
1228 # attributes. (MS-ADTS 3.1.1.5.5.1.1 Tombstone
1229 # Requirements and 3.1.1.5.5.1.3 Recycled-Object
1231 self.err_undead_linked_attribute(obj, attrname, val)
1234 elif target_is_deleted and not self.is_deleted_objects_dn(dsdb_dn) and linkID:
1235 # the target DN is not allowed to be deleted, unless the target DN is the
1236 # special Deleted Objects container
1238 local_usn = dsdb_dn.dn.get_extended_component("RMD_LOCAL_USN")
1240 if 'replPropertyMetaData' in res[0]:
1241 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1242 str(res[0]['replPropertyMetadata']))
1244 for o in repl.ctr.array:
1245 if o.attid == drsuapi.DRSUAPI_ATTID_isDeleted:
1246 deleted_usn = o.local_usn
1247 if deleted_usn >= int(local_usn):
1248 # If the object was deleted after the link
1249 # was last modified then, clean it up here
1254 self.err_deleted_dn(obj.dn, attrname,
1255 val, dsdb_dn, res[0].dn, True)
1258 self.err_deleted_dn(obj.dn, attrname, val, dsdb_dn, res[0].dn, False)
1261 # We should not check for incorrect
1262 # components on deleted links, as these are allowed to
1263 # go stale (we just need the GUID, not the name)
1264 rmd_blob = dsdb_dn.dn.get_extended_component("RMD_FLAGS")
1266 if rmd_blob is not None:
1267 rmd_flags = int(rmd_blob)
1269 # assert the DN matches in string form, where a reverse
1270 # link exists, otherwise (below) offer to fix it as a non-error.
1271 # The string form is essentially only kept for forensics,
1272 # as we always re-resolve by GUID in normal operations.
1273 if not rmd_flags & 1 and reverse_link_name is not None:
1274 if str(res[0].dn) != str(dsdb_dn.dn):
1276 self.err_dn_component_target_mismatch(obj.dn, attrname, val, dsdb_dn,
1277 res[0].dn, "string")
1280 if res[0].dn.get_extended_component("GUID") != dsdb_dn.dn.get_extended_component("GUID"):
1282 self.err_dn_component_target_mismatch(obj.dn, attrname, val, dsdb_dn,
1286 if res[0].dn.get_extended_component("SID") != dsdb_dn.dn.get_extended_component("SID"):
1288 self.err_dn_component_target_mismatch(obj.dn, attrname, val, dsdb_dn,
1292 # Now we have checked the GUID and SID, offer to fix old
1293 # DN strings as a non-error (for forward links with no
1294 # backlink). Samba does not maintain this string
1295 # otherwise, so we don't increment error_count.
1296 if reverse_link_name is None:
1297 if str(res[0].dn) != str(dsdb_dn.dn):
1298 self.err_dn_string_component_old(obj.dn, attrname, val, dsdb_dn,
1302 # check the reverse_link is correct if there should be one
1304 if reverse_link_name in res[0]:
1305 for v in res[0][reverse_link_name]:
1306 v_dn = dsdb_Dn(self.samdb, v)
1307 v_guid = v_dn.dn.get_extended_component("GUID")
1308 v_blob = v_dn.dn.get_extended_component("RMD_FLAGS")
1310 if v_blob is not None:
1311 v_rmd_flags = int(v_blob)
1314 if v_guid == obj_guid:
1317 if match_count != 1:
1318 if syntax_oid == dsdb.DSDB_SYNTAX_BINARY_DN or reverse_syntax_oid == dsdb.DSDB_SYNTAX_BINARY_DN:
1320 # Forward binary multi-valued linked attribute
1322 for w in obj[attrname]:
1323 w_guid = dsdb_Dn(self.samdb, w).dn.get_extended_component("GUID")
1327 if match_count == forward_count:
1330 for v in obj[attrname]:
1331 v_dn = dsdb_Dn(self.samdb, v)
1332 v_guid = v_dn.dn.get_extended_component("GUID")
1333 v_blob = v_dn.dn.get_extended_component("RMD_FLAGS")
1335 if v_blob is not None:
1336 v_rmd_flags = int(v_blob)
1342 if match_count == expected_count:
1345 diff_count = expected_count - match_count
1348 # If there's a backward link on binary multi-valued linked attribute,
1349 # let the check on the forward link remedy the value.
1350 # UNLESS, there is no forward link detected.
1351 if match_count == 0:
1353 self.err_orphaned_backlink(obj.dn, attrname,
1358 # Only warn here and let the forward link logic fix it.
1359 self.report("WARNING: Link (back) mismatch for '%s' (%d) on '%s' to '%s' (%d) on '%s'" % (
1360 attrname, expected_count, str(obj.dn),
1361 reverse_link_name, match_count, str(dsdb_dn.dn)))
1364 assert not target_is_deleted
1366 self.report("ERROR: Link (forward) mismatch for '%s' (%d) on '%s' to '%s' (%d) on '%s'" % (
1367 attrname, expected_count, str(obj.dn),
1368 reverse_link_name, match_count, str(dsdb_dn.dn)))
1370 # Loop until the difference between the forward and
1371 # the backward links is resolved.
1372 while diff_count != 0:
1375 if match_count > 0 or diff_count > 1:
1376 # TODO no method to fix these right now
1377 self.report("ERROR: Can't fix missing "
1378 "multi-valued backlinks on %s" % str(dsdb_dn.dn))
1380 self.err_missing_backlink(obj, attrname,
1381 obj.dn.extended_str(),
1386 self.err_orphaned_backlink(res[0].dn, reverse_link_name,
1387 obj.dn.extended_str(), obj.dn,
1388 attrname, syntax_oid)
1395 def get_originating_time(self, val, attid):
1396 '''Read metadata properties and return the originating time for
1397 a given attributeId.
1399 :return: the originating time or 0 if not found
1402 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
1405 for o in repl.ctr.array:
1406 if o.attid == attid:
1407 return o.originating_change_time
1411 def process_metadata(self, dn, val):
1412 '''Read metadata properties and list attributes in it.
1413 raises KeyError if the attid is unknown.'''
1416 wrong_attids = set()
1418 in_schema_nc = dn.is_child_of(self.schema_dn)
1420 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
1423 for o in repl.ctr.array:
1424 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1425 set_att.add(att.lower())
1426 list_attid.append(o.attid)
1427 correct_attid = self.samdb_schema.get_attid_from_lDAPDisplayName(att,
1428 is_schema_nc=in_schema_nc)
1429 if correct_attid != o.attid:
1430 wrong_attids.add(o.attid)
1432 return (set_att, list_attid, wrong_attids)
1435 def fix_metadata(self, obj, attr):
1436 '''re-write replPropertyMetaData elements for a single attribute for a
1437 object. This is used to fix missing replPropertyMetaData elements'''
1438 guid_str = str(ndr_unpack(misc.GUID, obj['objectGUID'][0]))
1439 dn = ldb.Dn(self.samdb, "<GUID=%s>" % guid_str)
1440 res = self.samdb.search(base = dn, scope=ldb.SCOPE_BASE, attrs = [attr],
1441 controls = ["search_options:1:2",
1444 nmsg = ldb.Message()
1446 nmsg[attr] = ldb.MessageElement(msg[attr], ldb.FLAG_MOD_REPLACE, attr)
1447 if self.do_modify(nmsg, ["relax:0", "provision:0", "show_recycled:1"],
1448 "Failed to fix metadata for attribute %s" % attr):
1449 self.report("Fixed metadata for attribute %s" % attr)
1451 def ace_get_effective_inherited_type(self, ace):
1452 if ace.flags & security.SEC_ACE_FLAG_INHERIT_ONLY:
1456 if ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
1458 elif ace.type == security.SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
1460 elif ace.type == security.SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
1462 elif ace.type == security.SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
1468 if not ace.object.flags & security.SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT:
1471 return str(ace.object.inherited_type)
1473 def lookup_class_schemaIDGUID(self, cls):
1474 if cls in self.class_schemaIDGUID:
1475 return self.class_schemaIDGUID[cls]
1477 flt = "(&(ldapDisplayName=%s)(objectClass=classSchema))" % cls
1478 res = self.samdb.search(base=self.schema_dn,
1480 attrs=["schemaIDGUID"])
1481 t = str(ndr_unpack(misc.GUID, res[0]["schemaIDGUID"][0]))
1483 self.class_schemaIDGUID[cls] = t
1486 def process_sd(self, dn, obj):
1487 sd_attr = "nTSecurityDescriptor"
1488 sd_val = obj[sd_attr]
1490 sd = ndr_unpack(security.descriptor, str(sd_val))
1492 is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper() == 'TRUE'
1494 # we don't fix deleted objects
1497 sd_clean = security.descriptor()
1498 sd_clean.owner_sid = sd.owner_sid
1499 sd_clean.group_sid = sd.group_sid
1500 sd_clean.type = sd.type
1501 sd_clean.revision = sd.revision
1504 last_inherited_type = None
1507 if sd.sacl is not None:
1509 for i in range(0, len(aces)):
1512 if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE:
1513 sd_clean.sacl_add(ace)
1516 t = self.ace_get_effective_inherited_type(ace)
1520 if last_inherited_type is not None:
1521 if t != last_inherited_type:
1522 # if it inherited from more than
1523 # one type it's very likely to be broken
1525 # If not the recalculation will calculate
1530 last_inherited_type = t
1533 if sd.dacl is not None:
1535 for i in range(0, len(aces)):
1538 if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE:
1539 sd_clean.dacl_add(ace)
1542 t = self.ace_get_effective_inherited_type(ace)
1546 if last_inherited_type is not None:
1547 if t != last_inherited_type:
1548 # if it inherited from more than
1549 # one type it's very likely to be broken
1551 # If not the recalculation will calculate
1556 last_inherited_type = t
1559 return (sd_clean, sd)
1561 if last_inherited_type is None:
1567 cls = obj["objectClass"][-1]
1572 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE,
1573 attrs=["isDeleted", "objectClass"],
1574 controls=["show_recycled:1"])
1576 is_deleted = 'isDeleted' in o and o['isDeleted'][0].upper() == 'TRUE'
1578 # we don't fix deleted objects
1580 cls = o["objectClass"][-1]
1582 t = self.lookup_class_schemaIDGUID(cls)
1584 if t != last_inherited_type:
1586 return (sd_clean, sd)
1591 def err_wrong_sd(self, dn, sd, sd_broken):
1592 '''re-write the SD due to incorrect inherited ACEs'''
1593 sd_attr = "nTSecurityDescriptor"
1594 sd_val = ndr_pack(sd)
1595 sd_flags = security.SECINFO_DACL | security.SECINFO_SACL
1597 if not self.confirm_all('Fix %s on %s?' % (sd_attr, dn), 'fix_ntsecuritydescriptor'):
1598 self.report('Not fixing %s on %s\n' % (sd_attr, dn))
1601 nmsg = ldb.Message()
1603 nmsg[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
1604 if self.do_modify(nmsg, ["sd_flags:1:%d" % sd_flags],
1605 "Failed to fix attribute %s" % sd_attr):
1606 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
1608 def err_wrong_default_sd(self, dn, sd, sd_old, diff):
1609 '''re-write the SD due to not matching the default (optional mode for fixing an incorrect provision)'''
1610 sd_attr = "nTSecurityDescriptor"
1611 sd_val = ndr_pack(sd)
1612 sd_old_val = ndr_pack(sd_old)
1613 sd_flags = security.SECINFO_DACL | security.SECINFO_SACL
1614 if sd.owner_sid is not None:
1615 sd_flags |= security.SECINFO_OWNER
1616 if sd.group_sid is not None:
1617 sd_flags |= security.SECINFO_GROUP
1619 if not self.confirm_all('Reset %s on %s back to provision default?\n%s' % (sd_attr, dn, diff), 'reset_all_well_known_acls'):
1620 self.report('Not resetting %s on %s\n' % (sd_attr, dn))
1625 m[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
1626 if self.do_modify(m, ["sd_flags:1:%d" % sd_flags],
1627 "Failed to reset attribute %s" % sd_attr):
1628 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
1630 def err_missing_sd_owner(self, dn, sd):
1631 '''re-write the SD due to a missing owner or group'''
1632 sd_attr = "nTSecurityDescriptor"
1633 sd_val = ndr_pack(sd)
1634 sd_flags = security.SECINFO_OWNER | security.SECINFO_GROUP
1636 if not self.confirm_all('Fix missing owner or group in %s on %s?' % (sd_attr, dn), 'fix_ntsecuritydescriptor_owner_group'):
1637 self.report('Not fixing missing owner or group %s on %s\n' % (sd_attr, dn))
1640 nmsg = ldb.Message()
1642 nmsg[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
1644 # By setting the session_info to admin_session_info and
1645 # setting the security.SECINFO_OWNER | security.SECINFO_GROUP
1646 # flags we cause the descriptor module to set the correct
1647 # owner and group on the SD, replacing the None/NULL values
1648 # for owner_sid and group_sid currently present.
1650 # The admin_session_info matches that used in provision, and
1651 # is the best guess we can make for an existing object that
1652 # hasn't had something specifically set.
1654 # This is important for the dns related naming contexts.
1655 self.samdb.set_session_info(self.admin_session_info)
1656 if self.do_modify(nmsg, ["sd_flags:1:%d" % sd_flags],
1657 "Failed to fix metadata for attribute %s" % sd_attr):
1658 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
1659 self.samdb.set_session_info(self.system_session_info)
1662 def has_replmetadata_zero_invocationid(self, dn, repl_meta_data):
1663 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1664 str(repl_meta_data))
1668 # Search for a zero invocationID
1669 if o.originating_invocation_id != misc.GUID("00000000-0000-0000-0000-000000000000"):
1673 self.report('''ERROR: on replPropertyMetaData of %s, the instanceType on attribute 0x%08x,
1674 version %d changed at %s is 00000000-0000-0000-0000-000000000000,
1675 but should be non-zero. Proposed fix is to set to our invocationID (%s).'''
1676 % (dn, o.attid, o.version,
1677 time.ctime(samba.nttime2unix(o.originating_change_time)),
1678 self.samdb.get_invocation_id()))
1683 def err_replmetadata_zero_invocationid(self, dn, attr, repl_meta_data):
1684 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1685 str(repl_meta_data))
1687 now = samba.unix2nttime(int(time.time()))
1690 # Search for a zero invocationID
1691 if o.originating_invocation_id != misc.GUID("00000000-0000-0000-0000-000000000000"):
1695 seq = self.samdb.sequence_number(ldb.SEQ_NEXT)
1696 o.version = o.version + 1
1697 o.originating_change_time = now
1698 o.originating_invocation_id = misc.GUID(self.samdb.get_invocation_id())
1699 o.originating_usn = seq
1703 replBlob = ndr_pack(repl)
1707 if not self.confirm_all('Fix %s on %s by setting originating_invocation_id on some elements to our invocationID %s?'
1708 % (attr, dn, self.samdb.get_invocation_id()), 'fix_replmetadata_zero_invocationid'):
1709 self.report('Not fixing zero originating_invocation_id in %s on %s\n' % (attr, dn))
1712 nmsg = ldb.Message()
1714 nmsg[attr] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, attr)
1715 if self.do_modify(nmsg, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA,
1716 "local_oid:1.3.6.1.4.1.7165.4.3.14:0"],
1717 "Failed to fix attribute %s" % attr):
1718 self.report("Fixed attribute '%s' of '%s'\n" % (attr, dn))
1721 def err_replmetadata_unknown_attid(self, dn, attr, repl_meta_data):
1722 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1723 str(repl_meta_data))
1726 # Search for an invalid attid
1728 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1730 self.report('ERROR: attributeID 0X%0X is not known in our schema, not fixing %s on %s\n' % (o.attid, attr, dn))
1734 def err_replmetadata_incorrect_attid(self, dn, attr, repl_meta_data, wrong_attids):
1735 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1736 str(repl_meta_data))
1740 remove_attid = set()
1743 in_schema_nc = dn.is_child_of(self.schema_dn)
1746 # Sort the array, except for the last element. This strange
1747 # construction, creating a new list, due to bugs in samba's
1748 # array handling in IDL generated objects.
1749 ctr.array = sorted(ctr.array[:], key=lambda o: o.attid)
1750 # Now walk it in reverse, so we see the low (and so incorrect,
1751 # the correct values are above 0x80000000) values first and
1752 # remove the 'second' value we see.
1753 for o in reversed(ctr.array):
1754 print "%s: 0x%08x" % (dn, o.attid)
1755 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1756 if att.lower() in set_att:
1757 self.report('ERROR: duplicate attributeID values for %s in %s on %s\n' % (att, attr, dn))
1758 if not self.confirm_all('Fix %s on %s by removing the duplicate value 0x%08x for %s (keeping 0x%08x)?'
1759 % (attr, dn, o.attid, att, hash_att[att].attid),
1760 'fix_replmetadata_duplicate_attid'):
1761 self.report('Not fixing duplicate value 0x%08x for %s in %s on %s\n'
1762 % (o.attid, att, attr, dn))
1765 remove_attid.add(o.attid)
1766 # We want to set the metadata for the most recent
1767 # update to have been applied locally, that is the metadata
1768 # matching the (eg string) value in the attribute
1769 if o.local_usn > hash_att[att].local_usn:
1770 # This is always what we would have sent over DRS,
1771 # because the DRS server will have sent the
1772 # msDS-IntID, but with the values from both
1773 # attribute entries.
1774 hash_att[att].version = o.version
1775 hash_att[att].originating_change_time = o.originating_change_time
1776 hash_att[att].originating_invocation_id = o.originating_invocation_id
1777 hash_att[att].originating_usn = o.originating_usn
1778 hash_att[att].local_usn = o.local_usn
1780 # Do not re-add the value to the set or overwrite the hash value
1784 set_att.add(att.lower())
1786 # Generate a real list we can sort on properly
1787 new_list = [o for o in ctr.array if o.attid not in remove_attid]
1789 if (len(wrong_attids) > 0):
1791 if o.attid in wrong_attids:
1792 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1793 correct_attid = self.samdb_schema.get_attid_from_lDAPDisplayName(att, is_schema_nc=in_schema_nc)
1794 self.report('ERROR: incorrect attributeID values in %s on %s\n' % (attr, dn))
1795 if not self.confirm_all('Fix %s on %s by replacing incorrect value 0x%08x for %s (new 0x%08x)?'
1796 % (attr, dn, o.attid, att, hash_att[att].attid), 'fix_replmetadata_wrong_attid'):
1797 self.report('Not fixing incorrect value 0x%08x with 0x%08x for %s in %s on %s\n'
1798 % (o.attid, correct_attid, att, attr, dn))
1801 o.attid = correct_attid
1803 # Sort the array, (we changed the value so must re-sort)
1804 new_list[:] = sorted(new_list[:], key=lambda o: o.attid)
1806 # If we did not already need to fix it, then ask about sorting
1808 self.report('ERROR: unsorted attributeID values in %s on %s\n' % (attr, dn))
1809 if not self.confirm_all('Fix %s on %s by sorting the attribute list?'
1810 % (attr, dn), 'fix_replmetadata_unsorted_attid'):
1811 self.report('Not fixing %s on %s\n' % (attr, dn))
1814 # The actual sort done is done at the top of the function
1816 ctr.count = len(new_list)
1817 ctr.array = new_list
1818 replBlob = ndr_pack(repl)
1820 nmsg = ldb.Message()
1822 nmsg[attr] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, attr)
1823 if self.do_modify(nmsg, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA,
1824 "local_oid:1.3.6.1.4.1.7165.4.3.14:0",
1825 "local_oid:1.3.6.1.4.1.7165.4.3.25:0"],
1826 "Failed to fix attribute %s" % attr):
1827 self.report("Fixed attribute '%s' of '%s'\n" % (attr, dn))
1830 def is_deleted_deleted_objects(self, obj):
1832 if "description" not in obj:
1833 self.report("ERROR: description not present on Deleted Objects container %s" % obj.dn)
1835 if "showInAdvancedViewOnly" not in obj or obj['showInAdvancedViewOnly'][0].upper() == 'FALSE':
1836 self.report("ERROR: showInAdvancedViewOnly not present on Deleted Objects container %s" % obj.dn)
1838 if "objectCategory" not in obj:
1839 self.report("ERROR: objectCategory not present on Deleted Objects container %s" % obj.dn)
1841 if "isCriticalSystemObject" not in obj or obj['isCriticalSystemObject'][0].upper() == 'FALSE':
1842 self.report("ERROR: isCriticalSystemObject not present on Deleted Objects container %s" % obj.dn)
1844 if "isRecycled" in obj:
1845 self.report("ERROR: isRecycled present on Deleted Objects container %s" % obj.dn)
1847 if "isDeleted" in obj and obj['isDeleted'][0].upper() == 'FALSE':
1848 self.report("ERROR: isDeleted not set on Deleted Objects container %s" % obj.dn)
1850 if "objectClass" not in obj or (len(obj['objectClass']) != 2 or
1851 obj['objectClass'][0] != 'top' or
1852 obj['objectClass'][1] != 'container'):
1853 self.report("ERROR: objectClass incorrectly set on Deleted Objects container %s" % obj.dn)
1855 if "systemFlags" not in obj or obj['systemFlags'][0] != '-1946157056':
1856 self.report("ERROR: systemFlags incorrectly set on Deleted Objects container %s" % obj.dn)
1860 def err_deleted_deleted_objects(self, obj):
1861 nmsg = ldb.Message()
1862 nmsg.dn = dn = obj.dn
1864 if "description" not in obj:
1865 nmsg["description"] = ldb.MessageElement("Container for deleted objects", ldb.FLAG_MOD_REPLACE, "description")
1866 if "showInAdvancedViewOnly" not in obj:
1867 nmsg["showInAdvancedViewOnly"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "showInAdvancedViewOnly")
1868 if "objectCategory" not in obj:
1869 nmsg["objectCategory"] = ldb.MessageElement("CN=Container,%s" % self.schema_dn, ldb.FLAG_MOD_REPLACE, "objectCategory")
1870 if "isCriticalSystemObject" not in obj:
1871 nmsg["isCriticalSystemObject"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isCriticalSystemObject")
1872 if "isRecycled" in obj:
1873 nmsg["isRecycled"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_DELETE, "isRecycled")
1875 nmsg["isDeleted"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isDeleted")
1876 nmsg["systemFlags"] = ldb.MessageElement("-1946157056", ldb.FLAG_MOD_REPLACE, "systemFlags")
1877 nmsg["objectClass"] = ldb.MessageElement(["top", "container"], ldb.FLAG_MOD_REPLACE, "objectClass")
1879 if not self.confirm_all('Fix Deleted Objects container %s by restoring default attributes?'
1880 % (dn), 'fix_deleted_deleted_objects'):
1881 self.report('Not fixing missing/incorrect attributes on %s\n' % (dn))
1884 if self.do_modify(nmsg, ["relax:0"],
1885 "Failed to fix Deleted Objects container %s" % dn):
1886 self.report("Fixed Deleted Objects container '%s'\n" % (dn))
1888 def err_replica_locations(self, obj, cross_ref, attr):
1889 nmsg = ldb.Message()
1891 target = self.samdb.get_dsServiceName()
1893 if self.samdb.am_rodc():
1894 self.report('Not fixing %s for the RODC' % (attr, obj.dn))
1897 if not self.confirm_all('Add yourself to the replica locations for %s?'
1898 % (obj.dn), 'fix_replica_locations'):
1899 self.report('Not fixing missing/incorrect attributes on %s\n' % (obj.dn))
1902 nmsg[attr] = ldb.MessageElement(target, ldb.FLAG_MOD_ADD, attr)
1903 if self.do_modify(nmsg, [], "Failed to add %s for %s" % (attr, obj.dn)):
1904 self.report("Fixed %s for %s" % (attr, obj.dn))
1906 def is_fsmo_role(self, dn):
1907 if dn == self.samdb.domain_dn:
1909 if dn == self.infrastructure_dn:
1911 if dn == self.naming_dn:
1913 if dn == self.schema_dn:
1915 if dn == self.rid_dn:
1920 def calculate_instancetype(self, dn):
1922 nc_root = self.samdb.get_nc_root(dn)
1924 instancetype |= dsdb.INSTANCE_TYPE_IS_NC_HEAD
1926 self.samdb.search(base=dn.parent(), scope=ldb.SCOPE_BASE, attrs=[], controls=["show_recycled:1"])
1927 except ldb.LdbError, (enum, estr):
1928 if enum != ldb.ERR_NO_SUCH_OBJECT:
1931 instancetype |= dsdb.INSTANCE_TYPE_NC_ABOVE
1933 if self.write_ncs is not None and str(nc_root) in self.write_ncs:
1934 instancetype |= dsdb.INSTANCE_TYPE_WRITE
1938 def get_wellknown_sd(self, dn):
1939 for [sd_dn, descriptor_fn] in self.wellknown_sds:
1941 domain_sid = security.dom_sid(self.samdb.get_domain_sid())
1942 return ndr_unpack(security.descriptor,
1943 descriptor_fn(domain_sid,
1944 name_map=self.name_map))
1948 def check_object(self, dn, attrs=['*']):
1949 '''check one object'''
1951 self.report("Checking object %s" % dn)
1953 # If we modify the pass-by-reference attrs variable, then we get a
1954 # replPropertyMetadata for every object that we check.
1956 if "dn" in map(str.lower, attrs):
1957 attrs.append("name")
1958 if "distinguishedname" in map(str.lower, attrs):
1959 attrs.append("name")
1960 if str(dn.get_rdn_name()).lower() in map(str.lower, attrs):
1961 attrs.append("name")
1962 if 'name' in map(str.lower, attrs):
1963 attrs.append(dn.get_rdn_name())
1964 attrs.append("isDeleted")
1965 attrs.append("systemFlags")
1966 need_replPropertyMetaData = False
1968 need_replPropertyMetaData = True
1971 linkID, _ = self.get_attr_linkID_and_reverse_name(a)
1976 need_replPropertyMetaData = True
1978 if need_replPropertyMetaData:
1979 attrs.append("replPropertyMetaData")
1980 attrs.append("objectGUID")
1984 sd_flags |= security.SECINFO_OWNER
1985 sd_flags |= security.SECINFO_GROUP
1986 sd_flags |= security.SECINFO_DACL
1987 sd_flags |= security.SECINFO_SACL
1989 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE,
1994 "sd_flags:1:%d" % sd_flags,
1995 "reveal_internals:0",
1998 except ldb.LdbError, (enum, estr):
1999 if enum == ldb.ERR_NO_SUCH_OBJECT:
2000 if self.in_transaction:
2001 self.report("ERROR: Object %s disappeared during check" % dn)
2006 self.report("ERROR: Object %s failed to load during check" % dn)
2010 set_attrs_from_md = set()
2011 set_attrs_seen = set()
2012 got_repl_property_meta_data = False
2013 got_objectclass = False
2015 nc_dn = self.samdb.get_nc_root(obj.dn)
2017 deleted_objects_dn = self.samdb.get_wellknown_dn(nc_dn,
2018 samba.dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER)
2020 # We have no deleted objects DN for schema, and we check for this above for the other
2022 deleted_objects_dn = None
2025 object_rdn_attr = None
2026 object_rdn_val = None
2031 for attrname in obj:
2032 if attrname == 'dn' or attrname == "distinguishedName":
2035 if str(attrname).lower() == 'objectclass':
2036 got_objectclass = True
2038 if str(attrname).lower() == "name":
2039 if len(obj[attrname]) != 1:
2041 self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" %
2042 (len(obj[attrname]), attrname, str(obj.dn)))
2044 name_val = obj[attrname][0]
2046 if str(attrname).lower() == str(obj.dn.get_rdn_name()).lower():
2047 object_rdn_attr = attrname
2048 if len(obj[attrname]) != 1:
2050 self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" %
2051 (len(obj[attrname]), attrname, str(obj.dn)))
2053 object_rdn_val = obj[attrname][0]
2055 if str(attrname).lower() == 'isdeleted':
2056 if obj[attrname][0] != "FALSE":
2059 if str(attrname).lower() == 'systemflags':
2060 systemFlags = int(obj[attrname][0])
2062 if str(attrname).lower() == 'replpropertymetadata':
2063 if self.has_replmetadata_zero_invocationid(dn, obj[attrname]):
2065 self.err_replmetadata_zero_invocationid(dn, attrname, obj[attrname])
2066 # We don't continue, as we may also have other fixes for this attribute
2067 # based on what other attributes we see.
2070 (set_attrs_from_md, list_attid_from_md, wrong_attids) \
2071 = self.process_metadata(dn, obj[attrname])
2074 self.err_replmetadata_unknown_attid(dn, attrname, obj[attrname])
2077 if len(set_attrs_from_md) < len(list_attid_from_md) \
2078 or len(wrong_attids) > 0 \
2079 or sorted(list_attid_from_md) != list_attid_from_md:
2081 self.err_replmetadata_incorrect_attid(dn, attrname, obj[attrname], wrong_attids)
2084 # Here we check that the first attid is 0
2086 if list_attid_from_md[0] != 0:
2088 self.report("ERROR: Not fixing incorrect inital attributeID in '%s' on '%s', it should be objectClass" %
2089 (attrname, str(dn)))
2091 got_repl_property_meta_data = True
2094 if str(attrname).lower() == 'ntsecuritydescriptor':
2095 (sd, sd_broken) = self.process_sd(dn, obj)
2096 if sd_broken is not None:
2097 self.err_wrong_sd(dn, sd, sd_broken)
2101 if sd.owner_sid is None or sd.group_sid is None:
2102 self.err_missing_sd_owner(dn, sd)
2106 if self.reset_well_known_acls:
2108 well_known_sd = self.get_wellknown_sd(dn)
2112 current_sd = ndr_unpack(security.descriptor,
2113 str(obj[attrname][0]))
2115 diff = get_diff_sds(well_known_sd, current_sd, security.dom_sid(self.samdb.get_domain_sid()))
2117 self.err_wrong_default_sd(dn, well_known_sd, current_sd, diff)
2122 if str(attrname).lower() == 'objectclass':
2123 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, obj[attrname])
2124 # Do not consider the attribute incorrect if:
2125 # - The sorted (alphabetically) list is the same, inclding case
2126 # - The first and last elements are the same
2128 # This avoids triggering an error due to
2129 # non-determinism in the sort routine in (at least)
2130 # 4.3 and earlier, and the fact that any AUX classes
2131 # in these attributes are also not sorted when
2132 # imported from Windows (they are just in the reverse
2133 # order of last set)
2134 if sorted(normalised) != sorted(obj[attrname]) \
2135 or normalised[0] != obj[attrname][0] \
2136 or normalised[-1] != obj[attrname][-1]:
2137 self.err_normalise_mismatch_replace(dn, attrname, list(obj[attrname]))
2141 if str(attrname).lower() == 'userparameters':
2142 if len(obj[attrname][0]) == 1 and obj[attrname][0][0] == '\x20':
2144 self.err_short_userParameters(obj, attrname, obj[attrname])
2147 elif obj[attrname][0][:16] == '\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00':
2148 # This is the correct, normal prefix
2151 elif obj[attrname][0][:20] == 'IAAgACAAIAAgACAAIAAg':
2152 # this is the typical prefix from a windows migration
2154 self.err_base64_userParameters(obj, attrname, obj[attrname])
2157 elif obj[attrname][0][1] != '\x00' and obj[attrname][0][3] != '\x00' and obj[attrname][0][5] != '\x00' and obj[attrname][0][7] != '\x00' and obj[attrname][0][9] != '\x00':
2158 # This is a prefix that is not in UTF-16 format for the space or munged dialback prefix
2160 self.err_utf8_userParameters(obj, attrname, obj[attrname])
2163 elif len(obj[attrname][0]) % 2 != 0:
2164 # This is a value that isn't even in length
2166 self.err_odd_userParameters(obj, attrname, obj[attrname])
2169 elif obj[attrname][0][1] == '\x00' and obj[attrname][0][2] == '\x00' and obj[attrname][0][3] == '\x00' and obj[attrname][0][4] != '\x00' and obj[attrname][0][5] == '\x00':
2170 # This is a prefix that would happen if a SAMR-written value was replicated from a Samba 4.1 server to a working server
2172 self.err_doubled_userParameters(obj, attrname, obj[attrname])
2175 if attrname.lower() == 'attributeid' or attrname.lower() == 'governsid':
2176 if obj[attrname][0] in self.attribute_or_class_ids:
2178 self.report('Error: %s %s on %s already exists as an attributeId or governsId'
2179 % (attrname, obj.dn, obj[attrname][0]))
2181 self.attribute_or_class_ids.add(obj[attrname][0])
2183 # check for empty attributes
2184 for val in obj[attrname]:
2186 self.err_empty_attribute(dn, attrname)
2190 # get the syntax oid for the attribute, so we can can have
2191 # special handling for some specific attribute types
2193 syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname)
2194 except Exception, msg:
2195 self.err_unknown_attribute(obj, attrname)
2199 linkID, reverse_link_name = self.get_attr_linkID_and_reverse_name(attrname)
2201 flag = self.samdb_schema.get_systemFlags_from_lDAPDisplayName(attrname)
2202 if (not flag & dsdb.DS_FLAG_ATTR_NOT_REPLICATED
2203 and not flag & dsdb.DS_FLAG_ATTR_IS_CONSTRUCTED
2205 set_attrs_seen.add(str(attrname).lower())
2207 if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_OR_NAME,
2208 dsdb.DSDB_SYNTAX_STRING_DN, ldb.SYNTAX_DN ]:
2209 # it's some form of DN, do specialised checking on those
2210 error_count += self.check_dn(obj, attrname, syntax_oid)
2214 # check for incorrectly normalised attributes
2215 for val in obj[attrname]:
2216 values.add(str(val))
2218 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, [val])
2219 if len(normalised) != 1 or normalised[0] != val:
2220 self.err_normalise_mismatch(dn, attrname, obj[attrname])
2224 if len(obj[attrname]) != len(values):
2225 self.err_duplicate_values(dn, attrname, obj[attrname], list(values))
2229 if str(attrname).lower() == "instancetype":
2230 calculated_instancetype = self.calculate_instancetype(dn)
2231 if len(obj["instanceType"]) != 1 or obj["instanceType"][0] != str(calculated_instancetype):
2233 self.err_wrong_instancetype(obj, calculated_instancetype)
2235 if not got_objectclass and ("*" in attrs or "objectclass" in map(str.lower, attrs)):
2237 self.err_missing_objectclass(dn)
2239 if ("*" in attrs or "name" in map(str.lower, attrs)):
2240 if name_val is None:
2242 self.report("ERROR: Not fixing missing 'name' on '%s'" % (str(obj.dn)))
2243 if object_rdn_attr is None:
2245 self.report("ERROR: Not fixing missing '%s' on '%s'" % (obj.dn.get_rdn_name(), str(obj.dn)))
2247 if name_val is not None:
2250 if not (systemFlags & samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE):
2251 parent_dn = deleted_objects_dn
2252 if parent_dn is None:
2253 parent_dn = obj.dn.parent()
2254 expected_dn = ldb.Dn(self.samdb, "RDN=RDN,%s" % (parent_dn))
2255 expected_dn.set_component(0, obj.dn.get_rdn_name(), name_val)
2257 if obj.dn == deleted_objects_dn:
2258 expected_dn = obj.dn
2260 if expected_dn != obj.dn:
2262 self.err_wrong_dn(obj, expected_dn, object_rdn_attr, object_rdn_val, name_val)
2263 elif obj.dn.get_rdn_value() != object_rdn_val:
2265 self.report("ERROR: Not fixing %s=%r on '%s'" % (object_rdn_attr, object_rdn_val, str(obj.dn)))
2268 if got_repl_property_meta_data:
2269 if obj.dn == deleted_objects_dn:
2270 isDeletedAttId = 131120
2271 # It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
2273 expectedTimeDo = 2650466015990000000
2274 originating = self.get_originating_time(obj["replPropertyMetaData"], isDeletedAttId)
2275 if originating != expectedTimeDo:
2276 if self.confirm_all("Fix isDeleted originating_change_time on '%s'" % str(dn), 'fix_time_metadata'):
2277 nmsg = ldb.Message()
2279 nmsg["isDeleted"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isDeleted")
2281 self.samdb.modify(nmsg, controls=["provision:0"])
2284 self.report("Not fixing isDeleted originating_change_time on '%s'" % str(dn))
2286 for att in set_attrs_seen.difference(set_attrs_from_md):
2288 self.report("On object %s" % dn)
2291 self.report("ERROR: Attribute %s not present in replication metadata" % att)
2292 if not self.confirm_all("Fix missing replPropertyMetaData element '%s'" % att, 'fix_all_metadata'):
2293 self.report("Not fixing missing replPropertyMetaData element '%s'" % att)
2295 self.fix_metadata(obj, att)
2297 if self.is_fsmo_role(dn):
2298 if "fSMORoleOwner" not in obj and ("*" in attrs or "fsmoroleowner" in map(str.lower, attrs)):
2299 self.err_no_fsmoRoleOwner(obj)
2303 if dn != self.samdb.get_root_basedn() and str(dn.parent()) not in self.dn_set:
2304 res = self.samdb.search(base=dn.parent(), scope=ldb.SCOPE_BASE,
2305 controls=["show_recycled:1", "show_deleted:1"])
2306 except ldb.LdbError, (enum, estr):
2307 if enum == ldb.ERR_NO_SUCH_OBJECT:
2308 self.err_missing_parent(obj)
2313 if dn in self.deleted_objects_containers and '*' in attrs:
2314 if self.is_deleted_deleted_objects(obj):
2315 self.err_deleted_deleted_objects(obj)
2318 for (dns_part, msg) in self.dns_partitions:
2319 if dn == dns_part and 'repsFrom' in obj:
2320 location = "msDS-NC-Replica-Locations"
2321 if self.samdb.am_rodc():
2322 location = "msDS-NC-RO-Replica-Locations"
2324 if location not in msg:
2325 # There are no replica locations!
2326 self.err_replica_locations(obj, msg.dn, location)
2331 for loc in msg[location]:
2332 if loc == self.samdb.get_dsServiceName():
2335 # This DC is not in the replica locations
2336 self.err_replica_locations(obj, msg.dn, location)
2339 if dn == self.server_ref_dn:
2340 # Check we have a valid RID Set
2341 if "*" in attrs or "rIDSetReferences" in attrs:
2342 if "rIDSetReferences" not in obj:
2343 # NO RID SET reference
2344 # We are RID master, allocate it.
2347 if self.is_rid_master:
2348 # Allocate a RID Set
2349 if self.confirm_all('Allocate the missing RID set for RID master?',
2350 'fix_missing_rid_set_master'):
2352 # We don't have auto-transaction logic on
2353 # extended operations, so we have to do it
2356 self.samdb.transaction_start()
2359 self.samdb.create_own_rid_set()
2362 self.samdb.transaction_cancel()
2365 self.samdb.transaction_commit()
2368 elif not self.samdb.am_rodc():
2369 self.report("No RID Set found for this server: %s, and we are not the RID Master (so can not self-allocate)" % dn)
2372 # Check some details of our own RID Set
2373 if dn == self.rid_set_dn:
2374 res = self.samdb.search(base=self.rid_set_dn, scope=ldb.SCOPE_BASE,
2375 attrs=["rIDAllocationPool",
2376 "rIDPreviousAllocationPool",
2379 if "rIDAllocationPool" not in res[0]:
2380 self.report("No rIDAllocationPool found in %s" % dn)
2383 next_pool = int(res[0]["rIDAllocationPool"][0])
2385 high = (0xFFFFFFFF00000000 & next_pool) >> 32
2386 low = 0x00000000FFFFFFFF & next_pool
2389 self.report("Invalid RID set %d-%s, %d > %d!" % (low, high, low, high))
2392 if "rIDNextRID" in res[0]:
2393 next_free_rid = int(res[0]["rIDNextRID"][0])
2397 if next_free_rid == 0:
2402 # Check the remainder of this pool for conflicts. If
2403 # ridalloc_allocate_rid() moves to a new pool, this
2404 # will be above high, so we will stop.
2405 while next_free_rid <= high:
2406 sid = "%s-%d" % (self.samdb.get_domain_sid(), next_free_rid)
2408 res = self.samdb.search(base="<SID=%s>" % sid, scope=ldb.SCOPE_BASE,
2410 except ldb.LdbError, (enum, estr):
2411 if enum != ldb.ERR_NO_SUCH_OBJECT:
2415 self.report("SID %s for %s conflicts with our current RID set in %s" % (sid, res[0].dn, dn))
2418 if self.confirm_all('Fix conflict between SID %s and RID pool in %s by allocating a new RID?'
2420 'fix_sid_rid_set_conflict'):
2421 self.samdb.transaction_start()
2423 # This will burn RIDs, which will move
2424 # past the conflict. We then check again
2425 # to see if the new RID conflicts, until
2426 # the end of the current pool. We don't
2427 # look at the next pool to avoid burning
2428 # all RIDs in one go in some strange
2432 allocated_rid = self.samdb.allocate_rid()
2433 if allocated_rid >= next_free_rid:
2434 next_free_rid = allocated_rid + 1
2437 self.samdb.transaction_cancel()
2440 self.samdb.transaction_commit()
2449 ################################################################
2450 # check special @ROOTDSE attributes
2451 def check_rootdse(self):
2452 '''check the @ROOTDSE special object'''
2453 dn = ldb.Dn(self.samdb, '@ROOTDSE')
2455 self.report("Checking object %s" % dn)
2456 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE)
2458 self.report("Object %s disappeared during check" % dn)
2463 # check that the dsServiceName is in GUID form
2464 if not 'dsServiceName' in obj:
2465 self.report('ERROR: dsServiceName missing in @ROOTDSE')
2466 return error_count+1
2468 if not obj['dsServiceName'][0].startswith('<GUID='):
2469 self.report('ERROR: dsServiceName not in GUID form in @ROOTDSE')
2471 if not self.confirm('Change dsServiceName to GUID form?'):
2473 res = self.samdb.search(base=ldb.Dn(self.samdb, obj['dsServiceName'][0]),
2474 scope=ldb.SCOPE_BASE, attrs=['objectGUID'])
2475 guid_str = str(ndr_unpack(misc.GUID, res[0]['objectGUID'][0]))
2478 m['dsServiceName'] = ldb.MessageElement("<GUID=%s>" % guid_str,
2479 ldb.FLAG_MOD_REPLACE, 'dsServiceName')
2480 if self.do_modify(m, [], "Failed to change dsServiceName to GUID form", validate=False):
2481 self.report("Changed dsServiceName to GUID form")
2485 ###############################################
2486 # re-index the database
2487 def reindex_database(self):
2488 '''re-index the whole database'''
2490 m.dn = ldb.Dn(self.samdb, "@ATTRIBUTES")
2491 m['add'] = ldb.MessageElement('NONE', ldb.FLAG_MOD_ADD, 'force_reindex')
2492 m['delete'] = ldb.MessageElement('NONE', ldb.FLAG_MOD_DELETE, 'force_reindex')
2493 return self.do_modify(m, [], 're-indexed database', validate=False)
2495 ###############################################
2497 def reset_modules(self):
2498 '''reset @MODULES to that needed for current sam.ldb (to read a very old database)'''
2500 m.dn = ldb.Dn(self.samdb, "@MODULES")
2501 m['@LIST'] = ldb.MessageElement('samba_dsdb', ldb.FLAG_MOD_REPLACE, '@LIST')
2502 return self.do_modify(m, [], 'reset @MODULES on database', validate=False)