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/>.
20 from __future__ import print_function
24 from base64 import b64decode
25 from samba import dsdb
26 from samba import common
27 from samba.dcerpc import misc
28 from samba.dcerpc import drsuapi
29 from samba.ndr import ndr_unpack, ndr_pack
30 from samba.dcerpc import drsblobs
31 from samba.common import dsdb_Dn
32 from samba.dcerpc import security
33 from samba.descriptor import get_wellknown_sds, get_diff_sds
34 from samba.auth import system_session, admin_session
35 from samba.netcmd import CommandError
36 from samba.netcmd.fsmo import get_fsmo_roleowner
39 class dbcheck(object):
40 """check a SAM database for errors"""
42 def __init__(self, samdb, samdb_schema=None, verbose=False, fix=False,
43 yes=False, quiet=False, in_transaction=False,
44 reset_well_known_acls=False):
46 self.dict_oid_name = None
47 self.samdb_schema = (samdb_schema or samdb)
48 self.verbose = verbose
52 self.remove_all_unknown_attributes = False
53 self.remove_all_empty_attributes = False
54 self.fix_all_normalisation = False
55 self.fix_all_duplicates = False
56 self.fix_all_DN_GUIDs = False
57 self.fix_all_binary_dn = False
58 self.remove_implausible_deleted_DN_links = False
59 self.remove_plausible_deleted_DN_links = False
60 self.fix_all_string_dn_component_mismatch = False
61 self.fix_all_GUID_dn_component_mismatch = False
62 self.fix_all_SID_dn_component_mismatch = False
63 self.fix_all_old_dn_string_component_mismatch = False
64 self.fix_all_metadata = False
65 self.fix_time_metadata = False
66 self.fix_undead_linked_attributes = False
67 self.fix_all_missing_backlinks = False
68 self.fix_all_orphaned_backlinks = False
69 self.fix_all_missing_forward_links = False
70 self.duplicate_link_cache = dict()
71 self.recover_all_forward_links = False
72 self.fix_rmd_flags = False
73 self.fix_ntsecuritydescriptor = False
74 self.fix_ntsecuritydescriptor_owner_group = False
75 self.seize_fsmo_role = False
76 self.move_to_lost_and_found = False
77 self.fix_instancetype = False
78 self.fix_replmetadata_zero_invocationid = False
79 self.fix_replmetadata_duplicate_attid = False
80 self.fix_replmetadata_wrong_attid = False
81 self.fix_replmetadata_unsorted_attid = False
82 self.fix_deleted_deleted_objects = False
83 self.fix_incorrect_deleted_objects = False
85 self.fix_base64_userparameters = False
86 self.fix_utf8_userparameters = False
87 self.fix_doubled_userparameters = False
88 self.fix_sid_rid_set_conflict = False
89 self.reset_well_known_acls = reset_well_known_acls
90 self.reset_all_well_known_acls = False
91 self.in_transaction = in_transaction
92 self.infrastructure_dn = ldb.Dn(samdb, "CN=Infrastructure," + samdb.domain_dn())
93 self.naming_dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
94 self.schema_dn = samdb.get_schema_basedn()
95 self.rid_dn = ldb.Dn(samdb, "CN=RID Manager$,CN=System," + samdb.domain_dn())
96 self.ntds_dsa = ldb.Dn(samdb, samdb.get_dsServiceName())
97 self.class_schemaIDGUID = {}
98 self.wellknown_sds = get_wellknown_sds(self.samdb)
99 self.fix_all_missing_objectclass = False
100 self.fix_missing_deleted_objects = False
101 self.fix_replica_locations = False
102 self.fix_missing_rid_set_master = False
105 self.link_id_cache = {}
108 res = samdb.search(base="CN=DnsAdmins,CN=Users,%s" % samdb.domain_dn(), scope=ldb.SCOPE_BASE,
110 dnsadmins_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0])
111 self.name_map['DnsAdmins'] = str(dnsadmins_sid)
112 except ldb.LdbError as e5:
113 (enum, estr) = e5.args
114 if enum != ldb.ERR_NO_SUCH_OBJECT:
118 self.system_session_info = system_session()
119 self.admin_session_info = admin_session(None, samdb.get_domain_sid())
121 res = self.samdb.search(base=self.ntds_dsa, scope=ldb.SCOPE_BASE, attrs=['msDS-hasMasterNCs', 'hasMasterNCs'])
122 if "msDS-hasMasterNCs" in res[0]:
123 self.write_ncs = res[0]["msDS-hasMasterNCs"]
125 # If the Forest Level is less than 2003 then there is no
126 # msDS-hasMasterNCs, so we fall back to hasMasterNCs
127 # no need to merge as all the NCs that are in hasMasterNCs must
128 # also be in msDS-hasMasterNCs (but not the opposite)
129 if "hasMasterNCs" in res[0]:
130 self.write_ncs = res[0]["hasMasterNCs"]
132 self.write_ncs = None
134 res = self.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=['namingContexts'])
135 self.deleted_objects_containers = []
136 self.ncs_lacking_deleted_containers = []
137 self.dns_partitions = []
139 self.ncs = res[0]["namingContexts"]
147 dn = self.samdb.get_wellknown_dn(ldb.Dn(self.samdb, nc),
148 dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER)
149 self.deleted_objects_containers.append(dn)
151 self.ncs_lacking_deleted_containers.append(ldb.Dn(self.samdb, nc))
153 domaindns_zone = 'DC=DomainDnsZones,%s' % self.samdb.get_default_basedn()
154 forestdns_zone = 'DC=ForestDnsZones,%s' % self.samdb.get_root_basedn()
155 domain = self.samdb.search(scope=ldb.SCOPE_ONELEVEL,
156 attrs=["msDS-NC-Replica-Locations", "msDS-NC-RO-Replica-Locations"],
157 base=self.samdb.get_partitions_dn(),
158 expression="(&(objectClass=crossRef)(ncName=%s))" % domaindns_zone)
160 self.dns_partitions.append((ldb.Dn(self.samdb, forestdns_zone), domain[0]))
162 forest = self.samdb.search(scope=ldb.SCOPE_ONELEVEL,
163 attrs=["msDS-NC-Replica-Locations", "msDS-NC-RO-Replica-Locations"],
164 base=self.samdb.get_partitions_dn(),
165 expression="(&(objectClass=crossRef)(ncName=%s))" % forestdns_zone)
167 self.dns_partitions.append((ldb.Dn(self.samdb, domaindns_zone), forest[0]))
169 fsmo_dn = ldb.Dn(self.samdb, "CN=RID Manager$,CN=System," + self.samdb.domain_dn())
170 rid_master = get_fsmo_roleowner(self.samdb, fsmo_dn, "rid")
171 if ldb.Dn(self.samdb, self.samdb.get_dsServiceName()) == rid_master:
172 self.is_rid_master = True
174 self.is_rid_master = False
176 # To get your rid set
178 res = self.samdb.search(base=ldb.Dn(self.samdb, self.samdb.get_serverName()),
179 scope=ldb.SCOPE_BASE, attrs=["serverReference"])
180 # 2. Get server reference
181 self.server_ref_dn = ldb.Dn(self.samdb, res[0]['serverReference'][0])
184 res = self.samdb.search(base=self.server_ref_dn,
185 scope=ldb.SCOPE_BASE, attrs=['rIDSetReferences'])
186 if "rIDSetReferences" in res[0]:
187 self.rid_set_dn = ldb.Dn(self.samdb, res[0]['rIDSetReferences'][0])
189 self.rid_set_dn = None
191 self.compatibleFeatures = []
192 self.requiredFeatures = []
195 res = self.samdb.search(scope=ldb.SCOPE_BASE,
197 attrs=["compatibleFeatures",
199 if "compatibleFeatures" in res[0]:
200 self.compatibleFeatures = res[0]["compatibleFeatures"]
201 if "requiredFeatures" in res[0]:
202 self.requiredFeatures = res[0]["requiredFeatures"]
203 except ldb.LdbError as e6:
204 (enum, estr) = e6.args
205 if enum != ldb.ERR_NO_SUCH_OBJECT:
209 def check_database(self, DN=None, scope=ldb.SCOPE_SUBTREE, controls=[], attrs=['*']):
210 '''perform a database check, returning the number of errors found'''
211 res = self.samdb.search(base=DN, scope=scope, attrs=['dn'], controls=controls)
212 self.report('Checking %u objects' % len(res))
215 error_count += self.check_deleted_objects_containers()
217 self.attribute_or_class_ids = set()
220 self.dn_set.add(str(object.dn))
221 error_count += self.check_object(object.dn, attrs=attrs)
224 error_count += self.check_rootdse()
226 if error_count != 0 and not self.fix:
227 self.report("Please use --fix to fix these errors")
229 self.report('Checked %u objects (%u errors)' % (len(res), error_count))
232 def check_deleted_objects_containers(self):
233 """This function only fixes conflicts on the Deleted Objects
234 containers, not the attributes"""
236 for nc in self.ncs_lacking_deleted_containers:
237 if nc == self.schema_dn:
240 self.report("ERROR: NC %s lacks a reference to a Deleted Objects container" % nc)
241 if not self.confirm_all('Fix missing Deleted Objects container for %s?' % (nc), 'fix_missing_deleted_objects'):
244 dn = ldb.Dn(self.samdb, "CN=Deleted Objects")
249 # If something already exists here, add a conflict
250 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[],
251 controls=["show_deleted:1", "extended_dn:1:1",
252 "show_recycled:1", "reveal_internals:0"])
254 guid = res[0].dn.get_extended_component("GUID")
255 conflict_dn = ldb.Dn(self.samdb,
256 "CN=Deleted Objects\\0ACNF:%s" % str(misc.GUID(guid)))
257 conflict_dn.add_base(nc)
259 except ldb.LdbError as e2:
260 (enum, estr) = e2.args
261 if enum == ldb.ERR_NO_SUCH_OBJECT:
264 self.report("Couldn't check for conflicting Deleted Objects container: %s" % estr)
267 if conflict_dn is not None:
269 self.samdb.rename(dn, conflict_dn, ["show_deleted:1", "relax:0", "show_recycled:1"])
270 except ldb.LdbError as e1:
271 (enum, estr) = e1.args
272 self.report("Couldn't move old Deleted Objects placeholder: %s to %s: %s" % (dn, conflict_dn, estr))
275 # Refresh wellKnownObjects links
276 res = self.samdb.search(base=nc, scope=ldb.SCOPE_BASE,
277 attrs=['wellKnownObjects'],
278 controls=["show_deleted:1", "extended_dn:0",
279 "show_recycled:1", "reveal_internals:0"])
281 self.report("wellKnownObjects was not found for NC %s" % nc)
284 # Prevent duplicate deleted objects containers just in case
285 wko = res[0]["wellKnownObjects"]
287 proposed_objectguid = None
289 dsdb_dn = dsdb_Dn(self.samdb, o.decode('utf8'), dsdb.DSDB_SYNTAX_BINARY_DN)
290 if self.is_deleted_objects_dn(dsdb_dn):
291 self.report("wellKnownObjects had duplicate Deleted Objects value %s" % o)
292 # We really want to put this back in the same spot
293 # as the original one, so that on replication we
294 # merge, rather than conflict.
295 proposed_objectguid = dsdb_dn.dn.get_extended_component("GUID")
298 if proposed_objectguid is not None:
299 guid_suffix = "\nobjectGUID: %s" % str(misc.GUID(proposed_objectguid))
301 wko_prefix = "B:32:%s" % dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER
302 listwko.append('%s:%s' % (wko_prefix, dn))
305 # Insert a brand new Deleted Objects container
306 self.samdb.add_ldif("""dn: %s
308 objectClass: container
309 description: Container for deleted objects
311 isCriticalSystemObject: TRUE
312 showInAdvancedViewOnly: TRUE
313 systemFlags: -1946157056%s""" % (dn, guid_suffix),
314 controls=["relax:0", "provision:0"])
316 delta = ldb.Message()
317 delta.dn = ldb.Dn(self.samdb, str(res[0]["dn"]))
318 delta["wellKnownObjects"] = ldb.MessageElement(listwko,
319 ldb.FLAG_MOD_REPLACE,
322 # Insert the link to the brand new container
323 if self.do_modify(delta, ["relax:0"],
324 "NC %s lacks Deleted Objects WKGUID" % nc,
326 self.report("Added %s well known guid link" % dn)
328 self.deleted_objects_containers.append(dn)
332 def report(self, msg):
333 '''print a message unless quiet is set'''
337 def confirm(self, msg, allow_all=False, forced=False):
338 '''confirm a change'''
345 return common.confirm(msg, forced=forced, allow_all=allow_all)
347 ################################################################
348 # a local confirm function with support for 'all'
349 def confirm_all(self, msg, all_attr):
350 '''confirm a change with support for "all" '''
353 if getattr(self, all_attr) == 'NONE':
355 if getattr(self, all_attr) == 'ALL':
361 c = common.confirm(msg, forced=forced, allow_all=True)
363 setattr(self, all_attr, 'ALL')
366 setattr(self, all_attr, 'NONE')
370 def do_delete(self, dn, controls, msg):
371 '''delete dn with optional verbose output'''
373 self.report("delete DN %s" % dn)
375 controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
376 self.samdb.delete(dn, controls=controls)
377 except Exception as err:
378 if self.in_transaction:
379 raise CommandError("%s : %s" % (msg, err))
380 self.report("%s : %s" % (msg, err))
384 def do_modify(self, m, controls, msg, validate=True):
385 '''perform a modify with optional verbose output'''
387 self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY))
389 controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
390 self.samdb.modify(m, controls=controls, validate=validate)
391 except Exception as err:
392 if self.in_transaction:
393 raise CommandError("%s : %s" % (msg, err))
394 self.report("%s : %s" % (msg, err))
398 def do_rename(self, from_dn, to_rdn, to_base, controls, msg):
399 '''perform a modify with optional verbose output'''
401 self.report("""dn: %s
405 newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
407 to_dn = to_rdn + to_base
408 controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
409 self.samdb.rename(from_dn, to_dn, controls=controls)
410 except Exception as err:
411 if self.in_transaction:
412 raise CommandError("%s : %s" % (msg, err))
413 self.report("%s : %s" % (msg, err))
417 def get_attr_linkID_and_reverse_name(self, attrname):
418 if attrname in self.link_id_cache:
419 return self.link_id_cache[attrname]
420 linkID = self.samdb_schema.get_linkId_from_lDAPDisplayName(attrname)
422 revname = self.samdb_schema.get_backlink_from_lDAPDisplayName(attrname)
425 self.link_id_cache[attrname] = (linkID, revname)
426 return linkID, revname
428 def err_empty_attribute(self, dn, attrname):
429 '''fix empty attributes'''
430 self.report("ERROR: Empty attribute %s in %s" % (attrname, dn))
431 if not self.confirm_all('Remove empty attribute %s from %s?' % (attrname, dn), 'remove_all_empty_attributes'):
432 self.report("Not fixing empty attribute %s" % attrname)
437 m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname)
438 if self.do_modify(m, ["relax:0", "show_recycled:1"],
439 "Failed to remove empty attribute %s" % attrname, validate=False):
440 self.report("Removed empty attribute %s" % attrname)
442 def err_normalise_mismatch(self, dn, attrname, values):
443 '''fix attribute normalisation errors'''
444 self.report("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn))
447 normalised = self.samdb.dsdb_normalise_attributes(
448 self.samdb_schema, attrname, [val])
449 if len(normalised) != 1:
450 self.report("Unable to normalise value '%s'" % val)
451 mod_list.append((val, ''))
452 elif (normalised[0] != val):
453 self.report("value '%s' should be '%s'" % (val, normalised[0]))
454 mod_list.append((val, normalised[0]))
455 if not self.confirm_all('Fix normalisation for %s from %s?' % (attrname, dn), 'fix_all_normalisation'):
456 self.report("Not fixing attribute %s" % attrname)
461 for i in range(0, len(mod_list)):
462 (val, nval) = mod_list[i]
463 m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
465 m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD,
468 if self.do_modify(m, ["relax:0", "show_recycled:1"],
469 "Failed to normalise attribute %s" % attrname,
471 self.report("Normalised attribute %s" % attrname)
473 def err_normalise_mismatch_replace(self, dn, attrname, values):
474 '''fix attribute normalisation errors'''
475 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, values)
476 self.report("ERROR: Normalisation error for attribute '%s' in '%s'" % (attrname, dn))
477 self.report("Values/Order of values do/does not match: %s/%s!" % (values, list(normalised)))
478 if list(normalised) == values:
480 if not self.confirm_all("Fix normalisation for '%s' from '%s'?" % (attrname, dn), 'fix_all_normalisation'):
481 self.report("Not fixing attribute '%s'" % attrname)
486 m[attrname] = ldb.MessageElement(normalised, ldb.FLAG_MOD_REPLACE, attrname)
488 if self.do_modify(m, ["relax:0", "show_recycled:1"],
489 "Failed to normalise attribute %s" % attrname,
491 self.report("Normalised attribute %s" % attrname)
493 def err_duplicate_values(self, dn, attrname, dup_values, values):
494 '''fix attribute normalisation errors'''
495 self.report("ERROR: Duplicate values for attribute '%s' in '%s'" % (attrname, dn))
496 self.report("Values contain a duplicate: [%s]/[%s]!" % (','.join(dup_values), ','.join(values)))
497 if not self.confirm_all("Fix duplicates for '%s' from '%s'?" % (attrname, dn), 'fix_all_duplicates'):
498 self.report("Not fixing attribute '%s'" % attrname)
503 m[attrname] = ldb.MessageElement(values, ldb.FLAG_MOD_REPLACE, attrname)
505 if self.do_modify(m, ["relax:0", "show_recycled:1"],
506 "Failed to remove duplicate value on attribute %s" % attrname,
508 self.report("Removed duplicate value on attribute %s" % attrname)
510 def is_deleted_objects_dn(self, dsdb_dn):
511 '''see if a dsdb_Dn is the special Deleted Objects DN'''
512 return dsdb_dn.prefix == "B:32:%s:" % dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER
514 def err_missing_objectclass(self, dn):
515 """handle object without objectclass"""
516 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)))
517 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'):
518 self.report("Not deleting object with missing objectclass '%s'" % dn)
520 if self.do_delete(dn, ["relax:0"],
521 "Failed to remove DN %s" % dn):
522 self.report("Removed DN %s" % dn)
524 def err_deleted_dn(self, dn, attrname, val, dsdb_dn, correct_dn, remove_plausible=False):
525 """handle a DN pointing to a deleted object"""
526 if not remove_plausible:
527 self.report("ERROR: target DN is deleted for %s in object %s - %s" % (attrname, dn, val))
528 self.report("Target GUID points at deleted DN %r" % str(correct_dn))
529 if not self.confirm_all('Remove DN link?', 'remove_implausible_deleted_DN_links'):
530 self.report("Not removing")
533 self.report("WARNING: target DN is deleted for %s in object %s - %s" % (attrname, dn, val))
534 self.report("Target GUID points at deleted DN %r" % str(correct_dn))
535 if not self.confirm_all('Remove stale DN link?', 'remove_plausible_deleted_DN_links'):
536 self.report("Not removing")
541 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
542 if self.do_modify(m, ["show_recycled:1",
543 "local_oid:%s:0" % dsdb.DSDB_CONTROL_REPLMD_VANISH_LINKS],
544 "Failed to remove deleted DN attribute %s" % attrname):
545 self.report("Removed deleted DN on attribute %s" % attrname)
547 def err_missing_target_dn_or_GUID(self, dn, attrname, val, dsdb_dn):
548 """handle a missing target DN (if specified, GUID form can't be found,
549 and otherwise DN string form can't be found)"""
550 # check if its a backlink
551 linkID, _ = self.get_attr_linkID_and_reverse_name(attrname)
552 if (linkID & 1 == 0) and str(dsdb_dn).find('\\0ADEL') == -1:
554 linkID, reverse_link_name \
555 = self.get_attr_linkID_and_reverse_name(attrname)
556 if reverse_link_name is not None:
557 self.report("WARNING: no target object found for GUID "
558 "component for one-way forward link "
560 "%s - %s" % (attrname, dn, val))
561 self.report("Not removing dangling forward link")
564 nc_root = self.samdb.get_nc_root(dn)
565 target_nc_root = self.samdb.get_nc_root(dsdb_dn.dn)
566 if nc_root != target_nc_root:
567 # We don't bump the error count as Samba produces these
568 # in normal operation
569 self.report("WARNING: no target object found for GUID "
570 "component for cross-partition link "
572 "%s - %s" % (attrname, dn, val))
573 self.report("Not removing dangling one-way "
574 "cross-partition link "
575 "(we might be mid-replication)")
578 # Due to our link handling one-way links pointing to
579 # missing objects are plausible.
581 # We don't bump the error count as Samba produces these
582 # in normal operation
583 self.report("WARNING: no target object found for GUID "
584 "component for DN value %s in object "
585 "%s - %s" % (attrname, dn, val))
586 self.err_deleted_dn(dn, attrname, val,
587 dsdb_dn, dsdb_dn, True)
590 # We bump the error count here, as we should have deleted this
591 self.report("ERROR: no target object found for GUID "
592 "component for link %s in object "
593 "%s - %s" % (attrname, dn, val))
594 self.err_deleted_dn(dn, attrname, val, dsdb_dn, dsdb_dn, False)
597 def err_missing_dn_GUID_component(self, dn, attrname, val, dsdb_dn, errstr):
598 """handle a missing GUID extended DN component"""
599 self.report("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val))
600 controls=["extended_dn:1:1", "show_recycled:1"]
602 res = self.samdb.search(base=str(dsdb_dn.dn), scope=ldb.SCOPE_BASE,
603 attrs=[], controls=controls)
604 except ldb.LdbError as e7:
605 (enum, estr) = e7.args
606 self.report("unable to find object for DN %s - (%s)" % (dsdb_dn.dn, estr))
607 if enum != ldb.ERR_NO_SUCH_OBJECT:
609 self.err_missing_target_dn_or_GUID(dn, attrname, val, dsdb_dn)
612 self.report("unable to find object for DN %s" % dsdb_dn.dn)
613 self.err_missing_target_dn_or_GUID(dn, attrname, val, dsdb_dn)
615 dsdb_dn.dn = res[0].dn
617 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn), 'fix_all_DN_GUIDs'):
618 self.report("Not fixing %s" % errstr)
622 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
623 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
625 if self.do_modify(m, ["show_recycled:1"],
626 "Failed to fix %s on attribute %s" % (errstr, attrname)):
627 self.report("Fixed %s on attribute %s" % (errstr, attrname))
629 def err_incorrect_binary_dn(self, dn, attrname, val, dsdb_dn, errstr):
630 """handle an incorrect binary DN component"""
631 self.report("ERROR: %s binary component for %s in object %s - %s" % (errstr, attrname, dn, val))
632 controls=["extended_dn:1:1", "show_recycled:1"]
634 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn), 'fix_all_binary_dn'):
635 self.report("Not fixing %s" % errstr)
639 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
640 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
642 if self.do_modify(m, ["show_recycled:1"],
643 "Failed to fix %s on attribute %s" % (errstr, attrname)):
644 self.report("Fixed %s on attribute %s" % (errstr, attrname))
646 def err_dn_string_component_old(self, dn, attrname, val, dsdb_dn, correct_dn):
647 """handle a DN string being incorrect"""
648 self.report("NOTE: old (due to rename or delete) DN string component for %s in object %s - %s" % (attrname, dn, val))
649 dsdb_dn.dn = correct_dn
651 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn),
652 'fix_all_old_dn_string_component_mismatch'):
653 self.report("Not fixing old string component")
657 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
658 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
659 if self.do_modify(m, ["show_recycled:1"],
660 "Failed to fix old DN string on attribute %s" % (attrname)):
661 self.report("Fixed old DN string on attribute %s" % (attrname))
663 def err_dn_component_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn, mismatch_type):
664 """handle a DN string being incorrect"""
665 self.report("ERROR: incorrect DN %s component for %s in object %s - %s" % (mismatch_type, attrname, dn, val))
666 dsdb_dn.dn = correct_dn
668 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn),
669 'fix_all_%s_dn_component_mismatch' % mismatch_type):
670 self.report("Not fixing %s component mismatch" % mismatch_type)
674 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
675 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
676 if self.do_modify(m, ["show_recycled:1"],
677 "Failed to fix incorrect DN %s on attribute %s" % (mismatch_type, attrname)):
678 self.report("Fixed incorrect DN %s on attribute %s" % (mismatch_type, attrname))
680 def err_unknown_attribute(self, obj, attrname):
681 '''handle an unknown attribute error'''
682 self.report("ERROR: unknown attribute '%s' in %s" % (attrname, obj.dn))
683 if not self.confirm_all('Remove unknown attribute %s' % attrname, 'remove_all_unknown_attributes'):
684 self.report("Not removing %s" % attrname)
688 m['old_value'] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, attrname)
689 if self.do_modify(m, ["relax:0", "show_recycled:1"],
690 "Failed to remove unknown attribute %s" % attrname):
691 self.report("Removed unknown attribute %s" % (attrname))
693 def err_undead_linked_attribute(self, obj, attrname, val):
694 '''handle a link that should not be there on a deleted object'''
695 self.report("ERROR: linked attribute '%s' to '%s' is present on "
696 "deleted object %s" % (attrname, val, obj.dn))
697 if not self.confirm_all('Remove linked attribute %s' % attrname, 'fix_undead_linked_attributes'):
698 self.report("Not removing linked attribute %s" % attrname)
702 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
704 if self.do_modify(m, ["show_recycled:1", "show_deleted:1", "reveal_internals:0",
705 "local_oid:%s:0" % dsdb.DSDB_CONTROL_REPLMD_VANISH_LINKS],
706 "Failed to delete forward link %s" % attrname):
707 self.report("Fixed undead forward link %s" % (attrname))
709 def err_missing_backlink(self, obj, attrname, val, backlink_name, target_dn):
710 '''handle a missing backlink value'''
711 self.report("ERROR: missing backlink attribute '%s' in %s for link %s in %s" % (backlink_name, target_dn, attrname, obj.dn))
712 if not self.confirm_all('Fix missing backlink %s' % backlink_name, 'fix_all_missing_backlinks'):
713 self.report("Not fixing missing backlink %s" % backlink_name)
717 m['new_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_ADD, backlink_name)
718 if self.do_modify(m, ["show_recycled:1", "relax:0"],
719 "Failed to fix missing backlink %s" % backlink_name):
720 self.report("Fixed missing backlink %s" % (backlink_name))
722 def err_incorrect_rmd_flags(self, obj, attrname, revealed_dn):
723 '''handle a incorrect RMD_FLAGS value'''
724 rmd_flags = int(revealed_dn.dn.get_extended_component("RMD_FLAGS"))
725 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()))
726 if not self.confirm_all('Fix incorrect RMD_FLAGS %u' % rmd_flags, 'fix_rmd_flags'):
727 self.report("Not fixing incorrect RMD_FLAGS %u" % rmd_flags)
731 m['old_value'] = ldb.MessageElement(str(revealed_dn), ldb.FLAG_MOD_DELETE, attrname)
732 if self.do_modify(m, ["show_recycled:1", "reveal_internals:0", "show_deleted:0"],
733 "Failed to fix incorrect RMD_FLAGS %u" % rmd_flags):
734 self.report("Fixed incorrect RMD_FLAGS %u" % (rmd_flags))
736 def err_orphaned_backlink(self, obj_dn, backlink_attr, backlink_val,
737 target_dn, forward_attr, forward_syntax,
738 check_duplicates=True):
739 '''handle a orphaned backlink value'''
740 if check_duplicates is True and self.has_duplicate_links(target_dn, forward_attr, forward_syntax):
741 self.report("WARNING: Keep orphaned backlink attribute " + \
742 "'%s' in '%s' for link '%s' in '%s'" % (
743 backlink_attr, obj_dn, forward_attr, target_dn))
745 self.report("ERROR: orphaned backlink attribute '%s' in %s for link %s in %s" % (backlink_attr, obj_dn, forward_attr, target_dn))
746 if not self.confirm_all('Remove orphaned backlink %s' % backlink_attr, 'fix_all_orphaned_backlinks'):
747 self.report("Not removing orphaned backlink %s" % backlink_attr)
751 m['value'] = ldb.MessageElement(backlink_val, ldb.FLAG_MOD_DELETE, backlink_attr)
752 if self.do_modify(m, ["show_recycled:1", "relax:0"],
753 "Failed to fix orphaned backlink %s" % backlink_attr):
754 self.report("Fixed orphaned backlink %s" % (backlink_attr))
756 def err_recover_forward_links(self, obj, forward_attr, forward_vals):
757 '''handle a duplicate links value'''
759 self.report("RECHECK: 'Missing/Duplicate/Correct link' lines above for attribute '%s' in '%s'" % (forward_attr, obj.dn))
761 if not self.confirm_all("Commit fixes for (missing/duplicate) forward links in attribute '%s'" % forward_attr, 'recover_all_forward_links'):
762 self.report("Not fixing corrupted (missing/duplicate) forward links in attribute '%s' of '%s'" % (
763 forward_attr, obj.dn))
767 m['value'] = ldb.MessageElement(forward_vals, ldb.FLAG_MOD_REPLACE, forward_attr)
768 if self.do_modify(m, ["local_oid:1.3.6.1.4.1.7165.4.3.19.2:1"],
769 "Failed to fix duplicate links in attribute '%s'" % forward_attr):
770 self.report("Fixed duplicate links in attribute '%s'" % (forward_attr))
771 duplicate_cache_key = "%s:%s" % (str(obj.dn), forward_attr)
772 assert duplicate_cache_key in self.duplicate_link_cache
773 self.duplicate_link_cache[duplicate_cache_key] = False
775 def err_no_fsmoRoleOwner(self, obj):
776 '''handle a missing fSMORoleOwner'''
777 self.report("ERROR: fSMORoleOwner not found for role %s" % (obj.dn))
778 res = self.samdb.search("",
779 scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
781 serviceName = res[0]["dsServiceName"][0]
782 if not self.confirm_all('Sieze role %s onto current DC by adding fSMORoleOwner=%s' % (obj.dn, serviceName), 'seize_fsmo_role'):
783 self.report("Not Siezing role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName))
787 m['value'] = ldb.MessageElement(serviceName, ldb.FLAG_MOD_ADD, 'fSMORoleOwner')
788 if self.do_modify(m, [],
789 "Failed to sieze role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName)):
790 self.report("Siezed role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName))
792 def err_missing_parent(self, obj):
793 '''handle a missing parent'''
794 self.report("ERROR: parent object not found for %s" % (obj.dn))
795 if not self.confirm_all('Move object %s into LostAndFound?' % (obj.dn), 'move_to_lost_and_found'):
796 self.report('Not moving object %s into LostAndFound' % (obj.dn))
799 keep_transaction = False
800 self.samdb.transaction_start()
802 nc_root = self.samdb.get_nc_root(obj.dn);
803 lost_and_found = self.samdb.get_wellknown_dn(nc_root, dsdb.DS_GUID_LOSTANDFOUND_CONTAINER)
804 new_dn = ldb.Dn(self.samdb, str(obj.dn))
805 new_dn.remove_base_components(len(new_dn) - 1)
806 if self.do_rename(obj.dn, new_dn, lost_and_found, ["show_deleted:0", "relax:0"],
807 "Failed to rename object %s into lostAndFound at %s" % (obj.dn, new_dn + lost_and_found)):
808 self.report("Renamed object %s into lostAndFound at %s" % (obj.dn, new_dn + lost_and_found))
812 m['lastKnownParent'] = ldb.MessageElement(str(obj.dn.parent()), ldb.FLAG_MOD_REPLACE, 'lastKnownParent')
814 if self.do_modify(m, [],
815 "Failed to set lastKnownParent on lostAndFound object at %s" % (new_dn + lost_and_found)):
816 self.report("Set lastKnownParent on lostAndFound object at %s" % (new_dn + lost_and_found))
817 keep_transaction = True
819 self.samdb.transaction_cancel()
823 self.samdb.transaction_commit()
825 self.samdb.transaction_cancel()
827 def err_wrong_dn(self, obj, new_dn, rdn_attr, rdn_val, name_val):
828 '''handle a wrong dn'''
830 new_rdn = ldb.Dn(self.samdb, str(new_dn))
831 new_rdn.remove_base_components(len(new_rdn) - 1)
832 new_parent = new_dn.parent()
835 if rdn_val != name_val:
836 attributes += "%s=%r " % (rdn_attr, rdn_val)
837 attributes += "name=%r" % (name_val)
839 self.report("ERROR: wrong dn[%s] %s new_dn[%s]" % (obj.dn, attributes, new_dn))
840 if not self.confirm_all("Rename %s to %s?" % (obj.dn, new_dn), 'fix_dn'):
841 self.report("Not renaming %s to %s" % (obj.dn, new_dn))
844 if self.do_rename(obj.dn, new_rdn, new_parent, ["show_recycled:1", "relax:0"],
845 "Failed to rename object %s into %s" % (obj.dn, new_dn)):
846 self.report("Renamed %s into %s" % (obj.dn, new_dn))
848 def err_wrong_instancetype(self, obj, calculated_instancetype):
849 '''handle a wrong instanceType'''
850 self.report("ERROR: wrong instanceType %s on %s, should be %d" % (obj["instanceType"], obj.dn, calculated_instancetype))
851 if not self.confirm_all('Change instanceType from %s to %d on %s?' % (obj["instanceType"], calculated_instancetype, obj.dn), 'fix_instancetype'):
852 self.report('Not changing instanceType from %s to %d on %s' % (obj["instanceType"], calculated_instancetype, obj.dn))
857 m['value'] = ldb.MessageElement(str(calculated_instancetype), ldb.FLAG_MOD_REPLACE, 'instanceType')
858 if self.do_modify(m, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA],
859 "Failed to correct missing instanceType on %s by setting instanceType=%d" % (obj.dn, calculated_instancetype)):
860 self.report("Corrected instancetype on %s by setting instanceType=%d" % (obj.dn, calculated_instancetype))
862 def err_short_userParameters(self, obj, attrname, value):
863 # This is a truncated userParameters due to a pre 4.1 replication bug
864 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)))
866 def err_base64_userParameters(self, obj, attrname, value):
867 '''handle a wrong userParameters'''
868 self.report("ERROR: wrongly formatted userParameters %s on %s, should not be base64-encoded" % (value, obj.dn))
869 if not self.confirm_all('Convert userParameters from base64 encoding on %s?' % (obj.dn), 'fix_base64_userparameters'):
870 self.report('Not changing userParameters from base64 encoding on %s' % (obj.dn))
875 m['value'] = ldb.MessageElement(b64decode(obj[attrname][0]), ldb.FLAG_MOD_REPLACE, 'userParameters')
876 if self.do_modify(m, [],
877 "Failed to correct base64-encoded userParameters on %s by converting from base64" % (obj.dn)):
878 self.report("Corrected base64-encoded userParameters on %s by converting from base64" % (obj.dn))
880 def err_utf8_userParameters(self, obj, attrname, value):
881 '''handle a wrong userParameters'''
882 self.report("ERROR: wrongly formatted userParameters on %s, should not be psudo-UTF8 encoded" % (obj.dn))
883 if not self.confirm_all('Convert userParameters from UTF8 encoding on %s?' % (obj.dn), 'fix_utf8_userparameters'):
884 self.report('Not changing userParameters from UTF8 encoding on %s' % (obj.dn))
889 m['value'] = ldb.MessageElement(obj[attrname][0].decode('utf8').encode('utf-16-le'),
890 ldb.FLAG_MOD_REPLACE, 'userParameters')
891 if self.do_modify(m, [],
892 "Failed to correct psudo-UTF8 encoded userParameters on %s by converting from UTF8" % (obj.dn)):
893 self.report("Corrected psudo-UTF8 encoded userParameters on %s by converting from UTF8" % (obj.dn))
895 def err_doubled_userParameters(self, obj, attrname, value):
896 '''handle a wrong userParameters'''
897 self.report("ERROR: wrongly formatted userParameters on %s, should not be double UTF16 encoded" % (obj.dn))
898 if not self.confirm_all('Convert userParameters from doubled UTF-16 encoding on %s?' % (obj.dn), 'fix_doubled_userparameters'):
899 self.report('Not changing userParameters from doubled UTF-16 encoding on %s' % (obj.dn))
904 m['value'] = ldb.MessageElement(obj[attrname][0].decode('utf-16-le').decode('utf-16-le').encode('utf-16-le'),
905 ldb.FLAG_MOD_REPLACE, 'userParameters')
906 if self.do_modify(m, [],
907 "Failed to correct doubled-UTF16 encoded userParameters on %s by converting" % (obj.dn)):
908 self.report("Corrected doubled-UTF16 encoded userParameters on %s by converting" % (obj.dn))
910 def err_odd_userParameters(self, obj, attrname):
911 # This is a truncated userParameters due to a pre 4.1 replication bug
912 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)))
914 def find_revealed_link(self, dn, attrname, guid):
915 '''return a revealed link in an object'''
916 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[attrname],
917 controls=["show_deleted:0", "extended_dn:0", "reveal_internals:0"])
918 syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname)
919 for val in res[0][attrname]:
920 dsdb_dn = dsdb_Dn(self.samdb, val.decode('utf8'), syntax_oid)
921 guid2 = dsdb_dn.dn.get_extended_component("GUID")
926 def check_duplicate_links(self, obj, forward_attr, forward_syntax, forward_linkID, backlink_attr):
927 '''check a linked values for duplicate forward links'''
930 duplicate_dict = dict()
933 # Only forward links can have this problem
934 if forward_linkID & 1:
935 # If we got the reverse, skip it
936 return (error_count, duplicate_dict, unique_dict)
938 if backlink_attr is None:
939 return (error_count, duplicate_dict, unique_dict)
941 duplicate_cache_key = "%s:%s" % (str(obj.dn), forward_attr)
942 if duplicate_cache_key not in self.duplicate_link_cache:
943 self.duplicate_link_cache[duplicate_cache_key] = False
945 for val in obj[forward_attr]:
946 dsdb_dn = dsdb_Dn(self.samdb, val.decode('utf8'), forward_syntax)
948 # all DNs should have a GUID component
949 guid = dsdb_dn.dn.get_extended_component("GUID")
952 guidstr = str(misc.GUID(guid))
953 keystr = guidstr + dsdb_dn.prefix
954 if keystr not in unique_dict:
955 unique_dict[keystr] = dsdb_dn
958 if keystr not in duplicate_dict:
959 duplicate_dict[keystr] = dict()
960 duplicate_dict[keystr]["keep"] = None
961 duplicate_dict[keystr]["delete"] = list()
963 # Now check for the highest RMD_VERSION
964 v1 = int(unique_dict[keystr].dn.get_extended_component("RMD_VERSION"))
965 v2 = int(dsdb_dn.dn.get_extended_component("RMD_VERSION"))
967 duplicate_dict[keystr]["keep"] = unique_dict[keystr]
968 duplicate_dict[keystr]["delete"].append(dsdb_dn)
971 duplicate_dict[keystr]["keep"] = dsdb_dn
972 duplicate_dict[keystr]["delete"].append(unique_dict[keystr])
973 unique_dict[keystr] = dsdb_dn
975 # Fallback to the highest RMD_LOCAL_USN
976 u1 = int(unique_dict[keystr].dn.get_extended_component("RMD_LOCAL_USN"))
977 u2 = int(dsdb_dn.dn.get_extended_component("RMD_LOCAL_USN"))
979 duplicate_dict[keystr]["keep"] = unique_dict[keystr]
980 duplicate_dict[keystr]["delete"].append(dsdb_dn)
982 duplicate_dict[keystr]["keep"] = dsdb_dn
983 duplicate_dict[keystr]["delete"].append(unique_dict[keystr])
984 unique_dict[keystr] = dsdb_dn
987 self.duplicate_link_cache[duplicate_cache_key] = True
989 return (error_count, duplicate_dict, unique_dict)
991 def has_duplicate_links(self, dn, forward_attr, forward_syntax):
992 '''check a linked values for duplicate forward links'''
995 duplicate_cache_key = "%s:%s" % (str(dn), forward_attr)
996 if duplicate_cache_key in self.duplicate_link_cache:
997 return self.duplicate_link_cache[duplicate_cache_key]
999 forward_linkID, backlink_attr = self.get_attr_linkID_and_reverse_name(forward_attr)
1001 attrs = [forward_attr]
1002 controls = ["extended_dn:1:1", "reveal_internals:0"]
1004 # check its the right GUID
1006 res = self.samdb.search(base=str(dn), scope=ldb.SCOPE_BASE,
1007 attrs=attrs, controls=controls)
1008 except ldb.LdbError as e8:
1009 (enum, estr) = e8.args
1010 if enum != ldb.ERR_NO_SUCH_OBJECT:
1016 error_count, duplicate_dict, unique_dict = \
1017 self.check_duplicate_links(obj, forward_attr, forward_syntax, forward_linkID, backlink_attr)
1019 if duplicate_cache_key in self.duplicate_link_cache:
1020 return self.duplicate_link_cache[duplicate_cache_key]
1024 def find_missing_forward_links_from_backlinks(self, obj,
1028 forward_unique_dict):
1029 '''Find all backlinks linking to obj_guid_str not already in forward_unique_dict'''
1030 missing_forward_links = []
1033 if backlink_attr is None:
1034 return (missing_forward_links, error_count)
1036 if forward_syntax != ldb.SYNTAX_DN:
1037 self.report("Not checking for missing forward links for syntax: %s",
1039 return (missing_forward_links, error_count)
1041 if "sortedLinks" in self.compatibleFeatures:
1042 self.report("Not checking for missing forward links because the db " + \
1043 "has the sortedLinks feature")
1044 return (missing_forward_links, error_count)
1047 obj_guid = obj['objectGUID'][0]
1048 obj_guid_str = str(ndr_unpack(misc.GUID, obj_guid))
1049 filter = "(%s=<GUID=%s>)" % (backlink_attr, obj_guid_str)
1051 res = self.samdb.search(expression=filter,
1052 scope=ldb.SCOPE_SUBTREE, attrs=["objectGUID"],
1053 controls=["extended_dn:1:1",
1054 "search_options:1:2",
1055 "paged_results:1:1000"])
1056 except ldb.LdbError as e9:
1057 (enum, estr) = e9.args
1061 target_dn = dsdb_Dn(self.samdb, r.dn.extended_str(), forward_syntax)
1063 guid = target_dn.dn.get_extended_component("GUID")
1064 guidstr = str(misc.GUID(guid))
1065 if guidstr in forward_unique_dict:
1068 # A valid forward link looks like this:
1070 # <GUID=9f92d30a-fc23-11e4-a5f6-30be15454808>;
1071 # <RMD_ADDTIME=131607546230000000>;
1072 # <RMD_CHANGETIME=131607546230000000>;
1074 # <RMD_INVOCID=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d>;
1075 # <RMD_LOCAL_USN=3765>;
1076 # <RMD_ORIGINATING_USN=3765>;
1078 # <SID=S-1-5-21-4177067393-1453636373-93818738-1124>;
1079 # CN=unsorted-u8,CN=Users,DC=release-4-5-0-pre1,DC=samba,DC=corp
1081 # Note that versions older than Samba 4.8 create
1082 # links with RMD_VERSION=0.
1084 # Try to get the local_usn and time from objectClass
1085 # if possible and fallback to any other one.
1086 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1087 obj['replPropertyMetadata'][0])
1088 for o in repl.ctr.array:
1089 local_usn = o.local_usn
1090 t = o.originating_change_time
1091 if o.attid == drsuapi.DRSUAPI_ATTID_objectClass:
1094 # We use a magic invocationID for restoring missing
1095 # forward links to recover from bug #13228.
1096 # This should allow some more future magic to fix the
1099 # It also means it looses the conflict resolution
1100 # against almost every real invocation, if the
1101 # version is also 0.
1102 originating_invocid = misc.GUID("ffffffff-4700-4700-4700-000000b13228")
1108 rmd_invocid = originating_invocid
1109 rmd_originating_usn = originating_usn
1110 rmd_local_usn = local_usn
1113 target_dn.dn.set_extended_component("RMD_ADDTIME", str(rmd_addtime))
1114 target_dn.dn.set_extended_component("RMD_CHANGETIME", str(rmd_changetime))
1115 target_dn.dn.set_extended_component("RMD_FLAGS", str(rmd_flags))
1116 target_dn.dn.set_extended_component("RMD_INVOCID", ndr_pack(rmd_invocid))
1117 target_dn.dn.set_extended_component("RMD_ORIGINATING_USN", str(rmd_originating_usn))
1118 target_dn.dn.set_extended_component("RMD_LOCAL_USN", str(rmd_local_usn))
1119 target_dn.dn.set_extended_component("RMD_VERSION", str(rmd_version))
1122 missing_forward_links.append(target_dn)
1124 return (missing_forward_links, error_count)
1126 def check_dn(self, obj, attrname, syntax_oid):
1127 '''check a DN attribute for correctness'''
1129 obj_guid = obj['objectGUID'][0]
1131 linkID, reverse_link_name = self.get_attr_linkID_and_reverse_name(attrname)
1132 if reverse_link_name is not None:
1133 reverse_syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(reverse_link_name)
1135 reverse_syntax_oid = None
1137 error_count, duplicate_dict, unique_dict = \
1138 self.check_duplicate_links(obj, attrname, syntax_oid, linkID, reverse_link_name)
1140 if len(duplicate_dict) != 0:
1142 missing_forward_links, missing_error_count = \
1143 self.find_missing_forward_links_from_backlinks(obj,
1144 attrname, syntax_oid,
1147 error_count += missing_error_count
1149 forward_links = [dn for dn in unique_dict.values()]
1151 if missing_error_count != 0:
1152 self.report("ERROR: Missing and duplicate forward link values for attribute '%s' in '%s'" % (
1155 self.report("ERROR: Duplicate forward link values for attribute '%s' in '%s'" % (attrname, obj.dn))
1156 for m in missing_forward_links:
1157 self.report("Missing link '%s'" % (m))
1158 if not self.confirm_all("Schedule readding missing forward link for attribute %s" % attrname,
1159 'fix_all_missing_forward_links'):
1160 self.err_orphaned_backlink(m.dn, reverse_link_name,
1161 obj.dn.extended_str(), obj.dn,
1162 attrname, syntax_oid,
1163 check_duplicates=False)
1165 forward_links += [m]
1166 for keystr in duplicate_dict.keys():
1167 d = duplicate_dict[keystr]
1168 for dd in d["delete"]:
1169 self.report("Duplicate link '%s'" % dd)
1170 self.report("Correct link '%s'" % d["keep"])
1172 # We now construct the sorted dn values.
1173 # They're sorted by the objectGUID of the target
1174 # See dsdb_Dn.__cmp__()
1175 vals = [str(dn) for dn in sorted(forward_links)]
1176 self.err_recover_forward_links(obj, attrname, vals)
1177 # We should continue with the fixed values
1178 obj[attrname] = ldb.MessageElement(vals, 0, attrname)
1180 for val in obj[attrname]:
1181 dsdb_dn = dsdb_Dn(self.samdb, val.decode('utf8'), syntax_oid)
1183 # all DNs should have a GUID component
1184 guid = dsdb_dn.dn.get_extended_component("GUID")
1187 self.err_missing_dn_GUID_component(obj.dn, attrname, val, dsdb_dn,
1191 guidstr = str(misc.GUID(guid))
1192 attrs = ['isDeleted', 'replPropertyMetaData']
1194 if (str(attrname).lower() == 'msds-hasinstantiatedncs') and (obj.dn == self.ntds_dsa):
1195 fixing_msDS_HasInstantiatedNCs = True
1196 attrs.append("instanceType")
1198 fixing_msDS_HasInstantiatedNCs = False
1200 if reverse_link_name is not None:
1201 attrs.append(reverse_link_name)
1203 # check its the right GUID
1205 res = self.samdb.search(base="<GUID=%s>" % guidstr, scope=ldb.SCOPE_BASE,
1206 attrs=attrs, controls=["extended_dn:1:1", "show_recycled:1",
1207 "reveal_internals:0"
1209 except ldb.LdbError as e3:
1210 (enum, estr) = e3.args
1211 if enum != ldb.ERR_NO_SUCH_OBJECT:
1214 # We don't always want to
1215 error_count += self.err_missing_target_dn_or_GUID(obj.dn,
1221 if fixing_msDS_HasInstantiatedNCs:
1222 dsdb_dn.prefix = "B:8:%08X:" % int(res[0]['instanceType'][0])
1223 dsdb_dn.binary = "%08X" % int(res[0]['instanceType'][0])
1225 if str(dsdb_dn) != val:
1227 self.err_incorrect_binary_dn(obj.dn, attrname, val, dsdb_dn, "incorrect instanceType part of Binary DN")
1230 # now we have two cases - the source object might or might not be deleted
1231 is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper() == 'TRUE'
1232 target_is_deleted = 'isDeleted' in res[0] and res[0]['isDeleted'][0].upper() == 'TRUE'
1235 if is_deleted and not obj.dn in self.deleted_objects_containers and linkID:
1236 # A fully deleted object should not have any linked
1237 # attributes. (MS-ADTS 3.1.1.5.5.1.1 Tombstone
1238 # Requirements and 3.1.1.5.5.1.3 Recycled-Object
1240 self.err_undead_linked_attribute(obj, attrname, val)
1243 elif target_is_deleted and not self.is_deleted_objects_dn(dsdb_dn) and linkID:
1244 # the target DN is not allowed to be deleted, unless the target DN is the
1245 # special Deleted Objects container
1247 local_usn = dsdb_dn.dn.get_extended_component("RMD_LOCAL_USN")
1249 if 'replPropertyMetaData' in res[0]:
1250 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1251 str(res[0]['replPropertyMetadata']))
1253 for o in repl.ctr.array:
1254 if o.attid == drsuapi.DRSUAPI_ATTID_isDeleted:
1255 deleted_usn = o.local_usn
1256 if deleted_usn >= int(local_usn):
1257 # If the object was deleted after the link
1258 # was last modified then, clean it up here
1263 self.err_deleted_dn(obj.dn, attrname,
1264 val, dsdb_dn, res[0].dn, True)
1267 self.err_deleted_dn(obj.dn, attrname, val, dsdb_dn, res[0].dn, False)
1270 # We should not check for incorrect
1271 # components on deleted links, as these are allowed to
1272 # go stale (we just need the GUID, not the name)
1273 rmd_blob = dsdb_dn.dn.get_extended_component("RMD_FLAGS")
1275 if rmd_blob is not None:
1276 rmd_flags = int(rmd_blob)
1278 # assert the DN matches in string form, where a reverse
1279 # link exists, otherwise (below) offer to fix it as a non-error.
1280 # The string form is essentially only kept for forensics,
1281 # as we always re-resolve by GUID in normal operations.
1282 if not rmd_flags & 1 and reverse_link_name is not None:
1283 if str(res[0].dn) != str(dsdb_dn.dn):
1285 self.err_dn_component_target_mismatch(obj.dn, attrname, val, dsdb_dn,
1286 res[0].dn, "string")
1289 if res[0].dn.get_extended_component("GUID") != dsdb_dn.dn.get_extended_component("GUID"):
1291 self.err_dn_component_target_mismatch(obj.dn, attrname, val, dsdb_dn,
1295 if res[0].dn.get_extended_component("SID") != dsdb_dn.dn.get_extended_component("SID"):
1297 self.err_dn_component_target_mismatch(obj.dn, attrname, val, dsdb_dn,
1301 # Now we have checked the GUID and SID, offer to fix old
1302 # DN strings as a non-error (for forward links with no
1303 # backlink). Samba does not maintain this string
1304 # otherwise, so we don't increment error_count.
1305 if reverse_link_name is None:
1306 if str(res[0].dn) != str(dsdb_dn.dn):
1307 self.err_dn_string_component_old(obj.dn, attrname, val, dsdb_dn,
1311 # check the reverse_link is correct if there should be one
1313 if reverse_link_name in res[0]:
1314 for v in res[0][reverse_link_name]:
1315 v_dn = dsdb_Dn(self.samdb, v.decode('utf8'))
1316 v_guid = v_dn.dn.get_extended_component("GUID")
1317 v_blob = v_dn.dn.get_extended_component("RMD_FLAGS")
1319 if v_blob is not None:
1320 v_rmd_flags = int(v_blob)
1323 if v_guid == obj_guid:
1326 if match_count != 1:
1327 if syntax_oid == dsdb.DSDB_SYNTAX_BINARY_DN or reverse_syntax_oid == dsdb.DSDB_SYNTAX_BINARY_DN:
1329 # Forward binary multi-valued linked attribute
1331 for w in obj[attrname]:
1332 w_guid = dsdb_Dn(self.samdb, w.decode('utf8')).dn.get_extended_component("GUID")
1336 if match_count == forward_count:
1339 for v in obj[attrname]:
1340 v_dn = dsdb_Dn(self.samdb, v.decode('utf8'))
1341 v_guid = v_dn.dn.get_extended_component("GUID")
1342 v_blob = v_dn.dn.get_extended_component("RMD_FLAGS")
1344 if v_blob is not None:
1345 v_rmd_flags = int(v_blob)
1351 if match_count == expected_count:
1354 diff_count = expected_count - match_count
1357 # If there's a backward link on binary multi-valued linked attribute,
1358 # let the check on the forward link remedy the value.
1359 # UNLESS, there is no forward link detected.
1360 if match_count == 0:
1362 self.err_orphaned_backlink(obj.dn, attrname,
1367 # Only warn here and let the forward link logic fix it.
1368 self.report("WARNING: Link (back) mismatch for '%s' (%d) on '%s' to '%s' (%d) on '%s'" % (
1369 attrname, expected_count, str(obj.dn),
1370 reverse_link_name, match_count, str(dsdb_dn.dn)))
1373 assert not target_is_deleted
1375 self.report("ERROR: Link (forward) mismatch for '%s' (%d) on '%s' to '%s' (%d) on '%s'" % (
1376 attrname, expected_count, str(obj.dn),
1377 reverse_link_name, match_count, str(dsdb_dn.dn)))
1379 # Loop until the difference between the forward and
1380 # the backward links is resolved.
1381 while diff_count != 0:
1384 if match_count > 0 or diff_count > 1:
1385 # TODO no method to fix these right now
1386 self.report("ERROR: Can't fix missing "
1387 "multi-valued backlinks on %s" % str(dsdb_dn.dn))
1389 self.err_missing_backlink(obj, attrname,
1390 obj.dn.extended_str(),
1395 self.err_orphaned_backlink(res[0].dn, reverse_link_name,
1396 obj.dn.extended_str(), obj.dn,
1397 attrname, syntax_oid)
1404 def get_originating_time(self, val, attid):
1405 '''Read metadata properties and return the originating time for
1406 a given attributeId.
1408 :return: the originating time or 0 if not found
1411 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
1414 for o in repl.ctr.array:
1415 if o.attid == attid:
1416 return o.originating_change_time
1420 def process_metadata(self, dn, val):
1421 '''Read metadata properties and list attributes in it.
1422 raises KeyError if the attid is unknown.'''
1425 wrong_attids = set()
1427 in_schema_nc = dn.is_child_of(self.schema_dn)
1429 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
1432 for o in repl.ctr.array:
1433 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1434 set_att.add(att.lower())
1435 list_attid.append(o.attid)
1436 correct_attid = self.samdb_schema.get_attid_from_lDAPDisplayName(att,
1437 is_schema_nc=in_schema_nc)
1438 if correct_attid != o.attid:
1439 wrong_attids.add(o.attid)
1441 return (set_att, list_attid, wrong_attids)
1444 def fix_metadata(self, obj, attr):
1445 '''re-write replPropertyMetaData elements for a single attribute for a
1446 object. This is used to fix missing replPropertyMetaData elements'''
1447 guid_str = str(ndr_unpack(misc.GUID, obj['objectGUID'][0]))
1448 dn = ldb.Dn(self.samdb, "<GUID=%s>" % guid_str)
1449 res = self.samdb.search(base = dn, scope=ldb.SCOPE_BASE, attrs = [attr],
1450 controls = ["search_options:1:2",
1453 nmsg = ldb.Message()
1455 nmsg[attr] = ldb.MessageElement(msg[attr], ldb.FLAG_MOD_REPLACE, attr)
1456 if self.do_modify(nmsg, ["relax:0", "provision:0", "show_recycled:1"],
1457 "Failed to fix metadata for attribute %s" % attr):
1458 self.report("Fixed metadata for attribute %s" % attr)
1460 def ace_get_effective_inherited_type(self, ace):
1461 if ace.flags & security.SEC_ACE_FLAG_INHERIT_ONLY:
1465 if ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
1467 elif ace.type == security.SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
1469 elif ace.type == security.SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
1471 elif ace.type == security.SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
1477 if not ace.object.flags & security.SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT:
1480 return str(ace.object.inherited_type)
1482 def lookup_class_schemaIDGUID(self, cls):
1483 if cls in self.class_schemaIDGUID:
1484 return self.class_schemaIDGUID[cls]
1486 flt = "(&(ldapDisplayName=%s)(objectClass=classSchema))" % cls
1487 res = self.samdb.search(base=self.schema_dn,
1489 attrs=["schemaIDGUID"])
1490 t = str(ndr_unpack(misc.GUID, res[0]["schemaIDGUID"][0]))
1492 self.class_schemaIDGUID[cls] = t
1495 def process_sd(self, dn, obj):
1496 sd_attr = "nTSecurityDescriptor"
1497 sd_val = obj[sd_attr]
1499 sd = ndr_unpack(security.descriptor, str(sd_val))
1501 is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper() == 'TRUE'
1503 # we don't fix deleted objects
1506 sd_clean = security.descriptor()
1507 sd_clean.owner_sid = sd.owner_sid
1508 sd_clean.group_sid = sd.group_sid
1509 sd_clean.type = sd.type
1510 sd_clean.revision = sd.revision
1513 last_inherited_type = None
1516 if sd.sacl is not None:
1518 for i in range(0, len(aces)):
1521 if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE:
1522 sd_clean.sacl_add(ace)
1525 t = self.ace_get_effective_inherited_type(ace)
1529 if last_inherited_type is not None:
1530 if t != last_inherited_type:
1531 # if it inherited from more than
1532 # one type it's very likely to be broken
1534 # If not the recalculation will calculate
1539 last_inherited_type = t
1542 if sd.dacl is not None:
1544 for i in range(0, len(aces)):
1547 if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE:
1548 sd_clean.dacl_add(ace)
1551 t = self.ace_get_effective_inherited_type(ace)
1555 if last_inherited_type is not None:
1556 if t != last_inherited_type:
1557 # if it inherited from more than
1558 # one type it's very likely to be broken
1560 # If not the recalculation will calculate
1565 last_inherited_type = t
1568 return (sd_clean, sd)
1570 if last_inherited_type is None:
1576 cls = obj["objectClass"][-1]
1577 except KeyError as e:
1581 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE,
1582 attrs=["isDeleted", "objectClass"],
1583 controls=["show_recycled:1"])
1585 is_deleted = 'isDeleted' in o and o['isDeleted'][0].upper() == 'TRUE'
1587 # we don't fix deleted objects
1589 cls = o["objectClass"][-1]
1591 t = self.lookup_class_schemaIDGUID(cls)
1593 if t != last_inherited_type:
1595 return (sd_clean, sd)
1600 def err_wrong_sd(self, dn, sd, sd_broken):
1601 '''re-write the SD due to incorrect inherited ACEs'''
1602 sd_attr = "nTSecurityDescriptor"
1603 sd_val = ndr_pack(sd)
1604 sd_flags = security.SECINFO_DACL | security.SECINFO_SACL
1606 if not self.confirm_all('Fix %s on %s?' % (sd_attr, dn), 'fix_ntsecuritydescriptor'):
1607 self.report('Not fixing %s on %s\n' % (sd_attr, dn))
1610 nmsg = ldb.Message()
1612 nmsg[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
1613 if self.do_modify(nmsg, ["sd_flags:1:%d" % sd_flags],
1614 "Failed to fix attribute %s" % sd_attr):
1615 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
1617 def err_wrong_default_sd(self, dn, sd, sd_old, diff):
1618 '''re-write the SD due to not matching the default (optional mode for fixing an incorrect provision)'''
1619 sd_attr = "nTSecurityDescriptor"
1620 sd_val = ndr_pack(sd)
1621 sd_old_val = ndr_pack(sd_old)
1622 sd_flags = security.SECINFO_DACL | security.SECINFO_SACL
1623 if sd.owner_sid is not None:
1624 sd_flags |= security.SECINFO_OWNER
1625 if sd.group_sid is not None:
1626 sd_flags |= security.SECINFO_GROUP
1628 if not self.confirm_all('Reset %s on %s back to provision default?\n%s' % (sd_attr, dn, diff), 'reset_all_well_known_acls'):
1629 self.report('Not resetting %s on %s\n' % (sd_attr, dn))
1634 m[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
1635 if self.do_modify(m, ["sd_flags:1:%d" % sd_flags],
1636 "Failed to reset attribute %s" % sd_attr):
1637 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
1639 def err_missing_sd_owner(self, dn, sd):
1640 '''re-write the SD due to a missing owner or group'''
1641 sd_attr = "nTSecurityDescriptor"
1642 sd_val = ndr_pack(sd)
1643 sd_flags = security.SECINFO_OWNER | security.SECINFO_GROUP
1645 if not self.confirm_all('Fix missing owner or group in %s on %s?' % (sd_attr, dn), 'fix_ntsecuritydescriptor_owner_group'):
1646 self.report('Not fixing missing owner or group %s on %s\n' % (sd_attr, dn))
1649 nmsg = ldb.Message()
1651 nmsg[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
1653 # By setting the session_info to admin_session_info and
1654 # setting the security.SECINFO_OWNER | security.SECINFO_GROUP
1655 # flags we cause the descriptor module to set the correct
1656 # owner and group on the SD, replacing the None/NULL values
1657 # for owner_sid and group_sid currently present.
1659 # The admin_session_info matches that used in provision, and
1660 # is the best guess we can make for an existing object that
1661 # hasn't had something specifically set.
1663 # This is important for the dns related naming contexts.
1664 self.samdb.set_session_info(self.admin_session_info)
1665 if self.do_modify(nmsg, ["sd_flags:1:%d" % sd_flags],
1666 "Failed to fix metadata for attribute %s" % sd_attr):
1667 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
1668 self.samdb.set_session_info(self.system_session_info)
1671 def has_replmetadata_zero_invocationid(self, dn, repl_meta_data):
1672 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1673 str(repl_meta_data))
1677 # Search for a zero invocationID
1678 if o.originating_invocation_id != misc.GUID("00000000-0000-0000-0000-000000000000"):
1682 self.report('''ERROR: on replPropertyMetaData of %s, the instanceType on attribute 0x%08x,
1683 version %d changed at %s is 00000000-0000-0000-0000-000000000000,
1684 but should be non-zero. Proposed fix is to set to our invocationID (%s).'''
1685 % (dn, o.attid, o.version,
1686 time.ctime(samba.nttime2unix(o.originating_change_time)),
1687 self.samdb.get_invocation_id()))
1692 def err_replmetadata_zero_invocationid(self, dn, attr, repl_meta_data):
1693 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1694 str(repl_meta_data))
1696 now = samba.unix2nttime(int(time.time()))
1699 # Search for a zero invocationID
1700 if o.originating_invocation_id != misc.GUID("00000000-0000-0000-0000-000000000000"):
1704 seq = self.samdb.sequence_number(ldb.SEQ_NEXT)
1705 o.version = o.version + 1
1706 o.originating_change_time = now
1707 o.originating_invocation_id = misc.GUID(self.samdb.get_invocation_id())
1708 o.originating_usn = seq
1712 replBlob = ndr_pack(repl)
1716 if not self.confirm_all('Fix %s on %s by setting originating_invocation_id on some elements to our invocationID %s?'
1717 % (attr, dn, self.samdb.get_invocation_id()), 'fix_replmetadata_zero_invocationid'):
1718 self.report('Not fixing zero originating_invocation_id in %s on %s\n' % (attr, dn))
1721 nmsg = ldb.Message()
1723 nmsg[attr] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, attr)
1724 if self.do_modify(nmsg, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA,
1725 "local_oid:1.3.6.1.4.1.7165.4.3.14:0"],
1726 "Failed to fix attribute %s" % attr):
1727 self.report("Fixed attribute '%s' of '%s'\n" % (attr, dn))
1730 def err_replmetadata_unknown_attid(self, dn, attr, repl_meta_data):
1731 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1732 str(repl_meta_data))
1735 # Search for an invalid attid
1737 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1739 self.report('ERROR: attributeID 0X%0X is not known in our schema, not fixing %s on %s\n' % (o.attid, attr, dn))
1743 def err_replmetadata_incorrect_attid(self, dn, attr, repl_meta_data, wrong_attids):
1744 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1745 str(repl_meta_data))
1749 remove_attid = set()
1752 in_schema_nc = dn.is_child_of(self.schema_dn)
1755 # Sort the array, except for the last element. This strange
1756 # construction, creating a new list, due to bugs in samba's
1757 # array handling in IDL generated objects.
1758 ctr.array = sorted(ctr.array[:], key=lambda o: o.attid)
1759 # Now walk it in reverse, so we see the low (and so incorrect,
1760 # the correct values are above 0x80000000) values first and
1761 # remove the 'second' value we see.
1762 for o in reversed(ctr.array):
1763 print("%s: 0x%08x" % (dn, o.attid))
1764 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1765 if att.lower() in set_att:
1766 self.report('ERROR: duplicate attributeID values for %s in %s on %s\n' % (att, attr, dn))
1767 if not self.confirm_all('Fix %s on %s by removing the duplicate value 0x%08x for %s (keeping 0x%08x)?'
1768 % (attr, dn, o.attid, att, hash_att[att].attid),
1769 'fix_replmetadata_duplicate_attid'):
1770 self.report('Not fixing duplicate value 0x%08x for %s in %s on %s\n'
1771 % (o.attid, att, attr, dn))
1774 remove_attid.add(o.attid)
1775 # We want to set the metadata for the most recent
1776 # update to have been applied locally, that is the metadata
1777 # matching the (eg string) value in the attribute
1778 if o.local_usn > hash_att[att].local_usn:
1779 # This is always what we would have sent over DRS,
1780 # because the DRS server will have sent the
1781 # msDS-IntID, but with the values from both
1782 # attribute entries.
1783 hash_att[att].version = o.version
1784 hash_att[att].originating_change_time = o.originating_change_time
1785 hash_att[att].originating_invocation_id = o.originating_invocation_id
1786 hash_att[att].originating_usn = o.originating_usn
1787 hash_att[att].local_usn = o.local_usn
1789 # Do not re-add the value to the set or overwrite the hash value
1793 set_att.add(att.lower())
1795 # Generate a real list we can sort on properly
1796 new_list = [o for o in ctr.array if o.attid not in remove_attid]
1798 if (len(wrong_attids) > 0):
1800 if o.attid in wrong_attids:
1801 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1802 correct_attid = self.samdb_schema.get_attid_from_lDAPDisplayName(att, is_schema_nc=in_schema_nc)
1803 self.report('ERROR: incorrect attributeID values in %s on %s\n' % (attr, dn))
1804 if not self.confirm_all('Fix %s on %s by replacing incorrect value 0x%08x for %s (new 0x%08x)?'
1805 % (attr, dn, o.attid, att, hash_att[att].attid), 'fix_replmetadata_wrong_attid'):
1806 self.report('Not fixing incorrect value 0x%08x with 0x%08x for %s in %s on %s\n'
1807 % (o.attid, correct_attid, att, attr, dn))
1810 o.attid = correct_attid
1812 # Sort the array, (we changed the value so must re-sort)
1813 new_list[:] = sorted(new_list[:], key=lambda o: o.attid)
1815 # If we did not already need to fix it, then ask about sorting
1817 self.report('ERROR: unsorted attributeID values in %s on %s\n' % (attr, dn))
1818 if not self.confirm_all('Fix %s on %s by sorting the attribute list?'
1819 % (attr, dn), 'fix_replmetadata_unsorted_attid'):
1820 self.report('Not fixing %s on %s\n' % (attr, dn))
1823 # The actual sort done is done at the top of the function
1825 ctr.count = len(new_list)
1826 ctr.array = new_list
1827 replBlob = ndr_pack(repl)
1829 nmsg = ldb.Message()
1831 nmsg[attr] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, attr)
1832 if self.do_modify(nmsg, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA,
1833 "local_oid:1.3.6.1.4.1.7165.4.3.14:0",
1834 "local_oid:1.3.6.1.4.1.7165.4.3.25:0"],
1835 "Failed to fix attribute %s" % attr):
1836 self.report("Fixed attribute '%s' of '%s'\n" % (attr, dn))
1839 def is_deleted_deleted_objects(self, obj):
1841 if "description" not in obj:
1842 self.report("ERROR: description not present on Deleted Objects container %s" % obj.dn)
1844 if "showInAdvancedViewOnly" not in obj or obj['showInAdvancedViewOnly'][0].upper() == 'FALSE':
1845 self.report("ERROR: showInAdvancedViewOnly not present on Deleted Objects container %s" % obj.dn)
1847 if "objectCategory" not in obj:
1848 self.report("ERROR: objectCategory not present on Deleted Objects container %s" % obj.dn)
1850 if "isCriticalSystemObject" not in obj or obj['isCriticalSystemObject'][0].upper() == 'FALSE':
1851 self.report("ERROR: isCriticalSystemObject not present on Deleted Objects container %s" % obj.dn)
1853 if "isRecycled" in obj:
1854 self.report("ERROR: isRecycled present on Deleted Objects container %s" % obj.dn)
1856 if "isDeleted" in obj and obj['isDeleted'][0].upper() == 'FALSE':
1857 self.report("ERROR: isDeleted not set on Deleted Objects container %s" % obj.dn)
1859 if "objectClass" not in obj or (len(obj['objectClass']) != 2 or
1860 obj['objectClass'][0] != 'top' or
1861 obj['objectClass'][1] != 'container'):
1862 self.report("ERROR: objectClass incorrectly set on Deleted Objects container %s" % obj.dn)
1864 if "systemFlags" not in obj or obj['systemFlags'][0] != '-1946157056':
1865 self.report("ERROR: systemFlags incorrectly set on Deleted Objects container %s" % obj.dn)
1869 def err_deleted_deleted_objects(self, obj):
1870 nmsg = ldb.Message()
1871 nmsg.dn = dn = obj.dn
1873 if "description" not in obj:
1874 nmsg["description"] = ldb.MessageElement("Container for deleted objects", ldb.FLAG_MOD_REPLACE, "description")
1875 if "showInAdvancedViewOnly" not in obj:
1876 nmsg["showInAdvancedViewOnly"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "showInAdvancedViewOnly")
1877 if "objectCategory" not in obj:
1878 nmsg["objectCategory"] = ldb.MessageElement("CN=Container,%s" % self.schema_dn, ldb.FLAG_MOD_REPLACE, "objectCategory")
1879 if "isCriticalSystemObject" not in obj:
1880 nmsg["isCriticalSystemObject"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isCriticalSystemObject")
1881 if "isRecycled" in obj:
1882 nmsg["isRecycled"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_DELETE, "isRecycled")
1884 nmsg["isDeleted"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isDeleted")
1885 nmsg["systemFlags"] = ldb.MessageElement("-1946157056", ldb.FLAG_MOD_REPLACE, "systemFlags")
1886 nmsg["objectClass"] = ldb.MessageElement(["top", "container"], ldb.FLAG_MOD_REPLACE, "objectClass")
1888 if not self.confirm_all('Fix Deleted Objects container %s by restoring default attributes?'
1889 % (dn), 'fix_deleted_deleted_objects'):
1890 self.report('Not fixing missing/incorrect attributes on %s\n' % (dn))
1893 if self.do_modify(nmsg, ["relax:0"],
1894 "Failed to fix Deleted Objects container %s" % dn):
1895 self.report("Fixed Deleted Objects container '%s'\n" % (dn))
1897 def err_replica_locations(self, obj, cross_ref, attr):
1898 nmsg = ldb.Message()
1900 target = self.samdb.get_dsServiceName()
1902 if self.samdb.am_rodc():
1903 self.report('Not fixing %s for the RODC' % (attr, obj.dn))
1906 if not self.confirm_all('Add yourself to the replica locations for %s?'
1907 % (obj.dn), 'fix_replica_locations'):
1908 self.report('Not fixing missing/incorrect attributes on %s\n' % (obj.dn))
1911 nmsg[attr] = ldb.MessageElement(target, ldb.FLAG_MOD_ADD, attr)
1912 if self.do_modify(nmsg, [], "Failed to add %s for %s" % (attr, obj.dn)):
1913 self.report("Fixed %s for %s" % (attr, obj.dn))
1915 def is_fsmo_role(self, dn):
1916 if dn == self.samdb.domain_dn:
1918 if dn == self.infrastructure_dn:
1920 if dn == self.naming_dn:
1922 if dn == self.schema_dn:
1924 if dn == self.rid_dn:
1929 def calculate_instancetype(self, dn):
1931 nc_root = self.samdb.get_nc_root(dn)
1933 instancetype |= dsdb.INSTANCE_TYPE_IS_NC_HEAD
1935 self.samdb.search(base=dn.parent(), scope=ldb.SCOPE_BASE, attrs=[], controls=["show_recycled:1"])
1936 except ldb.LdbError as e4:
1937 (enum, estr) = e4.args
1938 if enum != ldb.ERR_NO_SUCH_OBJECT:
1941 instancetype |= dsdb.INSTANCE_TYPE_NC_ABOVE
1943 if self.write_ncs is not None and str(nc_root) in self.write_ncs:
1944 instancetype |= dsdb.INSTANCE_TYPE_WRITE
1948 def get_wellknown_sd(self, dn):
1949 for [sd_dn, descriptor_fn] in self.wellknown_sds:
1951 domain_sid = security.dom_sid(self.samdb.get_domain_sid())
1952 return ndr_unpack(security.descriptor,
1953 descriptor_fn(domain_sid,
1954 name_map=self.name_map))
1958 def check_object(self, dn, attrs=['*']):
1959 '''check one object'''
1961 self.report("Checking object %s" % dn)
1963 # If we modify the pass-by-reference attrs variable, then we get a
1964 # replPropertyMetadata for every object that we check.
1966 if "dn" in map(str.lower, attrs):
1967 attrs.append("name")
1968 if "distinguishedname" in map(str.lower, attrs):
1969 attrs.append("name")
1970 if str(dn.get_rdn_name()).lower() in map(str.lower, attrs):
1971 attrs.append("name")
1972 if 'name' in map(str.lower, attrs):
1973 attrs.append(dn.get_rdn_name())
1974 attrs.append("isDeleted")
1975 attrs.append("systemFlags")
1976 need_replPropertyMetaData = False
1978 need_replPropertyMetaData = True
1981 linkID, _ = self.get_attr_linkID_and_reverse_name(a)
1986 need_replPropertyMetaData = True
1988 if need_replPropertyMetaData:
1989 attrs.append("replPropertyMetaData")
1990 attrs.append("objectGUID")
1994 sd_flags |= security.SECINFO_OWNER
1995 sd_flags |= security.SECINFO_GROUP
1996 sd_flags |= security.SECINFO_DACL
1997 sd_flags |= security.SECINFO_SACL
1999 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE,
2004 "sd_flags:1:%d" % sd_flags,
2005 "reveal_internals:0",
2008 except ldb.LdbError as e10:
2009 (enum, estr) = e10.args
2010 if enum == ldb.ERR_NO_SUCH_OBJECT:
2011 if self.in_transaction:
2012 self.report("ERROR: Object %s disappeared during check" % dn)
2017 self.report("ERROR: Object %s failed to load during check" % dn)
2021 set_attrs_from_md = set()
2022 set_attrs_seen = set()
2023 got_repl_property_meta_data = False
2024 got_objectclass = False
2026 nc_dn = self.samdb.get_nc_root(obj.dn)
2028 deleted_objects_dn = self.samdb.get_wellknown_dn(nc_dn,
2029 samba.dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER)
2031 # We have no deleted objects DN for schema, and we check for this above for the other
2033 deleted_objects_dn = None
2036 object_rdn_attr = None
2037 object_rdn_val = None
2042 for attrname in obj:
2043 if attrname == 'dn' or attrname == "distinguishedName":
2046 if str(attrname).lower() == 'objectclass':
2047 got_objectclass = True
2049 if str(attrname).lower() == "name":
2050 if len(obj[attrname]) != 1:
2052 self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" %
2053 (len(obj[attrname]), attrname, str(obj.dn)))
2055 name_val = obj[attrname][0]
2057 if str(attrname).lower() == str(obj.dn.get_rdn_name()).lower():
2058 object_rdn_attr = attrname
2059 if len(obj[attrname]) != 1:
2061 self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" %
2062 (len(obj[attrname]), attrname, str(obj.dn)))
2064 object_rdn_val = obj[attrname][0]
2066 if str(attrname).lower() == 'isdeleted':
2067 if obj[attrname][0] != "FALSE":
2070 if str(attrname).lower() == 'systemflags':
2071 systemFlags = int(obj[attrname][0])
2073 if str(attrname).lower() == 'replpropertymetadata':
2074 if self.has_replmetadata_zero_invocationid(dn, obj[attrname]):
2076 self.err_replmetadata_zero_invocationid(dn, attrname, obj[attrname])
2077 # We don't continue, as we may also have other fixes for this attribute
2078 # based on what other attributes we see.
2081 (set_attrs_from_md, list_attid_from_md, wrong_attids) \
2082 = self.process_metadata(dn, obj[attrname])
2085 self.err_replmetadata_unknown_attid(dn, attrname, obj[attrname])
2088 if len(set_attrs_from_md) < len(list_attid_from_md) \
2089 or len(wrong_attids) > 0 \
2090 or sorted(list_attid_from_md) != list_attid_from_md:
2092 self.err_replmetadata_incorrect_attid(dn, attrname, obj[attrname], wrong_attids)
2095 # Here we check that the first attid is 0
2097 if list_attid_from_md[0] != 0:
2099 self.report("ERROR: Not fixing incorrect inital attributeID in '%s' on '%s', it should be objectClass" %
2100 (attrname, str(dn)))
2102 got_repl_property_meta_data = True
2105 if str(attrname).lower() == 'ntsecuritydescriptor':
2106 (sd, sd_broken) = self.process_sd(dn, obj)
2107 if sd_broken is not None:
2108 self.err_wrong_sd(dn, sd, sd_broken)
2112 if sd.owner_sid is None or sd.group_sid is None:
2113 self.err_missing_sd_owner(dn, sd)
2117 if self.reset_well_known_acls:
2119 well_known_sd = self.get_wellknown_sd(dn)
2123 current_sd = ndr_unpack(security.descriptor,
2124 str(obj[attrname][0]))
2126 diff = get_diff_sds(well_known_sd, current_sd, security.dom_sid(self.samdb.get_domain_sid()))
2128 self.err_wrong_default_sd(dn, well_known_sd, current_sd, diff)
2133 if str(attrname).lower() == 'objectclass':
2134 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, obj[attrname])
2135 # Do not consider the attribute incorrect if:
2136 # - The sorted (alphabetically) list is the same, inclding case
2137 # - The first and last elements are the same
2139 # This avoids triggering an error due to
2140 # non-determinism in the sort routine in (at least)
2141 # 4.3 and earlier, and the fact that any AUX classes
2142 # in these attributes are also not sorted when
2143 # imported from Windows (they are just in the reverse
2144 # order of last set)
2145 if sorted(normalised) != sorted(obj[attrname]) \
2146 or normalised[0] != obj[attrname][0] \
2147 or normalised[-1] != obj[attrname][-1]:
2148 self.err_normalise_mismatch_replace(dn, attrname, list(obj[attrname]))
2152 if str(attrname).lower() == 'userparameters':
2153 if len(obj[attrname][0]) == 1 and obj[attrname][0][0] == '\x20':
2155 self.err_short_userParameters(obj, attrname, obj[attrname])
2158 elif obj[attrname][0][:16] == '\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00':
2159 # This is the correct, normal prefix
2162 elif obj[attrname][0][:20] == 'IAAgACAAIAAgACAAIAAg':
2163 # this is the typical prefix from a windows migration
2165 self.err_base64_userParameters(obj, attrname, obj[attrname])
2168 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':
2169 # This is a prefix that is not in UTF-16 format for the space or munged dialback prefix
2171 self.err_utf8_userParameters(obj, attrname, obj[attrname])
2174 elif len(obj[attrname][0]) % 2 != 0:
2175 # This is a value that isn't even in length
2177 self.err_odd_userParameters(obj, attrname, obj[attrname])
2180 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':
2181 # This is a prefix that would happen if a SAMR-written value was replicated from a Samba 4.1 server to a working server
2183 self.err_doubled_userParameters(obj, attrname, obj[attrname])
2186 if attrname.lower() == 'attributeid' or attrname.lower() == 'governsid':
2187 if obj[attrname][0] in self.attribute_or_class_ids:
2189 self.report('Error: %s %s on %s already exists as an attributeId or governsId'
2190 % (attrname, obj.dn, obj[attrname][0]))
2192 self.attribute_or_class_ids.add(obj[attrname][0])
2194 # check for empty attributes
2195 for val in obj[attrname]:
2197 self.err_empty_attribute(dn, attrname)
2201 # get the syntax oid for the attribute, so we can can have
2202 # special handling for some specific attribute types
2204 syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname)
2205 except Exception as msg:
2206 self.err_unknown_attribute(obj, attrname)
2210 linkID, reverse_link_name = self.get_attr_linkID_and_reverse_name(attrname)
2212 flag = self.samdb_schema.get_systemFlags_from_lDAPDisplayName(attrname)
2213 if (not flag & dsdb.DS_FLAG_ATTR_NOT_REPLICATED
2214 and not flag & dsdb.DS_FLAG_ATTR_IS_CONSTRUCTED
2216 set_attrs_seen.add(str(attrname).lower())
2218 if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_OR_NAME,
2219 dsdb.DSDB_SYNTAX_STRING_DN, ldb.SYNTAX_DN ]:
2220 # it's some form of DN, do specialised checking on those
2221 error_count += self.check_dn(obj, attrname, syntax_oid)
2225 # check for incorrectly normalised attributes
2226 for val in obj[attrname]:
2227 values.add(str(val))
2229 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, [val])
2230 if len(normalised) != 1 or normalised[0] != val:
2231 self.err_normalise_mismatch(dn, attrname, obj[attrname])
2235 if len(obj[attrname]) != len(values):
2236 self.err_duplicate_values(dn, attrname, obj[attrname], list(values))
2240 if str(attrname).lower() == "instancetype":
2241 calculated_instancetype = self.calculate_instancetype(dn)
2242 if len(obj["instanceType"]) != 1 or obj["instanceType"][0] != str(calculated_instancetype):
2244 self.err_wrong_instancetype(obj, calculated_instancetype)
2246 if not got_objectclass and ("*" in attrs or "objectclass" in map(str.lower, attrs)):
2248 self.err_missing_objectclass(dn)
2250 if ("*" in attrs or "name" in map(str.lower, attrs)):
2251 if name_val is None:
2253 self.report("ERROR: Not fixing missing 'name' on '%s'" % (str(obj.dn)))
2254 if object_rdn_attr is None:
2256 self.report("ERROR: Not fixing missing '%s' on '%s'" % (obj.dn.get_rdn_name(), str(obj.dn)))
2258 if name_val is not None:
2261 if not (systemFlags & samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE):
2262 parent_dn = deleted_objects_dn
2263 if parent_dn is None:
2264 parent_dn = obj.dn.parent()
2265 expected_dn = ldb.Dn(self.samdb, "RDN=RDN,%s" % (parent_dn))
2266 expected_dn.set_component(0, obj.dn.get_rdn_name(), name_val)
2268 if obj.dn == deleted_objects_dn:
2269 expected_dn = obj.dn
2271 if expected_dn != obj.dn:
2273 self.err_wrong_dn(obj, expected_dn, object_rdn_attr, object_rdn_val, name_val)
2274 elif obj.dn.get_rdn_value() != object_rdn_val:
2276 self.report("ERROR: Not fixing %s=%r on '%s'" % (object_rdn_attr, object_rdn_val, str(obj.dn)))
2279 if got_repl_property_meta_data:
2280 if obj.dn == deleted_objects_dn:
2281 isDeletedAttId = 131120
2282 # It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
2284 expectedTimeDo = 2650466015990000000
2285 originating = self.get_originating_time(obj["replPropertyMetaData"], isDeletedAttId)
2286 if originating != expectedTimeDo:
2287 if self.confirm_all("Fix isDeleted originating_change_time on '%s'" % str(dn), 'fix_time_metadata'):
2288 nmsg = ldb.Message()
2290 nmsg["isDeleted"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isDeleted")
2292 self.samdb.modify(nmsg, controls=["provision:0"])
2295 self.report("Not fixing isDeleted originating_change_time on '%s'" % str(dn))
2297 for att in set_attrs_seen.difference(set_attrs_from_md):
2299 self.report("On object %s" % dn)
2302 self.report("ERROR: Attribute %s not present in replication metadata" % att)
2303 if not self.confirm_all("Fix missing replPropertyMetaData element '%s'" % att, 'fix_all_metadata'):
2304 self.report("Not fixing missing replPropertyMetaData element '%s'" % att)
2306 self.fix_metadata(obj, att)
2308 if self.is_fsmo_role(dn):
2309 if "fSMORoleOwner" not in obj and ("*" in attrs or "fsmoroleowner" in map(str.lower, attrs)):
2310 self.err_no_fsmoRoleOwner(obj)
2314 if dn != self.samdb.get_root_basedn() and str(dn.parent()) not in self.dn_set:
2315 res = self.samdb.search(base=dn.parent(), scope=ldb.SCOPE_BASE,
2316 controls=["show_recycled:1", "show_deleted:1"])
2317 except ldb.LdbError as e11:
2318 (enum, estr) = e11.args
2319 if enum == ldb.ERR_NO_SUCH_OBJECT:
2320 self.err_missing_parent(obj)
2325 if dn in self.deleted_objects_containers and '*' in attrs:
2326 if self.is_deleted_deleted_objects(obj):
2327 self.err_deleted_deleted_objects(obj)
2330 for (dns_part, msg) in self.dns_partitions:
2331 if dn == dns_part and 'repsFrom' in obj:
2332 location = "msDS-NC-Replica-Locations"
2333 if self.samdb.am_rodc():
2334 location = "msDS-NC-RO-Replica-Locations"
2336 if location not in msg:
2337 # There are no replica locations!
2338 self.err_replica_locations(obj, msg.dn, location)
2343 for loc in msg[location]:
2344 if loc == self.samdb.get_dsServiceName():
2347 # This DC is not in the replica locations
2348 self.err_replica_locations(obj, msg.dn, location)
2351 if dn == self.server_ref_dn:
2352 # Check we have a valid RID Set
2353 if "*" in attrs or "rIDSetReferences" in attrs:
2354 if "rIDSetReferences" not in obj:
2355 # NO RID SET reference
2356 # We are RID master, allocate it.
2359 if self.is_rid_master:
2360 # Allocate a RID Set
2361 if self.confirm_all('Allocate the missing RID set for RID master?',
2362 'fix_missing_rid_set_master'):
2364 # We don't have auto-transaction logic on
2365 # extended operations, so we have to do it
2368 self.samdb.transaction_start()
2371 self.samdb.create_own_rid_set()
2374 self.samdb.transaction_cancel()
2377 self.samdb.transaction_commit()
2380 elif not self.samdb.am_rodc():
2381 self.report("No RID Set found for this server: %s, and we are not the RID Master (so can not self-allocate)" % dn)
2384 # Check some details of our own RID Set
2385 if dn == self.rid_set_dn:
2386 res = self.samdb.search(base=self.rid_set_dn, scope=ldb.SCOPE_BASE,
2387 attrs=["rIDAllocationPool",
2388 "rIDPreviousAllocationPool",
2391 if "rIDAllocationPool" not in res[0]:
2392 self.report("No rIDAllocationPool found in %s" % dn)
2395 next_pool = int(res[0]["rIDAllocationPool"][0])
2397 high = (0xFFFFFFFF00000000 & next_pool) >> 32
2398 low = 0x00000000FFFFFFFF & next_pool
2401 self.report("Invalid RID set %d-%s, %d > %d!" % (low, high, low, high))
2404 if "rIDNextRID" in res[0]:
2405 next_free_rid = int(res[0]["rIDNextRID"][0])
2409 if next_free_rid == 0:
2414 # Check the remainder of this pool for conflicts. If
2415 # ridalloc_allocate_rid() moves to a new pool, this
2416 # will be above high, so we will stop.
2417 while next_free_rid <= high:
2418 sid = "%s-%d" % (self.samdb.get_domain_sid(), next_free_rid)
2420 res = self.samdb.search(base="<SID=%s>" % sid, scope=ldb.SCOPE_BASE,
2422 except ldb.LdbError as e:
2423 (enum, estr) = e.args
2424 if enum != ldb.ERR_NO_SUCH_OBJECT:
2428 self.report("SID %s for %s conflicts with our current RID set in %s" % (sid, res[0].dn, dn))
2431 if self.confirm_all('Fix conflict between SID %s and RID pool in %s by allocating a new RID?'
2433 'fix_sid_rid_set_conflict'):
2434 self.samdb.transaction_start()
2436 # This will burn RIDs, which will move
2437 # past the conflict. We then check again
2438 # to see if the new RID conflicts, until
2439 # the end of the current pool. We don't
2440 # look at the next pool to avoid burning
2441 # all RIDs in one go in some strange
2445 allocated_rid = self.samdb.allocate_rid()
2446 if allocated_rid >= next_free_rid:
2447 next_free_rid = allocated_rid + 1
2450 self.samdb.transaction_cancel()
2453 self.samdb.transaction_commit()
2462 ################################################################
2463 # check special @ROOTDSE attributes
2464 def check_rootdse(self):
2465 '''check the @ROOTDSE special object'''
2466 dn = ldb.Dn(self.samdb, '@ROOTDSE')
2468 self.report("Checking object %s" % dn)
2469 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE)
2471 self.report("Object %s disappeared during check" % dn)
2476 # check that the dsServiceName is in GUID form
2477 if not 'dsServiceName' in obj:
2478 self.report('ERROR: dsServiceName missing in @ROOTDSE')
2479 return error_count+1
2481 if not obj['dsServiceName'][0].startswith('<GUID='):
2482 self.report('ERROR: dsServiceName not in GUID form in @ROOTDSE')
2484 if not self.confirm('Change dsServiceName to GUID form?'):
2486 res = self.samdb.search(base=ldb.Dn(self.samdb, obj['dsServiceName'][0]),
2487 scope=ldb.SCOPE_BASE, attrs=['objectGUID'])
2488 guid_str = str(ndr_unpack(misc.GUID, res[0]['objectGUID'][0]))
2491 m['dsServiceName'] = ldb.MessageElement("<GUID=%s>" % guid_str,
2492 ldb.FLAG_MOD_REPLACE, 'dsServiceName')
2493 if self.do_modify(m, [], "Failed to change dsServiceName to GUID form", validate=False):
2494 self.report("Changed dsServiceName to GUID form")
2498 ###############################################
2499 # re-index the database
2500 def reindex_database(self):
2501 '''re-index the whole database'''
2503 m.dn = ldb.Dn(self.samdb, "@ATTRIBUTES")
2504 m['add'] = ldb.MessageElement('NONE', ldb.FLAG_MOD_ADD, 'force_reindex')
2505 m['delete'] = ldb.MessageElement('NONE', ldb.FLAG_MOD_DELETE, 'force_reindex')
2506 return self.do_modify(m, [], 're-indexed database', validate=False)
2508 ###############################################
2510 def reset_modules(self):
2511 '''reset @MODULES to that needed for current sam.ldb (to read a very old database)'''
2513 m.dn = ldb.Dn(self.samdb, "@MODULES")
2514 m['@LIST'] = ldb.MessageElement('samba_dsdb', ldb.FLAG_MOD_REPLACE, '@LIST')
2515 return self.do_modify(m, [], 'reset @MODULES on database', validate=False)