s4-upgradeprovision: ignore objectSid
[Samba/gebeck_regimport.git] / source4 / scripting / bin / upgradeprovision
blobbf06a3c4f7905c9b579bfa1cb79562d307717731
1 #!/usr/bin/env python
2 # vim: expandtab
4 # Copyright (C) Matthieu Patou <mat@matws.net> 2009 - 2010
6 # Based on provision a Samba4 server by
7 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
8 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
26 import optparse
27 import os
28 import shutil
29 import sys
30 import tempfile
31 import re
32 import traceback
33 # Allow to run from s4 source directory (without installing samba)
34 sys.path.insert(0, "bin/python")
36 import ldb
37 import samba
38 import samba.getopt as options
40 from base64 import b64encode
41 from samba.credentials import DONT_USE_KERBEROS
42 from samba.auth import system_session, admin_session
43 from ldb import (SCOPE_SUBTREE, SCOPE_BASE,
44 FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE,
45 MessageElement, Message, Dn)
46 from samba import param, dsdb, Ldb
47 from samba.provision import (get_domain_descriptor, find_provision_key_parameters,
48 get_config_descriptor,
49 ProvisioningError, get_last_provision_usn,
50 get_max_usn, update_provision_usn, setup_path)
51 from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
52 from samba.dcerpc import security, drsblobs, xattr
53 from samba.ndr import ndr_unpack
54 from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
55 get_ldbs,
56 usn_in_range, identic_rename, get_diff_sddls,
57 update_secrets, CHANGE, ERROR, SIMPLE,
58 CHANGEALL, GUESS, CHANGESD, PROVISION,
59 updateOEMInfo, getOEMInfo, update_gpo,
60 delta_update_basesamdb, update_policyids,
61 update_machine_account_password,
62 search_constructed_attrs_stored,
63 int64range2str, update_dns_account_password,
64 increment_calculated_keyversion_number)
66 replace=2**FLAG_MOD_REPLACE
67 add=2**FLAG_MOD_ADD
68 delete=2**FLAG_MOD_DELETE
69 never=0
72 # Will be modified during provision to tell if default sd has been modified
73 # somehow ...
75 #Errors are always logged
77 __docformat__ = "restructuredText"
79 # Attributes that are never copied from the reference provision (even if they
80 # do not exist in the destination object).
81 # This is most probably because they are populated automatcally when object is
82 # created
83 # This also apply to imported object from reference provision
84 replAttrNotCopied = [ "dn", "whenCreated", "whenChanged", "objectGUID",
85 "parentGUID", "objectCategory", "distinguishedName",
86 "nTMixedDomain", "showInAdvancedViewOnly",
87 "instanceType", "msDS-Behavior-Version", "cn",
88 "lmPwdHistory", "pwdLastSet", "ntPwdHistory",
89 "unicodePwd", "dBCSPwd", "supplementalCredentials",
90 "gPCUserExtensionNames", "gPCMachineExtensionNames",
91 "maxPwdAge", "secret", "possibleInferiors", "privilege",
92 "sAMAccountType", "oEMInformation", "creationTime" ]
94 nonreplAttrNotCopied = ["uSNCreated", "replPropertyMetaData", "uSNChanged",
95 "nextRid" ,"rIDNextRID"]
97 nonDSDBAttrNotCopied = ["msDS-KeyVersionNumber", "priorSecret", "priorWhenChanged"]
100 attrNotCopied = replAttrNotCopied
101 attrNotCopied.extend(nonreplAttrNotCopied)
102 attrNotCopied.extend(nonDSDBAttrNotCopied)
103 # Usually for an object that already exists we do not overwrite attributes as
104 # they might have been changed for good reasons. Anyway for a few of them it's
105 # mandatory to replace them otherwise the provision will be broken somehow.
106 # But for attribute that are just missing we do not have to specify them as the default
107 # behavior is to add missing attribute
108 hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace,
109 "systemOnly":replace, "searchFlags":replace,
110 "mayContain":replace, "systemFlags":replace+add,
111 "description":replace, "operatingSystemVersion":replace,
112 "adminPropertyPages":replace, "groupType":replace,
113 "wellKnownObjects":replace, "privilege":never,
114 "defaultSecurityDescriptor": replace,
115 "rIDAvailablePool": never,
116 "rIDNextRID": add, "rIDUsedPool": never,
117 "defaultSecurityDescriptor": replace + add,
118 "isMemberOfPartialAttributeSet": delete,
119 "attributeDisplayNames": replace + add,
120 "versionNumber": add}
122 backlinked = []
123 forwardlinked = set()
124 dn_syntax_att = []
125 not_replicated = []
126 def define_what_to_log(opts):
127 what = 0
128 if opts.debugchange:
129 what = what | CHANGE
130 if opts.debugchangesd:
131 what = what | CHANGESD
132 if opts.debugguess:
133 what = what | GUESS
134 if opts.debugprovision:
135 what = what | PROVISION
136 if opts.debugall:
137 what = what | CHANGEALL
138 return what
141 parser = optparse.OptionParser("provision [options]")
142 sambaopts = options.SambaOptions(parser)
143 parser.add_option_group(sambaopts)
144 parser.add_option_group(options.VersionOptions(parser))
145 credopts = options.CredentialsOptions(parser)
146 parser.add_option_group(credopts)
147 parser.add_option("--setupdir", type="string", metavar="DIR",
148 help="directory with setup files")
149 parser.add_option("--debugprovision", help="Debug provision", action="store_true")
150 parser.add_option("--debugguess", action="store_true",
151 help="Print information on which values are guessed")
152 parser.add_option("--debugchange", action="store_true",
153 help="Print information on what is different but won't be changed")
154 parser.add_option("--debugchangesd", action="store_true",
155 help="Print security descriptor differences")
156 parser.add_option("--debugall", action="store_true",
157 help="Print all available information (very verbose)")
158 parser.add_option("--resetfileacl", action="store_true",
159 help="Force a reset on filesystem acls in sysvol / netlogon share")
160 parser.add_option("--fixntacl", action="store_true",
161 help="Only fix NT ACLs in sysvol / netlogon share")
162 parser.add_option("--full", action="store_true",
163 help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
165 opts = parser.parse_args()[0]
167 handler = logging.StreamHandler(sys.stdout)
168 upgrade_logger = logging.getLogger("upgradeprovision")
169 upgrade_logger.setLevel(logging.INFO)
171 upgrade_logger.addHandler(handler)
173 provision_logger = logging.getLogger("provision")
174 provision_logger.addHandler(handler)
176 whatToLog = define_what_to_log(opts)
178 def message(what, text):
179 """Print a message if this message type has been selected to be printed
181 :param what: Category of the message
182 :param text: Message to print """
183 if (whatToLog & what) or what <= 0:
184 upgrade_logger.info("%s", text)
186 if len(sys.argv) == 1:
187 opts.interactive = True
188 lp = sambaopts.get_loadparm()
189 smbconf = lp.configfile
191 creds = credopts.get_credentials(lp)
192 creds.set_kerberos_state(DONT_USE_KERBEROS)
196 def check_for_DNS(refprivate, private):
197 """Check if the provision has already the requirement for dynamic dns
199 :param refprivate: The path to the private directory of the reference
200 provision
201 :param private: The path to the private directory of the upgraded
202 provision"""
204 spnfile = "%s/spn_update_list" % private
205 dnsfile = "%s/dns_update_list" % private
206 namedfile = lp.get("dnsupdate:path")
208 if not namedfile:
209 namedfile = "%s/named.conf.update" % private
211 if not os.path.exists(spnfile):
212 shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
214 if not os.path.exists(dnsfile):
215 shutil.copy("%s/dns_update_list" % refprivate, "%s" % dnsfile)
217 destdir = "%s/new_dns" % private
218 dnsdir = "%s/dns" % private
220 if not os.path.exists(namedfile):
221 if not os.path.exists(destdir):
222 os.mkdir(destdir)
223 if not os.path.exists(dnsdir):
224 os.mkdir(dnsdir)
225 shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
226 shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
227 message(SIMPLE, "It seems that your provision did not integrate "
228 "new rules for dynamic dns update of domain related entries")
229 message(SIMPLE, "A copy of the new bind configuration files and "
230 "template has been put in %s, you should read them and "
231 "configure dynamic dns updates" % destdir)
234 def populate_links(samdb, schemadn):
235 """Populate an array with all the back linked attributes
237 This attributes that are modified automaticaly when
238 front attibutes are changed
240 :param samdb: A LDB object for sam.ldb file
241 :param schemadn: DN of the schema for the partition"""
242 linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
243 backlinked.extend(linkedAttHash.values())
244 for t in linkedAttHash.keys():
245 forwardlinked.add(t)
247 def isReplicated(att):
248 """ Indicate if the attribute is replicated or not
250 :param att: Name of the attribute to be tested
251 :return: True is the attribute is replicated, False otherwise
254 return (att not in not_replicated)
256 def populateNotReplicated(samdb, schemadn):
257 """Populate an array with all the attributes that are not replicated
259 :param samdb: A LDB object for sam.ldb file
260 :param schemadn: DN of the schema for the partition"""
261 res = samdb.search(expression="(&(objectclass=attributeSchema)(systemflags:1.2.840.113556.1.4.803:=1))", base=Dn(samdb,
262 str(schemadn)), scope=SCOPE_SUBTREE,
263 attrs=["lDAPDisplayName"])
264 for elem in res:
265 not_replicated.append(elem["lDAPDisplayName"])
267 def populate_dnsyntax(samdb, schemadn):
268 """Populate an array with all the attributes that have DN synthax
269 (oid 2.5.5.1)
271 :param samdb: A LDB object for sam.ldb file
272 :param schemadn: DN of the schema for the partition"""
273 res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
274 str(schemadn)), scope=SCOPE_SUBTREE,
275 attrs=["lDAPDisplayName"])
276 for elem in res:
277 dn_syntax_att.append(elem["lDAPDisplayName"])
280 def sanitychecks(samdb, names):
281 """Make some checks before trying to update
283 :param samdb: An LDB object opened on sam.ldb
284 :param names: list of key provision parameters
285 :return: Status of check (1 for Ok, 0 for not Ok) """
286 res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
287 scope=SCOPE_SUBTREE, attrs=["dn"],
288 controls=["search_options:1:2"])
289 if len(res) == 0:
290 print "No DC found. Your provision is most probably broken!"
291 return False
292 elif len(res) != 1:
293 print "Found %d domain controllers. For the moment " \
294 "upgradeprovision is not able to handle an upgrade on a " \
295 "domain with more than one DC. Please demote the other " \
296 "DC(s) before upgrading" % len(res)
297 return False
298 else:
299 return True
302 def print_provision_key_parameters(names):
303 """Do a a pretty print of provision parameters
305 :param names: list of key provision parameters """
306 message(GUESS, "rootdn :" + str(names.rootdn))
307 message(GUESS, "configdn :" + str(names.configdn))
308 message(GUESS, "schemadn :" + str(names.schemadn))
309 message(GUESS, "serverdn :" + str(names.serverdn))
310 message(GUESS, "netbiosname :" + names.netbiosname)
311 message(GUESS, "defaultsite :" + names.sitename)
312 message(GUESS, "dnsdomain :" + names.dnsdomain)
313 message(GUESS, "hostname :" + names.hostname)
314 message(GUESS, "domain :" + names.domain)
315 message(GUESS, "realm :" + names.realm)
316 message(GUESS, "invocationid:" + names.invocation)
317 message(GUESS, "policyguid :" + names.policyid)
318 message(GUESS, "policyguiddc:" + str(names.policyid_dc))
319 message(GUESS, "domainsid :" + str(names.domainsid))
320 message(GUESS, "domainguid :" + names.domainguid)
321 message(GUESS, "ntdsguid :" + names.ntdsguid)
322 message(GUESS, "domainlevel :" + str(names.domainlevel))
325 def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb):
326 """Define more complicate update rules for some attributes
328 :param att: The attribute to be updated
329 :param delta: A messageElement object that correspond to the difference
330 between the updated object and the reference one
331 :param new: The reference object
332 :param old: The Updated object
333 :param useReplMetadata: A boolean that indicate if the update process
334 use replPropertyMetaData to decide what has to be updated.
335 :param basedn: The base DN of the provision
336 :param aldb: An ldb object used to build DN
337 :return: True to indicate that the attribute should be kept, False for
338 discarding it"""
340 flag = delta.get(att).flags()
341 # We do most of the special case handle if we do not have the
342 # highest usn as otherwise the replPropertyMetaData will guide us more
343 # correctly
344 if not useReplMetadata:
345 if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and
346 ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT,"
347 "CN=Services,CN=Configuration,%s" % basedn)
348 == old[0].dn):
349 return True
350 if (att == "userAccountControl" and flag == FLAG_MOD_REPLACE and
351 ldb.Dn(aldb, "CN=Administrator,CN=Users,%s" % basedn)
352 == old[0].dn):
353 message(SIMPLE, "We suggest that you change the userAccountControl"
354 " for user Administrator from value %d to %d" %
355 (int(str(old[0][att])), int(str(new[0][att]))))
356 return False
357 if (att == "minPwdAge" and flag == FLAG_MOD_REPLACE):
358 if (long(str(old[0][att])) == 0):
359 delta[att] = MessageElement(new[0][att], FLAG_MOD_REPLACE, att)
360 return True
362 if (att == "member" and flag == FLAG_MOD_REPLACE):
363 hash = {}
364 newval = []
365 changeDelta=0
366 for elem in old[0][att]:
367 hash[str(elem).lower()]=1
368 newval.append(str(elem))
370 for elem in new[0][att]:
371 if not hash.has_key(str(elem).lower()):
372 changeDelta=1
373 newval.append(str(elem))
374 if changeDelta == 1:
375 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
376 else:
377 delta.remove(att)
378 return True
380 if (att in ("gPLink", "gPCFileSysPath") and
381 flag == FLAG_MOD_REPLACE and
382 str(new[0].dn).lower() == str(old[0].dn).lower()):
383 delta.remove(att)
384 return True
386 if att == "forceLogoff":
387 ref=0x8000000000000000
388 oldval=int(old[0][att][0])
389 newval=int(new[0][att][0])
390 ref == old and ref == abs(new)
391 return True
393 if att in ("adminDisplayName", "adminDescription"):
394 return True
396 if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (names.schemadn)
397 and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
398 return True
400 if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
401 att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
402 return True
404 if (str(old[0].dn) == "%s" % (str(names.rootdn))
405 and att == "subRefs" and flag == FLAG_MOD_REPLACE):
406 return True
407 #Allow to change revision of ForestUpdates objects
408 if (att == "revision" or att == "objectVersion"):
409 if str(delta.dn).lower().find("domainupdates") and str(delta.dn).lower().find("forestupdates") > 0:
410 return True
411 if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
412 return True
414 # This is a bit of special animal as we might have added
415 # already SPN entries to the list that has to be modified
416 # So we go in detail to try to find out what has to be added ...
417 if (att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):
418 hash = {}
419 newval = []
420 changeDelta = 0
421 for elem in old[0][att]:
422 hash[str(elem)]=1
423 newval.append(str(elem))
425 for elem in new[0][att]:
426 if not hash.has_key(str(elem)):
427 changeDelta = 1
428 newval.append(str(elem))
429 if changeDelta == 1:
430 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
431 else:
432 delta.remove(att)
433 return True
435 return False
437 def dump_denied_change(dn, att, flagtxt, current, reference):
438 """Print detailed information about why a change is denied
440 :param dn: DN of the object which attribute is denied
441 :param att: Attribute that was supposed to be upgraded
442 :param flagtxt: Type of the update that should be performed
443 (add, change, remove, ...)
444 :param current: Value(s) of the current attribute
445 :param reference: Value(s) of the reference attribute"""
447 message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
448 + " must not be changed/removed. Discarding the change")
449 if att == "objectSid" :
450 message(CHANGE, "old : %s" % ndr_unpack(security.dom_sid, current[0]))
451 message(CHANGE, "new : %s" % ndr_unpack(security.dom_sid, reference[0]))
452 elif att == "rIDPreviousAllocationPool" or att == "rIDAllocationPool":
453 message(CHANGE, "old : %s" % int64range2str(current[0]))
454 message(CHANGE, "new : %s" % int64range2str(reference[0]))
455 else:
456 i = 0
457 for e in range(0, len(current)):
458 message(CHANGE, "old %d : %s" % (i, str(current[e])))
459 i+=1
460 if reference is not None:
461 i = 0
462 for e in range(0, len(reference)):
463 message(CHANGE, "new %d : %s" % (i, str(reference[e])))
464 i+=1
466 def handle_special_add(samdb, dn, names):
467 """Handle special operation (like remove) on some object needed during
468 upgrade
470 This is mostly due to wrong creation of the object in previous provision.
471 :param samdb: An Ldb object representing the SAM database
472 :param dn: DN of the object to inspect
473 :param names: list of key provision parameters
476 dntoremove = None
477 objDn = Dn(samdb, "CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn)
478 if dn == objDn :
479 #This entry was misplaced lets remove it if it exists
480 dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
482 objDn = Dn(samdb,
483 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn)
484 if dn == objDn:
485 #This entry was misplaced lets remove it if it exists
486 dntoremove = "CN=Certificate Service DCOM Access,"\
487 "CN=Users, %s" % names.rootdn
489 objDn = Dn(samdb, "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn)
490 if dn == objDn:
491 #This entry was misplaced lets remove it if it exists
492 dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
494 objDn = Dn(samdb, "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn)
495 if dn == objDn:
496 #This entry was misplaced lets remove it if it exists
497 dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
499 objDn = Dn(samdb,"CN=System,CN=WellKnown Security Principals,"
500 "CN=Configuration,%s" % names.rootdn)
501 if dn == objDn:
502 oldDn = Dn(samdb,"CN=Well-Known-Security-Id-System,"
503 "CN=WellKnown Security Principals,"
504 "CN=Configuration,%s" % names.rootdn)
506 res = samdb.search(expression="(dn=%s)" % oldDn,
507 base=str(names.rootdn),
508 scope=SCOPE_SUBTREE, attrs=["dn"],
509 controls=["search_options:1:2"])
511 res2 = samdb.search(expression="(dn=%s)" % dn,
512 base=str(names.rootdn),
513 scope=SCOPE_SUBTREE, attrs=["dn"],
514 controls=["search_options:1:2"])
516 if len(res) > 0 and len(res2) == 0:
517 message(CHANGE, "Existing object %s must be replaced by %s. "
518 "Renaming old object" % (str(oldDn), str(dn)))
519 samdb.rename(oldDn, objDn, ["relax:0", "provision:0"])
521 return 0
523 if dntoremove is not None:
524 res = samdb.search(expression="(cn=RID Set)",
525 base=str(names.rootdn),
526 scope=SCOPE_SUBTREE, attrs=["dn"],
527 controls=["search_options:1:2"])
529 if len(res) == 0:
530 return 2
531 res = samdb.search(expression="(dn=%s)" % dntoremove,
532 base=str(names.rootdn),
533 scope=SCOPE_SUBTREE, attrs=["dn"],
534 controls=["search_options:1:2"])
535 if len(res) > 0:
536 message(CHANGE, "Existing object %s must be replaced by %s. "
537 "Removing old object" % (dntoremove, str(dn)))
538 samdb.delete(res[0]["dn"])
539 return 0
541 return 1
544 def check_dn_nottobecreated(hash, index, listdn):
545 """Check if one of the DN present in the list has a creation order
546 greater than the current.
548 Hash is indexed by dn to be created, with each key
549 is associated the creation order.
551 First dn to be created has the creation order 0, second has 1, ...
552 Index contain the current creation order
554 :param hash: Hash holding the different DN of the object to be
555 created as key
556 :param index: Current creation order
557 :param listdn: List of DNs on which the current DN depends on
558 :return: None if the current object do not depend on other
559 object or if all object have been created before."""
560 if listdn is None:
561 return None
562 for dn in listdn:
563 key = str(dn).lower()
564 if hash.has_key(key) and hash[key] > index:
565 return str(dn)
566 return None
570 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
571 """Add a new object if the dependencies are satisfied
573 The function add the object if the object on which it depends are already
574 created
576 :param ref_samdb: Ldb object representing the SAM db of the reference
577 provision
578 :param samdb: Ldb object representing the SAM db of the upgraded
579 provision
580 :param dn: DN of the object to be added
581 :param names: List of key provision parameters
582 :param basedn: DN of the partition to be updated
583 :param hash: Hash holding the different DN of the object to be
584 created as key
585 :param index: Current creation order
586 :return: True if the object was created False otherwise"""
588 ret = handle_special_add(samdb, dn, names)
590 if ret == 2:
591 return False
593 if ret == 0:
594 return True
597 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
598 scope=SCOPE_SUBTREE, controls=["search_options:1:2"])
599 empty = Message()
600 delta = samdb.msg_diff(empty, reference[0])
601 delta.dn
602 skip = False
603 try:
604 if str(reference[0].get("cn")) == "RID Set":
605 for klass in reference[0].get("objectClass"):
606 if str(klass).lower() == "ridset":
607 skip = True
608 finally:
609 if delta.get("objectSid"):
610 sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
611 m = re.match(r".*-(\d+)$", sid)
612 if m and int(m.group(1))>999:
613 delta.remove("objectSid")
614 for att in attrNotCopied:
615 delta.remove(att)
616 for att in backlinked:
617 delta.remove(att)
618 depend_on_yettobecreated = None
619 for att in dn_syntax_att:
620 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
621 delta.get(str(att)))
622 if depend_on_yet_tobecreated is not None:
623 message(CHANGE, "Object %s depends on %s in attribute %s. "
624 "Delaying the creation" % (dn,
625 depend_on_yet_tobecreated, att))
626 return False
628 delta.dn = dn
629 if not skip:
630 message(CHANGE,"Object %s will be added" % dn)
631 samdb.add(delta, ["relax:0", "provision:0"])
632 else:
633 message(CHANGE,"Object %s was skipped" % dn)
635 return True
637 def gen_dn_index_hash(listMissing):
638 """Generate a hash associating the DN to its creation order
640 :param listMissing: List of DN
641 :return: Hash with DN as keys and creation order as values"""
642 hash = {}
643 for i in range(0, len(listMissing)):
644 hash[str(listMissing[i]).lower()] = i
645 return hash
647 def add_deletedobj_containers(ref_samdb, samdb, names):
648 """Add the object containter: CN=Deleted Objects
650 This function create the container for each partition that need one and
651 then reference the object into the root of the partition
653 :param ref_samdb: Ldb object representing the SAM db of the reference
654 provision
655 :param samdb: Ldb object representing the SAM db of the upgraded provision
656 :param names: List of key provision parameters"""
659 wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
660 partitions = [str(names.rootdn), str(names.configdn)]
661 for part in partitions:
662 ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
663 base=part, scope=SCOPE_SUBTREE,
664 attrs=["dn"],
665 controls=["show_deleted:0",
666 "show_recycled:0"])
667 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
668 base=part, scope=SCOPE_SUBTREE,
669 attrs=["dn"],
670 controls=["show_deleted:0",
671 "show_recycled:0"])
672 if len(ref_delObjCnt) > len(delObjCnt):
673 reference = ref_samdb.search(expression="cn=Deleted Objects",
674 base=part, scope=SCOPE_SUBTREE,
675 controls=["show_deleted:0",
676 "show_recycled:0"])
677 empty = Message()
678 delta = samdb.msg_diff(empty, reference[0])
680 delta.dn = Dn(samdb, str(reference[0]["dn"]))
681 for att in attrNotCopied:
682 delta.remove(att)
684 modcontrols = ["relax:0", "provision:0"]
685 samdb.add(delta, modcontrols)
687 listwko = []
688 res = samdb.search(expression="(objectClass=*)", base=part,
689 scope=SCOPE_BASE,
690 attrs=["dn", "wellKnownObjects"])
692 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
693 found = False
695 if len(res[0]) > 0:
696 wko = res[0]["wellKnownObjects"]
698 # The wellKnownObject that we want to add.
699 for o in wko:
700 if str(o) == targetWKO:
701 found = True
702 listwko.append(str(o))
704 if not found:
705 listwko.append(targetWKO)
707 delta = Message()
708 delta.dn = Dn(samdb, str(res[0]["dn"]))
709 delta["wellKnownObjects"] = MessageElement(listwko,
710 FLAG_MOD_REPLACE,
711 "wellKnownObjects" )
712 samdb.modify(delta)
714 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
715 """Add the missing object whose DN is the list
717 The function add the object if the objects on which it depends are
718 already created.
720 :param ref_samdb: Ldb object representing the SAM db of the reference
721 provision
722 :param samdb: Ldb object representing the SAM db of the upgraded
723 provision
724 :param dn: DN of the object to be added
725 :param names: List of key provision parameters
726 :param basedn: DN of the partition to be updated
727 :param list: List of DN to be added in the upgraded provision"""
729 listMissing = []
730 listDefered = list
732 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
733 index = 0
734 listMissing = listDefered
735 listDefered = []
736 hashMissing = gen_dn_index_hash(listMissing)
737 for dn in listMissing:
738 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
739 hashMissing, index)
740 index = index + 1
741 if ret == 0:
742 # DN can't be created because it depends on some
743 # other DN in the list
744 listDefered.append(dn)
746 if len(listDefered) != 0:
747 raise ProvisioningError("Unable to insert missing elements: "
748 "circular references")
750 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
751 """This function handle updates on links
753 :param samdb: An LDB object pointing to the updated provision
754 :param att: Attribute to update
755 :param basedn: The root DN of the provision
756 :param dn: The DN of the inspected object
757 :param value: The value of the attribute
758 :param ref_value: The value of this attribute in the reference provision
759 :param delta: The MessageElement object that will be applied for
760 transforming the current provision"""
762 res = samdb.search(expression="dn=%s" % dn, base=basedn,
763 controls=["search_options:1:2", "reveal:1"],
764 attrs=[att])
766 blacklist = {}
767 hash = {}
768 newlinklist = []
769 changed = False
771 newlinklist.extend(value)
773 for e in value:
774 hash[e] = 1
775 # for w2k domain level the reveal won't reveal anything ...
776 # it means that we can readd links that were removed on purpose ...
777 # Also this function in fact just accept add not removal
779 for e in res[0][att]:
780 if not hash.has_key(e):
781 # We put in the blacklist all the element that are in the "revealed"
782 # result and not in the "standard" result
783 # This element are links that were removed before and so that
784 # we don't wan't to readd
785 blacklist[e] = 1
787 for e in ref_value:
788 if not blacklist.has_key(e) and not hash.has_key(e):
789 newlinklist.append(str(e))
790 changed = True
791 if changed:
792 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
793 else:
794 delta.remove(att)
797 msg_elt_flag_strs = {
798 ldb.FLAG_MOD_ADD: "MOD_ADD",
799 ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
800 ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
802 def checkKeepAttributeOldMtd(delta, att, reference, current,
803 basedn, samdb):
804 """ Check if we should keep the attribute modification or not.
805 This function didn't use replicationMetadata to take a decision.
807 :param delta: A message diff object
808 :param att: An attribute
809 :param reference: A message object for the current entry comming from
810 the reference provision.
811 :param current: A message object for the current entry commin from
812 the current provision.
813 :param basedn: The DN of the partition
814 :param samdb: A ldb connection to the sam database of the current provision.
816 :return: The modified message diff.
818 # Old school way of handling things for pre alpha12 upgrade
819 global defSDmodified
820 isFirst = False
821 txt = ""
822 dn = current[0].dn
824 for att in list(delta):
825 defSDmodified = True
826 msgElt = delta.get(att)
828 if att == "nTSecurityDescriptor":
829 delta.remove(att)
830 continue
832 if att == "dn":
833 continue
835 if not hashOverwrittenAtt.has_key(att):
836 if msgElt.flags() != FLAG_MOD_ADD:
837 if not handle_special_case(att, delta, reference, current,
838 False, basedn, samdb):
839 if opts.debugchange or opts.debugall:
840 try:
841 dump_denied_change(dn, att,
842 msg_elt_flag_strs[msgElt.flags()],
843 current[0][att], reference[0][att])
844 except KeyError:
845 dump_denied_change(dn, att,
846 msg_elt_flag_strs[msgElt.flags()],
847 current[0][att], None)
848 delta.remove(att)
849 continue
850 else:
851 if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
852 continue
853 elif hashOverwrittenAtt.get(att)==never:
854 delta.remove(att)
855 continue
857 return delta
859 def checkKeepAttributeWithMetadata(delta, att, message, reference, current,
860 hash_attr_usn, basedn, usns, samdb):
861 """ Check if we should keep the attribute modification or not
863 :param delta: A message diff object
864 :param att: An attribute
865 :param message: A function to print messages
866 :param reference: A message object for the current entry comming from
867 the reference provision.
868 :param current: A message object for the current entry commin from
869 the current provision.
870 :param hash_attr_usn: A dictionnary with attribute name as keys,
871 USN and invocation id as values.
872 :param basedn: The DN of the partition
873 :param usns: A dictionnary with invocation ID as keys and USN ranges
874 as values.
875 :param samdb: A ldb object pointing to the sam DB
877 :return: The modified message diff.
879 global defSDmodified
880 isFirst = True
881 txt = ""
882 dn = current[0].dn
884 for att in list(delta):
885 if att in ["dn", "objectSid"]:
886 delta.remove(att)
887 continue
889 # We have updated by provision usn information so let's exploit
890 # replMetadataProperties
891 if att in forwardlinked:
892 curval = current[0].get(att, ())
893 refval = reference[0].get(att, ())
894 handle_links(samdb, att, basedn, current[0]["dn"],
895 curval, refval, delta)
896 continue
898 if isFirst and len(delta.items())>1:
899 isFirst = False
900 txt = "%s\n" % (str(dn))
902 if handle_special_case(att, delta, reference, current, True, None, None):
903 # This attribute is "complicated" to handle and handling
904 # was done in handle_special_case
905 continue
907 attrUSN = None
908 if hash_attr_usn.get(att):
909 [attrUSN, attInvId] = hash_attr_usn.get(att)
911 if attrUSN is None:
912 # If it's a replicated attribute and we don't have any USN
913 # information about it. It means that we never saw it before
914 # so let's add it !
915 # If it is a replicated attribute but we are not master on it
916 # (ie. not initially added in the provision we masterize).
917 # attrUSN will be -1
918 if isReplicated(att):
919 continue
920 else:
921 message(CHANGE, "Non replicated attribute %s changed" % att)
922 continue
924 if att == "nTSecurityDescriptor":
925 cursd = ndr_unpack(security.descriptor,
926 str(current[0]["nTSecurityDescriptor"]))
927 cursddl = cursd.as_sddl(names.domainsid)
928 refsd = ndr_unpack(security.descriptor,
929 str(reference[0]["nTSecurityDescriptor"]))
930 refsddl = refsd.as_sddl(names.domainsid)
932 if get_diff_sddls(refsddl, cursddl) == "":
933 message(CHANGE, "sd are identical")
934 else:
935 message(CHANGE, "sd are not identical")
937 if attrUSN == -1:
938 # This attribute was last modified by another DC forget
939 # about it
940 message(CHANGE, "%sAttribute: %s has been "
941 "created/modified/deleted by another DC. "
942 "Doing nothing" % (txt, att))
943 txt = ""
944 delta.remove(att)
945 continue
946 elif not usn_in_range(int(attrUSN), usns.get(attInvId)):
947 message(CHANGE, "%sAttribute: %s was not "
948 "created/modified/deleted during a "
949 "provision or upgradeprovision. Current "
950 "usn: %d. Doing nothing" % (txt, att,
951 attrUSN))
952 txt = ""
953 delta.remove(att)
954 continue
955 else:
956 if att == "defaultSecurityDescriptor":
957 defSDmodified = True
958 if attrUSN:
959 message(CHANGE, "%sAttribute: %s will be modified"
960 "/deleted it was last modified "
961 "during a provision. Current usn: "
962 "%d" % (txt, att, attrUSN))
963 txt = ""
964 else:
965 message(CHANGE, "%sAttribute: %s will be added because "
966 "it did not exist before" % (txt, att))
967 txt = ""
968 continue
970 return delta
972 def update_present(ref_samdb, samdb, basedn, listPresent, usns):
973 """ This function updates the object that are already present in the
974 provision
976 :param ref_samdb: An LDB object pointing to the reference provision
977 :param samdb: An LDB object pointing to the updated provision
978 :param basedn: A string with the value of the base DN for the provision
979 (ie. DC=foo, DC=bar)
980 :param listPresent: A list of object that is present in the provision
981 :param usns: A list of USN range modified by previous provision and
982 upgradeprovision grouped by invocation ID
985 # This hash is meant to speedup lookup of attribute name from an oid,
986 # it's for the replPropertyMetaData handling
987 hash_oid_name = {}
988 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
989 controls=["search_options:1:2"], attrs=["attributeID",
990 "lDAPDisplayName"])
991 if len(res) > 0:
992 for e in res:
993 strDisplay = str(e.get("lDAPDisplayName"))
994 hash_oid_name[str(e.get("attributeID"))] = strDisplay
995 else:
996 msg = "Unable to insert missing elements: circular references"
997 raise ProvisioningError(msg)
999 changed = 0
1000 controls = ["search_options:1:2", "sd_flags:1:0"]
1001 if usns is not None:
1002 message(CHANGE, "Using replPropertyMetadata for change selection")
1003 for dn in listPresent:
1004 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1005 scope=SCOPE_SUBTREE,
1006 controls=controls)
1007 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1008 scope=SCOPE_SUBTREE, controls=controls)
1010 if (
1011 (str(current[0].dn) != str(reference[0].dn)) and
1012 (str(current[0].dn).upper() == str(reference[0].dn).upper())
1014 message(CHANGE, "Names are the same except for the case. "
1015 "Renaming %s to %s" % (str(current[0].dn),
1016 str(reference[0].dn)))
1017 identic_rename(samdb, reference[0].dn)
1018 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1019 scope=SCOPE_SUBTREE,
1020 controls=controls)
1022 delta = samdb.msg_diff(current[0], reference[0])
1024 for att in backlinked:
1025 delta.remove(att)
1027 for att in attrNotCopied:
1028 delta.remove(att)
1030 delta.remove("name")
1032 if len(delta.items()) == 1:
1033 continue
1035 if len(delta.items()) > 1 and usns is not None:
1036 # Fetch the replPropertyMetaData
1037 res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1038 scope=SCOPE_SUBTREE, controls=controls,
1039 attrs=["replPropertyMetaData"])
1040 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1041 str(res[0]["replPropertyMetaData"])).ctr
1043 hash_attr_usn = {}
1044 for o in ctr.array:
1045 # We put in this hash only modification
1046 # made on the current host
1047 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
1048 if str(o.originating_invocation_id) in usns.keys():
1049 hash_attr_usn[att] = [o.originating_usn, str(o.originating_invocation_id)]
1050 else:
1051 hash_attr_usn[att] = [-1, None]
1053 if usns is not None:
1054 delta = checkKeepAttributeWithMetadata(delta, att, message, reference,
1055 current, hash_attr_usn,
1056 basedn, usns, samdb)
1057 else:
1058 delta = checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb)
1060 delta.dn = dn
1061 if len(delta.items()) >1:
1062 # Skip dn as the value is not really changed ...
1063 attributes=", ".join(delta.keys()[1:])
1064 modcontrols = []
1065 relaxedatt = ['iscriticalsystemobject', 'grouptype']
1066 # Let's try to reduce as much as possible the use of relax control
1067 #for checkedatt in relaxedatt:
1068 for attr in delta.keys():
1069 if attr.lower() in relaxedatt:
1070 modcontrols = ["relax:0", "provision:0"]
1071 message(CHANGE, "%s is different from the reference one, changed"
1072 " attributes: %s\n" % (dn, attributes))
1073 changed += 1
1074 samdb.modify(delta, modcontrols)
1075 return changed
1077 def reload_full_schema(samdb, names):
1078 """Load the updated schema with all the new and existing classes
1079 and attributes.
1081 :param samdb: An LDB object connected to the sam.ldb of the update
1082 provision
1083 :param names: List of key provision parameters
1086 current = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1087 scope=SCOPE_SUBTREE)
1088 schema_ldif = ""
1089 prefixmap_data = ""
1091 for ent in current:
1092 schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
1094 prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
1095 prefixmap_data = b64encode(prefixmap_data)
1097 # We don't actually add this ldif, just parse it
1098 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap_data
1100 dsdb._dsdb_set_schema_from_ldif(samdb, prefixmap_ldif, schema_ldif)
1103 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, prereloadfunc):
1104 """Check differences between the reference provision and the upgraded one.
1106 It looks for all objects which base DN is name.
1108 This function will also add the missing object and update existing object
1109 to add or remove attributes that were missing.
1111 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1112 reference provision
1113 :param samdb: An LDB object connected to the sam.ldb of the update
1114 provision
1115 :param basedn: String value of the DN of the partition
1116 :param names: List of key provision parameters
1117 :param schema: A Schema object
1118 :param provisionUSNs: A dictionnary with range of USN modified during provision
1119 or upgradeprovision. Ranges are grouped by invocationID.
1120 :param prereloadfunc: A function that must be executed just before the reload
1121 of the schema
1124 hash_new = {}
1125 hash = {}
1126 listMissing = []
1127 listPresent = []
1128 reference = []
1129 current = []
1131 # Connect to the reference provision and get all the attribute in the
1132 # partition referred by name
1133 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
1134 scope=SCOPE_SUBTREE, attrs=["dn"],
1135 controls=["search_options:1:2"])
1137 current = samdb.search(expression="objectClass=*", base=basedn,
1138 scope=SCOPE_SUBTREE, attrs=["dn"],
1139 controls=["search_options:1:2"])
1140 # Create a hash for speeding the search of new object
1141 for i in range(0, len(reference)):
1142 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
1144 # Create a hash for speeding the search of existing object in the
1145 # current provision
1146 for i in range(0, len(current)):
1147 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
1150 for k in hash_new.keys():
1151 if not hash.has_key(k):
1152 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
1153 listMissing.append(hash_new[k])
1154 else:
1155 listPresent.append(hash_new[k])
1157 # Sort the missing object in order to have object of the lowest level
1158 # first (which can be containers for higher level objects)
1159 listMissing.sort(dn_sort)
1160 listPresent.sort(dn_sort)
1162 # The following lines is to load the up to
1163 # date schema into our current LDB
1164 # a complete schema is needed as the insertion of attributes
1165 # and class is done against it
1166 # and the schema is self validated
1167 samdb.set_schema(schema)
1168 try:
1169 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
1170 add_deletedobj_containers(ref_samdb, samdb, names)
1172 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
1174 prereloadfunc()
1175 message(SIMPLE, "Reloading a merged schema, which might trigger "
1176 "reindexing so please be patient")
1177 reload_full_schema(samdb, names)
1178 message(SIMPLE, "Schema reloaded!")
1180 changed = update_present(ref_samdb, samdb, basedn, listPresent,
1181 provisionUSNs)
1182 message(SIMPLE, "There are %d changed objects" % (changed))
1183 return 1
1185 except StandardError, err:
1186 message(ERROR, "Exception during upgrade of samdb:")
1187 (typ, val, tb) = sys.exc_info()
1188 traceback.print_exception(typ, val, tb)
1189 return 0
1192 def check_updated_sd(ref_sam, cur_sam, names):
1193 """Check if the security descriptor in the upgraded provision are the same
1194 as the reference
1196 :param ref_sam: A LDB object connected to the sam.ldb file used as
1197 the reference provision
1198 :param cur_sam: A LDB object connected to the sam.ldb file used as
1199 upgraded provision
1200 :param names: List of key provision parameters"""
1201 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1202 scope=SCOPE_SUBTREE,
1203 attrs=["dn", "nTSecurityDescriptor"],
1204 controls=["search_options:1:2"])
1205 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1206 scope=SCOPE_SUBTREE,
1207 attrs=["dn", "nTSecurityDescriptor"],
1208 controls=["search_options:1:2"])
1209 hash = {}
1210 for i in range(0, len(reference)):
1211 refsd = ndr_unpack(security.descriptor,
1212 str(reference[i]["nTSecurityDescriptor"]))
1213 hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
1216 for i in range(0, len(current)):
1217 key = str(current[i]["dn"]).lower()
1218 if hash.has_key(key):
1219 cursd = ndr_unpack(security.descriptor,
1220 str(current[i]["nTSecurityDescriptor"]))
1221 sddl = cursd.as_sddl(names.domainsid)
1222 if sddl != hash[key]:
1223 txt = get_diff_sddls(hash[key], sddl)
1224 if txt != "":
1225 message(CHANGESD, "On object %s ACL is different"
1226 " \n%s" % (current[i]["dn"], txt))
1230 def fix_partition_sd(samdb, names):
1231 """This function fix the SD for partition containers (basedn, configdn, ...)
1232 This is needed because some provision use to have broken SD on containers
1234 :param samdb: An LDB object pointing to the sam of the current provision
1235 :param names: A list of key provision parameters
1237 # First update the SD for the rootdn
1238 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1239 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1240 controls=["search_options:1:2"])
1241 delta = Message()
1242 delta.dn = Dn(samdb, str(res[0]["dn"]))
1243 descr = get_domain_descriptor(names.domainsid)
1244 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1245 "nTSecurityDescriptor")
1246 samdb.modify(delta)
1247 # Then the config dn
1248 res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1249 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1250 controls=["search_options:1:2"])
1251 delta = Message()
1252 delta.dn = Dn(samdb, str(res[0]["dn"]))
1253 descr = get_config_descriptor(names.domainsid)
1254 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1255 "nTSecurityDescriptor" )
1256 samdb.modify(delta)
1257 # Then the schema dn
1258 res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1259 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1260 controls=["search_options:1:2"])
1262 delta = Message()
1263 delta.dn = Dn(samdb, str(res[0]["dn"]))
1264 descr = get_schema_descriptor(names.domainsid)
1265 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1266 "nTSecurityDescriptor" )
1267 samdb.modify(delta)
1269 def rebuild_sd(samdb, names):
1270 """Rebuild security descriptor of the current provision from scratch
1272 During the different pre release of samba4 security descriptors (SD)
1273 were notarly broken (up to alpha11 included)
1274 This function allow to get them back in order, this function make the
1275 assumption that nobody has modified manualy an SD
1276 and so SD can be safely recalculated from scratch to get them right.
1278 :param names: List of key provision parameters"""
1281 hash = {}
1282 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1283 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1284 controls=["search_options:1:2"])
1285 for obj in res:
1286 if not (str(obj["dn"]) == str(names.rootdn) or
1287 str(obj["dn"]) == str(names.configdn) or
1288 str(obj["dn"]) == str(names.schemadn)):
1289 hash[str(obj["dn"])] = obj["whenCreated"]
1291 listkeys = hash.keys()
1292 listkeys.sort(dn_sort)
1294 for key in listkeys:
1295 try:
1296 delta = Message()
1297 delta.dn = Dn(samdb, key)
1298 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1299 "whenCreated" )
1300 samdb.modify(delta, ["recalculate_sd:0"])
1301 except:
1302 # XXX: We should always catch an explicit exception.
1303 # What could go wrong here?
1304 samdb.transaction_cancel()
1305 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1306 scope=SCOPE_SUBTREE,
1307 attrs=["dn", "nTSecurityDescriptor"],
1308 controls=["search_options:1:2"])
1309 badsd = ndr_unpack(security.descriptor,
1310 str(res[0]["nTSecurityDescriptor"]))
1311 print "bad stuff %s" % badsd.as_sddl(names.domainsid)
1312 return
1314 def removeProvisionUSN(samdb):
1315 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1316 entry = samdb.search(expression="dn=@PROVISION", base = "",
1317 scope=SCOPE_SUBTREE,
1318 attrs=attrs)
1319 empty = Message()
1320 empty.dn = entry[0].dn
1321 delta = samdb.msg_diff(entry[0], empty)
1322 delta.remove("dn")
1323 delta.dn = entry[0].dn
1324 samdb.modify(delta)
1326 def remove_stored_generated_attrs(paths, creds, session, lp):
1327 """Remove previously stored constructed attributes
1329 :param paths: List of paths for different provision objects
1330 from the upgraded provision
1331 :param creds: A credential object
1332 :param session: A session object
1333 :param lp: A line parser object
1334 :return: An associative array whose key are the different constructed
1335 attributes and the value the dn where this attributes were found.
1339 def simple_update_basesamdb(newpaths, paths, names):
1340 """Update the provision container db: sam.ldb
1341 This function is aimed at very old provision (before alpha9)
1343 :param newpaths: List of paths for different provision objects
1344 from the reference provision
1345 :param paths: List of paths for different provision objects
1346 from the upgraded provision
1347 :param names: List of key provision parameters"""
1349 message(SIMPLE, "Copy samdb")
1350 shutil.copy(newpaths.samdb, paths.samdb)
1352 message(SIMPLE, "Update partitions filename if needed")
1353 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1354 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1355 usersldb = os.path.join(paths.private_dir, "users.ldb")
1356 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1358 if not os.path.isdir(samldbdir):
1359 os.mkdir(samldbdir)
1360 os.chmod(samldbdir, 0700)
1361 if os.path.isfile(schemaldb):
1362 shutil.copy(schemaldb, os.path.join(samldbdir,
1363 "%s.ldb"%str(names.schemadn).upper()))
1364 os.remove(schemaldb)
1365 if os.path.isfile(usersldb):
1366 shutil.copy(usersldb, os.path.join(samldbdir,
1367 "%s.ldb"%str(names.rootdn).upper()))
1368 os.remove(usersldb)
1369 if os.path.isfile(configldb):
1370 shutil.copy(configldb, os.path.join(samldbdir,
1371 "%s.ldb"%str(names.configdn).upper()))
1372 os.remove(configldb)
1375 def update_privilege(ref_private_path, cur_private_path):
1376 """Update the privilege database
1378 :param ref_private_path: Path to the private directory of the reference
1379 provision.
1380 :param cur_private_path: Path to the private directory of the current
1381 (and to be updated) provision."""
1382 message(SIMPLE, "Copy privilege")
1383 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1384 os.path.join(cur_private_path, "privilege.ldb"))
1387 def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):
1388 """Upgrade the SAM DB contents for all the provision partitions
1390 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1391 provision
1392 :param samdb: An LDB object connected to the sam.ldb of the update
1393 provision
1394 :param names: List of key provision parameters
1395 :param provisionUSNs: A dictionnary with range of USN modified during provision
1396 or upgradeprovision. Ranges are grouped by invocationID.
1397 :param schema: A Schema object that represent the schema of the provision
1398 :param prereloadfunc: A function that must be executed just before the reload
1399 of the schema
1402 message(SIMPLE, "Starting update of samdb")
1403 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1404 schema, provisionUSNs, prereloadfunc)
1405 if ret:
1406 message(SIMPLE, "Update of samdb finished")
1407 return 1
1408 else:
1409 message(SIMPLE, "Update failed")
1410 return 0
1413 def copyxattrs(dir, refdir):
1414 """ Copy owner, groups, extended ACL and NT acls from
1415 a reference dir to a destination dir
1417 Both dir are supposed to hold the same files
1418 :param dir: Destination dir
1419 :param refdir: Reference directory"""
1421 noxattr = 0
1422 for root, dirs, files in os.walk(dir, topdown=True):
1423 for name in files:
1424 subdir=root[len(dir):]
1425 ref = os.path.join("%s%s" % (refdir, subdir), name)
1426 statsinfo = os.stat(ref)
1427 tgt = os.path.join(root, name)
1428 try:
1430 os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
1431 # Get the xattr attributes if any
1432 try:
1433 attribute = samba.xattr_native.wrap_getxattr(ref,
1434 xattr.XATTR_NTACL_NAME)
1435 samba.xattr_native.wrap_setxattr(tgt,
1436 xattr.XATTR_NTACL_NAME,
1437 attribute)
1438 except:
1439 noxattr = 1
1440 attribute = samba.xattr_native.wrap_getxattr(ref,
1441 "system.posix_acl_access")
1442 samba.xattr_native.wrap_setxattr(tgt,
1443 "system.posix_acl_access",
1444 attribute)
1445 except:
1446 continue
1447 for name in dirs:
1448 subdir=root[len(dir):]
1449 ref = os.path.join("%s%s" % (refdir, subdir), name)
1450 statsinfo = os.stat(ref)
1451 tgt = os.path.join(root, name)
1452 try:
1453 os.chown(os.path.join(root, name), statsinfo.st_uid,
1454 statsinfo.st_gid)
1455 try:
1456 attribute = samba.xattr_native.wrap_getxattr(ref,
1457 xattr.XATTR_NTACL_NAME)
1458 samba.xattr_native.wrap_setxattr(tgt,
1459 xattr.XATTR_NTACL_NAME,
1460 attribute)
1461 except:
1462 noxattr = 1
1463 attribute = samba.xattr_native.wrap_getxattr(ref,
1464 "system.posix_acl_access")
1465 samba.xattr_native.wrap_setxattr(tgt,
1466 "system.posix_acl_access",
1467 attribute)
1469 except:
1470 continue
1473 def backup_provision(paths, dir):
1474 """This function backup the provision files so that a rollback
1475 is possible
1477 :param paths: Paths to different objects
1478 :param dir: Directory where to store the backup
1481 shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
1482 copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
1483 shutil.copy2(paths.samdb, dir)
1484 shutil.copy2(paths.secrets, dir)
1485 shutil.copy2(paths.idmapdb, dir)
1486 shutil.copy2(paths.privilege, dir)
1487 if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1488 shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1489 shutil.copy2(paths.smbconf, dir)
1490 shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1492 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1493 if not os.path.isdir(samldbdir):
1494 samldbdir = paths.private_dir
1495 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1496 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1497 usersldb = os.path.join(paths.private_dir, "users.ldb")
1498 shutil.copy2(schemaldb, dir)
1499 shutil.copy2(usersldb, dir)
1500 shutil.copy2(configldb, dir)
1501 else:
1502 shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1507 def sync_calculated_attributes(samdb, names):
1508 """Synchronize attributes used for constructed ones, with the
1509 old constructed that were stored in the database.
1511 This apply for instance to msds-keyversionnumber that was
1512 stored and that is now constructed from replpropertymetadata.
1514 :param samdb: An LDB object attached to the currently upgraded samdb
1515 :param names: Various key parameter about current provision.
1517 listAttrs = ["msDs-KeyVersionNumber"]
1518 hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
1519 if hash.has_key("msDs-KeyVersionNumber"):
1520 increment_calculated_keyversion_number(samdb, names.rootdn,
1521 hash["msDs-KeyVersionNumber"])
1523 # Synopsis for updateprovision
1524 # 1) get path related to provision to be update (called current)
1525 # 2) open current provision ldbs
1526 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1527 # of the DC ....)
1528 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1529 # by either upgradeprovision or provision
1530 # 5) creation of a new provision the latest version of provision script
1531 # (called reference)
1532 # 6) get reference provision paths
1533 # 7) open reference provision ldbs
1534 # 8) setup helpers data that will help the update process
1535 # 9) update the privilege ldb by copying the one of referecence provision to
1536 # the current provision
1537 # 10)get the oemInfo field, this field contains information about the different
1538 # provision that have been done
1539 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1540 # integer) or none of this the following things are done
1541 # A) When alpha9 or alphaxx is present
1542 # The base sam.ldb file is updated by looking at the difference between
1543 # referrence one and the current one. Everything is copied with the
1544 # exception of lastProvisionUSN attributes.
1545 # B) Other case (it reflect that that provision was done before alpha9)
1546 # The base sam.ldb of the reference provision is copied over
1547 # the current one, if necessary ldb related to partitions are moved
1548 # and renamed
1549 # The highest used USN is fetched so that changed by upgradeprovision
1550 # usn can be tracked
1551 # 12)A Schema object is created, it will be used to provide a complete
1552 # schema to current provision during update (as the schema of the
1553 # current provision might not be complete and so won't allow some
1554 # object to be created)
1555 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1556 # 14)The secrets db is updated by pull all the difference from the reference
1557 # provision into the current provision
1558 # 15)As the previous step has most probably modified the password stored in
1559 # in secret for the current DC, a new password is generated,
1560 # the kvno is bumped and the entry in samdb is also updated
1561 # 16)For current provision older than alpha9, we must fix the SD a little bit
1562 # administrator to update them because SD used to be generated with the
1563 # system account before alpha9.
1564 # 17)The highest usn modified so far is searched in the database it will be
1565 # the upper limit for usn modified during provision.
1566 # This is done before potential SD recalculation because we do not want
1567 # SD modified during recalculation to be marked as modified during provision
1568 # (and so possibly remplaced at next upgradeprovision)
1569 # 18)Rebuilt SD if the flag indicate to do so
1570 # 19)Check difference between SD of reference provision and those of the
1571 # current provision. The check is done by getting the sddl representation
1572 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1573 # Each part is verified separetly, for dacl and sacl ACL is splited into
1574 # ACEs and each ACE is verified separately (so that a permutation in ACE
1575 # didn't raise as an error).
1576 # 20)The oemInfo field is updated to add information about the fact that the
1577 # provision has been updated by the upgradeprovision version xxx
1578 # (the version is the one obtained when starting samba with the --version
1579 # parameter)
1580 # 21)Check if the current provision has all the settings needed for dynamic
1581 # DNS update to work (that is to say the provision is newer than
1582 # january 2010). If not dns configuration file from reference provision
1583 # are copied in a sub folder and the administrator is invited to
1584 # do what is needed.
1585 # 22)If the lastProvisionUSN attribute was present it is updated to add
1586 # the range of usns modified by the current upgradeprovision
1589 # About updating the sam DB
1590 # The update takes place in update_partition function
1591 # This function read both current and reference provision and list all
1592 # the available DN of objects
1593 # If the string representation of a DN in reference provision is
1594 # equal to the string representation of a DN in current provision
1595 # (without taking care of case) then the object is flaged as being
1596 # present. If the object is not present in current provision the object
1597 # is being flaged as missing in current provision. Object present in current
1598 # provision but not in reference provision are ignored.
1599 # Once the list of objects present and missing is done, the deleted object
1600 # containers are created in the differents partitions (if missing)
1602 # Then the function add_missing_entries is called
1603 # This function will go through the list of missing entries by calling
1604 # add_missing_object for the given object. If this function returns 0
1605 # it means that the object needs some other object in order to be created
1606 # The object is reappended at the end of the list to be created later
1607 # (and preferably after all the needed object have been created)
1608 # The function keeps on looping on the list of object to be created until
1609 # it's empty or that the number of defered creation is equal to the number
1610 # of object that still needs to be created.
1612 # The function add_missing_object will first check if the object can be created.
1613 # That is to say that it didn't depends other not yet created objects
1614 # If requisit can't be fullfilled it exists with 0
1615 # Then it will try to create the missing entry by creating doing
1616 # an ldb_message_diff between the object in the reference provision and
1617 # an empty object.
1618 # This resulting object is filtered to remove all the back link attribute
1619 # (ie. memberOf) as they will be created by the other linked object (ie.
1620 # the one with the member attribute)
1621 # All attributes specified in the attrNotCopied array are
1622 # also removed it's most of the time generated attributes
1624 # After missing entries have been added the update_partition function will
1625 # take care of object that exist but that need some update.
1626 # In order to do so the function update_present is called with the list
1627 # of object that are present in both provision and that might need an update.
1629 # This function handle first case mismatch so that the DN in the current
1630 # provision have the same case as in reference provision
1632 # It will then construct an associative array consiting of attributes as
1633 # key and invocationid as value( if the originating invocation id is
1634 # different from the invocation id of the current DC the value is -1 instead).
1636 # If the range of provision modified attributes is present, the function will
1637 # use the replMetadataProperty update method which is the following:
1638 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1639 # creationTime, msDs-KeyVersionNumber, oEMInformation
1640 # Check for each attribute if its usn is within one of the modified by
1641 # provision range and if its originating id is the invocation id of the
1642 # current DC, then validate the update from reference to current.
1643 # If not or if there is no replMetatdataProperty for this attribute then we
1644 # do not update it.
1645 # Otherwise (case the range of provision modified attribute is not present) it
1646 # use the following process:
1647 # All attributes that need to be added are accepted at the exeption of those
1648 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1649 # correct flags specified.
1650 # For attributes that need to be modified or removed, a check is performed
1651 # in OverwrittenAtt, if the attribute is present and the modification flag
1652 # (remove, delete) is one of those listed for this attribute then modification
1653 # is accepted. For complicated handling of attribute update, the control is passed
1654 # to handle_special_case
1658 if __name__ == '__main__':
1659 global defSDmodified
1660 defSDmodified = False
1661 # From here start the big steps of the program
1662 # 1) First get files paths
1663 paths = get_paths(param, smbconf=smbconf)
1664 # Get ldbs with the system session, it is needed for searching
1665 # provision parameters
1666 session = system_session()
1668 # This variable will hold the last provision USN once if it exists.
1669 minUSN = 0
1670 # 2)
1671 ldbs = get_ldbs(paths, creds, session, lp)
1672 backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1673 prefix="backupprovision")
1674 backup_provision(paths, backupdir)
1675 try:
1676 ldbs.startTransactions()
1678 # 3) Guess all the needed names (variables in fact) from the current
1679 # provision.
1680 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1681 paths, smbconf, lp)
1682 # 4)
1683 lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1684 if lastProvisionUSNs is not None:
1685 v = 0
1686 for k in lastProvisionUSNs.keys():
1687 for r in lastProvisionUSNs[k]:
1688 v = v + 1
1690 message(CHANGE,
1691 "Find last provision USN, %d invocation(s) for a total of %d ranges" % \
1692 (len(lastProvisionUSNs.keys()), v /2 ))
1694 if lastProvisionUSNs.get("default") != None:
1695 message(CHANGE, "Old style for usn ranges used")
1696 lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"]
1697 del lastProvisionUSNs["default"]
1698 # Objects will be created with the admin session
1699 # (not anymore system session)
1700 adm_session = admin_session(lp, str(names.domainsid))
1701 # So we reget handle on objects
1702 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1703 if not opts.fixntacl:
1704 if not sanitychecks(ldbs.sam, names):
1705 message(SIMPLE, "Sanity checks for the upgrade have failed. "
1706 "Check the messages and correct the errors "
1707 "before rerunning upgradeprovision")
1708 ldbs.groupedRollback()
1709 sys.exit(1)
1711 # Let's see provision parameters
1712 print_provision_key_parameters(names)
1714 # 5) With all this information let's create a fresh new provision used as
1715 # reference
1716 message(SIMPLE, "Creating a reference provision")
1717 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1718 prefix="referenceprovision")
1719 newprovision(names, creds, session, smbconf, provisiondir,
1720 provision_logger)
1722 # TODO
1723 # 6) and 7)
1724 # We need to get a list of object which SD is directly computed from
1725 # defaultSecurityDescriptor.
1726 # This will allow us to know which object we can rebuild the SD in case
1727 # of change of the parent's SD or of the defaultSD.
1728 # Get file paths of this new provision
1729 newpaths = get_paths(param, targetdir=provisiondir)
1730 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1731 new_ldbs.startTransactions()
1733 # 8) Populate some associative array to ease the update process
1734 # List of attribute which are link and backlink
1735 populate_links(new_ldbs.sam, names.schemadn)
1736 # List of attribute with ASN DN synthax)
1737 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1738 # 9)
1739 update_privilege(newpaths.private_dir, paths.private_dir)
1740 # 10)
1741 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1742 # Do some modification on sam.ldb
1743 ldbs.groupedCommit()
1744 new_ldbs.groupedCommit()
1745 deltaattr = None
1746 # 11)
1747 if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1748 # 11) A
1749 # Starting from alpha9 we can consider that the structure is quite ok
1750 # and that we should do only dela
1751 deltaattr = delta_update_basesamdb(newpaths.samdb,
1752 paths.samdb,
1753 creds,
1754 session,
1756 message)
1757 else:
1758 # 11) B
1759 simple_update_basesamdb(newpaths, paths, names)
1760 ldbs = get_ldbs(paths, creds, session, lp)
1761 removeProvisionUSN(ldbs.sam)
1763 ldbs.startTransactions()
1764 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1765 new_ldbs.startTransactions()
1767 # 12)
1768 schema = Schema(names.domainsid, schemadn=str(names.schemadn))
1769 # We create a closure that will be invoked just before schema reload
1770 def schemareloadclosure():
1771 basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
1772 options=["modules:"])
1773 doit = False
1774 if deltaattr is not None and len(deltaattr) > 1:
1775 doit = True
1776 if doit:
1777 deltaattr.remove("dn")
1778 for att in deltaattr:
1779 if att.lower() == "dn":
1780 continue
1781 if (deltaattr.get(att) is not None
1782 and deltaattr.get(att).flags() != FLAG_MOD_ADD):
1783 doit = False
1784 elif deltaattr.get(att) is None:
1785 doit = False
1786 if doit:
1787 message(CHANGE, "Applying delta to @ATTRIBUTES")
1788 deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
1789 basesam.modify(deltaattr)
1790 else:
1791 message(CHANGE, "Not applying delta to @ATTRIBUTES because "
1792 "there is not only add")
1793 # 13)
1794 if opts.full:
1795 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1796 schema, schemareloadclosure):
1797 message(SIMPLE, "Rolling back all changes. Check the cause"
1798 " of the problem")
1799 message(SIMPLE, "Your system is as it was before the upgrade")
1800 ldbs.groupedRollback()
1801 new_ldbs.groupedRollback()
1802 shutil.rmtree(provisiondir)
1803 sys.exit(1)
1804 else:
1805 # Try to reapply the change also when we do not change the sam
1806 # as the delta_upgrade
1807 schemareloadclosure()
1808 sync_calculated_attributes(ldbs.sam, names)
1809 res = ldbs.sam.search(expression="(samaccountname=dns)",
1810 scope=SCOPE_SUBTREE, attrs=["dn"],
1811 controls=["search_options:1:2"])
1812 if len(res) > 0:
1813 message(SIMPLE, "You still have the old DNS object for managing "
1814 "dynamic DNS, but you didn't supply --full so "
1815 "a correct update can't be done")
1816 ldbs.groupedRollback()
1817 new_ldbs.groupedRollback()
1818 shutil.rmtree(provisiondir)
1819 sys.exit(1)
1820 # 14)
1821 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1822 # 14bis)
1823 res = ldbs.sam.search(expression="(samaccountname=dns)",
1824 scope=SCOPE_SUBTREE, attrs=["dn"],
1825 controls=["search_options:1:2"])
1827 if (len(res) == 1):
1828 ldbs.sam.delete(res[0]["dn"])
1829 res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
1830 scope=SCOPE_SUBTREE, attrs=["dn"])
1831 update_dns_account_password(ldbs.sam, ldbs.secrets, names)
1832 message(SIMPLE, "IMPORTANT!!! "
1833 "If you were using Dynamic DNS before you need "
1834 "to update your configuration, so that the "
1835 "tkey-gssapi-credential has the following value: "
1836 "DNS/%s.%s" % (names.netbiosname.lower(),
1837 names.realm.lower()))
1838 # 15)
1839 message(SIMPLE, "Update machine account")
1840 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1842 # 16) SD should be created with admin but as some previous acl were so wrong
1843 # that admin can't modify them we have first to recreate them with the good
1844 # form but with system account and then give the ownership to admin ...
1845 if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1846 message(SIMPLE, "Fixing old povision SD")
1847 fix_partition_sd(ldbs.sam, names)
1848 rebuild_sd(ldbs.sam, names)
1850 # We calculate the max USN before recalculating the SD because we might
1851 # touch object that have been modified after a provision and we do not
1852 # want that the next upgradeprovision thinks that it has a green light
1853 # to modify them
1855 # 17)
1856 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1858 # 18) We rebuild SD only if defaultSecurityDescriptor is modified
1859 # But in fact we should do it also if one object has its SD modified as
1860 # child might need rebuild
1861 if defSDmodified:
1862 message(SIMPLE, "Updating SD")
1863 ldbs.sam.set_session_info(adm_session)
1864 # Alpha10 was a bit broken still
1865 if re.match(r'.*alpha(\d|10)', str(oem)):
1866 fix_partition_sd(ldbs.sam, names)
1867 rebuild_sd(ldbs.sam, names)
1869 # 19)
1870 # Now we are quite confident in the recalculate process of the SD, we make
1871 # it optional.
1872 # Also the check must be done in a clever way as for the moment we just
1873 # compare SDDL
1874 if opts.debugchangesd:
1875 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1877 # 20)
1878 updateOEMInfo(ldbs.sam, str(names.rootdn))
1879 # 21)
1880 check_for_DNS(newpaths.private_dir, paths.private_dir)
1881 # 22)
1882 if lastProvisionUSNs is not None:
1883 update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
1884 if opts.full and (names.policyid is None or names.policyid_dc is None):
1885 update_policyids(names, ldbs.sam)
1886 if opts.full or opts.resetfileacl or opts.fixntacl:
1887 try:
1888 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1889 except ProvisioningError, e:
1890 message(ERROR, "The policy for domain controller is missing. "
1891 "You should restart upgradeprovision with --full")
1892 except IOError, e:
1893 message(ERROR, "Setting ACL not supported on your filesystem")
1894 else:
1895 try:
1896 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1897 except ProvisioningError, e:
1898 message(ERROR, "The policy for domain controller is missing. "
1899 "You should restart upgradeprovision with --full")
1900 if not opts.fixntacl:
1901 ldbs.groupedCommit()
1902 new_ldbs.groupedCommit()
1903 message(SIMPLE, "Upgrade finished!")
1904 # remove reference provision now that everything is done !
1905 # So we have reindexed first if need when the merged schema was reloaded
1906 # (as new attributes could have quick in)
1907 # But the second part of the update (when we update existing objects
1908 # can also have an influence on indexing as some attribute might have their
1909 # searchflag modificated
1910 message(SIMPLE, "Reopenning samdb to trigger reindexing if needed "
1911 "after modification")
1912 samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
1913 message(SIMPLE, "Reindexing finished")
1915 shutil.rmtree(provisiondir)
1916 else:
1917 ldbs.groupedRollback()
1918 message(SIMPLE, "ACLs fixed !")
1919 except StandardError, err:
1920 message(ERROR, "A problem occurred while trying to upgrade your "
1921 "provision. A full backup is located at %s" % backupdir)
1922 if opts.debugall or opts.debugchange:
1923 (typ, val, tb) = sys.exc_info()
1924 traceback.print_exception(typ, val, tb)
1925 sys.exit(1)