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.decode('utf8')),
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.decode('utf8')))
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].decode('utf8'))
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].decode('utf8'))
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 "local_oid:%s:1" % dsdb.DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME],
661 "Failed to fix old DN string on attribute %s" % (attrname)):
662 self.report("Fixed old DN string on attribute %s" % (attrname))
664 def err_dn_component_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn, mismatch_type):
665 """handle a DN string being incorrect"""
666 self.report("ERROR: incorrect DN %s component for %s in object %s - %s" % (mismatch_type, attrname, dn, val))
667 dsdb_dn.dn = correct_dn
669 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn),
670 'fix_all_%s_dn_component_mismatch' % mismatch_type):
671 self.report("Not fixing %s component mismatch" % mismatch_type)
675 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
676 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
677 if self.do_modify(m, ["show_recycled:1"],
678 "Failed to fix incorrect DN %s on attribute %s" % (mismatch_type, attrname)):
679 self.report("Fixed incorrect DN %s on attribute %s" % (mismatch_type, attrname))
681 def err_unknown_attribute(self, obj, attrname):
682 '''handle an unknown attribute error'''
683 self.report("ERROR: unknown attribute '%s' in %s" % (attrname, obj.dn))
684 if not self.confirm_all('Remove unknown attribute %s' % attrname, 'remove_all_unknown_attributes'):
685 self.report("Not removing %s" % attrname)
689 m['old_value'] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, attrname)
690 if self.do_modify(m, ["relax:0", "show_recycled:1"],
691 "Failed to remove unknown attribute %s" % attrname):
692 self.report("Removed unknown attribute %s" % (attrname))
694 def err_undead_linked_attribute(self, obj, attrname, val):
695 '''handle a link that should not be there on a deleted object'''
696 self.report("ERROR: linked attribute '%s' to '%s' is present on "
697 "deleted object %s" % (attrname, val, obj.dn))
698 if not self.confirm_all('Remove linked attribute %s' % attrname, 'fix_undead_linked_attributes'):
699 self.report("Not removing linked attribute %s" % attrname)
703 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
705 if self.do_modify(m, ["show_recycled:1", "show_deleted:1", "reveal_internals:0",
706 "local_oid:%s:0" % dsdb.DSDB_CONTROL_REPLMD_VANISH_LINKS],
707 "Failed to delete forward link %s" % attrname):
708 self.report("Fixed undead forward link %s" % (attrname))
710 def err_missing_backlink(self, obj, attrname, val, backlink_name, target_dn):
711 '''handle a missing backlink value'''
712 self.report("ERROR: missing backlink attribute '%s' in %s for link %s in %s" % (backlink_name, target_dn, attrname, obj.dn))
713 if not self.confirm_all('Fix missing backlink %s' % backlink_name, 'fix_all_missing_backlinks'):
714 self.report("Not fixing missing backlink %s" % backlink_name)
718 m['new_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_ADD, backlink_name)
719 if self.do_modify(m, ["show_recycled:1", "relax:0"],
720 "Failed to fix missing backlink %s" % backlink_name):
721 self.report("Fixed missing backlink %s" % (backlink_name))
723 def err_incorrect_rmd_flags(self, obj, attrname, revealed_dn):
724 '''handle a incorrect RMD_FLAGS value'''
725 rmd_flags = int(revealed_dn.dn.get_extended_component("RMD_FLAGS"))
726 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()))
727 if not self.confirm_all('Fix incorrect RMD_FLAGS %u' % rmd_flags, 'fix_rmd_flags'):
728 self.report("Not fixing incorrect RMD_FLAGS %u" % rmd_flags)
732 m['old_value'] = ldb.MessageElement(str(revealed_dn), ldb.FLAG_MOD_DELETE, attrname)
733 if self.do_modify(m, ["show_recycled:1", "reveal_internals:0", "show_deleted:0"],
734 "Failed to fix incorrect RMD_FLAGS %u" % rmd_flags):
735 self.report("Fixed incorrect RMD_FLAGS %u" % (rmd_flags))
737 def err_orphaned_backlink(self, obj_dn, backlink_attr, backlink_val,
738 target_dn, forward_attr, forward_syntax,
739 check_duplicates=True):
740 '''handle a orphaned backlink value'''
741 if check_duplicates is True and self.has_duplicate_links(target_dn, forward_attr, forward_syntax):
742 self.report("WARNING: Keep orphaned backlink attribute " + \
743 "'%s' in '%s' for link '%s' in '%s'" % (
744 backlink_attr, obj_dn, forward_attr, target_dn))
746 self.report("ERROR: orphaned backlink attribute '%s' in %s for link %s in %s" % (backlink_attr, obj_dn, forward_attr, target_dn))
747 if not self.confirm_all('Remove orphaned backlink %s' % backlink_attr, 'fix_all_orphaned_backlinks'):
748 self.report("Not removing orphaned backlink %s" % backlink_attr)
752 m['value'] = ldb.MessageElement(backlink_val, ldb.FLAG_MOD_DELETE, backlink_attr)
753 if self.do_modify(m, ["show_recycled:1", "relax:0"],
754 "Failed to fix orphaned backlink %s" % backlink_attr):
755 self.report("Fixed orphaned backlink %s" % (backlink_attr))
757 def err_recover_forward_links(self, obj, forward_attr, forward_vals):
758 '''handle a duplicate links value'''
760 self.report("RECHECK: 'Missing/Duplicate/Correct link' lines above for attribute '%s' in '%s'" % (forward_attr, obj.dn))
762 if not self.confirm_all("Commit fixes for (missing/duplicate) forward links in attribute '%s'" % forward_attr, 'recover_all_forward_links'):
763 self.report("Not fixing corrupted (missing/duplicate) forward links in attribute '%s' of '%s'" % (
764 forward_attr, obj.dn))
768 m['value'] = ldb.MessageElement(forward_vals, ldb.FLAG_MOD_REPLACE, forward_attr)
769 if self.do_modify(m, ["local_oid:%s:1" % dsdb.DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS],
770 "Failed to fix duplicate links in attribute '%s'" % forward_attr):
771 self.report("Fixed duplicate links in attribute '%s'" % (forward_attr))
772 duplicate_cache_key = "%s:%s" % (str(obj.dn), forward_attr)
773 assert duplicate_cache_key in self.duplicate_link_cache
774 self.duplicate_link_cache[duplicate_cache_key] = False
776 def err_no_fsmoRoleOwner(self, obj):
777 '''handle a missing fSMORoleOwner'''
778 self.report("ERROR: fSMORoleOwner not found for role %s" % (obj.dn))
779 res = self.samdb.search("",
780 scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
782 serviceName = res[0]["dsServiceName"][0]
783 if not self.confirm_all('Sieze role %s onto current DC by adding fSMORoleOwner=%s' % (obj.dn, serviceName), 'seize_fsmo_role'):
784 self.report("Not Siezing role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName))
788 m['value'] = ldb.MessageElement(serviceName, ldb.FLAG_MOD_ADD, 'fSMORoleOwner')
789 if self.do_modify(m, [],
790 "Failed to sieze role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName)):
791 self.report("Siezed role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName))
793 def err_missing_parent(self, obj):
794 '''handle a missing parent'''
795 self.report("ERROR: parent object not found for %s" % (obj.dn))
796 if not self.confirm_all('Move object %s into LostAndFound?' % (obj.dn), 'move_to_lost_and_found'):
797 self.report('Not moving object %s into LostAndFound' % (obj.dn))
800 keep_transaction = False
801 self.samdb.transaction_start()
803 nc_root = self.samdb.get_nc_root(obj.dn);
804 lost_and_found = self.samdb.get_wellknown_dn(nc_root, dsdb.DS_GUID_LOSTANDFOUND_CONTAINER)
805 new_dn = ldb.Dn(self.samdb, str(obj.dn))
806 new_dn.remove_base_components(len(new_dn) - 1)
807 if self.do_rename(obj.dn, new_dn, lost_and_found, ["show_deleted:0", "relax:0"],
808 "Failed to rename object %s into lostAndFound at %s" % (obj.dn, new_dn + lost_and_found)):
809 self.report("Renamed object %s into lostAndFound at %s" % (obj.dn, new_dn + lost_and_found))
813 m['lastKnownParent'] = ldb.MessageElement(str(obj.dn.parent()), ldb.FLAG_MOD_REPLACE, 'lastKnownParent')
815 if self.do_modify(m, [],
816 "Failed to set lastKnownParent on lostAndFound object at %s" % (new_dn + lost_and_found)):
817 self.report("Set lastKnownParent on lostAndFound object at %s" % (new_dn + lost_and_found))
818 keep_transaction = True
820 self.samdb.transaction_cancel()
824 self.samdb.transaction_commit()
826 self.samdb.transaction_cancel()
828 def err_wrong_dn(self, obj, new_dn, rdn_attr, rdn_val, name_val):
829 '''handle a wrong dn'''
831 new_rdn = ldb.Dn(self.samdb, str(new_dn))
832 new_rdn.remove_base_components(len(new_rdn) - 1)
833 new_parent = new_dn.parent()
836 if rdn_val != name_val:
837 attributes += "%s=%r " % (rdn_attr, rdn_val)
838 attributes += "name=%r" % (name_val)
840 self.report("ERROR: wrong dn[%s] %s new_dn[%s]" % (obj.dn, attributes, new_dn))
841 if not self.confirm_all("Rename %s to %s?" % (obj.dn, new_dn), 'fix_dn'):
842 self.report("Not renaming %s to %s" % (obj.dn, new_dn))
845 if self.do_rename(obj.dn, new_rdn, new_parent, ["show_recycled:1", "relax:0"],
846 "Failed to rename object %s into %s" % (obj.dn, new_dn)):
847 self.report("Renamed %s into %s" % (obj.dn, new_dn))
849 def err_wrong_instancetype(self, obj, calculated_instancetype):
850 '''handle a wrong instanceType'''
851 self.report("ERROR: wrong instanceType %s on %s, should be %d" % (obj["instanceType"], obj.dn, calculated_instancetype))
852 if not self.confirm_all('Change instanceType from %s to %d on %s?' % (obj["instanceType"], calculated_instancetype, obj.dn), 'fix_instancetype'):
853 self.report('Not changing instanceType from %s to %d on %s' % (obj["instanceType"], calculated_instancetype, obj.dn))
858 m['value'] = ldb.MessageElement(str(calculated_instancetype), ldb.FLAG_MOD_REPLACE, 'instanceType')
859 if self.do_modify(m, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA],
860 "Failed to correct missing instanceType on %s by setting instanceType=%d" % (obj.dn, calculated_instancetype)):
861 self.report("Corrected instancetype on %s by setting instanceType=%d" % (obj.dn, calculated_instancetype))
863 def err_short_userParameters(self, obj, attrname, value):
864 # This is a truncated userParameters due to a pre 4.1 replication bug
865 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)))
867 def err_base64_userParameters(self, obj, attrname, value):
868 '''handle a wrong userParameters'''
869 self.report("ERROR: wrongly formatted userParameters %s on %s, should not be base64-encoded" % (value, obj.dn))
870 if not self.confirm_all('Convert userParameters from base64 encoding on %s?' % (obj.dn), 'fix_base64_userparameters'):
871 self.report('Not changing userParameters from base64 encoding on %s' % (obj.dn))
876 m['value'] = ldb.MessageElement(b64decode(obj[attrname][0]), ldb.FLAG_MOD_REPLACE, 'userParameters')
877 if self.do_modify(m, [],
878 "Failed to correct base64-encoded userParameters on %s by converting from base64" % (obj.dn)):
879 self.report("Corrected base64-encoded userParameters on %s by converting from base64" % (obj.dn))
881 def err_utf8_userParameters(self, obj, attrname, value):
882 '''handle a wrong userParameters'''
883 self.report("ERROR: wrongly formatted userParameters on %s, should not be psudo-UTF8 encoded" % (obj.dn))
884 if not self.confirm_all('Convert userParameters from UTF8 encoding on %s?' % (obj.dn), 'fix_utf8_userparameters'):
885 self.report('Not changing userParameters from UTF8 encoding on %s' % (obj.dn))
890 m['value'] = ldb.MessageElement(obj[attrname][0].decode('utf8').encode('utf-16-le'),
891 ldb.FLAG_MOD_REPLACE, 'userParameters')
892 if self.do_modify(m, [],
893 "Failed to correct psudo-UTF8 encoded userParameters on %s by converting from UTF8" % (obj.dn)):
894 self.report("Corrected psudo-UTF8 encoded userParameters on %s by converting from UTF8" % (obj.dn))
896 def err_doubled_userParameters(self, obj, attrname, value):
897 '''handle a wrong userParameters'''
898 self.report("ERROR: wrongly formatted userParameters on %s, should not be double UTF16 encoded" % (obj.dn))
899 if not self.confirm_all('Convert userParameters from doubled UTF-16 encoding on %s?' % (obj.dn), 'fix_doubled_userparameters'):
900 self.report('Not changing userParameters from doubled UTF-16 encoding on %s' % (obj.dn))
905 m['value'] = ldb.MessageElement(obj[attrname][0].decode('utf-16-le').decode('utf-16-le').encode('utf-16-le'),
906 ldb.FLAG_MOD_REPLACE, 'userParameters')
907 if self.do_modify(m, [],
908 "Failed to correct doubled-UTF16 encoded userParameters on %s by converting" % (obj.dn)):
909 self.report("Corrected doubled-UTF16 encoded userParameters on %s by converting" % (obj.dn))
911 def err_odd_userParameters(self, obj, attrname):
912 # This is a truncated userParameters due to a pre 4.1 replication bug
913 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)))
915 def find_revealed_link(self, dn, attrname, guid):
916 '''return a revealed link in an object'''
917 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[attrname],
918 controls=["show_deleted:0", "extended_dn:0", "reveal_internals:0"])
919 syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname)
920 for val in res[0][attrname]:
921 dsdb_dn = dsdb_Dn(self.samdb, val.decode('utf8'), syntax_oid)
922 guid2 = dsdb_dn.dn.get_extended_component("GUID")
927 def check_duplicate_links(self, obj, forward_attr, forward_syntax, forward_linkID, backlink_attr):
928 '''check a linked values for duplicate forward links'''
931 duplicate_dict = dict()
934 # Only forward links can have this problem
935 if forward_linkID & 1:
936 # If we got the reverse, skip it
937 return (error_count, duplicate_dict, unique_dict)
939 if backlink_attr is None:
940 return (error_count, duplicate_dict, unique_dict)
942 duplicate_cache_key = "%s:%s" % (str(obj.dn), forward_attr)
943 if duplicate_cache_key not in self.duplicate_link_cache:
944 self.duplicate_link_cache[duplicate_cache_key] = False
946 for val in obj[forward_attr]:
947 dsdb_dn = dsdb_Dn(self.samdb, val.decode('utf8'), forward_syntax)
949 # all DNs should have a GUID component
950 guid = dsdb_dn.dn.get_extended_component("GUID")
953 guidstr = str(misc.GUID(guid))
954 keystr = guidstr + dsdb_dn.prefix
955 if keystr not in unique_dict:
956 unique_dict[keystr] = dsdb_dn
959 if keystr not in duplicate_dict:
960 duplicate_dict[keystr] = dict()
961 duplicate_dict[keystr]["keep"] = None
962 duplicate_dict[keystr]["delete"] = list()
964 # Now check for the highest RMD_VERSION
965 v1 = int(unique_dict[keystr].dn.get_extended_component("RMD_VERSION"))
966 v2 = int(dsdb_dn.dn.get_extended_component("RMD_VERSION"))
968 duplicate_dict[keystr]["keep"] = unique_dict[keystr]
969 duplicate_dict[keystr]["delete"].append(dsdb_dn)
972 duplicate_dict[keystr]["keep"] = dsdb_dn
973 duplicate_dict[keystr]["delete"].append(unique_dict[keystr])
974 unique_dict[keystr] = dsdb_dn
976 # Fallback to the highest RMD_LOCAL_USN
977 u1 = int(unique_dict[keystr].dn.get_extended_component("RMD_LOCAL_USN"))
978 u2 = int(dsdb_dn.dn.get_extended_component("RMD_LOCAL_USN"))
980 duplicate_dict[keystr]["keep"] = unique_dict[keystr]
981 duplicate_dict[keystr]["delete"].append(dsdb_dn)
983 duplicate_dict[keystr]["keep"] = dsdb_dn
984 duplicate_dict[keystr]["delete"].append(unique_dict[keystr])
985 unique_dict[keystr] = dsdb_dn
988 self.duplicate_link_cache[duplicate_cache_key] = True
990 return (error_count, duplicate_dict, unique_dict)
992 def has_duplicate_links(self, dn, forward_attr, forward_syntax):
993 '''check a linked values for duplicate forward links'''
996 duplicate_cache_key = "%s:%s" % (str(dn), forward_attr)
997 if duplicate_cache_key in self.duplicate_link_cache:
998 return self.duplicate_link_cache[duplicate_cache_key]
1000 forward_linkID, backlink_attr = self.get_attr_linkID_and_reverse_name(forward_attr)
1002 attrs = [forward_attr]
1003 controls = ["extended_dn:1:1", "reveal_internals:0"]
1005 # check its the right GUID
1007 res = self.samdb.search(base=str(dn), scope=ldb.SCOPE_BASE,
1008 attrs=attrs, controls=controls)
1009 except ldb.LdbError as e8:
1010 (enum, estr) = e8.args
1011 if enum != ldb.ERR_NO_SUCH_OBJECT:
1017 error_count, duplicate_dict, unique_dict = \
1018 self.check_duplicate_links(obj, forward_attr, forward_syntax, forward_linkID, backlink_attr)
1020 if duplicate_cache_key in self.duplicate_link_cache:
1021 return self.duplicate_link_cache[duplicate_cache_key]
1025 def find_missing_forward_links_from_backlinks(self, obj,
1029 forward_unique_dict):
1030 '''Find all backlinks linking to obj_guid_str not already in forward_unique_dict'''
1031 missing_forward_links = []
1034 if backlink_attr is None:
1035 return (missing_forward_links, error_count)
1037 if forward_syntax != ldb.SYNTAX_DN:
1038 self.report("Not checking for missing forward links for syntax: %s",
1040 return (missing_forward_links, error_count)
1042 if "sortedLinks" in self.compatibleFeatures:
1043 self.report("Not checking for missing forward links because the db " + \
1044 "has the sortedLinks feature")
1045 return (missing_forward_links, error_count)
1048 obj_guid = obj['objectGUID'][0]
1049 obj_guid_str = str(ndr_unpack(misc.GUID, obj_guid))
1050 filter = "(%s=<GUID=%s>)" % (backlink_attr, obj_guid_str)
1052 res = self.samdb.search(expression=filter,
1053 scope=ldb.SCOPE_SUBTREE, attrs=["objectGUID"],
1054 controls=["extended_dn:1:1",
1055 "search_options:1:2",
1056 "paged_results:1:1000"])
1057 except ldb.LdbError as e9:
1058 (enum, estr) = e9.args
1062 target_dn = dsdb_Dn(self.samdb, r.dn.extended_str(), forward_syntax)
1064 guid = target_dn.dn.get_extended_component("GUID")
1065 guidstr = str(misc.GUID(guid))
1066 if guidstr in forward_unique_dict:
1069 # A valid forward link looks like this:
1071 # <GUID=9f92d30a-fc23-11e4-a5f6-30be15454808>;
1072 # <RMD_ADDTIME=131607546230000000>;
1073 # <RMD_CHANGETIME=131607546230000000>;
1075 # <RMD_INVOCID=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d>;
1076 # <RMD_LOCAL_USN=3765>;
1077 # <RMD_ORIGINATING_USN=3765>;
1079 # <SID=S-1-5-21-4177067393-1453636373-93818738-1124>;
1080 # CN=unsorted-u8,CN=Users,DC=release-4-5-0-pre1,DC=samba,DC=corp
1082 # Note that versions older than Samba 4.8 create
1083 # links with RMD_VERSION=0.
1085 # Try to get the local_usn and time from objectClass
1086 # if possible and fallback to any other one.
1087 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1088 obj['replPropertyMetadata'][0])
1089 for o in repl.ctr.array:
1090 local_usn = o.local_usn
1091 t = o.originating_change_time
1092 if o.attid == drsuapi.DRSUAPI_ATTID_objectClass:
1095 # We use a magic invocationID for restoring missing
1096 # forward links to recover from bug #13228.
1097 # This should allow some more future magic to fix the
1100 # It also means it looses the conflict resolution
1101 # against almost every real invocation, if the
1102 # version is also 0.
1103 originating_invocid = misc.GUID("ffffffff-4700-4700-4700-000000b13228")
1109 rmd_invocid = originating_invocid
1110 rmd_originating_usn = originating_usn
1111 rmd_local_usn = local_usn
1114 target_dn.dn.set_extended_component("RMD_ADDTIME", str(rmd_addtime))
1115 target_dn.dn.set_extended_component("RMD_CHANGETIME", str(rmd_changetime))
1116 target_dn.dn.set_extended_component("RMD_FLAGS", str(rmd_flags))
1117 target_dn.dn.set_extended_component("RMD_INVOCID", ndr_pack(rmd_invocid))
1118 target_dn.dn.set_extended_component("RMD_ORIGINATING_USN", str(rmd_originating_usn))
1119 target_dn.dn.set_extended_component("RMD_LOCAL_USN", str(rmd_local_usn))
1120 target_dn.dn.set_extended_component("RMD_VERSION", str(rmd_version))
1123 missing_forward_links.append(target_dn)
1125 return (missing_forward_links, error_count)
1127 def check_dn(self, obj, attrname, syntax_oid):
1128 '''check a DN attribute for correctness'''
1130 obj_guid = obj['objectGUID'][0]
1132 linkID, reverse_link_name = self.get_attr_linkID_and_reverse_name(attrname)
1133 if reverse_link_name is not None:
1134 reverse_syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(reverse_link_name)
1136 reverse_syntax_oid = None
1138 error_count, duplicate_dict, unique_dict = \
1139 self.check_duplicate_links(obj, attrname, syntax_oid, linkID, reverse_link_name)
1141 if len(duplicate_dict) != 0:
1143 missing_forward_links, missing_error_count = \
1144 self.find_missing_forward_links_from_backlinks(obj,
1145 attrname, syntax_oid,
1148 error_count += missing_error_count
1150 forward_links = [dn for dn in unique_dict.values()]
1152 if missing_error_count != 0:
1153 self.report("ERROR: Missing and duplicate forward link values for attribute '%s' in '%s'" % (
1156 self.report("ERROR: Duplicate forward link values for attribute '%s' in '%s'" % (attrname, obj.dn))
1157 for m in missing_forward_links:
1158 self.report("Missing link '%s'" % (m))
1159 if not self.confirm_all("Schedule readding missing forward link for attribute %s" % attrname,
1160 'fix_all_missing_forward_links'):
1161 self.err_orphaned_backlink(m.dn, reverse_link_name,
1162 obj.dn.extended_str(), obj.dn,
1163 attrname, syntax_oid,
1164 check_duplicates=False)
1166 forward_links += [m]
1167 for keystr in duplicate_dict.keys():
1168 d = duplicate_dict[keystr]
1169 for dd in d["delete"]:
1170 self.report("Duplicate link '%s'" % dd)
1171 self.report("Correct link '%s'" % d["keep"])
1173 # We now construct the sorted dn values.
1174 # They're sorted by the objectGUID of the target
1175 # See dsdb_Dn.__cmp__()
1176 vals = [str(dn) for dn in sorted(forward_links)]
1177 self.err_recover_forward_links(obj, attrname, vals)
1178 # We should continue with the fixed values
1179 obj[attrname] = ldb.MessageElement(vals, 0, attrname)
1181 for val in obj[attrname]:
1182 dsdb_dn = dsdb_Dn(self.samdb, val.decode('utf8'), syntax_oid)
1184 # all DNs should have a GUID component
1185 guid = dsdb_dn.dn.get_extended_component("GUID")
1188 self.err_missing_dn_GUID_component(obj.dn, attrname, val, dsdb_dn,
1192 guidstr = str(misc.GUID(guid))
1193 attrs = ['isDeleted', 'replPropertyMetaData']
1195 if (str(attrname).lower() == 'msds-hasinstantiatedncs') and (obj.dn == self.ntds_dsa):
1196 fixing_msDS_HasInstantiatedNCs = True
1197 attrs.append("instanceType")
1199 fixing_msDS_HasInstantiatedNCs = False
1201 if reverse_link_name is not None:
1202 attrs.append(reverse_link_name)
1204 # check its the right GUID
1206 res = self.samdb.search(base="<GUID=%s>" % guidstr, scope=ldb.SCOPE_BASE,
1207 attrs=attrs, controls=["extended_dn:1:1", "show_recycled:1",
1208 "reveal_internals:0"
1210 except ldb.LdbError as e3:
1211 (enum, estr) = e3.args
1212 if enum != ldb.ERR_NO_SUCH_OBJECT:
1215 # We don't always want to
1216 error_count += self.err_missing_target_dn_or_GUID(obj.dn,
1222 if fixing_msDS_HasInstantiatedNCs:
1223 dsdb_dn.prefix = "B:8:%08X:" % int(res[0]['instanceType'][0])
1224 dsdb_dn.binary = "%08X" % int(res[0]['instanceType'][0])
1226 if str(dsdb_dn) != val:
1228 self.err_incorrect_binary_dn(obj.dn, attrname, val, dsdb_dn, "incorrect instanceType part of Binary DN")
1231 # now we have two cases - the source object might or might not be deleted
1232 is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper() == 'TRUE'
1233 target_is_deleted = 'isDeleted' in res[0] and res[0]['isDeleted'][0].upper() == 'TRUE'
1236 if is_deleted and not obj.dn in self.deleted_objects_containers and linkID:
1237 # A fully deleted object should not have any linked
1238 # attributes. (MS-ADTS 3.1.1.5.5.1.1 Tombstone
1239 # Requirements and 3.1.1.5.5.1.3 Recycled-Object
1241 self.err_undead_linked_attribute(obj, attrname, val)
1244 elif target_is_deleted and not self.is_deleted_objects_dn(dsdb_dn) and linkID:
1245 # the target DN is not allowed to be deleted, unless the target DN is the
1246 # special Deleted Objects container
1248 local_usn = dsdb_dn.dn.get_extended_component("RMD_LOCAL_USN")
1250 if 'replPropertyMetaData' in res[0]:
1251 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1252 str(res[0]['replPropertyMetadata']))
1254 for o in repl.ctr.array:
1255 if o.attid == drsuapi.DRSUAPI_ATTID_isDeleted:
1256 deleted_usn = o.local_usn
1257 if deleted_usn >= int(local_usn):
1258 # If the object was deleted after the link
1259 # was last modified then, clean it up here
1264 self.err_deleted_dn(obj.dn, attrname,
1265 val, dsdb_dn, res[0].dn, True)
1268 self.err_deleted_dn(obj.dn, attrname, val, dsdb_dn, res[0].dn, False)
1271 # We should not check for incorrect
1272 # components on deleted links, as these are allowed to
1273 # go stale (we just need the GUID, not the name)
1274 rmd_blob = dsdb_dn.dn.get_extended_component("RMD_FLAGS")
1276 if rmd_blob is not None:
1277 rmd_flags = int(rmd_blob)
1279 # assert the DN matches in string form, where a reverse
1280 # link exists, otherwise (below) offer to fix it as a non-error.
1281 # The string form is essentially only kept for forensics,
1282 # as we always re-resolve by GUID in normal operations.
1283 if not rmd_flags & 1 and reverse_link_name is not None:
1284 if str(res[0].dn) != str(dsdb_dn.dn):
1286 self.err_dn_component_target_mismatch(obj.dn, attrname, val, dsdb_dn,
1287 res[0].dn, "string")
1290 if res[0].dn.get_extended_component("GUID") != dsdb_dn.dn.get_extended_component("GUID"):
1292 self.err_dn_component_target_mismatch(obj.dn, attrname, val, dsdb_dn,
1296 if res[0].dn.get_extended_component("SID") != dsdb_dn.dn.get_extended_component("SID"):
1298 self.err_dn_component_target_mismatch(obj.dn, attrname, val, dsdb_dn,
1302 # Only for non-links, not even forward-only links
1303 # (otherwise this breaks repl_meta_data):
1305 # Now we have checked the GUID and SID, offer to fix old
1306 # DN strings as a non-error (DNs, not links so no
1307 # backlink). Samba does not maintain this string
1308 # otherwise, so we don't increment error_count.
1309 if reverse_link_name is None:
1310 if linkID == 0 and str(res[0].dn) != str(dsdb_dn.dn):
1311 # Pass in the old/bad DN without the <GUID=...> part,
1312 # otherwise the LDB code will correct it on the way through
1313 # (Note: we still want to preserve the DSDB DN prefix in the
1314 # case of binary DNs)
1315 bad_dn = dsdb_dn.prefix + dsdb_dn.dn.get_linearized()
1316 self.err_dn_string_component_old(obj.dn, attrname, bad_dn,
1320 # check the reverse_link is correct if there should be one
1322 if reverse_link_name in res[0]:
1323 for v in res[0][reverse_link_name]:
1324 v_dn = dsdb_Dn(self.samdb, v.decode('utf8'))
1325 v_guid = v_dn.dn.get_extended_component("GUID")
1326 v_blob = v_dn.dn.get_extended_component("RMD_FLAGS")
1328 if v_blob is not None:
1329 v_rmd_flags = int(v_blob)
1332 if v_guid == obj_guid:
1335 if match_count != 1:
1336 if syntax_oid == dsdb.DSDB_SYNTAX_BINARY_DN or reverse_syntax_oid == dsdb.DSDB_SYNTAX_BINARY_DN:
1338 # Forward binary multi-valued linked attribute
1340 for w in obj[attrname]:
1341 w_guid = dsdb_Dn(self.samdb, w.decode('utf8')).dn.get_extended_component("GUID")
1345 if match_count == forward_count:
1348 for v in obj[attrname]:
1349 v_dn = dsdb_Dn(self.samdb, v.decode('utf8'))
1350 v_guid = v_dn.dn.get_extended_component("GUID")
1351 v_blob = v_dn.dn.get_extended_component("RMD_FLAGS")
1353 if v_blob is not None:
1354 v_rmd_flags = int(v_blob)
1360 if match_count == expected_count:
1363 diff_count = expected_count - match_count
1366 # If there's a backward link on binary multi-valued linked attribute,
1367 # let the check on the forward link remedy the value.
1368 # UNLESS, there is no forward link detected.
1369 if match_count == 0:
1371 self.err_orphaned_backlink(obj.dn, attrname,
1376 # Only warn here and let the forward link logic fix it.
1377 self.report("WARNING: Link (back) mismatch for '%s' (%d) on '%s' to '%s' (%d) on '%s'" % (
1378 attrname, expected_count, str(obj.dn),
1379 reverse_link_name, match_count, str(dsdb_dn.dn)))
1382 assert not target_is_deleted
1384 self.report("ERROR: Link (forward) mismatch for '%s' (%d) on '%s' to '%s' (%d) on '%s'" % (
1385 attrname, expected_count, str(obj.dn),
1386 reverse_link_name, match_count, str(dsdb_dn.dn)))
1388 # Loop until the difference between the forward and
1389 # the backward links is resolved.
1390 while diff_count != 0:
1393 if match_count > 0 or diff_count > 1:
1394 # TODO no method to fix these right now
1395 self.report("ERROR: Can't fix missing "
1396 "multi-valued backlinks on %s" % str(dsdb_dn.dn))
1398 self.err_missing_backlink(obj, attrname,
1399 obj.dn.extended_str(),
1404 self.err_orphaned_backlink(res[0].dn, reverse_link_name,
1405 obj.dn.extended_str(), obj.dn,
1406 attrname, syntax_oid)
1413 def get_originating_time(self, val, attid):
1414 '''Read metadata properties and return the originating time for
1415 a given attributeId.
1417 :return: the originating time or 0 if not found
1420 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
1423 for o in repl.ctr.array:
1424 if o.attid == attid:
1425 return o.originating_change_time
1429 def process_metadata(self, dn, val):
1430 '''Read metadata properties and list attributes in it.
1431 raises KeyError if the attid is unknown.'''
1434 wrong_attids = set()
1436 in_schema_nc = dn.is_child_of(self.schema_dn)
1438 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
1441 for o in repl.ctr.array:
1442 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1443 set_att.add(att.lower())
1444 list_attid.append(o.attid)
1445 correct_attid = self.samdb_schema.get_attid_from_lDAPDisplayName(att,
1446 is_schema_nc=in_schema_nc)
1447 if correct_attid != o.attid:
1448 wrong_attids.add(o.attid)
1450 return (set_att, list_attid, wrong_attids)
1453 def fix_metadata(self, obj, attr):
1454 '''re-write replPropertyMetaData elements for a single attribute for a
1455 object. This is used to fix missing replPropertyMetaData elements'''
1456 guid_str = str(ndr_unpack(misc.GUID, obj['objectGUID'][0]))
1457 dn = ldb.Dn(self.samdb, "<GUID=%s>" % guid_str)
1458 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[attr],
1459 controls=["search_options:1:2",
1462 nmsg = ldb.Message()
1464 nmsg[attr] = ldb.MessageElement(msg[attr], ldb.FLAG_MOD_REPLACE, attr)
1465 if self.do_modify(nmsg, ["relax:0", "provision:0", "show_recycled:1"],
1466 "Failed to fix metadata for attribute %s" % attr):
1467 self.report("Fixed metadata for attribute %s" % attr)
1469 def ace_get_effective_inherited_type(self, ace):
1470 if ace.flags & security.SEC_ACE_FLAG_INHERIT_ONLY:
1474 if ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
1476 elif ace.type == security.SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
1478 elif ace.type == security.SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
1480 elif ace.type == security.SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
1486 if not ace.object.flags & security.SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT:
1489 return str(ace.object.inherited_type)
1491 def lookup_class_schemaIDGUID(self, cls):
1492 if cls in self.class_schemaIDGUID:
1493 return self.class_schemaIDGUID[cls]
1495 flt = "(&(ldapDisplayName=%s)(objectClass=classSchema))" % cls
1496 res = self.samdb.search(base=self.schema_dn,
1498 attrs=["schemaIDGUID"])
1499 t = str(ndr_unpack(misc.GUID, res[0]["schemaIDGUID"][0]))
1501 self.class_schemaIDGUID[cls] = t
1504 def process_sd(self, dn, obj):
1505 sd_attr = "nTSecurityDescriptor"
1506 sd_val = obj[sd_attr]
1508 sd = ndr_unpack(security.descriptor, str(sd_val))
1510 is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper() == 'TRUE'
1512 # we don't fix deleted objects
1515 sd_clean = security.descriptor()
1516 sd_clean.owner_sid = sd.owner_sid
1517 sd_clean.group_sid = sd.group_sid
1518 sd_clean.type = sd.type
1519 sd_clean.revision = sd.revision
1522 last_inherited_type = None
1525 if sd.sacl is not None:
1527 for i in range(0, len(aces)):
1530 if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE:
1531 sd_clean.sacl_add(ace)
1534 t = self.ace_get_effective_inherited_type(ace)
1538 if last_inherited_type is not None:
1539 if t != last_inherited_type:
1540 # if it inherited from more than
1541 # one type it's very likely to be broken
1543 # If not the recalculation will calculate
1548 last_inherited_type = t
1551 if sd.dacl is not None:
1553 for i in range(0, len(aces)):
1556 if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE:
1557 sd_clean.dacl_add(ace)
1560 t = self.ace_get_effective_inherited_type(ace)
1564 if last_inherited_type is not None:
1565 if t != last_inherited_type:
1566 # if it inherited from more than
1567 # one type it's very likely to be broken
1569 # If not the recalculation will calculate
1574 last_inherited_type = t
1577 return (sd_clean, sd)
1579 if last_inherited_type is None:
1585 cls = obj["objectClass"][-1]
1586 except KeyError as e:
1590 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE,
1591 attrs=["isDeleted", "objectClass"],
1592 controls=["show_recycled:1"])
1594 is_deleted = 'isDeleted' in o and o['isDeleted'][0].upper() == 'TRUE'
1596 # we don't fix deleted objects
1598 cls = o["objectClass"][-1]
1600 t = self.lookup_class_schemaIDGUID(cls)
1602 if t != last_inherited_type:
1604 return (sd_clean, sd)
1609 def err_wrong_sd(self, dn, sd, sd_broken):
1610 '''re-write the SD due to incorrect inherited ACEs'''
1611 sd_attr = "nTSecurityDescriptor"
1612 sd_val = ndr_pack(sd)
1613 sd_flags = security.SECINFO_DACL | security.SECINFO_SACL
1615 if not self.confirm_all('Fix %s on %s?' % (sd_attr, dn), 'fix_ntsecuritydescriptor'):
1616 self.report('Not fixing %s on %s\n' % (sd_attr, dn))
1619 nmsg = ldb.Message()
1621 nmsg[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
1622 if self.do_modify(nmsg, ["sd_flags:1:%d" % sd_flags],
1623 "Failed to fix attribute %s" % sd_attr):
1624 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
1626 def err_wrong_default_sd(self, dn, sd, sd_old, diff):
1627 '''re-write the SD due to not matching the default (optional mode for fixing an incorrect provision)'''
1628 sd_attr = "nTSecurityDescriptor"
1629 sd_val = ndr_pack(sd)
1630 sd_old_val = ndr_pack(sd_old)
1631 sd_flags = security.SECINFO_DACL | security.SECINFO_SACL
1632 if sd.owner_sid is not None:
1633 sd_flags |= security.SECINFO_OWNER
1634 if sd.group_sid is not None:
1635 sd_flags |= security.SECINFO_GROUP
1637 if not self.confirm_all('Reset %s on %s back to provision default?\n%s' % (sd_attr, dn, diff), 'reset_all_well_known_acls'):
1638 self.report('Not resetting %s on %s\n' % (sd_attr, dn))
1643 m[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
1644 if self.do_modify(m, ["sd_flags:1:%d" % sd_flags],
1645 "Failed to reset attribute %s" % sd_attr):
1646 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
1648 def err_missing_sd_owner(self, dn, sd):
1649 '''re-write the SD due to a missing owner or group'''
1650 sd_attr = "nTSecurityDescriptor"
1651 sd_val = ndr_pack(sd)
1652 sd_flags = security.SECINFO_OWNER | security.SECINFO_GROUP
1654 if not self.confirm_all('Fix missing owner or group in %s on %s?' % (sd_attr, dn), 'fix_ntsecuritydescriptor_owner_group'):
1655 self.report('Not fixing missing owner or group %s on %s\n' % (sd_attr, dn))
1658 nmsg = ldb.Message()
1660 nmsg[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
1662 # By setting the session_info to admin_session_info and
1663 # setting the security.SECINFO_OWNER | security.SECINFO_GROUP
1664 # flags we cause the descriptor module to set the correct
1665 # owner and group on the SD, replacing the None/NULL values
1666 # for owner_sid and group_sid currently present.
1668 # The admin_session_info matches that used in provision, and
1669 # is the best guess we can make for an existing object that
1670 # hasn't had something specifically set.
1672 # This is important for the dns related naming contexts.
1673 self.samdb.set_session_info(self.admin_session_info)
1674 if self.do_modify(nmsg, ["sd_flags:1:%d" % sd_flags],
1675 "Failed to fix metadata for attribute %s" % sd_attr):
1676 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
1677 self.samdb.set_session_info(self.system_session_info)
1680 def has_replmetadata_zero_invocationid(self, dn, repl_meta_data):
1681 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1682 str(repl_meta_data))
1686 # Search for a zero invocationID
1687 if o.originating_invocation_id != misc.GUID("00000000-0000-0000-0000-000000000000"):
1691 self.report('''ERROR: on replPropertyMetaData of %s, the instanceType on attribute 0x%08x,
1692 version %d changed at %s is 00000000-0000-0000-0000-000000000000,
1693 but should be non-zero. Proposed fix is to set to our invocationID (%s).'''
1694 % (dn, o.attid, o.version,
1695 time.ctime(samba.nttime2unix(o.originating_change_time)),
1696 self.samdb.get_invocation_id()))
1701 def err_replmetadata_zero_invocationid(self, dn, attr, repl_meta_data):
1702 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1703 str(repl_meta_data))
1705 now = samba.unix2nttime(int(time.time()))
1708 # Search for a zero invocationID
1709 if o.originating_invocation_id != misc.GUID("00000000-0000-0000-0000-000000000000"):
1713 seq = self.samdb.sequence_number(ldb.SEQ_NEXT)
1714 o.version = o.version + 1
1715 o.originating_change_time = now
1716 o.originating_invocation_id = misc.GUID(self.samdb.get_invocation_id())
1717 o.originating_usn = seq
1721 replBlob = ndr_pack(repl)
1725 if not self.confirm_all('Fix %s on %s by setting originating_invocation_id on some elements to our invocationID %s?'
1726 % (attr, dn, self.samdb.get_invocation_id()), 'fix_replmetadata_zero_invocationid'):
1727 self.report('Not fixing zero originating_invocation_id in %s on %s\n' % (attr, dn))
1730 nmsg = ldb.Message()
1732 nmsg[attr] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, attr)
1733 if self.do_modify(nmsg, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA,
1734 "local_oid:1.3.6.1.4.1.7165.4.3.14:0"],
1735 "Failed to fix attribute %s" % attr):
1736 self.report("Fixed attribute '%s' of '%s'\n" % (attr, dn))
1739 def err_replmetadata_unknown_attid(self, dn, attr, repl_meta_data):
1740 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1741 str(repl_meta_data))
1744 # Search for an invalid attid
1746 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1748 self.report('ERROR: attributeID 0X%0X is not known in our schema, not fixing %s on %s\n' % (o.attid, attr, dn))
1752 def err_replmetadata_incorrect_attid(self, dn, attr, repl_meta_data, wrong_attids):
1753 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1754 str(repl_meta_data))
1758 remove_attid = set()
1761 in_schema_nc = dn.is_child_of(self.schema_dn)
1764 # Sort the array, except for the last element. This strange
1765 # construction, creating a new list, due to bugs in samba's
1766 # array handling in IDL generated objects.
1767 ctr.array = sorted(ctr.array[:], key=lambda o: o.attid)
1768 # Now walk it in reverse, so we see the low (and so incorrect,
1769 # the correct values are above 0x80000000) values first and
1770 # remove the 'second' value we see.
1771 for o in reversed(ctr.array):
1772 print("%s: 0x%08x" % (dn, o.attid))
1773 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1774 if att.lower() in set_att:
1775 self.report('ERROR: duplicate attributeID values for %s in %s on %s\n' % (att, attr, dn))
1776 if not self.confirm_all('Fix %s on %s by removing the duplicate value 0x%08x for %s (keeping 0x%08x)?'
1777 % (attr, dn, o.attid, att, hash_att[att].attid),
1778 'fix_replmetadata_duplicate_attid'):
1779 self.report('Not fixing duplicate value 0x%08x for %s in %s on %s\n'
1780 % (o.attid, att, attr, dn))
1783 remove_attid.add(o.attid)
1784 # We want to set the metadata for the most recent
1785 # update to have been applied locally, that is the metadata
1786 # matching the (eg string) value in the attribute
1787 if o.local_usn > hash_att[att].local_usn:
1788 # This is always what we would have sent over DRS,
1789 # because the DRS server will have sent the
1790 # msDS-IntID, but with the values from both
1791 # attribute entries.
1792 hash_att[att].version = o.version
1793 hash_att[att].originating_change_time = o.originating_change_time
1794 hash_att[att].originating_invocation_id = o.originating_invocation_id
1795 hash_att[att].originating_usn = o.originating_usn
1796 hash_att[att].local_usn = o.local_usn
1798 # Do not re-add the value to the set or overwrite the hash value
1802 set_att.add(att.lower())
1804 # Generate a real list we can sort on properly
1805 new_list = [o for o in ctr.array if o.attid not in remove_attid]
1807 if (len(wrong_attids) > 0):
1809 if o.attid in wrong_attids:
1810 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1811 correct_attid = self.samdb_schema.get_attid_from_lDAPDisplayName(att, is_schema_nc=in_schema_nc)
1812 self.report('ERROR: incorrect attributeID values in %s on %s\n' % (attr, dn))
1813 if not self.confirm_all('Fix %s on %s by replacing incorrect value 0x%08x for %s (new 0x%08x)?'
1814 % (attr, dn, o.attid, att, hash_att[att].attid), 'fix_replmetadata_wrong_attid'):
1815 self.report('Not fixing incorrect value 0x%08x with 0x%08x for %s in %s on %s\n'
1816 % (o.attid, correct_attid, att, attr, dn))
1819 o.attid = correct_attid
1821 # Sort the array, (we changed the value so must re-sort)
1822 new_list[:] = sorted(new_list[:], key=lambda o: o.attid)
1824 # If we did not already need to fix it, then ask about sorting
1826 self.report('ERROR: unsorted attributeID values in %s on %s\n' % (attr, dn))
1827 if not self.confirm_all('Fix %s on %s by sorting the attribute list?'
1828 % (attr, dn), 'fix_replmetadata_unsorted_attid'):
1829 self.report('Not fixing %s on %s\n' % (attr, dn))
1832 # The actual sort done is done at the top of the function
1834 ctr.count = len(new_list)
1835 ctr.array = new_list
1836 replBlob = ndr_pack(repl)
1838 nmsg = ldb.Message()
1840 nmsg[attr] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, attr)
1841 if self.do_modify(nmsg, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA,
1842 "local_oid:1.3.6.1.4.1.7165.4.3.14:0",
1843 "local_oid:1.3.6.1.4.1.7165.4.3.25:0"],
1844 "Failed to fix attribute %s" % attr):
1845 self.report("Fixed attribute '%s' of '%s'\n" % (attr, dn))
1848 def is_deleted_deleted_objects(self, obj):
1850 if "description" not in obj:
1851 self.report("ERROR: description not present on Deleted Objects container %s" % obj.dn)
1853 if "showInAdvancedViewOnly" not in obj or obj['showInAdvancedViewOnly'][0].upper() == 'FALSE':
1854 self.report("ERROR: showInAdvancedViewOnly not present on Deleted Objects container %s" % obj.dn)
1856 if "objectCategory" not in obj:
1857 self.report("ERROR: objectCategory not present on Deleted Objects container %s" % obj.dn)
1859 if "isCriticalSystemObject" not in obj or obj['isCriticalSystemObject'][0].upper() == 'FALSE':
1860 self.report("ERROR: isCriticalSystemObject not present on Deleted Objects container %s" % obj.dn)
1862 if "isRecycled" in obj:
1863 self.report("ERROR: isRecycled present on Deleted Objects container %s" % obj.dn)
1865 if "isDeleted" in obj and obj['isDeleted'][0].upper() == 'FALSE':
1866 self.report("ERROR: isDeleted not set on Deleted Objects container %s" % obj.dn)
1868 if "objectClass" not in obj or (len(obj['objectClass']) != 2 or
1869 obj['objectClass'][0] != 'top' or
1870 obj['objectClass'][1] != 'container'):
1871 self.report("ERROR: objectClass incorrectly set on Deleted Objects container %s" % obj.dn)
1873 if "systemFlags" not in obj or obj['systemFlags'][0] != '-1946157056':
1874 self.report("ERROR: systemFlags incorrectly set on Deleted Objects container %s" % obj.dn)
1878 def err_deleted_deleted_objects(self, obj):
1879 nmsg = ldb.Message()
1880 nmsg.dn = dn = obj.dn
1882 if "description" not in obj:
1883 nmsg["description"] = ldb.MessageElement("Container for deleted objects", ldb.FLAG_MOD_REPLACE, "description")
1884 if "showInAdvancedViewOnly" not in obj:
1885 nmsg["showInAdvancedViewOnly"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "showInAdvancedViewOnly")
1886 if "objectCategory" not in obj:
1887 nmsg["objectCategory"] = ldb.MessageElement("CN=Container,%s" % self.schema_dn, ldb.FLAG_MOD_REPLACE, "objectCategory")
1888 if "isCriticalSystemObject" not in obj:
1889 nmsg["isCriticalSystemObject"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isCriticalSystemObject")
1890 if "isRecycled" in obj:
1891 nmsg["isRecycled"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_DELETE, "isRecycled")
1893 nmsg["isDeleted"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isDeleted")
1894 nmsg["systemFlags"] = ldb.MessageElement("-1946157056", ldb.FLAG_MOD_REPLACE, "systemFlags")
1895 nmsg["objectClass"] = ldb.MessageElement(["top", "container"], ldb.FLAG_MOD_REPLACE, "objectClass")
1897 if not self.confirm_all('Fix Deleted Objects container %s by restoring default attributes?'
1898 % (dn), 'fix_deleted_deleted_objects'):
1899 self.report('Not fixing missing/incorrect attributes on %s\n' % (dn))
1902 if self.do_modify(nmsg, ["relax:0"],
1903 "Failed to fix Deleted Objects container %s" % dn):
1904 self.report("Fixed Deleted Objects container '%s'\n" % (dn))
1906 def err_replica_locations(self, obj, cross_ref, attr):
1907 nmsg = ldb.Message()
1909 target = self.samdb.get_dsServiceName()
1911 if self.samdb.am_rodc():
1912 self.report('Not fixing %s for the RODC' % (attr, obj.dn))
1915 if not self.confirm_all('Add yourself to the replica locations for %s?'
1916 % (obj.dn), 'fix_replica_locations'):
1917 self.report('Not fixing missing/incorrect attributes on %s\n' % (obj.dn))
1920 nmsg[attr] = ldb.MessageElement(target, ldb.FLAG_MOD_ADD, attr)
1921 if self.do_modify(nmsg, [], "Failed to add %s for %s" % (attr, obj.dn)):
1922 self.report("Fixed %s for %s" % (attr, obj.dn))
1924 def is_fsmo_role(self, dn):
1925 if dn == self.samdb.domain_dn:
1927 if dn == self.infrastructure_dn:
1929 if dn == self.naming_dn:
1931 if dn == self.schema_dn:
1933 if dn == self.rid_dn:
1938 def calculate_instancetype(self, dn):
1940 nc_root = self.samdb.get_nc_root(dn)
1942 instancetype |= dsdb.INSTANCE_TYPE_IS_NC_HEAD
1944 self.samdb.search(base=dn.parent(), scope=ldb.SCOPE_BASE, attrs=[], controls=["show_recycled:1"])
1945 except ldb.LdbError as e4:
1946 (enum, estr) = e4.args
1947 if enum != ldb.ERR_NO_SUCH_OBJECT:
1950 instancetype |= dsdb.INSTANCE_TYPE_NC_ABOVE
1952 if self.write_ncs is not None and str(nc_root) in self.write_ncs:
1953 instancetype |= dsdb.INSTANCE_TYPE_WRITE
1957 def get_wellknown_sd(self, dn):
1958 for [sd_dn, descriptor_fn] in self.wellknown_sds:
1960 domain_sid = security.dom_sid(self.samdb.get_domain_sid())
1961 return ndr_unpack(security.descriptor,
1962 descriptor_fn(domain_sid,
1963 name_map=self.name_map))
1967 def check_object(self, dn, attrs=['*']):
1968 '''check one object'''
1970 self.report("Checking object %s" % dn)
1972 # If we modify the pass-by-reference attrs variable, then we get a
1973 # replPropertyMetadata for every object that we check.
1975 if "dn" in map(str.lower, attrs):
1976 attrs.append("name")
1977 if "distinguishedname" in map(str.lower, attrs):
1978 attrs.append("name")
1979 if str(dn.get_rdn_name()).lower() in map(str.lower, attrs):
1980 attrs.append("name")
1981 if 'name' in map(str.lower, attrs):
1982 attrs.append(dn.get_rdn_name())
1983 attrs.append("isDeleted")
1984 attrs.append("systemFlags")
1985 need_replPropertyMetaData = False
1987 need_replPropertyMetaData = True
1990 linkID, _ = self.get_attr_linkID_and_reverse_name(a)
1995 need_replPropertyMetaData = True
1997 if need_replPropertyMetaData:
1998 attrs.append("replPropertyMetaData")
1999 attrs.append("objectGUID")
2003 sd_flags |= security.SECINFO_OWNER
2004 sd_flags |= security.SECINFO_GROUP
2005 sd_flags |= security.SECINFO_DACL
2006 sd_flags |= security.SECINFO_SACL
2008 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE,
2013 "sd_flags:1:%d" % sd_flags,
2014 "reveal_internals:0",
2017 except ldb.LdbError as e10:
2018 (enum, estr) = e10.args
2019 if enum == ldb.ERR_NO_SUCH_OBJECT:
2020 if self.in_transaction:
2021 self.report("ERROR: Object %s disappeared during check" % dn)
2026 self.report("ERROR: Object %s failed to load during check" % dn)
2030 set_attrs_from_md = set()
2031 set_attrs_seen = set()
2032 got_repl_property_meta_data = False
2033 got_objectclass = False
2035 nc_dn = self.samdb.get_nc_root(obj.dn)
2037 deleted_objects_dn = self.samdb.get_wellknown_dn(nc_dn,
2038 samba.dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER)
2040 # We have no deleted objects DN for schema, and we check for this above for the other
2042 deleted_objects_dn = None
2045 object_rdn_attr = None
2046 object_rdn_val = None
2051 for attrname in obj:
2052 if attrname == 'dn' or attrname == "distinguishedName":
2055 if str(attrname).lower() == 'objectclass':
2056 got_objectclass = True
2058 if str(attrname).lower() == "name":
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 name_val = obj[attrname][0]
2066 if str(attrname).lower() == str(obj.dn.get_rdn_name()).lower():
2067 object_rdn_attr = attrname
2068 if len(obj[attrname]) != 1:
2070 self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" %
2071 (len(obj[attrname]), attrname, str(obj.dn)))
2073 object_rdn_val = obj[attrname][0]
2075 if str(attrname).lower() == 'isdeleted':
2076 if obj[attrname][0] != "FALSE":
2079 if str(attrname).lower() == 'systemflags':
2080 systemFlags = int(obj[attrname][0])
2082 if str(attrname).lower() == 'replpropertymetadata':
2083 if self.has_replmetadata_zero_invocationid(dn, obj[attrname]):
2085 self.err_replmetadata_zero_invocationid(dn, attrname, obj[attrname])
2086 # We don't continue, as we may also have other fixes for this attribute
2087 # based on what other attributes we see.
2090 (set_attrs_from_md, list_attid_from_md, wrong_attids) \
2091 = self.process_metadata(dn, obj[attrname])
2094 self.err_replmetadata_unknown_attid(dn, attrname, obj[attrname])
2097 if len(set_attrs_from_md) < len(list_attid_from_md) \
2098 or len(wrong_attids) > 0 \
2099 or sorted(list_attid_from_md) != list_attid_from_md:
2101 self.err_replmetadata_incorrect_attid(dn, attrname, obj[attrname], wrong_attids)
2104 # Here we check that the first attid is 0
2106 if list_attid_from_md[0] != 0:
2108 self.report("ERROR: Not fixing incorrect inital attributeID in '%s' on '%s', it should be objectClass" %
2109 (attrname, str(dn)))
2111 got_repl_property_meta_data = True
2114 if str(attrname).lower() == 'ntsecuritydescriptor':
2115 (sd, sd_broken) = self.process_sd(dn, obj)
2116 if sd_broken is not None:
2117 self.err_wrong_sd(dn, sd, sd_broken)
2121 if sd.owner_sid is None or sd.group_sid is None:
2122 self.err_missing_sd_owner(dn, sd)
2126 if self.reset_well_known_acls:
2128 well_known_sd = self.get_wellknown_sd(dn)
2132 current_sd = ndr_unpack(security.descriptor,
2133 str(obj[attrname][0]))
2135 diff = get_diff_sds(well_known_sd, current_sd, security.dom_sid(self.samdb.get_domain_sid()))
2137 self.err_wrong_default_sd(dn, well_known_sd, current_sd, diff)
2142 if str(attrname).lower() == 'objectclass':
2143 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, obj[attrname])
2144 # Do not consider the attribute incorrect if:
2145 # - The sorted (alphabetically) list is the same, inclding case
2146 # - The first and last elements are the same
2148 # This avoids triggering an error due to
2149 # non-determinism in the sort routine in (at least)
2150 # 4.3 and earlier, and the fact that any AUX classes
2151 # in these attributes are also not sorted when
2152 # imported from Windows (they are just in the reverse
2153 # order of last set)
2154 if sorted(normalised) != sorted(obj[attrname]) \
2155 or normalised[0] != obj[attrname][0] \
2156 or normalised[-1] != obj[attrname][-1]:
2157 self.err_normalise_mismatch_replace(dn, attrname, list(obj[attrname]))
2161 if str(attrname).lower() == 'userparameters':
2162 if len(obj[attrname][0]) == 1 and obj[attrname][0][0] == '\x20':
2164 self.err_short_userParameters(obj, attrname, obj[attrname])
2167 elif obj[attrname][0][:16] == '\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00':
2168 # This is the correct, normal prefix
2171 elif obj[attrname][0][:20] == 'IAAgACAAIAAgACAAIAAg':
2172 # this is the typical prefix from a windows migration
2174 self.err_base64_userParameters(obj, attrname, obj[attrname])
2177 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':
2178 # This is a prefix that is not in UTF-16 format for the space or munged dialback prefix
2180 self.err_utf8_userParameters(obj, attrname, obj[attrname])
2183 elif len(obj[attrname][0]) % 2 != 0:
2184 # This is a value that isn't even in length
2186 self.err_odd_userParameters(obj, attrname, obj[attrname])
2189 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':
2190 # This is a prefix that would happen if a SAMR-written value was replicated from a Samba 4.1 server to a working server
2192 self.err_doubled_userParameters(obj, attrname, obj[attrname])
2195 if attrname.lower() == 'attributeid' or attrname.lower() == 'governsid':
2196 if obj[attrname][0] in self.attribute_or_class_ids:
2198 self.report('Error: %s %s on %s already exists as an attributeId or governsId'
2199 % (attrname, obj.dn, obj[attrname][0]))
2201 self.attribute_or_class_ids.add(obj[attrname][0])
2203 # check for empty attributes
2204 for val in obj[attrname]:
2206 self.err_empty_attribute(dn, attrname)
2210 # get the syntax oid for the attribute, so we can can have
2211 # special handling for some specific attribute types
2213 syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname)
2214 except Exception as msg:
2215 self.err_unknown_attribute(obj, attrname)
2219 linkID, reverse_link_name = self.get_attr_linkID_and_reverse_name(attrname)
2221 flag = self.samdb_schema.get_systemFlags_from_lDAPDisplayName(attrname)
2222 if (not flag & dsdb.DS_FLAG_ATTR_NOT_REPLICATED
2223 and not flag & dsdb.DS_FLAG_ATTR_IS_CONSTRUCTED
2225 set_attrs_seen.add(str(attrname).lower())
2227 if syntax_oid in [dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_OR_NAME,
2228 dsdb.DSDB_SYNTAX_STRING_DN, ldb.SYNTAX_DN]:
2229 # it's some form of DN, do specialised checking on those
2230 error_count += self.check_dn(obj, attrname, syntax_oid)
2234 # check for incorrectly normalised attributes
2235 for val in obj[attrname]:
2236 values.add(str(val))
2238 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, [val])
2239 if len(normalised) != 1 or normalised[0] != val:
2240 self.err_normalise_mismatch(dn, attrname, obj[attrname])
2244 if len(obj[attrname]) != len(values):
2245 self.err_duplicate_values(dn, attrname, obj[attrname], list(values))
2249 if str(attrname).lower() == "instancetype":
2250 calculated_instancetype = self.calculate_instancetype(dn)
2251 if len(obj["instanceType"]) != 1 or obj["instanceType"][0] != str(calculated_instancetype):
2253 self.err_wrong_instancetype(obj, calculated_instancetype)
2255 if not got_objectclass and ("*" in attrs or "objectclass" in map(str.lower, attrs)):
2257 self.err_missing_objectclass(dn)
2259 if ("*" in attrs or "name" in map(str.lower, attrs)):
2260 if name_val is None:
2262 self.report("ERROR: Not fixing missing 'name' on '%s'" % (str(obj.dn)))
2263 if object_rdn_attr is None:
2265 self.report("ERROR: Not fixing missing '%s' on '%s'" % (obj.dn.get_rdn_name(), str(obj.dn)))
2267 if name_val is not None:
2270 if not (systemFlags & samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE):
2271 parent_dn = deleted_objects_dn
2272 if parent_dn is None:
2273 parent_dn = obj.dn.parent()
2274 expected_dn = ldb.Dn(self.samdb, "RDN=RDN,%s" % (parent_dn))
2275 expected_dn.set_component(0, obj.dn.get_rdn_name(), name_val)
2277 if obj.dn == deleted_objects_dn:
2278 expected_dn = obj.dn
2280 if expected_dn != obj.dn:
2282 self.err_wrong_dn(obj, expected_dn, object_rdn_attr, object_rdn_val, name_val)
2283 elif obj.dn.get_rdn_value() != object_rdn_val:
2285 self.report("ERROR: Not fixing %s=%r on '%s'" % (object_rdn_attr, object_rdn_val, str(obj.dn)))
2288 if got_repl_property_meta_data:
2289 if obj.dn == deleted_objects_dn:
2290 isDeletedAttId = 131120
2291 # It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
2293 expectedTimeDo = 2650466015990000000
2294 originating = self.get_originating_time(obj["replPropertyMetaData"], isDeletedAttId)
2295 if originating != expectedTimeDo:
2296 if self.confirm_all("Fix isDeleted originating_change_time on '%s'" % str(dn), 'fix_time_metadata'):
2297 nmsg = ldb.Message()
2299 nmsg["isDeleted"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isDeleted")
2301 self.samdb.modify(nmsg, controls=["provision:0"])
2304 self.report("Not fixing isDeleted originating_change_time on '%s'" % str(dn))
2306 for att in set_attrs_seen.difference(set_attrs_from_md):
2308 self.report("On object %s" % dn)
2311 self.report("ERROR: Attribute %s not present in replication metadata" % att)
2312 if not self.confirm_all("Fix missing replPropertyMetaData element '%s'" % att, 'fix_all_metadata'):
2313 self.report("Not fixing missing replPropertyMetaData element '%s'" % att)
2315 self.fix_metadata(obj, att)
2317 if self.is_fsmo_role(dn):
2318 if "fSMORoleOwner" not in obj and ("*" in attrs or "fsmoroleowner" in map(str.lower, attrs)):
2319 self.err_no_fsmoRoleOwner(obj)
2323 if dn != self.samdb.get_root_basedn() and str(dn.parent()) not in self.dn_set:
2324 res = self.samdb.search(base=dn.parent(), scope=ldb.SCOPE_BASE,
2325 controls=["show_recycled:1", "show_deleted:1"])
2326 except ldb.LdbError as e11:
2327 (enum, estr) = e11.args
2328 if enum == ldb.ERR_NO_SUCH_OBJECT:
2329 self.err_missing_parent(obj)
2334 if dn in self.deleted_objects_containers and '*' in attrs:
2335 if self.is_deleted_deleted_objects(obj):
2336 self.err_deleted_deleted_objects(obj)
2339 for (dns_part, msg) in self.dns_partitions:
2340 if dn == dns_part and 'repsFrom' in obj:
2341 location = "msDS-NC-Replica-Locations"
2342 if self.samdb.am_rodc():
2343 location = "msDS-NC-RO-Replica-Locations"
2345 if location not in msg:
2346 # There are no replica locations!
2347 self.err_replica_locations(obj, msg.dn, location)
2352 for loc in msg[location]:
2353 if loc == self.samdb.get_dsServiceName():
2356 # This DC is not in the replica locations
2357 self.err_replica_locations(obj, msg.dn, location)
2360 if dn == self.server_ref_dn:
2361 # Check we have a valid RID Set
2362 if "*" in attrs or "rIDSetReferences" in attrs:
2363 if "rIDSetReferences" not in obj:
2364 # NO RID SET reference
2365 # We are RID master, allocate it.
2368 if self.is_rid_master:
2369 # Allocate a RID Set
2370 if self.confirm_all('Allocate the missing RID set for RID master?',
2371 'fix_missing_rid_set_master'):
2373 # We don't have auto-transaction logic on
2374 # extended operations, so we have to do it
2377 self.samdb.transaction_start()
2380 self.samdb.create_own_rid_set()
2383 self.samdb.transaction_cancel()
2386 self.samdb.transaction_commit()
2389 elif not self.samdb.am_rodc():
2390 self.report("No RID Set found for this server: %s, and we are not the RID Master (so can not self-allocate)" % dn)
2393 # Check some details of our own RID Set
2394 if dn == self.rid_set_dn:
2395 res = self.samdb.search(base=self.rid_set_dn, scope=ldb.SCOPE_BASE,
2396 attrs=["rIDAllocationPool",
2397 "rIDPreviousAllocationPool",
2400 if "rIDAllocationPool" not in res[0]:
2401 self.report("No rIDAllocationPool found in %s" % dn)
2404 next_pool = int(res[0]["rIDAllocationPool"][0])
2406 high = (0xFFFFFFFF00000000 & next_pool) >> 32
2407 low = 0x00000000FFFFFFFF & next_pool
2410 self.report("Invalid RID set %d-%s, %d > %d!" % (low, high, low, high))
2413 if "rIDNextRID" in res[0]:
2414 next_free_rid = int(res[0]["rIDNextRID"][0])
2418 if next_free_rid == 0:
2423 # Check the remainder of this pool for conflicts. If
2424 # ridalloc_allocate_rid() moves to a new pool, this
2425 # will be above high, so we will stop.
2426 while next_free_rid <= high:
2427 sid = "%s-%d" % (self.samdb.get_domain_sid(), next_free_rid)
2429 res = self.samdb.search(base="<SID=%s>" % sid, scope=ldb.SCOPE_BASE,
2431 except ldb.LdbError as e:
2432 (enum, estr) = e.args
2433 if enum != ldb.ERR_NO_SUCH_OBJECT:
2437 self.report("SID %s for %s conflicts with our current RID set in %s" % (sid, res[0].dn, dn))
2440 if self.confirm_all('Fix conflict between SID %s and RID pool in %s by allocating a new RID?'
2442 'fix_sid_rid_set_conflict'):
2443 self.samdb.transaction_start()
2445 # This will burn RIDs, which will move
2446 # past the conflict. We then check again
2447 # to see if the new RID conflicts, until
2448 # the end of the current pool. We don't
2449 # look at the next pool to avoid burning
2450 # all RIDs in one go in some strange
2454 allocated_rid = self.samdb.allocate_rid()
2455 if allocated_rid >= next_free_rid:
2456 next_free_rid = allocated_rid + 1
2459 self.samdb.transaction_cancel()
2462 self.samdb.transaction_commit()
2471 ################################################################
2472 # check special @ROOTDSE attributes
2473 def check_rootdse(self):
2474 '''check the @ROOTDSE special object'''
2475 dn = ldb.Dn(self.samdb, '@ROOTDSE')
2477 self.report("Checking object %s" % dn)
2478 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE)
2480 self.report("Object %s disappeared during check" % dn)
2485 # check that the dsServiceName is in GUID form
2486 if not 'dsServiceName' in obj:
2487 self.report('ERROR: dsServiceName missing in @ROOTDSE')
2488 return error_count + 1
2490 if not obj['dsServiceName'][0].startswith('<GUID='):
2491 self.report('ERROR: dsServiceName not in GUID form in @ROOTDSE')
2493 if not self.confirm('Change dsServiceName to GUID form?'):
2495 res = self.samdb.search(base=ldb.Dn(self.samdb, obj['dsServiceName'][0].decode('utf8')),
2496 scope=ldb.SCOPE_BASE, attrs=['objectGUID'])
2497 guid_str = str(ndr_unpack(misc.GUID, res[0]['objectGUID'][0]))
2500 m['dsServiceName'] = ldb.MessageElement("<GUID=%s>" % guid_str,
2501 ldb.FLAG_MOD_REPLACE, 'dsServiceName')
2502 if self.do_modify(m, [], "Failed to change dsServiceName to GUID form", validate=False):
2503 self.report("Changed dsServiceName to GUID form")
2507 ###############################################
2508 # re-index the database
2511 def reindex_database(self):
2512 '''re-index the whole database'''
2514 m.dn = ldb.Dn(self.samdb, "@ATTRIBUTES")
2515 m['add'] = ldb.MessageElement('NONE', ldb.FLAG_MOD_ADD, 'force_reindex')
2516 m['delete'] = ldb.MessageElement('NONE', ldb.FLAG_MOD_DELETE, 'force_reindex')
2517 return self.do_modify(m, [], 're-indexed database', validate=False)
2519 ###############################################
2521 def reset_modules(self):
2522 '''reset @MODULES to that needed for current sam.ldb (to read a very old database)'''
2524 m.dn = ldb.Dn(self.samdb, "@MODULES")
2525 m['@LIST'] = ldb.MessageElement('samba_dsdb', ldb.FLAG_MOD_REPLACE, '@LIST')
2526 return self.do_modify(m, [], 'reset @MODULES on database', validate=False)