lib/poll_funcs: free contexts in poll_funcs_state_destructor()
[Samba.git] / python / samba / dbchecker.py
blob77bfec5a078c6713ff72766091762b508f7d8c1e
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 import ldb
21 import samba
22 import time
23 from base64 import b64decode
24 from samba import dsdb
25 from samba import common
26 from samba.dcerpc import misc
27 from samba.dcerpc import drsuapi
28 from samba.ndr import ndr_unpack, ndr_pack
29 from samba.dcerpc import drsblobs
30 from samba.common import dsdb_Dn
31 from samba.dcerpc import security
32 from samba.descriptor import get_wellknown_sds, get_diff_sds
33 from samba.auth import system_session, admin_session
34 from samba.netcmd import CommandError
37 class dbcheck(object):
38 """check a SAM database for errors"""
40 def __init__(self, samdb, samdb_schema=None, verbose=False, fix=False,
41 yes=False, quiet=False, in_transaction=False,
42 reset_well_known_acls=False):
43 self.samdb = samdb
44 self.dict_oid_name = None
45 self.samdb_schema = (samdb_schema or samdb)
46 self.verbose = verbose
47 self.fix = fix
48 self.yes = yes
49 self.quiet = quiet
50 self.remove_all_unknown_attributes = False
51 self.remove_all_empty_attributes = False
52 self.fix_all_normalisation = False
53 self.fix_all_duplicates = False
54 self.fix_all_DN_GUIDs = False
55 self.fix_all_binary_dn = False
56 self.remove_all_deleted_DN_links = False
57 self.fix_all_target_mismatch = False
58 self.fix_all_metadata = False
59 self.fix_time_metadata = False
60 self.fix_all_missing_backlinks = False
61 self.fix_all_orphaned_backlinks = False
62 self.fix_rmd_flags = False
63 self.fix_ntsecuritydescriptor = False
64 self.fix_ntsecuritydescriptor_owner_group = False
65 self.seize_fsmo_role = False
66 self.move_to_lost_and_found = False
67 self.fix_instancetype = False
68 self.fix_replmetadata_zero_invocationid = False
69 self.fix_replmetadata_duplicate_attid = False
70 self.fix_replmetadata_wrong_attid = False
71 self.fix_replmetadata_unsorted_attid = False
72 self.fix_deleted_deleted_objects = False
73 self.fix_dn = False
74 self.fix_base64_userparameters = False
75 self.fix_utf8_userparameters = False
76 self.fix_doubled_userparameters = False
77 self.reset_well_known_acls = reset_well_known_acls
78 self.reset_all_well_known_acls = False
79 self.in_transaction = in_transaction
80 self.infrastructure_dn = ldb.Dn(samdb, "CN=Infrastructure," + samdb.domain_dn())
81 self.naming_dn = ldb.Dn(samdb, "CN=Partitions,%s" % samdb.get_config_basedn())
82 self.schema_dn = samdb.get_schema_basedn()
83 self.rid_dn = ldb.Dn(samdb, "CN=RID Manager$,CN=System," + samdb.domain_dn())
84 self.ntds_dsa = ldb.Dn(samdb, samdb.get_dsServiceName())
85 self.class_schemaIDGUID = {}
86 self.wellknown_sds = get_wellknown_sds(self.samdb)
87 self.fix_all_missing_objectclass = False
89 self.dn_set = set()
91 self.name_map = {}
92 try:
93 res = samdb.search(base="CN=DnsAdmins,CN=Users,%s" % samdb.domain_dn(), scope=ldb.SCOPE_BASE,
94 attrs=["objectSid"])
95 dnsadmins_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0])
96 self.name_map['DnsAdmins'] = str(dnsadmins_sid)
97 except ldb.LdbError, (enum, estr):
98 if enum != ldb.ERR_NO_SUCH_OBJECT:
99 raise
100 pass
102 self.system_session_info = system_session()
103 self.admin_session_info = admin_session(None, samdb.get_domain_sid())
105 res = self.samdb.search(base=self.ntds_dsa, scope=ldb.SCOPE_BASE, attrs=['msDS-hasMasterNCs', 'hasMasterNCs'])
106 if "msDS-hasMasterNCs" in res[0]:
107 self.write_ncs = res[0]["msDS-hasMasterNCs"]
108 else:
109 # If the Forest Level is less than 2003 then there is no
110 # msDS-hasMasterNCs, so we fall back to hasMasterNCs
111 # no need to merge as all the NCs that are in hasMasterNCs must
112 # also be in msDS-hasMasterNCs (but not the opposite)
113 if "hasMasterNCs" in res[0]:
114 self.write_ncs = res[0]["hasMasterNCs"]
115 else:
116 self.write_ncs = None
118 res = self.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=['namingContexts'])
119 try:
120 ncs = res[0]["namingContexts"]
121 self.deleted_objects_containers = []
122 for nc in ncs:
123 try:
124 dn = self.samdb.get_wellknown_dn(ldb.Dn(self.samdb, nc),
125 dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER)
126 self.deleted_objects_containers.append(dn)
127 except KeyError:
128 pass
129 except KeyError:
130 pass
131 except IndexError:
132 pass
134 def check_database(self, DN=None, scope=ldb.SCOPE_SUBTREE, controls=[], attrs=['*']):
135 '''perform a database check, returning the number of errors found'''
136 res = self.samdb.search(base=DN, scope=scope, attrs=['dn'], controls=controls)
137 self.report('Checking %u objects' % len(res))
138 error_count = 0
140 for object in res:
141 self.dn_set.add(str(object.dn))
142 error_count += self.check_object(object.dn, attrs=attrs)
144 if DN is None:
145 error_count += self.check_rootdse()
147 if error_count != 0 and not self.fix:
148 self.report("Please use --fix to fix these errors")
150 self.report('Checked %u objects (%u errors)' % (len(res), error_count))
151 return error_count
153 def report(self, msg):
154 '''print a message unless quiet is set'''
155 if not self.quiet:
156 print(msg)
158 def confirm(self, msg, allow_all=False, forced=False):
159 '''confirm a change'''
160 if not self.fix:
161 return False
162 if self.quiet:
163 return self.yes
164 if self.yes:
165 forced = True
166 return common.confirm(msg, forced=forced, allow_all=allow_all)
168 ################################################################
169 # a local confirm function with support for 'all'
170 def confirm_all(self, msg, all_attr):
171 '''confirm a change with support for "all" '''
172 if not self.fix:
173 return False
174 if self.quiet:
175 return self.yes
176 if getattr(self, all_attr) == 'NONE':
177 return False
178 if getattr(self, all_attr) == 'ALL':
179 forced = True
180 else:
181 forced = self.yes
182 c = common.confirm(msg, forced=forced, allow_all=True)
183 if c == 'ALL':
184 setattr(self, all_attr, 'ALL')
185 return True
186 if c == 'NONE':
187 setattr(self, all_attr, 'NONE')
188 return False
189 return c
191 def do_delete(self, dn, controls, msg):
192 '''delete dn with optional verbose output'''
193 if self.verbose:
194 self.report("delete DN %s" % dn)
195 try:
196 controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
197 self.samdb.delete(dn, controls=controls)
198 except Exception, err:
199 if self.in_transaction:
200 raise CommandError("%s : %s" % (msg, err))
201 self.report("%s : %s" % (msg, err))
202 return False
203 return True
205 def do_modify(self, m, controls, msg, validate=True):
206 '''perform a modify with optional verbose output'''
207 if self.verbose:
208 self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY))
209 try:
210 controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
211 self.samdb.modify(m, controls=controls, validate=validate)
212 except Exception, err:
213 if self.in_transaction:
214 raise CommandError("%s : %s" % (msg, err))
215 self.report("%s : %s" % (msg, err))
216 return False
217 return True
219 def do_rename(self, from_dn, to_rdn, to_base, controls, msg):
220 '''perform a modify with optional verbose output'''
221 if self.verbose:
222 self.report("""dn: %s
223 changeType: modrdn
224 newrdn: %s
225 deleteOldRdn: 1
226 newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
227 try:
228 to_dn = to_rdn + to_base
229 controls = controls + ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK]
230 self.samdb.rename(from_dn, to_dn, controls=controls)
231 except Exception, err:
232 if self.in_transaction:
233 raise CommandError("%s : %s" % (msg, err))
234 self.report("%s : %s" % (msg, err))
235 return False
236 return True
238 def err_empty_attribute(self, dn, attrname):
239 '''fix empty attributes'''
240 self.report("ERROR: Empty attribute %s in %s" % (attrname, dn))
241 if not self.confirm_all('Remove empty attribute %s from %s?' % (attrname, dn), 'remove_all_empty_attributes'):
242 self.report("Not fixing empty attribute %s" % attrname)
243 return
245 m = ldb.Message()
246 m.dn = dn
247 m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname)
248 if self.do_modify(m, ["relax:0", "show_recycled:1"],
249 "Failed to remove empty attribute %s" % attrname, validate=False):
250 self.report("Removed empty attribute %s" % attrname)
252 def err_normalise_mismatch(self, dn, attrname, values):
253 '''fix attribute normalisation errors'''
254 self.report("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn))
255 mod_list = []
256 for val in values:
257 normalised = self.samdb.dsdb_normalise_attributes(
258 self.samdb_schema, attrname, [val])
259 if len(normalised) != 1:
260 self.report("Unable to normalise value '%s'" % val)
261 mod_list.append((val, ''))
262 elif (normalised[0] != val):
263 self.report("value '%s' should be '%s'" % (val, normalised[0]))
264 mod_list.append((val, normalised[0]))
265 if not self.confirm_all('Fix normalisation for %s from %s?' % (attrname, dn), 'fix_all_normalisation'):
266 self.report("Not fixing attribute %s" % attrname)
267 return
269 m = ldb.Message()
270 m.dn = dn
271 for i in range(0, len(mod_list)):
272 (val, nval) = mod_list[i]
273 m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
274 if nval != '':
275 m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD,
276 attrname)
278 if self.do_modify(m, ["relax:0", "show_recycled:1"],
279 "Failed to normalise attribute %s" % attrname,
280 validate=False):
281 self.report("Normalised attribute %s" % attrname)
283 def err_normalise_mismatch_replace(self, dn, attrname, values):
284 '''fix attribute normalisation errors'''
285 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, values)
286 self.report("ERROR: Normalisation error for attribute '%s' in '%s'" % (attrname, dn))
287 self.report("Values/Order of values do/does not match: %s/%s!" % (values, list(normalised)))
288 if list(normalised) == values:
289 return
290 if not self.confirm_all("Fix normalisation for '%s' from '%s'?" % (attrname, dn), 'fix_all_normalisation'):
291 self.report("Not fixing attribute '%s'" % attrname)
292 return
294 m = ldb.Message()
295 m.dn = dn
296 m[attrname] = ldb.MessageElement(normalised, ldb.FLAG_MOD_REPLACE, attrname)
298 if self.do_modify(m, ["relax:0", "show_recycled:1"],
299 "Failed to normalise attribute %s" % attrname,
300 validate=False):
301 self.report("Normalised attribute %s" % attrname)
303 def err_duplicate_values(self, dn, attrname, dup_values, values):
304 '''fix attribute normalisation errors'''
305 self.report("ERROR: Duplicate values for attribute '%s' in '%s'" % (attrname, dn))
306 self.report("Values contain a duplicate: [%s]/[%s]!" % (','.join(dup_values), ','.join(values)))
307 if not self.confirm_all("Fix duplicates for '%s' from '%s'?" % (attrname, dn), 'fix_all_duplicates'):
308 self.report("Not fixing attribute '%s'" % attrname)
309 return
311 m = ldb.Message()
312 m.dn = dn
313 m[attrname] = ldb.MessageElement(values, ldb.FLAG_MOD_REPLACE, attrname)
315 if self.do_modify(m, ["relax:0", "show_recycled:1"],
316 "Failed to remove duplicate value on attribute %s" % attrname,
317 validate=False):
318 self.report("Removed duplicate value on attribute %s" % attrname)
320 def is_deleted_objects_dn(self, dsdb_dn):
321 '''see if a dsdb_Dn is the special Deleted Objects DN'''
322 return dsdb_dn.prefix == "B:32:%s:" % dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER
324 def err_missing_objectclass(self, dn):
325 """handle object without objectclass"""
326 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)))
327 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'):
328 self.report("Not deleting object with missing objectclass '%s'" % dn)
329 return
330 if self.do_delete(dn, ["relax:0"],
331 "Failed to remove DN %s" % dn):
332 self.report("Removed DN %s" % dn)
334 def err_deleted_dn(self, dn, attrname, val, dsdb_dn, correct_dn):
335 """handle a DN pointing to a deleted object"""
336 self.report("ERROR: target DN is deleted for %s in object %s - %s" % (attrname, dn, val))
337 self.report("Target GUID points at deleted DN %s" % correct_dn)
338 if not self.confirm_all('Remove DN link?', 'remove_all_deleted_DN_links'):
339 self.report("Not removing")
340 return
341 m = ldb.Message()
342 m.dn = dn
343 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
344 if self.do_modify(m, ["show_recycled:1", "local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK],
345 "Failed to remove deleted DN attribute %s" % attrname):
346 self.report("Removed deleted DN on attribute %s" % attrname)
348 def err_missing_dn_GUID(self, dn, attrname, val, dsdb_dn):
349 """handle a missing target DN (both GUID and DN string form are missing)"""
350 # check if its a backlink
351 linkID = self.samdb_schema.get_linkId_from_lDAPDisplayName(attrname)
352 if (linkID & 1 == 0) and str(dsdb_dn).find('\\0ADEL') == -1:
353 self.report("Not removing dangling forward link")
354 return
355 self.err_deleted_dn(dn, attrname, val, dsdb_dn, dsdb_dn)
357 def err_incorrect_dn_GUID(self, dn, attrname, val, dsdb_dn, errstr):
358 """handle a missing GUID extended DN component"""
359 self.report("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val))
360 controls=["extended_dn:1:1", "show_recycled:1"]
361 try:
362 res = self.samdb.search(base=str(dsdb_dn.dn), scope=ldb.SCOPE_BASE,
363 attrs=[], controls=controls)
364 except ldb.LdbError, (enum, estr):
365 self.report("unable to find object for DN %s - (%s)" % (dsdb_dn.dn, estr))
366 self.err_missing_dn_GUID(dn, attrname, val, dsdb_dn)
367 return
368 if len(res) == 0:
369 self.report("unable to find object for DN %s" % dsdb_dn.dn)
370 self.err_missing_dn_GUID(dn, attrname, val, dsdb_dn)
371 return
372 dsdb_dn.dn = res[0].dn
374 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn), 'fix_all_DN_GUIDs'):
375 self.report("Not fixing %s" % errstr)
376 return
377 m = ldb.Message()
378 m.dn = dn
379 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
380 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
382 if self.do_modify(m, ["show_recycled:1"],
383 "Failed to fix %s on attribute %s" % (errstr, attrname)):
384 self.report("Fixed %s on attribute %s" % (errstr, attrname))
386 def err_incorrect_binary_dn(self, dn, attrname, val, dsdb_dn, errstr):
387 """handle an incorrect binary DN component"""
388 self.report("ERROR: %s binary component for %s in object %s - %s" % (errstr, attrname, dn, val))
389 controls=["extended_dn:1:1", "show_recycled:1"]
391 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn), 'fix_all_binary_dn'):
392 self.report("Not fixing %s" % errstr)
393 return
394 m = ldb.Message()
395 m.dn = dn
396 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
397 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
399 if self.do_modify(m, ["show_recycled:1"],
400 "Failed to fix %s on attribute %s" % (errstr, attrname)):
401 self.report("Fixed %s on attribute %s" % (errstr, attrname))
403 def err_dn_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn, errstr):
404 """handle a DN string being incorrect"""
405 self.report("ERROR: incorrect DN string component for %s in object %s - %s" % (attrname, dn, val))
406 dsdb_dn.dn = correct_dn
408 if not self.confirm_all('Change DN to %s?' % str(dsdb_dn), 'fix_all_target_mismatch'):
409 self.report("Not fixing %s" % errstr)
410 return
411 m = ldb.Message()
412 m.dn = dn
413 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
414 m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname)
415 if self.do_modify(m, ["show_recycled:1"],
416 "Failed to fix incorrect DN string on attribute %s" % attrname):
417 self.report("Fixed incorrect DN string on attribute %s" % (attrname))
419 def err_unknown_attribute(self, obj, attrname):
420 '''handle an unknown attribute error'''
421 self.report("ERROR: unknown attribute '%s' in %s" % (attrname, obj.dn))
422 if not self.confirm_all('Remove unknown attribute %s' % attrname, 'remove_all_unknown_attributes'):
423 self.report("Not removing %s" % attrname)
424 return
425 m = ldb.Message()
426 m.dn = obj.dn
427 m['old_value'] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, attrname)
428 if self.do_modify(m, ["relax:0", "show_recycled:1"],
429 "Failed to remove unknown attribute %s" % attrname):
430 self.report("Removed unknown attribute %s" % (attrname))
432 def err_missing_backlink(self, obj, attrname, val, backlink_name, target_dn):
433 '''handle a missing backlink value'''
434 self.report("ERROR: missing backlink attribute '%s' in %s for link %s in %s" % (backlink_name, target_dn, attrname, obj.dn))
435 if not self.confirm_all('Fix missing backlink %s' % backlink_name, 'fix_all_missing_backlinks'):
436 self.report("Not fixing missing backlink %s" % backlink_name)
437 return
438 m = ldb.Message()
439 m.dn = obj.dn
440 m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
441 m['new_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_ADD, attrname)
442 if self.do_modify(m, ["show_recycled:1"],
443 "Failed to fix missing backlink %s" % backlink_name):
444 self.report("Fixed missing backlink %s" % (backlink_name))
446 def err_incorrect_rmd_flags(self, obj, attrname, revealed_dn):
447 '''handle a incorrect RMD_FLAGS value'''
448 rmd_flags = int(revealed_dn.dn.get_extended_component("RMD_FLAGS"))
449 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()))
450 if not self.confirm_all('Fix incorrect RMD_FLAGS %u' % rmd_flags, 'fix_rmd_flags'):
451 self.report("Not fixing incorrect RMD_FLAGS %u" % rmd_flags)
452 return
453 m = ldb.Message()
454 m.dn = obj.dn
455 m['old_value'] = ldb.MessageElement(str(revealed_dn), ldb.FLAG_MOD_DELETE, attrname)
456 if self.do_modify(m, ["show_recycled:1", "reveal_internals:0", "show_deleted:0"],
457 "Failed to fix incorrect RMD_FLAGS %u" % rmd_flags):
458 self.report("Fixed incorrect RMD_FLAGS %u" % (rmd_flags))
460 def err_orphaned_backlink(self, obj, attrname, val, link_name, target_dn):
461 '''handle a orphaned backlink value'''
462 self.report("ERROR: orphaned backlink attribute '%s' in %s for link %s in %s" % (attrname, obj.dn, link_name, target_dn))
463 if not self.confirm_all('Remove orphaned backlink %s' % link_name, 'fix_all_orphaned_backlinks'):
464 self.report("Not removing orphaned backlink %s" % link_name)
465 return
466 m = ldb.Message()
467 m.dn = obj.dn
468 m['value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname)
469 if self.do_modify(m, ["show_recycled:1", "relax:0"],
470 "Failed to fix orphaned backlink %s" % link_name):
471 self.report("Fixed orphaned backlink %s" % (link_name))
473 def err_no_fsmoRoleOwner(self, obj):
474 '''handle a missing fSMORoleOwner'''
475 self.report("ERROR: fSMORoleOwner not found for role %s" % (obj.dn))
476 res = self.samdb.search("",
477 scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
478 assert len(res) == 1
479 serviceName = res[0]["dsServiceName"][0]
480 if not self.confirm_all('Sieze role %s onto current DC by adding fSMORoleOwner=%s' % (obj.dn, serviceName), 'seize_fsmo_role'):
481 self.report("Not Siezing role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName))
482 return
483 m = ldb.Message()
484 m.dn = obj.dn
485 m['value'] = ldb.MessageElement(serviceName, ldb.FLAG_MOD_ADD, 'fSMORoleOwner')
486 if self.do_modify(m, [],
487 "Failed to sieze role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName)):
488 self.report("Siezed role %s onto current DC by adding fSMORoleOwner=%s" % (obj.dn, serviceName))
490 def err_missing_parent(self, obj):
491 '''handle a missing parent'''
492 self.report("ERROR: parent object not found for %s" % (obj.dn))
493 if not self.confirm_all('Move object %s into LostAndFound?' % (obj.dn), 'move_to_lost_and_found'):
494 self.report('Not moving object %s into LostAndFound' % (obj.dn))
495 return
497 keep_transaction = True
498 self.samdb.transaction_start()
499 try:
500 nc_root = self.samdb.get_nc_root(obj.dn);
501 lost_and_found = self.samdb.get_wellknown_dn(nc_root, dsdb.DS_GUID_LOSTANDFOUND_CONTAINER)
502 new_dn = ldb.Dn(self.samdb, str(obj.dn))
503 new_dn.remove_base_components(len(new_dn) - 1)
504 if self.do_rename(obj.dn, new_dn, lost_and_found, ["show_deleted:0", "relax:0"],
505 "Failed to rename object %s into lostAndFound at %s" % (obj.dn, new_dn + lost_and_found)):
506 self.report("Renamed object %s into lostAndFound at %s" % (obj.dn, new_dn + lost_and_found))
508 m = ldb.Message()
509 m.dn = obj.dn
510 m['lastKnownParent'] = ldb.MessageElement(str(obj.dn.parent()), ldb.FLAG_MOD_REPLACE, 'lastKnownParent')
512 if self.do_modify(m, [],
513 "Failed to set lastKnownParent on lostAndFound object at %s" % (new_dn + lost_and_found)):
514 self.report("Set lastKnownParent on lostAndFound object at %s" % (new_dn + lost_and_found))
515 keep_transaction = True
516 except:
517 self.samdb.transaction_cancel()
518 raise
520 if keep_transaction:
521 self.samdb.transaction_commit()
522 else:
523 self.samdb.transaction_cancel()
525 def err_wrong_dn(self, obj, new_dn, rdn_attr, rdn_val, name_val):
526 '''handle a wrong dn'''
528 new_rdn = ldb.Dn(self.samdb, str(new_dn))
529 new_rdn.remove_base_components(len(new_rdn) - 1)
530 new_parent = new_dn.parent()
532 attributes = ""
533 if rdn_val != name_val:
534 attributes += "%s=%r " % (rdn_attr, rdn_val)
535 attributes += "name=%r" % (name_val)
537 self.report("ERROR: wrong dn[%s] %s new_dn[%s]" % (obj.dn, attributes, new_dn))
538 if not self.confirm_all("Rename %s to %s?" % (obj.dn, new_dn), 'fix_dn'):
539 self.report("Not renaming %s to %s" % (obj.dn, new_dn))
540 return
542 if self.do_rename(obj.dn, new_rdn, new_parent, ["show_recycled:1", "relax:0"],
543 "Failed to rename object %s into %s" % (obj.dn, new_dn)):
544 self.report("Renamed %s into %s" % (obj.dn, new_dn))
546 def err_wrong_instancetype(self, obj, calculated_instancetype):
547 '''handle a wrong instanceType'''
548 self.report("ERROR: wrong instanceType %s on %s, should be %d" % (obj["instanceType"], obj.dn, calculated_instancetype))
549 if not self.confirm_all('Change instanceType from %s to %d on %s?' % (obj["instanceType"], calculated_instancetype, obj.dn), 'fix_instancetype'):
550 self.report('Not changing instanceType from %s to %d on %s' % (obj["instanceType"], calculated_instancetype, obj.dn))
551 return
553 m = ldb.Message()
554 m.dn = obj.dn
555 m['value'] = ldb.MessageElement(str(calculated_instancetype), ldb.FLAG_MOD_REPLACE, 'instanceType')
556 if self.do_modify(m, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA],
557 "Failed to correct missing instanceType on %s by setting instanceType=%d" % (obj.dn, calculated_instancetype)):
558 self.report("Corrected instancetype on %s by setting instanceType=%d" % (obj.dn, calculated_instancetype))
560 def err_short_userParameters(self, obj, attrname, value):
561 # This is a truncated userParameters due to a pre 4.1 replication bug
562 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)))
564 def err_base64_userParameters(self, obj, attrname, value):
565 '''handle a wrong userParameters'''
566 self.report("ERROR: wrongly formatted userParameters %s on %s, should not be base64-encoded" % (value, obj.dn))
567 if not self.confirm_all('Convert userParameters from base64 encoding on %s?' % (obj.dn), 'fix_base64_userparameters'):
568 self.report('Not changing userParameters from base64 encoding on %s' % (obj.dn))
569 return
571 m = ldb.Message()
572 m.dn = obj.dn
573 m['value'] = ldb.MessageElement(b64decode(obj[attrname][0]), ldb.FLAG_MOD_REPLACE, 'userParameters')
574 if self.do_modify(m, [],
575 "Failed to correct base64-encoded userParameters on %s by converting from base64" % (obj.dn)):
576 self.report("Corrected base64-encoded userParameters on %s by converting from base64" % (obj.dn))
578 def err_utf8_userParameters(self, obj, attrname, value):
579 '''handle a wrong userParameters'''
580 self.report("ERROR: wrongly formatted userParameters on %s, should not be psudo-UTF8 encoded" % (obj.dn))
581 if not self.confirm_all('Convert userParameters from UTF8 encoding on %s?' % (obj.dn), 'fix_utf8_userparameters'):
582 self.report('Not changing userParameters from UTF8 encoding on %s' % (obj.dn))
583 return
585 m = ldb.Message()
586 m.dn = obj.dn
587 m['value'] = ldb.MessageElement(obj[attrname][0].decode('utf8').encode('utf-16-le'),
588 ldb.FLAG_MOD_REPLACE, 'userParameters')
589 if self.do_modify(m, [],
590 "Failed to correct psudo-UTF8 encoded userParameters on %s by converting from UTF8" % (obj.dn)):
591 self.report("Corrected psudo-UTF8 encoded userParameters on %s by converting from UTF8" % (obj.dn))
593 def err_doubled_userParameters(self, obj, attrname, value):
594 '''handle a wrong userParameters'''
595 self.report("ERROR: wrongly formatted userParameters on %s, should not be double UTF16 encoded" % (obj.dn))
596 if not self.confirm_all('Convert userParameters from doubled UTF-16 encoding on %s?' % (obj.dn), 'fix_doubled_userparameters'):
597 self.report('Not changing userParameters from doubled UTF-16 encoding on %s' % (obj.dn))
598 return
600 m = ldb.Message()
601 m.dn = obj.dn
602 m['value'] = ldb.MessageElement(obj[attrname][0].decode('utf-16-le').decode('utf-16-le').encode('utf-16-le'),
603 ldb.FLAG_MOD_REPLACE, 'userParameters')
604 if self.do_modify(m, [],
605 "Failed to correct doubled-UTF16 encoded userParameters on %s by converting" % (obj.dn)):
606 self.report("Corrected doubled-UTF16 encoded userParameters on %s by converting" % (obj.dn))
608 def err_odd_userParameters(self, obj, attrname):
609 # This is a truncated userParameters due to a pre 4.1 replication bug
610 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)))
612 def find_revealed_link(self, dn, attrname, guid):
613 '''return a revealed link in an object'''
614 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[attrname],
615 controls=["show_deleted:0", "extended_dn:0", "reveal_internals:0"])
616 syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname)
617 for val in res[0][attrname]:
618 dsdb_dn = dsdb_Dn(self.samdb, val, syntax_oid)
619 guid2 = dsdb_dn.dn.get_extended_component("GUID")
620 if guid == guid2:
621 return dsdb_dn
622 return None
624 def check_dn(self, obj, attrname, syntax_oid):
625 '''check a DN attribute for correctness'''
626 error_count = 0
627 for val in obj[attrname]:
628 dsdb_dn = dsdb_Dn(self.samdb, val, syntax_oid)
630 # all DNs should have a GUID component
631 guid = dsdb_dn.dn.get_extended_component("GUID")
632 if guid is None:
633 error_count += 1
634 self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn,
635 "missing GUID")
636 continue
638 guidstr = str(misc.GUID(guid))
640 attrs = ['isDeleted']
642 if (str(attrname).lower() == 'msds-hasinstantiatedncs') and (obj.dn == self.ntds_dsa):
643 fixing_msDS_HasInstantiatedNCs = True
644 attrs.append("instanceType")
645 else:
646 fixing_msDS_HasInstantiatedNCs = False
648 linkID = self.samdb_schema.get_linkId_from_lDAPDisplayName(attrname)
649 reverse_link_name = self.samdb_schema.get_backlink_from_lDAPDisplayName(attrname)
650 if reverse_link_name is not None:
651 attrs.append(reverse_link_name)
653 # check its the right GUID
654 try:
655 res = self.samdb.search(base="<GUID=%s>" % guidstr, scope=ldb.SCOPE_BASE,
656 attrs=attrs, controls=["extended_dn:1:1", "show_recycled:1"])
657 except ldb.LdbError, (enum, estr):
658 error_count += 1
659 self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn, "incorrect GUID")
660 continue
662 if fixing_msDS_HasInstantiatedNCs:
663 dsdb_dn.prefix = "B:8:%08X:" % int(res[0]['instanceType'][0])
664 dsdb_dn.binary = "%08X" % int(res[0]['instanceType'][0])
666 if str(dsdb_dn) != val:
667 error_count +=1
668 self.err_incorrect_binary_dn(obj.dn, attrname, val, dsdb_dn, "incorrect instanceType part of Binary DN")
669 continue
671 # now we have two cases - the source object might or might not be deleted
672 is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper() == 'TRUE'
673 target_is_deleted = 'isDeleted' in res[0] and res[0]['isDeleted'][0].upper() == 'TRUE'
675 # the target DN is not allowed to be deleted, unless the target DN is the
676 # special Deleted Objects container
677 if target_is_deleted and not is_deleted and not self.is_deleted_objects_dn(dsdb_dn):
678 error_count += 1
679 self.err_deleted_dn(obj.dn, attrname, val, dsdb_dn, res[0].dn)
680 continue
682 # check the DN matches in string form
683 if res[0].dn.extended_str() != dsdb_dn.dn.extended_str():
684 error_count += 1
685 self.err_dn_target_mismatch(obj.dn, attrname, val, dsdb_dn,
686 res[0].dn, "incorrect string version of DN")
687 continue
689 if is_deleted and not target_is_deleted and reverse_link_name is not None:
690 revealed_dn = self.find_revealed_link(obj.dn, attrname, guid)
691 rmd_flags = revealed_dn.dn.get_extended_component("RMD_FLAGS")
692 if rmd_flags is not None and (int(rmd_flags) & 1) == 0:
693 # the RMD_FLAGS for this link should be 1, as the target is deleted
694 self.err_incorrect_rmd_flags(obj, attrname, revealed_dn)
695 continue
697 # check the reverse_link is correct if there should be one
698 if reverse_link_name is not None:
699 match_count = 0
700 if reverse_link_name in res[0]:
701 for v in res[0][reverse_link_name]:
702 if v == obj.dn.extended_str():
703 match_count += 1
704 if match_count != 1:
705 error_count += 1
706 if linkID & 1:
707 self.err_orphaned_backlink(obj, attrname, val, reverse_link_name, dsdb_dn.dn)
708 else:
709 self.err_missing_backlink(obj, attrname, val, reverse_link_name, dsdb_dn.dn)
710 continue
712 return error_count
715 def get_originating_time(self, val, attid):
716 '''Read metadata properties and return the originating time for
717 a given attributeId.
719 :return: the originating time or 0 if not found
722 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
723 obj = repl.ctr
725 for o in repl.ctr.array:
726 if o.attid == attid:
727 return o.originating_change_time
729 return 0
731 def process_metadata(self, dn, val):
732 '''Read metadata properties and list attributes in it.
733 raises KeyError if the attid is unknown.'''
735 set_att = set()
736 wrong_attids = set()
737 list_attid = []
738 in_schema_nc = dn.is_child_of(self.schema_dn)
740 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
741 obj = repl.ctr
743 for o in repl.ctr.array:
744 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
745 set_att.add(att.lower())
746 list_attid.append(o.attid)
747 correct_attid = self.samdb_schema.get_attid_from_lDAPDisplayName(att,
748 is_schema_nc=in_schema_nc)
749 if correct_attid != o.attid:
750 wrong_attids.add(o.attid)
752 return (set_att, list_attid, wrong_attids)
755 def fix_metadata(self, dn, attr):
756 '''re-write replPropertyMetaData elements for a single attribute for a
757 object. This is used to fix missing replPropertyMetaData elements'''
758 res = self.samdb.search(base = dn, scope=ldb.SCOPE_BASE, attrs = [attr],
759 controls = ["search_options:1:2", "show_recycled:1"])
760 msg = res[0]
761 nmsg = ldb.Message()
762 nmsg.dn = dn
763 nmsg[attr] = ldb.MessageElement(msg[attr], ldb.FLAG_MOD_REPLACE, attr)
764 if self.do_modify(nmsg, ["relax:0", "provision:0", "show_recycled:1"],
765 "Failed to fix metadata for attribute %s" % attr):
766 self.report("Fixed metadata for attribute %s" % attr)
768 def ace_get_effective_inherited_type(self, ace):
769 if ace.flags & security.SEC_ACE_FLAG_INHERIT_ONLY:
770 return None
772 check = False
773 if ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
774 check = True
775 elif ace.type == security.SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
776 check = True
777 elif ace.type == security.SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
778 check = True
779 elif ace.type == security.SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
780 check = True
782 if not check:
783 return None
785 if not ace.object.flags & security.SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT:
786 return None
788 return str(ace.object.inherited_type)
790 def lookup_class_schemaIDGUID(self, cls):
791 if cls in self.class_schemaIDGUID:
792 return self.class_schemaIDGUID[cls]
794 flt = "(&(ldapDisplayName=%s)(objectClass=classSchema))" % cls
795 res = self.samdb.search(base=self.schema_dn,
796 expression=flt,
797 attrs=["schemaIDGUID"])
798 t = str(ndr_unpack(misc.GUID, res[0]["schemaIDGUID"][0]))
800 self.class_schemaIDGUID[cls] = t
801 return t
803 def process_sd(self, dn, obj):
804 sd_attr = "nTSecurityDescriptor"
805 sd_val = obj[sd_attr]
807 sd = ndr_unpack(security.descriptor, str(sd_val))
809 is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper() == 'TRUE'
810 if is_deleted:
811 # we don't fix deleted objects
812 return (sd, None)
814 sd_clean = security.descriptor()
815 sd_clean.owner_sid = sd.owner_sid
816 sd_clean.group_sid = sd.group_sid
817 sd_clean.type = sd.type
818 sd_clean.revision = sd.revision
820 broken = False
821 last_inherited_type = None
823 aces = []
824 if sd.sacl is not None:
825 aces = sd.sacl.aces
826 for i in range(0, len(aces)):
827 ace = aces[i]
829 if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE:
830 sd_clean.sacl_add(ace)
831 continue
833 t = self.ace_get_effective_inherited_type(ace)
834 if t is None:
835 continue
837 if last_inherited_type is not None:
838 if t != last_inherited_type:
839 # if it inherited from more than
840 # one type it's very likely to be broken
842 # If not the recalculation will calculate
843 # the same result.
844 broken = True
845 continue
847 last_inherited_type = t
849 aces = []
850 if sd.dacl is not None:
851 aces = sd.dacl.aces
852 for i in range(0, len(aces)):
853 ace = aces[i]
855 if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE:
856 sd_clean.dacl_add(ace)
857 continue
859 t = self.ace_get_effective_inherited_type(ace)
860 if t is None:
861 continue
863 if last_inherited_type is not None:
864 if t != last_inherited_type:
865 # if it inherited from more than
866 # one type it's very likely to be broken
868 # If not the recalculation will calculate
869 # the same result.
870 broken = True
871 continue
873 last_inherited_type = t
875 if broken:
876 return (sd_clean, sd)
878 if last_inherited_type is None:
879 # ok
880 return (sd, None)
882 cls = None
883 try:
884 cls = obj["objectClass"][-1]
885 except KeyError, e:
886 pass
888 if cls is None:
889 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE,
890 attrs=["isDeleted", "objectClass"],
891 controls=["show_recycled:1"])
892 o = res[0]
893 is_deleted = 'isDeleted' in o and o['isDeleted'][0].upper() == 'TRUE'
894 if is_deleted:
895 # we don't fix deleted objects
896 return (sd, None)
897 cls = o["objectClass"][-1]
899 t = self.lookup_class_schemaIDGUID(cls)
901 if t != last_inherited_type:
902 # broken
903 return (sd_clean, sd)
905 # ok
906 return (sd, None)
908 def err_wrong_sd(self, dn, sd, sd_broken):
909 '''re-write the SD due to incorrect inherited ACEs'''
910 sd_attr = "nTSecurityDescriptor"
911 sd_val = ndr_pack(sd)
912 sd_flags = security.SECINFO_DACL | security.SECINFO_SACL
914 if not self.confirm_all('Fix %s on %s?' % (sd_attr, dn), 'fix_ntsecuritydescriptor'):
915 self.report('Not fixing %s on %s\n' % (sd_attr, dn))
916 return
918 nmsg = ldb.Message()
919 nmsg.dn = dn
920 nmsg[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
921 if self.do_modify(nmsg, ["sd_flags:1:%d" % sd_flags],
922 "Failed to fix attribute %s" % sd_attr):
923 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
925 def err_wrong_default_sd(self, dn, sd, sd_old, diff):
926 '''re-write the SD due to not matching the default (optional mode for fixing an incorrect provision)'''
927 sd_attr = "nTSecurityDescriptor"
928 sd_val = ndr_pack(sd)
929 sd_old_val = ndr_pack(sd_old)
930 sd_flags = security.SECINFO_DACL | security.SECINFO_SACL
931 if sd.owner_sid is not None:
932 sd_flags |= security.SECINFO_OWNER
933 if sd.group_sid is not None:
934 sd_flags |= security.SECINFO_GROUP
936 if not self.confirm_all('Reset %s on %s back to provision default?\n%s' % (sd_attr, dn, diff), 'reset_all_well_known_acls'):
937 self.report('Not resetting %s on %s\n' % (sd_attr, dn))
938 return
940 m = ldb.Message()
941 m.dn = dn
942 m[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
943 if self.do_modify(m, ["sd_flags:1:%d" % sd_flags],
944 "Failed to reset attribute %s" % sd_attr):
945 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
947 def err_missing_sd_owner(self, dn, sd):
948 '''re-write the SD due to a missing owner or group'''
949 sd_attr = "nTSecurityDescriptor"
950 sd_val = ndr_pack(sd)
951 sd_flags = security.SECINFO_OWNER | security.SECINFO_GROUP
953 if not self.confirm_all('Fix missing owner or group in %s on %s?' % (sd_attr, dn), 'fix_ntsecuritydescriptor_owner_group'):
954 self.report('Not fixing missing owner or group %s on %s\n' % (sd_attr, dn))
955 return
957 nmsg = ldb.Message()
958 nmsg.dn = dn
959 nmsg[sd_attr] = ldb.MessageElement(sd_val, ldb.FLAG_MOD_REPLACE, sd_attr)
961 # By setting the session_info to admin_session_info and
962 # setting the security.SECINFO_OWNER | security.SECINFO_GROUP
963 # flags we cause the descriptor module to set the correct
964 # owner and group on the SD, replacing the None/NULL values
965 # for owner_sid and group_sid currently present.
967 # The admin_session_info matches that used in provision, and
968 # is the best guess we can make for an existing object that
969 # hasn't had something specifically set.
971 # This is important for the dns related naming contexts.
972 self.samdb.set_session_info(self.admin_session_info)
973 if self.do_modify(nmsg, ["sd_flags:1:%d" % sd_flags],
974 "Failed to fix metadata for attribute %s" % sd_attr):
975 self.report("Fixed attribute '%s' of '%s'\n" % (sd_attr, dn))
976 self.samdb.set_session_info(self.system_session_info)
979 def has_replmetadata_zero_invocationid(self, dn, repl_meta_data):
980 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
981 str(repl_meta_data))
982 ctr = repl.ctr
983 found = False
984 for o in ctr.array:
985 # Search for a zero invocationID
986 if o.originating_invocation_id != misc.GUID("00000000-0000-0000-0000-000000000000"):
987 continue
989 found = True
990 self.report('''ERROR: on replPropertyMetaData of %s, the instanceType on attribute 0x%08x,
991 version %d changed at %s is 00000000-0000-0000-0000-000000000000,
992 but should be non-zero. Proposed fix is to set to our invocationID (%s).'''
993 % (dn, o.attid, o.version,
994 time.ctime(samba.nttime2unix(o.originating_change_time)),
995 self.samdb.get_invocation_id()))
997 return found
1000 def err_replmetadata_zero_invocationid(self, dn, attr, repl_meta_data):
1001 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1002 str(repl_meta_data))
1003 ctr = repl.ctr
1004 now = samba.unix2nttime(int(time.time()))
1005 found = False
1006 for o in ctr.array:
1007 # Search for a zero invocationID
1008 if o.originating_invocation_id != misc.GUID("00000000-0000-0000-0000-000000000000"):
1009 continue
1011 found = True
1012 seq = self.samdb.sequence_number(ldb.SEQ_NEXT)
1013 o.version = o.version + 1
1014 o.originating_change_time = now
1015 o.originating_invocation_id = misc.GUID(self.samdb.get_invocation_id())
1016 o.originating_usn = seq
1017 o.local_usn = seq
1019 if found:
1020 replBlob = ndr_pack(repl)
1021 msg = ldb.Message()
1022 msg.dn = dn
1024 if not self.confirm_all('Fix %s on %s by setting originating_invocation_id on some elements to our invocationID %s?'
1025 % (attr, dn, self.samdb.get_invocation_id()), 'fix_replmetadata_zero_invocationid'):
1026 self.report('Not fixing zero originating_invocation_id in %s on %s\n' % (attr, dn))
1027 return
1029 nmsg = ldb.Message()
1030 nmsg.dn = dn
1031 nmsg[attr] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, attr)
1032 if self.do_modify(nmsg, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA,
1033 "local_oid:1.3.6.1.4.1.7165.4.3.14:0"],
1034 "Failed to fix attribute %s" % attr):
1035 self.report("Fixed attribute '%s' of '%s'\n" % (attr, dn))
1038 def err_replmetadata_unknown_attid(self, dn, attr, repl_meta_data):
1039 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1040 str(repl_meta_data))
1041 ctr = repl.ctr
1042 for o in ctr.array:
1043 # Search for an invalid attid
1044 try:
1045 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1046 except KeyError:
1047 self.report('ERROR: attributeID 0X%0X is not known in our schema, not fixing %s on %s\n' % (o.attid, attr, dn))
1048 return
1051 def err_replmetadata_incorrect_attid(self, dn, attr, repl_meta_data, wrong_attids):
1052 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1053 str(repl_meta_data))
1054 fix = False
1056 set_att = set()
1057 remove_attid = set()
1058 hash_att = {}
1060 in_schema_nc = dn.is_child_of(self.schema_dn)
1062 ctr = repl.ctr
1063 # Sort the array, except for the last element. This strange
1064 # construction, creating a new list, due to bugs in samba's
1065 # array handling in IDL generated objects.
1066 ctr.array = sorted(ctr.array[:-1], key=lambda o: o.attid) + [ctr.array[-1]]
1067 # Now walk it in reverse, so we see the low (and so incorrect,
1068 # the correct values are above 0x80000000) values first and
1069 # remove the 'second' value we see.
1070 for o in reversed(ctr.array):
1071 print "%s: 0x%08x" % (dn, o.attid)
1072 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1073 if att.lower() in set_att:
1074 self.report('ERROR: duplicate attributeID values for %s in %s on %s\n' % (att, attr, dn))
1075 if not self.confirm_all('Fix %s on %s by removing the duplicate value 0x%08x for %s (keeping 0x%08x)?'
1076 % (attr, dn, o.attid, att, hash_att[att].attid),
1077 'fix_replmetadata_duplicate_attid'):
1078 self.report('Not fixing duplicate value 0x%08x for %s in %s on %s\n'
1079 % (o.attid, att, attr, dn))
1080 return
1081 fix = True
1082 remove_attid.add(o.attid)
1083 # We want to set the metadata for the most recent
1084 # update to have been applied locally, that is the metadata
1085 # matching the (eg string) value in the attribute
1086 if o.local_usn > hash_att[att].local_usn:
1087 # This is always what we would have sent over DRS,
1088 # because the DRS server will have sent the
1089 # msDS-IntID, but with the values from both
1090 # attribute entries.
1091 hash_att[att].version = o.version
1092 hash_att[att].originating_change_time = o.originating_change_time
1093 hash_att[att].originating_invocation_id = o.originating_invocation_id
1094 hash_att[att].originating_usn = o.originating_usn
1095 hash_att[att].local_usn = o.local_usn
1097 # Do not re-add the value to the set or overwrite the hash value
1098 continue
1100 hash_att[att] = o
1101 set_att.add(att.lower())
1103 # Generate a real list we can sort on properly
1104 new_list = [o for o in ctr.array if o.attid not in remove_attid]
1106 if (len(wrong_attids) > 0):
1107 for o in new_list:
1108 if o.attid in wrong_attids:
1109 att = self.samdb_schema.get_lDAPDisplayName_by_attid(o.attid)
1110 correct_attid = self.samdb_schema.get_attid_from_lDAPDisplayName(att, is_schema_nc=in_schema_nc)
1111 self.report('ERROR: incorrect attributeID values in %s on %s\n' % (attr, dn))
1112 if not self.confirm_all('Fix %s on %s by replacing incorrect value 0x%08x for %s (new 0x%08x)?'
1113 % (attr, dn, o.attid, att, hash_att[att].attid), 'fix_replmetadata_wrong_attid'):
1114 self.report('Not fixing incorrect value 0x%08x with 0x%08x for %s in %s on %s\n'
1115 % (o.attid, correct_attid, att, attr, dn))
1116 return
1117 fix = True
1118 o.attid = correct_attid
1119 if fix:
1120 # Sort the array, except for the last element (we changed
1121 # the value so must re-sort)
1122 new_list[:-1] = sorted(new_list[:-1], key=lambda o: o.attid)
1124 # If we did not already need to fix it, then ask about sorting
1125 if not fix:
1126 self.report('ERROR: unsorted attributeID values in %s on %s\n' % (attr, dn))
1127 if not self.confirm_all('Fix %s on %s by sorting the attribute list?'
1128 % (attr, dn), 'fix_replmetadata_unsorted_attid'):
1129 self.report('Not fixing %s on %s\n' % (attr, dn))
1130 return
1132 # The actual sort done is done at the top of the function
1134 ctr.count = len(new_list)
1135 ctr.array = new_list
1136 replBlob = ndr_pack(repl)
1138 nmsg = ldb.Message()
1139 nmsg.dn = dn
1140 nmsg[attr] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, attr)
1141 if self.do_modify(nmsg, ["local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA,
1142 "local_oid:1.3.6.1.4.1.7165.4.3.14:0",
1143 "local_oid:1.3.6.1.4.1.7165.4.3.25:0"],
1144 "Failed to fix attribute %s" % attr):
1145 self.report("Fixed attribute '%s' of '%s'\n" % (attr, dn))
1148 def is_deleted_deleted_objects(self, obj):
1149 faulty = False
1150 if "description" not in obj:
1151 self.report("ERROR: description not present on Deleted Objects container %s" % obj.dn)
1152 faulty = True
1153 if "showInAdvancedViewOnly" not in obj:
1154 self.report("ERROR: showInAdvancedViewOnly not present on Deleted Objects container %s" % obj.dn)
1155 faulty = True
1156 if "objectCategory" not in obj:
1157 self.report("ERROR: objectCategory not present on Deleted Objects container %s" % obj.dn)
1158 faulty = True
1159 if "isCriticalSystemObject" not in obj:
1160 self.report("ERROR: isCriticalSystemObject not present on Deleted Objects container %s" % obj.dn)
1161 faulty = True
1162 if "isRecycled" in obj:
1163 self.report("ERROR: isRecycled present on Deleted Objects container %s" % obj.dn)
1164 faulty = True
1165 return faulty
1168 def err_deleted_deleted_objects(self, obj):
1169 nmsg = ldb.Message()
1170 nmsg.dn = dn = obj.dn
1172 if "description" not in obj:
1173 nmsg["description"] = ldb.MessageElement("Container for deleted objects", ldb.FLAG_MOD_REPLACE, "description")
1174 if "showInAdvancedViewOnly" not in obj:
1175 nmsg["showInAdvancedViewOnly"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "showInAdvancedViewOnly")
1176 if "objectCategory" not in obj:
1177 nmsg["objectCategory"] = ldb.MessageElement("CN=Container,%s" % self.schema_dn, ldb.FLAG_MOD_REPLACE, "objectCategory")
1178 if "isCriticalSystemObject" not in obj:
1179 nmsg["isCriticalSystemObject"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isCriticalSystemObject")
1180 if "isRecycled" in obj:
1181 nmsg["isRecycled"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_DELETE, "isRecycled")
1183 if not self.confirm_all('Fix Deleted Objects container %s by restoring default attributes?'
1184 % (dn), 'fix_deleted_deleted_objects'):
1185 self.report('Not fixing missing/incorrect attributes on %s\n' % (dn))
1186 return
1188 if self.do_modify(nmsg, ["relax:0"],
1189 "Failed to fix Deleted Objects container %s" % dn):
1190 self.report("Fixed Deleted Objects container '%s'\n" % (dn))
1193 def is_fsmo_role(self, dn):
1194 if dn == self.samdb.domain_dn:
1195 return True
1196 if dn == self.infrastructure_dn:
1197 return True
1198 if dn == self.naming_dn:
1199 return True
1200 if dn == self.schema_dn:
1201 return True
1202 if dn == self.rid_dn:
1203 return True
1205 return False
1207 def calculate_instancetype(self, dn):
1208 instancetype = 0
1209 nc_root = self.samdb.get_nc_root(dn)
1210 if dn == nc_root:
1211 instancetype |= dsdb.INSTANCE_TYPE_IS_NC_HEAD
1212 try:
1213 self.samdb.search(base=dn.parent(), scope=ldb.SCOPE_BASE, attrs=[], controls=["show_recycled:1"])
1214 except ldb.LdbError, (enum, estr):
1215 if enum != ldb.ERR_NO_SUCH_OBJECT:
1216 raise
1217 else:
1218 instancetype |= dsdb.INSTANCE_TYPE_NC_ABOVE
1220 if self.write_ncs is not None and str(nc_root) in self.write_ncs:
1221 instancetype |= dsdb.INSTANCE_TYPE_WRITE
1223 return instancetype
1225 def get_wellknown_sd(self, dn):
1226 for [sd_dn, descriptor_fn] in self.wellknown_sds:
1227 if dn == sd_dn:
1228 domain_sid = security.dom_sid(self.samdb.get_domain_sid())
1229 return ndr_unpack(security.descriptor,
1230 descriptor_fn(domain_sid,
1231 name_map=self.name_map))
1233 raise KeyError
1235 def check_object(self, dn, attrs=['*']):
1236 '''check one object'''
1237 if self.verbose:
1238 self.report("Checking object %s" % dn)
1239 if "dn" in map(str.lower, attrs):
1240 attrs.append("name")
1241 if "distinguishedname" in map(str.lower, attrs):
1242 attrs.append("name")
1243 if str(dn.get_rdn_name()).lower() in map(str.lower, attrs):
1244 attrs.append("name")
1245 if 'name' in map(str.lower, attrs):
1246 attrs.append(dn.get_rdn_name())
1247 attrs.append("isDeleted")
1248 attrs.append("systemFlags")
1249 if '*' in attrs:
1250 attrs.append("replPropertyMetaData")
1252 try:
1253 sd_flags = 0
1254 sd_flags |= security.SECINFO_OWNER
1255 sd_flags |= security.SECINFO_GROUP
1256 sd_flags |= security.SECINFO_DACL
1257 sd_flags |= security.SECINFO_SACL
1259 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE,
1260 controls=[
1261 "extended_dn:1:1",
1262 "show_recycled:1",
1263 "show_deleted:1",
1264 "sd_flags:1:%d" % sd_flags,
1266 attrs=attrs)
1267 except ldb.LdbError, (enum, estr):
1268 if enum == ldb.ERR_NO_SUCH_OBJECT:
1269 if self.in_transaction:
1270 self.report("ERROR: Object %s disappeared during check" % dn)
1271 return 1
1272 return 0
1273 raise
1274 if len(res) != 1:
1275 self.report("ERROR: Object %s failed to load during check" % dn)
1276 return 1
1277 obj = res[0]
1278 error_count = 0
1279 set_attrs_from_md = set()
1280 set_attrs_seen = set()
1281 got_repl_property_meta_data = False
1282 got_objectclass = False
1284 nc_dn = self.samdb.get_nc_root(obj.dn)
1285 try:
1286 deleted_objects_dn = self.samdb.get_wellknown_dn(nc_dn,
1287 samba.dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER)
1288 except KeyError, e:
1289 deleted_objects_dn = ldb.Dn(self.samdb, "CN=Deleted Objects,%s" % nc_dn)
1291 object_rdn_attr = None
1292 object_rdn_val = None
1293 name_val = None
1294 isDeleted = False
1295 systemFlags = 0
1297 for attrname in obj:
1298 if attrname == 'dn':
1299 continue
1301 if str(attrname).lower() == 'objectclass':
1302 got_objectclass = True
1304 if str(attrname).lower() == "name":
1305 if len(obj[attrname]) != 1:
1306 error_count += 1
1307 self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" %
1308 (len(obj[attrname]), attrname, str(obj.dn)))
1309 else:
1310 name_val = obj[attrname][0]
1312 if str(attrname).lower() == str(obj.dn.get_rdn_name()).lower():
1313 object_rdn_attr = attrname
1314 if len(obj[attrname]) != 1:
1315 error_count += 1
1316 self.report("ERROR: Not fixing num_values(%d) for '%s' on '%s'" %
1317 (len(obj[attrname]), attrname, str(obj.dn)))
1318 else:
1319 object_rdn_val = obj[attrname][0]
1321 if str(attrname).lower() == 'isdeleted':
1322 if obj[attrname][0] != "FALSE":
1323 isDeleted = True
1325 if str(attrname).lower() == 'systemflags':
1326 systemFlags = int(obj[attrname][0])
1328 if str(attrname).lower() == 'replpropertymetadata':
1329 if self.has_replmetadata_zero_invocationid(dn, obj[attrname]):
1330 error_count += 1
1331 self.err_replmetadata_zero_invocationid(dn, attrname, obj[attrname])
1332 # We don't continue, as we may also have other fixes for this attribute
1333 # based on what other attributes we see.
1335 try:
1336 (set_attrs_from_md, list_attid_from_md, wrong_attids) \
1337 = self.process_metadata(dn, obj[attrname])
1338 except KeyError:
1339 error_count += 1
1340 self.err_replmetadata_unknown_attid(dn, attrname, obj[attrname])
1341 continue
1343 if len(set_attrs_from_md) < len(list_attid_from_md) \
1344 or len(wrong_attids) > 0 \
1345 or sorted(list_attid_from_md[:-1]) != list_attid_from_md[:-1]:
1346 error_count +=1
1347 self.err_replmetadata_incorrect_attid(dn, attrname, obj[attrname], wrong_attids)
1349 else:
1350 # Here we check that the first attid is 0
1351 # (objectClass) and that the last on is the RDN
1352 # from the DN.
1353 rdn_attid = self.samdb_schema.get_attid_from_lDAPDisplayName(dn.get_rdn_name())
1354 if list_attid_from_md[-1] != rdn_attid:
1355 error_count += 1
1356 self.report("ERROR: Not fixing incorrect final attributeID in '%s' on '%s', it should match the RDN %s" %
1357 (attrname, str(dn), dn.get_rdn_name()))
1359 if list_attid_from_md[0] != 0:
1360 error_count += 1
1361 self.report("ERROR: Not fixing incorrect inital attributeID in '%s' on '%s', it should be objectClass" %
1362 (attrname, str(dn)))
1364 got_repl_property_meta_data = True
1365 continue
1367 if str(attrname).lower() == 'ntsecuritydescriptor':
1368 (sd, sd_broken) = self.process_sd(dn, obj)
1369 if sd_broken is not None:
1370 self.err_wrong_sd(dn, sd, sd_broken)
1371 error_count += 1
1372 continue
1374 if sd.owner_sid is None or sd.group_sid is None:
1375 self.err_missing_sd_owner(dn, sd)
1376 error_count += 1
1377 continue
1379 if self.reset_well_known_acls:
1380 try:
1381 well_known_sd = self.get_wellknown_sd(dn)
1382 except KeyError:
1383 continue
1385 current_sd = ndr_unpack(security.descriptor,
1386 str(obj[attrname][0]))
1388 diff = get_diff_sds(well_known_sd, current_sd, security.dom_sid(self.samdb.get_domain_sid()))
1389 if diff != "":
1390 self.err_wrong_default_sd(dn, well_known_sd, current_sd, diff)
1391 error_count += 1
1392 continue
1393 continue
1395 if str(attrname).lower() == 'objectclass':
1396 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, obj[attrname])
1397 # Do not consider the attribute incorrect if:
1398 # - The sorted (alphabetically) list is the same, inclding case
1399 # - The first and last elements are the same
1401 # This avoids triggering an error due to
1402 # non-determinism in the sort routine in (at least)
1403 # 4.3 and earlier, and the fact that any AUX classes
1404 # in these attributes are also not sorted when
1405 # imported from Windows (they are just in the reverse
1406 # order of last set)
1407 if sorted(normalised) != sorted(obj[attrname]) \
1408 or normalised[0] != obj[attrname][0] \
1409 or normalised[-1] != obj[attrname][-1]:
1410 self.err_normalise_mismatch_replace(dn, attrname, list(obj[attrname]))
1411 error_count += 1
1412 continue
1414 if str(attrname).lower() == 'userparameters':
1415 if len(obj[attrname][0]) == 1 and obj[attrname][0][0] == '\x20':
1416 error_count += 1
1417 self.err_short_userParameters(obj, attrname, obj[attrname])
1418 continue
1420 elif obj[attrname][0][:16] == '\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00':
1421 # This is the correct, normal prefix
1422 continue
1424 elif obj[attrname][0][:20] == 'IAAgACAAIAAgACAAIAAg':
1425 # this is the typical prefix from a windows migration
1426 error_count += 1
1427 self.err_base64_userParameters(obj, attrname, obj[attrname])
1428 continue
1430 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':
1431 # This is a prefix that is not in UTF-16 format for the space or munged dialback prefix
1432 error_count += 1
1433 self.err_utf8_userParameters(obj, attrname, obj[attrname])
1434 continue
1436 elif len(obj[attrname][0]) % 2 != 0:
1437 # This is a value that isn't even in length
1438 error_count += 1
1439 self.err_odd_userParameters(obj, attrname, obj[attrname])
1440 continue
1442 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':
1443 # This is a prefix that would happen if a SAMR-written value was replicated from a Samba 4.1 server to a working server
1444 error_count += 1
1445 self.err_doubled_userParameters(obj, attrname, obj[attrname])
1446 continue
1448 # check for empty attributes
1449 for val in obj[attrname]:
1450 if val == '':
1451 self.err_empty_attribute(dn, attrname)
1452 error_count += 1
1453 continue
1455 # get the syntax oid for the attribute, so we can can have
1456 # special handling for some specific attribute types
1457 try:
1458 syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname)
1459 except Exception, msg:
1460 self.err_unknown_attribute(obj, attrname)
1461 error_count += 1
1462 continue
1464 flag = self.samdb_schema.get_systemFlags_from_lDAPDisplayName(attrname)
1465 if (not flag & dsdb.DS_FLAG_ATTR_NOT_REPLICATED
1466 and not flag & dsdb.DS_FLAG_ATTR_IS_CONSTRUCTED
1467 and not self.samdb_schema.get_linkId_from_lDAPDisplayName(attrname)):
1468 set_attrs_seen.add(str(attrname).lower())
1470 if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_OR_NAME,
1471 dsdb.DSDB_SYNTAX_STRING_DN, ldb.SYNTAX_DN ]:
1472 # it's some form of DN, do specialised checking on those
1473 error_count += self.check_dn(obj, attrname, syntax_oid)
1475 values = set()
1476 # check for incorrectly normalised attributes
1477 for val in obj[attrname]:
1478 values.add(str(val))
1480 normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, [val])
1481 if len(normalised) != 1 or normalised[0] != val:
1482 self.err_normalise_mismatch(dn, attrname, obj[attrname])
1483 error_count += 1
1484 break
1486 if len(obj[attrname]) != len(values):
1487 self.err_duplicate_values(dn, attrname, obj[attrname], list(values))
1488 error_count += 1
1489 break
1491 if str(attrname).lower() == "instancetype":
1492 calculated_instancetype = self.calculate_instancetype(dn)
1493 if len(obj["instanceType"]) != 1 or obj["instanceType"][0] != str(calculated_instancetype):
1494 error_count += 1
1495 self.err_wrong_instancetype(obj, calculated_instancetype)
1497 if not got_objectclass and ("*" in attrs or "objectclass" in map(str.lower, attrs)):
1498 error_count += 1
1499 self.err_missing_objectclass(dn)
1501 if ("*" in attrs or "name" in map(str.lower, attrs)):
1502 if name_val is None:
1503 error_count += 1
1504 self.report("ERROR: Not fixing missing 'name' on '%s'" % (str(obj.dn)))
1505 if object_rdn_attr is None:
1506 error_count += 1
1507 self.report("ERROR: Not fixing missing '%s' on '%s'" % (obj.dn.get_rdn_name(), str(obj.dn)))
1509 if name_val is not None:
1510 parent_dn = None
1511 if isDeleted:
1512 if not (systemFlags & samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE):
1513 parent_dn = deleted_objects_dn
1514 if parent_dn is None:
1515 parent_dn = obj.dn.parent()
1516 expected_dn = ldb.Dn(self.samdb, "RDN=RDN,%s" % (parent_dn))
1517 expected_dn.set_component(0, obj.dn.get_rdn_name(), name_val)
1519 if obj.dn == deleted_objects_dn:
1520 expected_dn = obj.dn
1522 if expected_dn != obj.dn:
1523 error_count += 1
1524 self.err_wrong_dn(obj, expected_dn, object_rdn_attr, object_rdn_val, name_val)
1525 elif obj.dn.get_rdn_value() != object_rdn_val:
1526 error_count += 1
1527 self.report("ERROR: Not fixing %s=%r on '%s'" % (object_rdn_attr, object_rdn_val, str(obj.dn)))
1529 show_dn = True
1530 if got_repl_property_meta_data:
1531 if obj.dn == deleted_objects_dn:
1532 isDeletedAttId = 131120
1533 # It's 29/12/9999 at 23:59:59 UTC as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
1535 expectedTimeDo = 2650466015990000000
1536 originating = self.get_originating_time(obj["replPropertyMetaData"], isDeletedAttId)
1537 if originating != expectedTimeDo:
1538 if self.confirm_all("Fix isDeleted originating_change_time on '%s'" % str(dn), 'fix_time_metadata'):
1539 nmsg = ldb.Message()
1540 nmsg.dn = dn
1541 nmsg["isDeleted"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isDeleted")
1542 error_count += 1
1543 self.samdb.modify(nmsg, controls=["provision:0"])
1545 else:
1546 self.report("Not fixing isDeleted originating_change_time on '%s'" % str(dn))
1548 for att in set_attrs_seen.difference(set_attrs_from_md):
1549 if show_dn:
1550 self.report("On object %s" % dn)
1551 show_dn = False
1552 error_count += 1
1553 self.report("ERROR: Attribute %s not present in replication metadata" % att)
1554 if not self.confirm_all("Fix missing replPropertyMetaData element '%s'" % att, 'fix_all_metadata'):
1555 self.report("Not fixing missing replPropertyMetaData element '%s'" % att)
1556 continue
1557 self.fix_metadata(dn, att)
1559 if self.is_fsmo_role(dn):
1560 if "fSMORoleOwner" not in obj and ("*" in attrs or "fsmoroleowner" in map(str.lower, attrs)):
1561 self.err_no_fsmoRoleOwner(obj)
1562 error_count += 1
1564 try:
1565 if dn != self.samdb.get_root_basedn() and str(dn.parent()) not in self.dn_set:
1566 res = self.samdb.search(base=dn.parent(), scope=ldb.SCOPE_BASE,
1567 controls=["show_recycled:1", "show_deleted:1"])
1568 except ldb.LdbError, (enum, estr):
1569 if enum == ldb.ERR_NO_SUCH_OBJECT:
1570 self.err_missing_parent(obj)
1571 error_count += 1
1572 else:
1573 raise
1575 if dn in self.deleted_objects_containers and '*' in attrs:
1576 if self.is_deleted_deleted_objects(obj):
1577 self.err_deleted_deleted_objects(obj)
1578 error_count += 1
1580 return error_count
1582 ################################################################
1583 # check special @ROOTDSE attributes
1584 def check_rootdse(self):
1585 '''check the @ROOTDSE special object'''
1586 dn = ldb.Dn(self.samdb, '@ROOTDSE')
1587 if self.verbose:
1588 self.report("Checking object %s" % dn)
1589 res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE)
1590 if len(res) != 1:
1591 self.report("Object %s disappeared during check" % dn)
1592 return 1
1593 obj = res[0]
1594 error_count = 0
1596 # check that the dsServiceName is in GUID form
1597 if not 'dsServiceName' in obj:
1598 self.report('ERROR: dsServiceName missing in @ROOTDSE')
1599 return error_count+1
1601 if not obj['dsServiceName'][0].startswith('<GUID='):
1602 self.report('ERROR: dsServiceName not in GUID form in @ROOTDSE')
1603 error_count += 1
1604 if not self.confirm('Change dsServiceName to GUID form?'):
1605 return error_count
1606 res = self.samdb.search(base=ldb.Dn(self.samdb, obj['dsServiceName'][0]),
1607 scope=ldb.SCOPE_BASE, attrs=['objectGUID'])
1608 guid_str = str(ndr_unpack(misc.GUID, res[0]['objectGUID'][0]))
1609 m = ldb.Message()
1610 m.dn = dn
1611 m['dsServiceName'] = ldb.MessageElement("<GUID=%s>" % guid_str,
1612 ldb.FLAG_MOD_REPLACE, 'dsServiceName')
1613 if self.do_modify(m, [], "Failed to change dsServiceName to GUID form", validate=False):
1614 self.report("Changed dsServiceName to GUID form")
1615 return error_count
1618 ###############################################
1619 # re-index the database
1620 def reindex_database(self):
1621 '''re-index the whole database'''
1622 m = ldb.Message()
1623 m.dn = ldb.Dn(self.samdb, "@ATTRIBUTES")
1624 m['add'] = ldb.MessageElement('NONE', ldb.FLAG_MOD_ADD, 'force_reindex')
1625 m['delete'] = ldb.MessageElement('NONE', ldb.FLAG_MOD_DELETE, 'force_reindex')
1626 return self.do_modify(m, [], 're-indexed database', validate=False)
1628 ###############################################
1629 # reset @MODULES
1630 def reset_modules(self):
1631 '''reset @MODULES to that needed for current sam.ldb (to read a very old database)'''
1632 m = ldb.Message()
1633 m.dn = ldb.Dn(self.samdb, "@MODULES")
1634 m['@LIST'] = ldb.MessageElement('samba_dsdb', ldb.FLAG_MOD_REPLACE, '@LIST')
1635 return self.do_modify(m, [], 'reset @MODULES on database', validate=False)