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/>.
33 # Allow to run from s4 source directory (without installing samba)
34 sys.path.insert(0, "bin/python")
37 import samba.getopt as options
38 from samba.credentials import DONT_USE_KERBEROS
39 from samba.auth import system_session, admin_session
40 from ldb import (SCOPE_SUBTREE, SCOPE_BASE,
41 FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE,
42 MessageElement, Message, Dn)
43 from samba import param
44 from samba.misc import messageEltFlagToString
45 from samba.provision import (find_setup_dir, get_domain_descriptor,
46 get_config_descriptor, secretsdb_self_join,
47 ProvisioningError, getLastProvisionUSN,
48 get_max_usn, updateProvisionUSN)
49 from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
50 from samba.dcerpc import security, drsblobs
51 from samba.ndr import ndr_unpack
52 from samba.dcerpc.misc import SEC_CHAN_BDC
53 from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
54 find_provision_key_parameters, get_ldbs,
55 usn_in_range, identic_rename, get_diff_sddls,
56 update_secrets, CHANGE, ERROR, SIMPLE,
57 CHANGEALL, GUESS, CHANGESD, PROVISION,
58 updateOEMInfo, getOEMInfo, update_gpo,
59 delta_update_basesamdb)
61 replace=2**FLAG_MOD_REPLACE
63 delete=2**FLAG_MOD_DELETE
67 # Will be modified during provision to tell if default sd has been modified
70 #Errors are always logged
72 __docformat__ = "restructuredText"
74 # Attributes that are never copied from the reference provision (even if they
75 # do not exist in the destination object).
76 # This is most probably because they are populated automatcally when object is
78 # This also apply to imported object from reference provision
79 hashAttrNotCopied = { "dn": 1, "whenCreated": 1, "whenChanged": 1,
80 "objectGUID": 1, "uSNCreated": 1,
81 "replPropertyMetaData": 1, "uSNChanged": 1,
82 "parentGUID": 1, "objectCategory": 1,
83 "distinguishedName": 1, "nTMixedDomain": 1,
84 "showInAdvancedViewOnly": 1, "instanceType": 1,
85 "msDS-Behavior-Version":1, "nextRid":1, "cn": 1,
86 "versionNumber":1, "lmPwdHistory":1, "pwdLastSet": 1,
87 "ntPwdHistory":1, "unicodePwd":1,"dBCSPwd":1,
88 "supplementalCredentials":1, "gPCUserExtensionNames":1,
89 "gPCMachineExtensionNames":1,"maxPwdAge":1, "secret":1,
90 "possibleInferiors":1, "privilege":1,
93 # Usually for an object that already exists we do not overwrite attributes as
94 # they might have been changed for good reasons. Anyway for a few of them it's
95 # mandatory to replace them otherwise the provision will be broken somehow.
96 # But for attribute that are just missing we do not have to specify them as the default
97 # behavior is to add missing attribute
98 hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace,
99 "systemOnly":replace, "searchFlags":replace,
100 "mayContain":replace, "systemFlags":replace+add,
101 "description":replace, "operatingSystemVersion":replace,
102 "adminPropertyPages":replace, "groupType":replace,
103 "wellKnownObjects":replace, "privilege":never,
104 "defaultSecurityDescriptor": replace,
105 "rIDAvailablePool": never,
106 "defaultSecurityDescriptor": replace + add }
112 def define_what_to_log(opts):
116 if opts.debugchangesd:
117 what = what | CHANGESD
120 if opts.debugprovision:
121 what = what | PROVISION
123 what = what | CHANGEALL
127 parser = optparse.OptionParser("provision [options]")
128 sambaopts = options.SambaOptions(parser)
129 parser.add_option_group(sambaopts)
130 parser.add_option_group(options.VersionOptions(parser))
131 credopts = options.CredentialsOptions(parser)
132 parser.add_option_group(credopts)
133 parser.add_option("--setupdir", type="string", metavar="DIR",
134 help="directory with setup files")
135 parser.add_option("--debugprovision", help="Debug provision", action="store_true")
136 parser.add_option("--debugguess", action="store_true",
137 help="Print information on what is different but won't be changed")
138 parser.add_option("--debugchange", action="store_true",
139 help="Print information on what is different but won't be changed")
140 parser.add_option("--debugchangesd", action="store_true",
141 help="Print information security descriptors differences")
142 parser.add_option("--debugall", action="store_true",
143 help="Print all available information (very verbose)")
144 parser.add_option("--full", action="store_true",
145 help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
147 opts = parser.parse_args()[0]
149 handler = logging.StreamHandler(sys.stdout)
150 upgrade_logger = logging.getLogger("upgradeprovision")
151 upgrade_logger.addHandler(handler)
153 provision_logger = logging.getLogger("provision")
154 provision_logger.addHandler(handler)
156 whatToLog = define_what_to_log(opts)
158 def message(what, text):
159 """Print a message if this message type has been selected to be printed
161 :param what: Category of the message
162 :param text: Message to print """
163 if (whatToLog & what) or what <= 0:
164 upgrade_logger.info("%s", text)
166 if len(sys.argv) == 1:
167 opts.interactive = True
168 lp = sambaopts.get_loadparm()
169 smbconf = lp.configfile
171 creds = credopts.get_credentials(lp)
172 creds.set_kerberos_state(DONT_USE_KERBEROS)
173 setup_dir = opts.setupdir
174 if setup_dir is None:
175 setup_dir = find_setup_dir()
179 def check_for_DNS(refprivate, private):
180 """Check if the provision has already the requirement for dynamic dns
182 :param refprivate: The path to the private directory of the reference
184 :param private: The path to the private directory of the upgraded
187 spnfile = "%s/spn_update_list" % private
188 namedfile = lp.get("dnsupdate:path")
191 namedfile = "%s/named.conf.update" % private
193 if not os.path.exists(spnfile):
194 shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
196 destdir = "%s/new_dns" % private
197 dnsdir = "%s/dns" % private
199 if not os.path.exists(namedfile):
200 if not os.path.exists(destdir):
202 if not os.path.exists(dnsdir):
204 shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
205 shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
206 message(SIMPLE, "It seems that you provision didn't integrate new rules "
207 "for dynamic dns update of domain related entries")
208 message(SIMPLE, "A copy of the new bind configuration files and "
209 "template as been put in %s, you should read them and configure dynamic "
210 " dns update" % destdir)
213 def populate_links(samdb, schemadn):
214 """Populate an array with all the back linked attributes
216 This attributes that are modified automaticaly when
217 front attibutes are changed
219 :param samdb: A LDB object for sam.ldb file
220 :param schemadn: DN of the schema for the partition"""
221 linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
222 backlinked.extend(linkedAttHash.values())
223 for t in linkedAttHash.keys():
226 def populate_dnsyntax(samdb, schemadn):
227 """Populate an array with all the attributes that have DN synthax
230 :param samdb: A LDB object for sam.ldb file
231 :param schemadn: DN of the schema for the partition"""
232 res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
233 str(schemadn)), scope=SCOPE_SUBTREE,
234 attrs=["lDAPDisplayName"])
236 dn_syntax_att.append(elem["lDAPDisplayName"])
239 def sanitychecks(samdb, names):
240 """Make some checks before trying to update
242 :param samdb: An LDB object opened on sam.ldb
243 :param names: list of key provision parameters
244 :return: Status of check (1 for Ok, 0 for not Ok) """
245 res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
246 scope=SCOPE_SUBTREE, attrs=["dn"],
247 controls=["search_options:1:2"])
249 print "No DC found, your provision is most probably hardly broken !"
252 print "Found %d domain controllers, for the moment upgradeprovision" \
253 "is not able to handle upgrade on domain with more than one DC, please demote" \
254 " the other(s) DC(s) before upgrading" % len(res)
260 def print_provision_key_parameters(names):
261 """Do a a pretty print of provision parameters
263 :param names: list of key provision parameters """
264 message(GUESS, "rootdn :" + str(names.rootdn))
265 message(GUESS, "configdn :" + str(names.configdn))
266 message(GUESS, "schemadn :" + str(names.schemadn))
267 message(GUESS, "serverdn :" + str(names.serverdn))
268 message(GUESS, "netbiosname :" + names.netbiosname)
269 message(GUESS, "defaultsite :" + names.sitename)
270 message(GUESS, "dnsdomain :" + names.dnsdomain)
271 message(GUESS, "hostname :" + names.hostname)
272 message(GUESS, "domain :" + names.domain)
273 message(GUESS, "realm :" + names.realm)
274 message(GUESS, "invocationid:" + names.invocation)
275 message(GUESS, "policyguid :" + names.policyid)
276 message(GUESS, "policyguiddc:" + str(names.policyid_dc))
277 message(GUESS, "domainsid :" + str(names.domainsid))
278 message(GUESS, "domainguid :" + names.domainguid)
279 message(GUESS, "ntdsguid :" + names.ntdsguid)
280 message(GUESS, "domainlevel :" + str(names.domainlevel))
283 def handle_special_case(att, delta, new, old, usn):
284 """Define more complicate update rules for some attributes
286 :param att: The attribute to be updated
287 :param delta: A messageElement object that correspond to the difference
288 between the updated object and the reference one
289 :param new: The reference object
290 :param old: The Updated object
291 :param usn: The highest usn modified by a previous (upgrade)provision
292 :return: True to indicate that the attribute should be kept, False for
295 flag = delta.get(att).flags()
296 # We do most of the special case handle if we do not have the
297 # highest usn as otherwise the replPropertyMetaData will guide us more
300 if (att == "member" and flag == FLAG_MOD_REPLACE):
304 for elem in old[0][att]:
305 hash[str(elem).lower()]=1
306 newval.append(str(elem))
308 for elem in new[0][att]:
309 if not hash.has_key(str(elem).lower()):
311 newval.append(str(elem))
313 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
318 if (att == "gPLink" or att == "gPCFileSysPath") and \
319 flag == FLAG_MOD_REPLACE and\
320 str(new[0].dn).lower() == str(old[0].dn).lower():
324 if att == "forceLogoff":
325 ref=0x8000000000000000
326 oldval=int(old[0][att][0])
327 newval=int(new[0][att][0])
328 ref == old and ref == abs(new)
331 if (att == "adminDisplayName" or att == "adminDescription"):
334 if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (str(names.schemadn))\
335 and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
338 if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
339 att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
342 if (str(old[0].dn) == "%s" % (str(names.rootdn))
343 and att == "subRefs" and flag == FLAG_MOD_REPLACE):
346 if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
349 # This is a bit of special animal as we might have added
350 # already SPN entries to the list that has to be modified
351 # So we go in detail to try to find out what has to be added ...
352 if ( att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):
356 for elem in old[0][att]:
358 newval.append(str(elem))
360 for elem in new[0][att]:
361 if not hash.has_key(str(elem)):
363 newval.append(str(elem))
365 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
372 def dump_denied_change(dn, att, flagtxt, current, reference):
373 """Print detailed information about why a changed is denied
375 :param dn: DN of the object which attribute is denied
376 :param att: Attribute that was supposed to be upgraded
377 :param flagtxt: Type of the update that should be performed
378 (add, change, remove, ...)
379 :param current: Value(s) of the current attribute
380 :param reference: Value(s) of the reference attribute"""
382 message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
383 +" is not allowed to be changed/removed, I discard this change")
384 if att != "objectSid" :
386 for e in range(0, len(current)):
387 message(CHANGE, "old %d : %s" % (i, str(current[e])))
389 if reference != None:
391 for e in range(0, len(reference)):
392 message(CHANGE, "new %d : %s" % (i, str(reference[e])))
395 message(CHANGE, "old : %s" % str(ndr_unpack( security.dom_sid, current[0])))
396 message(CHANGE, "new : %s" % str(ndr_unpack( security.dom_sid, reference[0])))
399 def handle_special_add(samdb, dn, names):
400 """Handle special operation (like remove) on some object needed during
403 This is mostly due to wrong creation of the object in previous provision.
404 :param samdb: An Ldb object representing the SAM database
405 :param dn: DN of the object to inspect
406 :param names: list of key provision parameters"""
409 objDn = Dn(samdb, "CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn)
411 #This entry was misplaced lets remove it if it exists
412 dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
415 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn)
417 #This entry was misplaced lets remove it if it exists
418 dntoremove = "CN=Certificate Service DCOM Access,"\
419 "CN=Users, %s" % names.rootdn
422 objDn = Dn(samdb, "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn)
424 #This entry was misplaced lets remove it if it exists
425 dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
427 objDn = Dn(samdb, "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn)
429 #This entry was misplaced lets remove it if it exists
430 dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
432 objDn = Dn(samdb,"CN=System,CN=WellKnown Security Principals,"\
433 "CN=Configuration,%s" % names.rootdn)
435 oldDn = Dn(samdb,"CN=Well-Known-Security-Id-System,"\
436 "CN=WellKnown Security Principals,"\
437 "CN=Configuration,%s" % names.rootdn)
439 res = samdb.search(expression="(dn=%s)" % oldDn,
440 base=str(names.rootdn),
441 scope=SCOPE_SUBTREE, attrs=["dn"],
442 controls=["search_options:1:2"])
444 message(CHANGE, "Existing object %s must be replaced by %s,"\
445 "Renaming old object" % (str(oldDn), str(dn)))
446 samdb.rename(oldDn, objDn)
450 if dntoremove != None:
451 res = samdb.search(expression="(dn=%s)" % dntoremove,
452 base=str(names.rootdn),
453 scope=SCOPE_SUBTREE, attrs=["dn"],
454 controls=["search_options:1:2"])
456 message(CHANGE, "Existing object %s must be replaced by %s,"\
457 "removing old object" % (dntoremove, str(dn)))
458 samdb.delete(res[0]["dn"])
461 def check_dn_nottobecreated(hash, index, listdn):
462 """Check if one of the DN present in the list has a creation order
463 greater than the current.
465 Hash is indexed by dn to be created, with each key
466 is associated the creation order.
468 First dn to be created has the creation order 0, second has 1, ...
469 Index contain the current creation order
471 :param hash: Hash holding the different DN of the object to be
473 :param index: Current creation order
474 :param listdn: List of DNs on which the current DN depends on
475 :return: None if the current object do not depend on other
476 object or if all object have been created before."""
480 key = str(dn).lower()
481 if hash.has_key(key) and hash[key] > index:
487 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
488 """Add a new object if the dependencies are satisfied
490 The function add the object if the object on which it depends are already
493 :param ref_samdb: Ldb object representing the SAM db of the reference
495 :param samdb: Ldb object representing the SAM db of the upgraded
497 :param dn: DN of the object to be added
498 :param names: List of key provision parameters
499 :param basedn: DN of the partition to be updated
500 :param hash: Hash holding the different DN of the object to be
502 :param index: Current creation order
503 :return: True if the object was created False otherwise"""
505 if handle_special_add(samdb, dn, names):
507 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
508 scope=SCOPE_SUBTREE, controls=["search_options:1:2"])
510 delta = samdb.msg_diff(empty, reference[0])
512 for att in hashAttrNotCopied.keys():
514 for att in backlinked:
516 depend_on_yettobecreated = None
517 for att in dn_syntax_att:
518 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
520 if depend_on_yet_tobecreated != None:
521 message(CHANGE, "Object %s depends on %s in attribute %s," \
522 "delaying the creation" % (str(dn), \
523 depend_on_yet_tobecreated, str(att)))
527 message(CHANGE,"Object %s will be added" % dn)
528 samdb.add(delta, ["relax:0"])
531 def gen_dn_index_hash(listMissing):
532 """Generate a hash associating the DN to its creation order
534 :param listMissing: List of DN
535 :return: Hash with DN as keys and creation order as values"""
537 for i in range(0, len(listMissing)):
538 hash[str(listMissing[i]).lower()] = i
541 def add_deletedobj_containers(ref_samdb, samdb, names):
542 """Add the object containter: CN=Deleted Objects
544 This function create the container for each partition that need one and
545 then reference the object into the root of the partition
547 :param ref_samdb: Ldb object representing the SAM db of the reference
549 :param samdb: Ldb object representing the SAM db of the upgraded provision
550 :param names: List of key provision parameters"""
553 wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
554 partitions = [str(names.rootdn), str(names.configdn)]
555 for part in partitions:
556 ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
557 base=part, scope=SCOPE_SUBTREE,
559 controls=["show_deleted:0"])
560 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
561 base=part, scope=SCOPE_SUBTREE,
563 controls=["show_deleted:0"])
564 if len(ref_delObjCnt) > len(delObjCnt):
565 reference = ref_samdb.search(expression="cn=Deleted Objects",
566 base=part, scope=SCOPE_SUBTREE,
567 controls=["show_deleted:0"])
569 delta = samdb.msg_diff(empty, reference[0])
571 delta.dn = Dn(samdb, str(reference[0]["dn"]))
572 for att in hashAttrNotCopied.keys():
577 res = samdb.search(expression="(objectClass=*)", base=part,
579 attrs=["dn", "wellKnownObjects"])
581 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
585 wko = res[0]["wellKnownObjects"]
587 # The wellKnownObject that we want to add.
589 if str(o) == targetWKO:
591 listwko.append(str(o))
594 listwko.append(targetWKO)
597 delta.dn = Dn(samdb, str(res[0]["dn"]))
598 delta["wellKnownObjects"] = MessageElement(listwko,
603 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
604 """Add the missing object whose DN is the list
606 The function add the object if the objects on which it depends are
609 :param ref_samdb: Ldb object representing the SAM db of the reference
611 :param samdb: Ldb object representing the SAM db of the upgraded
613 :param dn: DN of the object to be added
614 :param names: List of key provision parameters
615 :param basedn: DN of the partition to be updated
616 :param list: List of DN to be added in the upgraded provision"""
621 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
623 listMissing = listDefered
625 hashMissing = gen_dn_index_hash(listMissing)
626 for dn in listMissing:
627 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
631 # DN can't be created because it depends on some
632 # other DN in the list
633 listDefered.append(dn)
634 if len(listDefered) != 0:
635 raise ProvisioningError("Unable to insert missing elements:" \
636 "circular references")
638 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
639 """This function handle updates on links
641 :param samdb: An LDB object pointing to the updated provision
642 :param att: Attribute to update
643 :param basedn: The root DN of the provision
644 :param dn: The DN of the inspected object
645 :param value: The value of the attribute
646 :param ref_value: The value of this attribute in the reference provision
647 :param delta: The MessageElement object that will be applied for
648 transforming the current provision"""
650 res = samdb.search(expression="dn=%s" % dn, base=basedn,
651 controls=["search_options:1:2", "reveal:1"],
659 newlinklist.extend(value)
663 # for w2k domain level the reveal won't reveal anything ...
664 # it means that we can readd links that were removed on purpose ...
665 # Also this function in fact just accept add not removal
667 for e in res[0][att]:
668 if not hash.has_key(e):
669 # We put in the blacklist all the element that are in the "revealed"
670 # result and not in the "standard" result
671 # This element are links that were removed before and so that
672 # we don't wan't to readd
676 if not blacklist.has_key(e) and not hash.has_key(e):
677 newlinklist.append(str(e))
680 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
684 def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
685 """ This function updates the object that are already present in the
688 :param ref_samdb: An LDB object pointing to the reference provision
689 :param samdb: An LDB object pointing to the updated provision
690 :param basedn: A string with the value of the base DN for the provision
692 :param listPresent: A list of object that is present in the provision
693 :param usns: A list of USN range modified by previous provision and
695 :param invocationid: The value of the invocationid for the current DC"""
698 # This hash is meant to speedup lookup of attribute name from an oid,
699 # it's for the replPropertyMetaData handling
701 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
702 controls=["search_options:1:2"], attrs=["attributeID",
706 strDisplay = str(e.get("lDAPDisplayName"))
707 hash_oid_name[str(e.get("attributeID"))] = strDisplay
709 msg = "Unable to insert missing elements: circular references"
710 raise ProvisioningError(msg)
713 controls = ["search_options:1:2", "sd_flags:1:2"]
714 for dn in listPresent:
715 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
718 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
719 scope=SCOPE_SUBTREE, controls=controls)
722 (str(current[0].dn) != str(reference[0].dn)) and
723 (str(current[0].dn).upper() == str(reference[0].dn).upper())
725 message(CHANGE, "Name are the same but case change,"\
726 "let's rename %s to %s" % (str(current[0].dn),
727 str(reference[0].dn)))
728 identic_rename(samdb, reference[0].dn)
729 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
731 controls=["search_options:1:2"])
733 delta = samdb.msg_diff(current[0], reference[0])
735 for att in hashAttrNotCopied.keys():
738 for att in backlinked:
743 if len(delta.items()) > 1 and usns != None:
744 # Fetch the replPropertyMetaData
745 res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
746 scope=SCOPE_SUBTREE, controls=controls,
747 attrs=["replPropertyMetaData"])
748 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
749 str(res[0]["replPropertyMetaData"])).ctr
753 # We put in this hash only modification
754 # made on the current host
755 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
756 if str(o.originating_invocation_id) == str(invocationid):
757 # Note we could just use 1 here
758 hash_attr_usn[att] = o.originating_usn
760 hash_attr_usn[att] = -1
767 # We have updated by provision usn information so let's exploit
768 # replMetadataProperties
769 if forwardlinked.has_key(att):
770 handle_links(samdb, att, basedn, current[0]["dn"],
771 current[0][att], reference[0][att], delta)
773 if isFirst == 0 and len(delta.items())>1:
775 txt = "%s\n" % (str(dn))
777 # There is always a dn attribute after a msg_diff
779 if att == "rIDAvailablePool":
782 if att == "objectSid":
785 if att == "creationTime":
788 if att == "oEMInformation":
791 if att == "msDs-KeyVersionNumber":
792 # This is the kvno of the computer/user it's a very bad
796 if handle_special_case(att, delta, reference, current, usns):
797 # This attribute is "complicated" to handle and handling
798 # was done in handle_special_case
800 attrUSN = hash_attr_usn.get(att)
801 if att == "forceLogoff" and attrUSN == None:
808 # This attribute was last modified by another DC forget
810 message(CHANGE, "%sAttribute: %s has been" \
811 "created/modified/deleted by another DC,"
812 " do nothing" % (txt, att ))
816 elif usn_in_range(int(attrUSN), usns) == 0:
817 message(CHANGE, "%sAttribute: %s has been" \
818 "created/modified/deleted not during a" \
819 " provision or upgradeprovision: current" \
820 " usn %d , do nothing" % (txt, att, attrUSN))
825 if att == "defaultSecurityDescriptor":
828 message(CHANGE, "%sAttribute: %s will be modified" \
829 "/deleted it was last modified" \
830 "during a provision, current usn:" \
831 "%d" % (txt, att, attrUSN))
834 message(CHANGE, "%sAttribute: %s will be added because" \
835 " it hasn't existed before " % (txt, att))
840 # Old school way of handling things for pre alpha12 upgrade
842 msgElt = delta.get(att)
844 if att == "nTSecurityDescriptor":
851 if not hashOverwrittenAtt.has_key(att):
852 if msgElt.flags() != FLAG_MOD_ADD:
853 if not handle_special_case(att, delta, reference, current,
855 if opts.debugchange or opts.debugall:
857 dump_denied_change(dn, att,
858 messageEltFlagToString(msgElt.flags()),
859 current[0][att], reference[0][att])
861 dump_denied_change(dn, att,
862 messageEltFlagToString(msgElt.flags()),
863 current[0][att], None)
867 if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
869 elif hashOverwrittenAtt.get(att)==never:
874 if len(delta.items()) >1:
875 attributes=", ".join(delta.keys())
876 message(CHANGE, "%s is different from the reference one, changed" \
877 " attributes: %s\n" % (dn, attributes))
878 changed = changed + 1
882 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs):
883 """Check differences between the reference provision and the upgraded one.
885 It looks for all objects which base DN is name.
887 This function will also add the missing object and update existing object
888 to add or remove attributes that were missing.
890 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
892 :param samdb: An LDB object connected to the sam.ldb of the update
894 :param basedn: String value of the DN of the partition
895 :param names: List of key provision parameters
896 :param schema: A Schema object
897 :param provisionUSNs: The USNs modified by provision/upgradeprovision
907 # Connect to the reference provision and get all the attribute in the
908 # partition referred by name
909 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
910 scope=SCOPE_SUBTREE, attrs=["dn"],
911 controls=["search_options:1:2"])
913 current = samdb.search(expression="objectClass=*", base=basedn,
914 scope=SCOPE_SUBTREE, attrs=["dn"],
915 controls=["search_options:1:2"])
916 # Create a hash for speeding the search of new object
917 for i in range(0, len(reference)):
918 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
920 # Create a hash for speeding the search of existing object in the
922 for i in range(0, len(current)):
923 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
926 for k in hash_new.keys():
927 if not hash.has_key(k):
928 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
929 listMissing.append(hash_new[k])
931 listPresent.append(hash_new[k])
933 # Sort the missing object in order to have object of the lowest level
934 # first (which can be containers for higher level objects)
935 listMissing.sort(dn_sort)
936 listPresent.sort(dn_sort)
938 # The following lines is to load the up to
939 # date schema into our current LDB
940 # a complete schema is needed as the insertion of attributes
941 # and class is done against it
942 # and the schema is self validated
943 samdb.set_schema_from_ldb(schema.ldb)
945 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
946 add_deletedobj_containers(ref_samdb, samdb, names)
948 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
949 changed = update_present(ref_samdb, samdb, basedn, listPresent,
950 provisionUSNs, names.invocation)
951 message(SIMPLE, "There are %d changed objects" % (changed))
954 except StandardError, err:
955 message(ERROR, "Exception during upgrade of samdb:")
956 (typ, val, tb) = sys.exc_info()
957 traceback.print_exception(typ, val, tb)
962 def check_updated_sd(ref_sam, cur_sam, names):
963 """Check if the security descriptor in the upgraded provision are the same
966 :param ref_sam: A LDB object connected to the sam.ldb file used as
967 the reference provision
968 :param cur_sam: A LDB object connected to the sam.ldb file used as
970 :param names: List of key provision parameters"""
971 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
973 attrs=["dn", "nTSecurityDescriptor"],
974 controls=["search_options:1:2"])
975 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
977 attrs=["dn", "nTSecurityDescriptor"],
978 controls=["search_options:1:2"])
980 for i in range(0, len(reference)):
981 refsd = ndr_unpack(security.descriptor,
982 str(reference[i]["nTSecurityDescriptor"]))
983 hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
985 for i in range(0, len(current)):
986 key = str(current[i]["dn"]).lower()
987 if hash.has_key(key):
988 cursd = ndr_unpack(security.descriptor,
989 str(current[i]["nTSecurityDescriptor"]))
990 sddl = cursd.as_sddl(names.domainsid)
991 if sddl != hash[key]:
992 txt = get_diff_sddls(hash[key], sddl)
994 message(CHANGESD, "On object %s ACL is different"\
995 " \n%s" % (current[i]["dn"], txt))
999 def fix_partition_sd(samdb, names):
1000 """This function fix the SD for partition containers (basedn, configdn, ...)
1001 This is needed because some provision use to have broken SD on containers
1003 :param samdb: An LDB object pointing to the sam of the current provision
1004 :param names: A list of key provision parameters
1006 # First update the SD for the rootdn
1007 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1008 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1009 controls=["search_options:1:2"])
1011 delta.dn = Dn(samdb, str(res[0]["dn"]))
1012 descr = get_domain_descriptor(names.domainsid)
1013 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1014 "nTSecurityDescriptor")
1015 samdb.modify(delta, ["recalculate_sd:0"])
1016 # Then the config dn
1017 res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1018 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1019 controls=["search_options:1:2"])
1021 delta.dn = Dn(samdb, str(res[0]["dn"]))
1022 descr = get_config_descriptor(names.domainsid)
1023 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1024 "nTSecurityDescriptor" )
1025 samdb.modify(delta, ["recalculate_sd:0"])
1026 # Then the schema dn
1027 res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1028 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1029 controls=["search_options:1:2"])
1032 delta.dn = Dn(samdb, str(res[0]["dn"]))
1033 descr = get_schema_descriptor(names.domainsid)
1034 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1035 "nTSecurityDescriptor" )
1036 samdb.modify(delta, ["recalculate_sd:0"])
1038 def rebuild_sd(samdb, names):
1039 """Rebuild security descriptor of the current provision from scratch
1041 During the different pre release of samba4 security descriptors (SD)
1042 were notarly broken (up to alpha11 included)
1043 This function allow to get them back in order, this function make the
1044 assumption that nobody has modified manualy an SD
1045 and so SD can be safely recalculated from scratch to get them right.
1047 :param names: List of key provision parameters"""
1051 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1052 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1053 controls=["search_options:1:2"])
1055 if not (str(obj["dn"]) == str(names.rootdn) or
1056 str(obj["dn"]) == str(names.configdn) or \
1057 str(obj["dn"]) == str(names.schemadn)):
1058 hash[str(obj["dn"])] = obj["whenCreated"]
1060 listkeys = hash.keys()
1061 listkeys.sort(dn_sort)
1063 for key in listkeys:
1066 delta.dn = Dn(samdb, key)
1067 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1069 samdb.modify(delta, ["recalculate_sd:0"])
1071 # XXX: We should always catch an explicit exception.
1072 # What could go wrong here?
1073 samdb.transaction_cancel()
1074 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1075 scope=SCOPE_SUBTREE,
1076 attrs=["dn", "nTSecurityDescriptor"],
1077 controls=["search_options:1:2"])
1078 badsd = ndr_unpack(security.descriptor,
1079 str(res[0]["nTSecurityDescriptor"]))
1080 print "bad stuff %s" % badsd.as_sddl(names.domainsid)
1083 def removeProvisionUSN(samdb):
1084 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1085 entry = samdb.search(expression="dn=@PROVISION", base = "",
1086 scope=SCOPE_SUBTREE,
1087 controls=["search_options:1:2"],
1090 empty.dn = entry[0].dn
1091 delta = samdb.msg_diff(entry[0], empty)
1093 delta.dn = entry[0].dn
1097 def simple_update_basesamdb(newpaths, paths, names):
1098 """Update the provision container db: sam.ldb
1099 This function is aimed at very old provision (before alpha9)
1101 :param newpaths: List of paths for different provision objects
1102 from the reference provision
1103 :param paths: List of paths for different provision objects
1104 from the upgraded provision
1105 :param names: List of key provision parameters"""
1107 message(SIMPLE, "Copy samdb")
1108 shutil.copy(newpaths.samdb, paths.samdb)
1110 message(SIMPLE, "Update partitions filename if needed")
1111 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1112 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1113 usersldb = os.path.join(paths.private_dir, "users.ldb")
1114 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1116 if not os.path.isdir(samldbdir):
1118 os.chmod(samldbdir, 0700)
1119 if os.path.isfile(schemaldb):
1120 shutil.copy(schemaldb, os.path.join(samldbdir,
1121 "%s.ldb"%str(names.schemadn).upper()))
1122 os.remove(schemaldb)
1123 if os.path.isfile(usersldb):
1124 shutil.copy(usersldb, os.path.join(samldbdir,
1125 "%s.ldb"%str(names.rootdn).upper()))
1127 if os.path.isfile(configldb):
1128 shutil.copy(configldb, os.path.join(samldbdir,
1129 "%s.ldb"%str(names.configdn).upper()))
1130 os.remove(configldb)
1133 def update_privilege(ref_private_path, cur_private_path):
1134 """Update the privilege database
1136 :param ref_private_path: Path to the private directory of the reference
1138 :param cur_private_path: Path to the private directory of the current
1139 (and to be updated) provision."""
1140 message(SIMPLE, "Copy privilege")
1141 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1142 os.path.join(cur_private_path, "privilege.ldb"))
1145 def update_samdb(ref_samdb, samdb, names, highestUSN, schema):
1146 """Upgrade the SAM DB contents for all the provision partitions
1148 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1150 :param samdb: An LDB object connected to the sam.ldb of the update
1152 :param names: List of key provision parameters
1153 :param highestUSN: The highest USN modified by provision/upgradeprovision
1155 :param schema: A Schema object that represent the schema of the provision"""
1157 message(SIMPLE, "Starting update of samdb")
1158 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1161 message(SIMPLE, "Update of samdb finished")
1164 message(SIMPLE, "Update failed")
1168 def update_machine_account_password(samdb, secrets_ldb, names):
1169 """Update (change) the password of the current DC both in the SAM db and in
1172 :param samdb: An LDB object related to the sam.ldb file of a given provision
1173 :param secrets_ldb: An LDB object related to the secrets.ldb file of a given
1175 :param names: List of key provision parameters"""
1177 message(SIMPLE, "Update machine account")
1178 expression = "samAccountName=%s$" % names.netbiosname
1179 secrets_msg = secrets_ldb.search(expression=expression,
1180 attrs=["secureChannelType"])
1181 if int(secrets_msg[0]["secureChannelType"][0]) == SEC_CHAN_BDC:
1182 res = samdb.search(expression=expression, attrs=[])
1183 assert(len(res) == 1)
1185 msg = Message(res[0].dn)
1186 machinepass = samba.generate_random_password(128, 255)
1187 msg["userPassword"] = MessageElement(machinepass, FLAG_MOD_REPLACE,
1191 res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname),
1192 attrs=["msDs-keyVersionNumber"])
1193 assert(len(res) == 1)
1194 kvno = int(str(res[0]["msDs-keyVersionNumber"]))
1195 secChanType = int(secrets_msg[0]["secureChannelType"][0])
1197 secretsdb_self_join(secrets_ldb, domain=names.domain,
1198 realm=names.realm or sambaopts._lp.get('realm'),
1199 domainsid=names.domainsid,
1200 dnsdomain=names.dnsdomain,
1201 netbiosname=names.netbiosname,
1202 machinepass=machinepass,
1203 key_version_number=kvno,
1204 secure_channel_type=secChanType)
1206 raise ProvisioningError("Unable to find a Secure Channel" \
1207 "of type SEC_CHAN_BDC")
1211 def setup_path(file):
1212 return os.path.join(setup_dir, file)
1214 # Synopsis for updateprovision
1215 # 1) get path related to provision to be update (called current)
1216 # 2) open current provision ldbs
1217 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1219 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1220 # by either upgradeprovision or provision
1221 # 5) creation of a new provision the latest version of provision script
1222 # (called reference)
1223 # 6) get reference provision paths
1224 # 7) open reference provision ldbs
1225 # 8) setup helpers data that will help the update process
1226 # 9) update the privilege ldb by copying the one of referecence provision to
1227 # the current provision
1228 # 10)get the oemInfo field, this field contains information about the different
1229 # provision that have been done
1230 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1231 # integer) or none of this the following things are done
1232 # A) When alpha9 or alphaxx is present
1233 # The base sam.ldb file is updated by looking at the difference between
1234 # referrence one and the current one. Everything is copied with the
1235 # exception of lastProvisionUSN attributes. The highest used USN
1236 # is fetched so that changed by upgradeprovision usn can be tracked
1237 # B) Other case (it reflect that that provision was done before alpha9)
1238 # The base sam.ldb of the reference provision is copied over
1239 # the current one, if necessary ldb related to partitions are moved
1241 # 12)A Schema object is created, it will be used to provide a complete
1242 # schema to current provision during update (as the schema of the
1243 # current provision might not be complete and so won't allow some
1244 # object to be created)
1245 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1246 # 14)The secrets db is updated by pull all the difference from the reference
1247 # provision into the current provision
1248 # 15)As the previous step has most probably modified the password stored in
1249 # in secret for the current DC, a new password is generated,
1250 # the kvno is bumped and the entry in samdb is also updated
1251 # 16)For current provision older than alpha9, we must fix the SD a little bit
1252 # administrator to update them because SD used to be generated with the
1253 # system account before alpha9.
1254 # 17)The highest usn modified so far is searched in the database it will be
1255 # the upper limit for usn modified during provision.
1256 # This is done before potential SD recalculation because we do not want
1257 # SD modified during recalculation to be marked as modified during provision
1258 # (and so possibly remplaced at next upgradeprovision)
1259 # 18)Rebuilt SD if the flag indicate to do so
1260 # 19)Check difference between SD of reference provision and those of the
1261 # current provision. The check is done by getting the sddl representation
1262 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1263 # Each part is verified separetly, for dacl and sacl ACL is splited into
1264 # ACEs and each ACE is verified separately (so that a permutation in ACE
1265 # didn't raise as an error).
1266 # 20)The oemInfo field is updated to add information about the fact that the
1267 # provision has been updated by the upgradeprovision version xxx
1268 # (the version is the one obtained when starting samba with the --version
1270 # 21)Check if the current provision has all the settings needed for dynamic
1271 # DNS update to work (that is to say the provision is newer than
1272 # january 2010). If not dns configuration file from reference provision
1273 # are copied in a sub folder and the administrator is invited to
1274 # do what is needed.
1275 # 22)If the lastProvisionUSN attribute was present it is updated to add
1276 # the range of usns modified by the current upgradeprovision
1279 # About updating the sam DB
1280 # The update takes place in update_partition function
1281 # This function read both current and reference provision and list all
1282 # the available DN of objects
1283 # If the string representation of a DN in reference provision is
1284 # equal to the string representation of a DN in current provision
1285 # (without taking care of case) then the object is flaged as being
1286 # present. If the object is not present in current provision the object
1287 # is being flaged as missing in current provision. Object present in current
1288 # provision but not in reference provision are ignored.
1289 # Once the list of objects present and missing is done, the deleted object
1290 # containers are created in the differents partitions (if missing)
1292 # Then the function add_missing_entries is called
1293 # This function will go through the list of missing entries by calling
1294 # add_missing_object for the given object. If this function returns 0
1295 # it means that the object needs some other object in order to be created
1296 # The object is reappended at the end of the list to be created later
1297 # (and preferably after all the needed object have been created)
1298 # The function keeps on looping on the list of object to be created until
1299 # it's empty or that the number of defered creation is equal to the number
1300 # of object that still needs to be created.
1302 # The function add_missing_object will first check if the object can be created.
1303 # That is to say that it didn't depends other not yet created objects
1304 # If requisit can't be fullfilled it exists with 0
1305 # Then it will try to create the missing entry by creating doing
1306 # an ldb_message_diff between the object in the reference provision and
1308 # This resulting object is filtered to remove all the back link attribute
1309 # (ie. memberOf) as they will be created by the other linked object (ie.
1310 # the one with the member attribute)
1311 # All attributes specified in the hashAttrNotCopied associative array are
1312 # also removed it's most of the time generated attributes
1314 # After missing entries have been added the update_partition function will
1315 # take care of object that exist but that need some update.
1316 # In order to do so the function update_present is called with the list
1317 # of object that are present in both provision and that might need an update.
1319 # This function handle first case mismatch so that the DN in the current
1320 # provision have the same case as in reference provision
1322 # It will then construct an associative array consiting of attributes as
1323 # key and invocationid as value( if the originating invocation id is
1324 # different from the invocation id of the current DC the value is -1 instead).
1326 # If the range of provision modified attributes is present, the function will
1327 # use the replMetadataProperty update method which is the following:
1328 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1329 # creationTime, msDs-KeyVersionNumber, oEMInformation
1330 # Check for each attribute if its usn is within one of the modified by
1331 # provision range and if its originating id is the invocation id of the
1332 # current DC, then validate the update from reference to current.
1333 # If not or if there is no replMetatdataProperty for this attribute then we
1335 # Otherwise (case the range of provision modified attribute is not present) it
1336 # use the following process:
1337 # All attributes that need to be added are accepted at the exeption of those
1338 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1339 # correct flags specified.
1340 # For attributes that need to be modified or removed, a check is performed
1341 # in OverwrittenAtt, if the attribute is present and the modification flag
1342 # (remove, delete) is one of those listed for this attribute then modification
1343 # is accepted. For complicated handling of attribute update, the control is passed
1344 # to handle_special_case
1348 if __name__ == '__main__':
1349 global defSDmodified
1351 # From here start the big steps of the program
1352 # 1) First get files paths
1353 paths = get_paths(param, smbconf=smbconf)
1354 paths.setup = setup_dir
1355 # Get ldbs with the system session, it is needed for searching
1356 # provision parameters
1357 session = system_session()
1359 # This variable will hold the last provision USN once if it exists.
1362 ldbs = get_ldbs(paths, creds, session, lp)
1363 ldbs.startTransactions()
1365 # 3) Guess all the needed names (variables in fact) from the current
1367 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1370 lastProvisionUSNs = getLastProvisionUSN(ldbs.sam)
1371 if lastProvisionUSNs != None:
1373 "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
1375 # Objects will be created with the admin session
1376 # (not anymore system session)
1377 adm_session = admin_session(lp, str(names.domainsid))
1378 # So we reget handle on objects
1379 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1381 if not sanitychecks(ldbs.sam, names):
1382 message(SIMPLE, "Sanity checks for the upgrade fails, checks messages" \
1383 " and correct them before rerunning upgradeprovision")
1386 # Let's see provision parameters
1387 print_provision_key_parameters(names)
1389 # 5) With all this information let's create a fresh new provision used as
1391 message(SIMPLE, "Creating a reference provision")
1392 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1393 prefix="referenceprovision")
1394 newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
1399 # We need to get a list of object which SD is directly computed from
1400 # defaultSecurityDescriptor.
1401 # This will allow us to know which object we can rebuild the SD in case
1402 # of change of the parent's SD or of the defaultSD.
1403 # Get file paths of this new provision
1404 newpaths = get_paths(param, targetdir=provisiondir)
1405 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1406 new_ldbs.startTransactions()
1408 # 8) Populate some associative array to ease the update process
1409 # List of attribute which are link and backlink
1410 populate_links(new_ldbs.sam, names.schemadn)
1411 # List of attribute with ASN DN synthax)
1412 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1414 update_privilege(newpaths.private_dir, paths.private_dir)
1416 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1417 # Do some modification on sam.ldb
1418 ldbs.groupedCommit()
1420 if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1422 # Starting from alpha9 we can consider that the structure is quite ok
1423 # and that we should do only dela
1424 new_ldbs.groupedCommit()
1425 delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message)
1426 ldbs.startTransactions()
1427 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1428 new_ldbs.startTransactions()
1431 simple_update_basesamdb(newpaths, paths, names)
1432 ldbs = get_ldbs(paths, creds, session, lp)
1433 ldbs.startTransactions()
1434 removeProvisionUSN(ldbs.sam)
1437 schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
1438 serverdn=str(names.serverdn))
1441 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1443 message(SIMPLE, "Rollbacking every changes. Check the reason" \
1445 message(SIMPLE, "In any case your system as it was before" \
1447 ldbs.groupedRollback()
1448 new_ldbs.groupedRollback()
1449 shutil.rmtree(provisiondir)
1452 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1454 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1456 # 16) SD should be created with admin but as some previous acl were so wrong
1457 # that admin can't modify them we have first to recreate them with the good
1458 # form but with system account and then give the ownership to admin ...
1459 if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1460 message(SIMPLE, "Fixing old povision SD")
1461 fix_partition_sd(ldbs.sam, names)
1462 rebuild_sd(ldbs.sam, names)
1464 # We calculate the max USN before recalculating the SD because we might
1465 # touch object that have been modified after a provision and we do not
1466 # want that the next upgradeprovision thinks that it has a green light
1470 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1472 # 18) We rebuild SD only if defaultSecurityDescriptor is modified
1473 # But in fact we should do it also if one object has its SD modified as
1474 # child might need rebuild
1475 if defSDmodified == 1:
1476 message(SIMPLE, "Updating SD")
1477 ldbs.sam.set_session_info(adm_session)
1478 # Alpha10 was a bit broken still
1479 if re.match(r'.*alpha(\d|10)', str(oem)):
1480 fix_partition_sd(ldbs.sam, names)
1481 rebuild_sd(ldbs.sam, names)
1484 # Now we are quite confident in the recalculate process of the SD, we make
1486 # Also the check must be done in a clever way as for the moment we just
1488 if opts.debugchangesd:
1489 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1492 updateOEMInfo(ldbs.sam, str(names.rootdn))
1494 check_for_DNS(newpaths.private_dir, paths.private_dir)
1496 if lastProvisionUSNs != None:
1497 updateProvisionUSN(ldbs.sam, minUSN, maxUSN)
1498 update_gpo(paths, ldbs.sam, names, lp, message)
1499 ldbs.groupedCommit()
1500 new_ldbs.groupedCommit()
1501 message(SIMPLE, "Upgrade finished !")
1502 # remove reference provision now that everything is done !
1503 shutil.rmtree(provisiondir)