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/>.
34 # Allow to run from s4 source directory (without installing samba)
35 sys.path.insert(0, "bin/python")
38 import samba.getopt as options
39 from samba.credentials import DONT_USE_KERBEROS
40 from samba.auth import system_session, admin_session
41 from ldb import (SCOPE_SUBTREE, SCOPE_BASE,
42 FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE,
43 MessageElement, Message, Dn)
44 from samba import param
45 from samba.provision import (find_setup_dir, get_domain_descriptor,
46 get_config_descriptor, secretsdb_self_join,
47 ProvisioningError, get_last_provision_usn,
48 get_max_usn, update_provision_usn)
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, update_policyids)
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 }
110 forwardlinked = set()
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("--resetfileacl", action="store_true",
145 help="Force a reset on filesystem acls in sysvol / netlogon share")
146 parser.add_option("--full", action="store_true",
147 help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
149 opts = parser.parse_args()[0]
151 handler = logging.StreamHandler(sys.stdout)
152 upgrade_logger = logging.getLogger("upgradeprovision")
153 upgrade_logger.addHandler(handler)
155 provision_logger = logging.getLogger("provision")
156 provision_logger.addHandler(handler)
158 whatToLog = define_what_to_log(opts)
160 def message(what, text):
161 """Print a message if this message type has been selected to be printed
163 :param what: Category of the message
164 :param text: Message to print """
165 if (whatToLog & what) or what <= 0:
166 upgrade_logger.info("%s", text)
168 if len(sys.argv) == 1:
169 opts.interactive = True
170 lp = sambaopts.get_loadparm()
171 smbconf = lp.configfile
173 creds = credopts.get_credentials(lp)
174 creds.set_kerberos_state(DONT_USE_KERBEROS)
175 setup_dir = opts.setupdir
176 if setup_dir is None:
177 setup_dir = find_setup_dir()
181 def check_for_DNS(refprivate, private):
182 """Check if the provision has already the requirement for dynamic dns
184 :param refprivate: The path to the private directory of the reference
186 :param private: The path to the private directory of the upgraded
189 spnfile = "%s/spn_update_list" % private
190 namedfile = lp.get("dnsupdate:path")
193 namedfile = "%s/named.conf.update" % private
195 if not os.path.exists(spnfile):
196 shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
198 destdir = "%s/new_dns" % private
199 dnsdir = "%s/dns" % private
201 if not os.path.exists(namedfile):
202 if not os.path.exists(destdir):
204 if not os.path.exists(dnsdir):
206 shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
207 shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
208 message(SIMPLE, "It seems that you provision didn't integrate new rules "
209 "for dynamic dns update of domain related entries")
210 message(SIMPLE, "A copy of the new bind configuration files and "
211 "template as been put in %s, you should read them and configure dynamic "
212 " dns update" % destdir)
215 def populate_links(samdb, schemadn):
216 """Populate an array with all the back linked attributes
218 This attributes that are modified automaticaly when
219 front attibutes are changed
221 :param samdb: A LDB object for sam.ldb file
222 :param schemadn: DN of the schema for the partition"""
223 linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
224 backlinked.extend(linkedAttHash.values())
225 for t in linkedAttHash.keys():
229 def populate_dnsyntax(samdb, schemadn):
230 """Populate an array with all the attributes that have DN synthax
233 :param samdb: A LDB object for sam.ldb file
234 :param schemadn: DN of the schema for the partition"""
235 res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
236 str(schemadn)), scope=SCOPE_SUBTREE,
237 attrs=["lDAPDisplayName"])
239 dn_syntax_att.append(elem["lDAPDisplayName"])
242 def sanitychecks(samdb, names):
243 """Make some checks before trying to update
245 :param samdb: An LDB object opened on sam.ldb
246 :param names: list of key provision parameters
247 :return: Status of check (1 for Ok, 0 for not Ok) """
248 res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
249 scope=SCOPE_SUBTREE, attrs=["dn"],
250 controls=["search_options:1:2"])
252 print "No DC found, your provision is most probably hardly broken !"
255 print "Found %d domain controllers, for the moment upgradeprovision" \
256 "is not able to handle upgrade on domain with more than one DC, please demote" \
257 " the other(s) DC(s) before upgrading" % len(res)
263 def print_provision_key_parameters(names):
264 """Do a a pretty print of provision parameters
266 :param names: list of key provision parameters """
267 message(GUESS, "rootdn :" + str(names.rootdn))
268 message(GUESS, "configdn :" + str(names.configdn))
269 message(GUESS, "schemadn :" + str(names.schemadn))
270 message(GUESS, "serverdn :" + str(names.serverdn))
271 message(GUESS, "netbiosname :" + names.netbiosname)
272 message(GUESS, "defaultsite :" + names.sitename)
273 message(GUESS, "dnsdomain :" + names.dnsdomain)
274 message(GUESS, "hostname :" + names.hostname)
275 message(GUESS, "domain :" + names.domain)
276 message(GUESS, "realm :" + names.realm)
277 message(GUESS, "invocationid:" + names.invocation)
278 message(GUESS, "policyguid :" + names.policyid)
279 message(GUESS, "policyguiddc:" + str(names.policyid_dc))
280 message(GUESS, "domainsid :" + str(names.domainsid))
281 message(GUESS, "domainguid :" + names.domainguid)
282 message(GUESS, "ntdsguid :" + names.ntdsguid)
283 message(GUESS, "domainlevel :" + str(names.domainlevel))
286 def handle_special_case(att, delta, new, old, usn):
287 """Define more complicate update rules for some attributes
289 :param att: The attribute to be updated
290 :param delta: A messageElement object that correspond to the difference
291 between the updated object and the reference one
292 :param new: The reference object
293 :param old: The Updated object
294 :param usn: The highest usn modified by a previous (upgrade)provision
295 :return: True to indicate that the attribute should be kept, False for
298 flag = delta.get(att).flags()
299 # We do most of the special case handle if we do not have the
300 # highest usn as otherwise the replPropertyMetaData will guide us more
303 if (att == "member" and flag == FLAG_MOD_REPLACE):
307 for elem in old[0][att]:
308 hash[str(elem).lower()]=1
309 newval.append(str(elem))
311 for elem in new[0][att]:
312 if not hash.has_key(str(elem).lower()):
314 newval.append(str(elem))
316 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
321 if (att in ("gPLink", "gPCFileSysPath") and
322 flag == FLAG_MOD_REPLACE and
323 str(new[0].dn).lower() == str(old[0].dn).lower()):
327 if att == "forceLogoff":
328 ref=0x8000000000000000
329 oldval=int(old[0][att][0])
330 newval=int(new[0][att][0])
331 ref == old and ref == abs(new)
334 if att in ("adminDisplayName", "adminDescription"):
337 if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (names.schemadn)
338 and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
341 if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
342 att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
345 if (str(old[0].dn) == "%s" % (str(names.rootdn))
346 and att == "subRefs" and flag == FLAG_MOD_REPLACE):
349 if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
352 # This is a bit of special animal as we might have added
353 # already SPN entries to the list that has to be modified
354 # So we go in detail to try to find out what has to be added ...
355 if (att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):
359 for elem in old[0][att]:
361 newval.append(str(elem))
363 for elem in new[0][att]:
364 if not hash.has_key(str(elem)):
366 newval.append(str(elem))
368 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
375 def dump_denied_change(dn, att, flagtxt, current, reference):
376 """Print detailed information about why a changed is denied
378 :param dn: DN of the object which attribute is denied
379 :param att: Attribute that was supposed to be upgraded
380 :param flagtxt: Type of the update that should be performed
381 (add, change, remove, ...)
382 :param current: Value(s) of the current attribute
383 :param reference: Value(s) of the reference attribute"""
385 message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
386 +" is not allowed to be changed/removed, I discard this change")
387 if att != "objectSid" :
389 for e in range(0, len(current)):
390 message(CHANGE, "old %d : %s" % (i, str(current[e])))
392 if reference is not None:
394 for e in range(0, len(reference)):
395 message(CHANGE, "new %d : %s" % (i, str(reference[e])))
398 message(CHANGE, "old : %s" % ndr_unpack(security.dom_sid, current[0]))
399 message(CHANGE, "new : %s" % ndr_unpack(security.dom_sid, reference[0]))
402 def handle_special_add(samdb, dn, names):
403 """Handle special operation (like remove) on some object needed during
406 This is mostly due to wrong creation of the object in previous provision.
407 :param samdb: An Ldb object representing the SAM database
408 :param dn: DN of the object to inspect
409 :param names: list of key provision parameters
413 objDn = Dn(samdb, "CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn)
415 #This entry was misplaced lets remove it if it exists
416 dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
419 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn)
421 #This entry was misplaced lets remove it if it exists
422 dntoremove = "CN=Certificate Service DCOM Access,"\
423 "CN=Users, %s" % names.rootdn
426 objDn = Dn(samdb, "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn)
428 #This entry was misplaced lets remove it if it exists
429 dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
431 objDn = Dn(samdb, "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn)
433 #This entry was misplaced lets remove it if it exists
434 dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
436 objDn = Dn(samdb,"CN=System,CN=WellKnown Security Principals,"
437 "CN=Configuration,%s" % names.rootdn)
439 oldDn = Dn(samdb,"CN=Well-Known-Security-Id-System,"
440 "CN=WellKnown Security Principals,"
441 "CN=Configuration,%s" % names.rootdn)
443 res = samdb.search(expression="(dn=%s)" % oldDn,
444 base=str(names.rootdn),
445 scope=SCOPE_SUBTREE, attrs=["dn"],
446 controls=["search_options:1:2"])
448 message(CHANGE, "Existing object %s must be replaced by %s,"
449 "Renaming old object" % (str(oldDn), str(dn)))
450 samdb.rename(oldDn, objDn)
454 if dntoremove is not None:
455 res = samdb.search(expression="(dn=%s)" % dntoremove,
456 base=str(names.rootdn),
457 scope=SCOPE_SUBTREE, attrs=["dn"],
458 controls=["search_options:1:2"])
460 message(CHANGE, "Existing object %s must be replaced by %s,"
461 "removing old object" % (dntoremove, str(dn)))
462 samdb.delete(res[0]["dn"])
466 def check_dn_nottobecreated(hash, index, listdn):
467 """Check if one of the DN present in the list has a creation order
468 greater than the current.
470 Hash is indexed by dn to be created, with each key
471 is associated the creation order.
473 First dn to be created has the creation order 0, second has 1, ...
474 Index contain the current creation order
476 :param hash: Hash holding the different DN of the object to be
478 :param index: Current creation order
479 :param listdn: List of DNs on which the current DN depends on
480 :return: None if the current object do not depend on other
481 object or if all object have been created before."""
485 key = str(dn).lower()
486 if hash.has_key(key) and hash[key] > index:
492 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
493 """Add a new object if the dependencies are satisfied
495 The function add the object if the object on which it depends are already
498 :param ref_samdb: Ldb object representing the SAM db of the reference
500 :param samdb: Ldb object representing the SAM db of the upgraded
502 :param dn: DN of the object to be added
503 :param names: List of key provision parameters
504 :param basedn: DN of the partition to be updated
505 :param hash: Hash holding the different DN of the object to be
507 :param index: Current creation order
508 :return: True if the object was created False otherwise"""
510 if handle_special_add(samdb, dn, names):
512 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
513 scope=SCOPE_SUBTREE, controls=["search_options:1:2"])
515 delta = samdb.msg_diff(empty, reference[0])
517 for att in hashAttrNotCopied.keys():
519 for att in backlinked:
521 depend_on_yettobecreated = None
522 for att in dn_syntax_att:
523 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
525 if depend_on_yet_tobecreated is not None:
526 message(CHANGE, "Object %s depends on %s in attribute %s,"
527 "delaying the creation" % (dn,
528 depend_on_yet_tobecreated, att))
532 message(CHANGE,"Object %s will be added" % dn)
533 samdb.add(delta, ["relax:0"])
536 def gen_dn_index_hash(listMissing):
537 """Generate a hash associating the DN to its creation order
539 :param listMissing: List of DN
540 :return: Hash with DN as keys and creation order as values"""
542 for i in range(0, len(listMissing)):
543 hash[str(listMissing[i]).lower()] = i
546 def add_deletedobj_containers(ref_samdb, samdb, names):
547 """Add the object containter: CN=Deleted Objects
549 This function create the container for each partition that need one and
550 then reference the object into the root of the partition
552 :param ref_samdb: Ldb object representing the SAM db of the reference
554 :param samdb: Ldb object representing the SAM db of the upgraded provision
555 :param names: List of key provision parameters"""
558 wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
559 partitions = [str(names.rootdn), str(names.configdn)]
560 for part in partitions:
561 ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
562 base=part, scope=SCOPE_SUBTREE,
564 controls=["show_deleted:0"])
565 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
566 base=part, scope=SCOPE_SUBTREE,
568 controls=["show_deleted:0"])
569 if len(ref_delObjCnt) > len(delObjCnt):
570 reference = ref_samdb.search(expression="cn=Deleted Objects",
571 base=part, scope=SCOPE_SUBTREE,
572 controls=["show_deleted:0"])
574 delta = samdb.msg_diff(empty, reference[0])
576 delta.dn = Dn(samdb, str(reference[0]["dn"]))
577 for att in hashAttrNotCopied.keys():
582 res = samdb.search(expression="(objectClass=*)", base=part,
584 attrs=["dn", "wellKnownObjects"])
586 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
590 wko = res[0]["wellKnownObjects"]
592 # The wellKnownObject that we want to add.
594 if str(o) == targetWKO:
596 listwko.append(str(o))
599 listwko.append(targetWKO)
602 delta.dn = Dn(samdb, str(res[0]["dn"]))
603 delta["wellKnownObjects"] = MessageElement(listwko,
608 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
609 """Add the missing object whose DN is the list
611 The function add the object if the objects on which it depends are
614 :param ref_samdb: Ldb object representing the SAM db of the reference
616 :param samdb: Ldb object representing the SAM db of the upgraded
618 :param dn: DN of the object to be added
619 :param names: List of key provision parameters
620 :param basedn: DN of the partition to be updated
621 :param list: List of DN to be added in the upgraded provision"""
626 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
628 listMissing = listDefered
630 hashMissing = gen_dn_index_hash(listMissing)
631 for dn in listMissing:
632 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
636 # DN can't be created because it depends on some
637 # other DN in the list
638 listDefered.append(dn)
639 if len(listDefered) != 0:
640 raise ProvisioningError("Unable to insert missing elements:" \
641 "circular references")
643 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
644 """This function handle updates on links
646 :param samdb: An LDB object pointing to the updated provision
647 :param att: Attribute to update
648 :param basedn: The root DN of the provision
649 :param dn: The DN of the inspected object
650 :param value: The value of the attribute
651 :param ref_value: The value of this attribute in the reference provision
652 :param delta: The MessageElement object that will be applied for
653 transforming the current provision"""
655 res = samdb.search(expression="dn=%s" % dn, base=basedn,
656 controls=["search_options:1:2", "reveal:1"],
664 newlinklist.extend(value)
668 # for w2k domain level the reveal won't reveal anything ...
669 # it means that we can readd links that were removed on purpose ...
670 # Also this function in fact just accept add not removal
672 for e in res[0][att]:
673 if not hash.has_key(e):
674 # We put in the blacklist all the element that are in the "revealed"
675 # result and not in the "standard" result
676 # This element are links that were removed before and so that
677 # we don't wan't to readd
681 if not blacklist.has_key(e) and not hash.has_key(e):
682 newlinklist.append(str(e))
685 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
690 msg_elt_flag_strs = {
691 ldb.FLAG_MOD_ADD: "MOD_ADD",
692 ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
693 ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
696 def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
697 """ This function updates the object that are already present in the
700 :param ref_samdb: An LDB object pointing to the reference provision
701 :param samdb: An LDB object pointing to the updated provision
702 :param basedn: A string with the value of the base DN for the provision
704 :param listPresent: A list of object that is present in the provision
705 :param usns: A list of USN range modified by previous provision and
707 :param invocationid: The value of the invocationid for the current DC"""
710 # This hash is meant to speedup lookup of attribute name from an oid,
711 # it's for the replPropertyMetaData handling
713 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
714 controls=["search_options:1:2"], attrs=["attributeID",
718 strDisplay = str(e.get("lDAPDisplayName"))
719 hash_oid_name[str(e.get("attributeID"))] = strDisplay
721 msg = "Unable to insert missing elements: circular references"
722 raise ProvisioningError(msg)
725 controls = ["search_options:1:2", "sd_flags:1:2"]
726 for dn in listPresent:
727 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
730 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
731 scope=SCOPE_SUBTREE, controls=controls)
734 (str(current[0].dn) != str(reference[0].dn)) and
735 (str(current[0].dn).upper() == str(reference[0].dn).upper())
737 message(CHANGE, "Name are the same but case change,"\
738 "let's rename %s to %s" % (str(current[0].dn),
739 str(reference[0].dn)))
740 identic_rename(samdb, reference[0].dn)
741 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
743 controls=["search_options:1:2"])
745 delta = samdb.msg_diff(current[0], reference[0])
747 for att in hashAttrNotCopied.keys():
750 for att in backlinked:
755 if len(delta.items()) > 1 and usns is not None:
756 # Fetch the replPropertyMetaData
757 res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
758 scope=SCOPE_SUBTREE, controls=controls,
759 attrs=["replPropertyMetaData"])
760 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
761 str(res[0]["replPropertyMetaData"])).ctr
765 # We put in this hash only modification
766 # made on the current host
767 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
768 if str(o.originating_invocation_id) == str(invocationid):
769 # Note we could just use 1 here
770 hash_attr_usn[att] = o.originating_usn
772 hash_attr_usn[att] = -1
779 # We have updated by provision usn information so let's exploit
780 # replMetadataProperties
781 if att in forwardlinked:
782 handle_links(samdb, att, basedn, current[0]["dn"],
783 current[0][att], reference[0][att], delta)
785 if isFirst == 0 and len(delta.items())>1:
787 txt = "%s\n" % (str(dn))
789 # There is always a dn attribute after a msg_diff
791 if att == "rIDAvailablePool":
794 if att == "objectSid":
797 if att == "creationTime":
800 if att == "oEMInformation":
803 if att == "msDs-KeyVersionNumber":
804 # This is the kvno of the computer/user it's a very bad
808 if handle_special_case(att, delta, reference, current, usns):
809 # This attribute is "complicated" to handle and handling
810 # was done in handle_special_case
812 attrUSN = hash_attr_usn.get(att)
813 if att == "forceLogoff" and attrUSN is None:
820 # This attribute was last modified by another DC forget
822 message(CHANGE, "%sAttribute: %s has been"
823 "created/modified/deleted by another DC,"
824 " do nothing" % (txt, att ))
828 elif not usn_in_range(int(attrUSN), usns):
829 message(CHANGE, "%sAttribute: %s has been"
830 "created/modified/deleted not during a"
831 " provision or upgradeprovision: current"
832 " usn %d , do nothing" % (txt, att, attrUSN))
837 if att == "defaultSecurityDescriptor":
840 message(CHANGE, "%sAttribute: %s will be modified"
841 "/deleted it was last modified"
842 "during a provision, current usn:"
843 "%d" % (txt, att, attrUSN))
846 message(CHANGE, "%sAttribute: %s will be added because"
847 " it hasn't existed before " % (txt, att))
852 # Old school way of handling things for pre alpha12 upgrade
854 msgElt = delta.get(att)
856 if att == "nTSecurityDescriptor":
863 if not hashOverwrittenAtt.has_key(att):
864 if msgElt.flags() != FLAG_MOD_ADD:
865 if not handle_special_case(att, delta, reference, current,
867 if opts.debugchange or opts.debugall:
869 dump_denied_change(dn, att,
870 msg_elt_flag_strs[msgElt.flags()],
871 current[0][att], reference[0][att])
873 dump_denied_change(dn, att,
874 msg_elt_flag_strs[msgElt.flags()],
875 current[0][att], None)
879 if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
881 elif hashOverwrittenAtt.get(att)==never:
886 if len(delta.items()) >1:
887 attributes=", ".join(delta.keys())
888 message(CHANGE, "%s is different from the reference one, changed"
889 " attributes: %s\n" % (dn, attributes))
895 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs):
896 """Check differences between the reference provision and the upgraded one.
898 It looks for all objects which base DN is name.
900 This function will also add the missing object and update existing object
901 to add or remove attributes that were missing.
903 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
905 :param samdb: An LDB object connected to the sam.ldb of the update
907 :param basedn: String value of the DN of the partition
908 :param names: List of key provision parameters
909 :param schema: A Schema object
910 :param provisionUSNs: The USNs modified by provision/upgradeprovision
920 # Connect to the reference provision and get all the attribute in the
921 # partition referred by name
922 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
923 scope=SCOPE_SUBTREE, attrs=["dn"],
924 controls=["search_options:1:2"])
926 current = samdb.search(expression="objectClass=*", base=basedn,
927 scope=SCOPE_SUBTREE, attrs=["dn"],
928 controls=["search_options:1:2"])
929 # Create a hash for speeding the search of new object
930 for i in range(0, len(reference)):
931 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
933 # Create a hash for speeding the search of existing object in the
935 for i in range(0, len(current)):
936 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
939 for k in hash_new.keys():
940 if not hash.has_key(k):
941 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
942 listMissing.append(hash_new[k])
944 listPresent.append(hash_new[k])
946 # Sort the missing object in order to have object of the lowest level
947 # first (which can be containers for higher level objects)
948 listMissing.sort(dn_sort)
949 listPresent.sort(dn_sort)
951 # The following lines is to load the up to
952 # date schema into our current LDB
953 # a complete schema is needed as the insertion of attributes
954 # and class is done against it
955 # and the schema is self validated
956 samdb.set_schema(schema)
958 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
959 add_deletedobj_containers(ref_samdb, samdb, names)
961 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
962 changed = update_present(ref_samdb, samdb, basedn, listPresent,
963 provisionUSNs, names.invocation)
964 message(SIMPLE, "There are %d changed objects" % (changed))
967 except StandardError, err:
968 message(ERROR, "Exception during upgrade of samdb:")
969 (typ, val, tb) = sys.exc_info()
970 traceback.print_exception(typ, val, tb)
974 def check_updated_sd(ref_sam, cur_sam, names):
975 """Check if the security descriptor in the upgraded provision are the same
978 :param ref_sam: A LDB object connected to the sam.ldb file used as
979 the reference provision
980 :param cur_sam: A LDB object connected to the sam.ldb file used as
982 :param names: List of key provision parameters"""
983 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
985 attrs=["dn", "nTSecurityDescriptor"],
986 controls=["search_options:1:2"])
987 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
989 attrs=["dn", "nTSecurityDescriptor"],
990 controls=["search_options:1:2"])
992 for i in range(0, len(reference)):
993 refsd = ndr_unpack(security.descriptor,
994 str(reference[i]["nTSecurityDescriptor"]))
995 hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
997 for i in range(0, len(current)):
998 key = str(current[i]["dn"]).lower()
999 if hash.has_key(key):
1000 cursd = ndr_unpack(security.descriptor,
1001 str(current[i]["nTSecurityDescriptor"]))
1002 sddl = cursd.as_sddl(names.domainsid)
1003 if sddl != hash[key]:
1004 txt = get_diff_sddls(hash[key], sddl)
1006 message(CHANGESD, "On object %s ACL is different"
1007 " \n%s" % (current[i]["dn"], txt))
1011 def fix_partition_sd(samdb, names):
1012 """This function fix the SD for partition containers (basedn, configdn, ...)
1013 This is needed because some provision use to have broken SD on containers
1015 :param samdb: An LDB object pointing to the sam of the current provision
1016 :param names: A list of key provision parameters
1018 # First update the SD for the rootdn
1019 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1020 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1021 controls=["search_options:1:2"])
1023 delta.dn = Dn(samdb, str(res[0]["dn"]))
1024 descr = get_domain_descriptor(names.domainsid)
1025 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1026 "nTSecurityDescriptor")
1027 samdb.modify(delta, ["recalculate_sd:0"])
1028 # Then the config dn
1029 res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1030 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1031 controls=["search_options:1:2"])
1033 delta.dn = Dn(samdb, str(res[0]["dn"]))
1034 descr = get_config_descriptor(names.domainsid)
1035 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1036 "nTSecurityDescriptor" )
1037 samdb.modify(delta, ["recalculate_sd:0"])
1038 # Then the schema dn
1039 res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1040 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1041 controls=["search_options:1:2"])
1044 delta.dn = Dn(samdb, str(res[0]["dn"]))
1045 descr = get_schema_descriptor(names.domainsid)
1046 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1047 "nTSecurityDescriptor" )
1048 samdb.modify(delta, ["recalculate_sd:0"])
1050 def rebuild_sd(samdb, names):
1051 """Rebuild security descriptor of the current provision from scratch
1053 During the different pre release of samba4 security descriptors (SD)
1054 were notarly broken (up to alpha11 included)
1055 This function allow to get them back in order, this function make the
1056 assumption that nobody has modified manualy an SD
1057 and so SD can be safely recalculated from scratch to get them right.
1059 :param names: List of key provision parameters"""
1063 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1064 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1065 controls=["search_options:1:2"])
1067 if not (str(obj["dn"]) == str(names.rootdn) or
1068 str(obj["dn"]) == str(names.configdn) or
1069 str(obj["dn"]) == str(names.schemadn)):
1070 hash[str(obj["dn"])] = obj["whenCreated"]
1072 listkeys = hash.keys()
1073 listkeys.sort(dn_sort)
1075 for key in listkeys:
1078 delta.dn = Dn(samdb, key)
1079 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1081 samdb.modify(delta, ["recalculate_sd:0"])
1083 # XXX: We should always catch an explicit exception.
1084 # What could go wrong here?
1085 samdb.transaction_cancel()
1086 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1087 scope=SCOPE_SUBTREE,
1088 attrs=["dn", "nTSecurityDescriptor"],
1089 controls=["search_options:1:2"])
1090 badsd = ndr_unpack(security.descriptor,
1091 str(res[0]["nTSecurityDescriptor"]))
1092 print "bad stuff %s" % badsd.as_sddl(names.domainsid)
1095 def removeProvisionUSN(samdb):
1096 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1097 entry = samdb.search(expression="dn=@PROVISION", base = "",
1098 scope=SCOPE_SUBTREE,
1099 controls=["search_options:1:2"],
1102 empty.dn = entry[0].dn
1103 delta = samdb.msg_diff(entry[0], empty)
1105 delta.dn = entry[0].dn
1109 def simple_update_basesamdb(newpaths, paths, names):
1110 """Update the provision container db: sam.ldb
1111 This function is aimed at very old provision (before alpha9)
1113 :param newpaths: List of paths for different provision objects
1114 from the reference provision
1115 :param paths: List of paths for different provision objects
1116 from the upgraded provision
1117 :param names: List of key provision parameters"""
1119 message(SIMPLE, "Copy samdb")
1120 shutil.copy(newpaths.samdb, paths.samdb)
1122 message(SIMPLE, "Update partitions filename if needed")
1123 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1124 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1125 usersldb = os.path.join(paths.private_dir, "users.ldb")
1126 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1128 if not os.path.isdir(samldbdir):
1130 os.chmod(samldbdir, 0700)
1131 if os.path.isfile(schemaldb):
1132 shutil.copy(schemaldb, os.path.join(samldbdir,
1133 "%s.ldb"%str(names.schemadn).upper()))
1134 os.remove(schemaldb)
1135 if os.path.isfile(usersldb):
1136 shutil.copy(usersldb, os.path.join(samldbdir,
1137 "%s.ldb"%str(names.rootdn).upper()))
1139 if os.path.isfile(configldb):
1140 shutil.copy(configldb, os.path.join(samldbdir,
1141 "%s.ldb"%str(names.configdn).upper()))
1142 os.remove(configldb)
1145 def update_privilege(ref_private_path, cur_private_path):
1146 """Update the privilege database
1148 :param ref_private_path: Path to the private directory of the reference
1150 :param cur_private_path: Path to the private directory of the current
1151 (and to be updated) provision."""
1152 message(SIMPLE, "Copy privilege")
1153 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1154 os.path.join(cur_private_path, "privilege.ldb"))
1157 def update_samdb(ref_samdb, samdb, names, highestUSN, schema):
1158 """Upgrade the SAM DB contents for all the provision partitions
1160 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1162 :param samdb: An LDB object connected to the sam.ldb of the update
1164 :param names: List of key provision parameters
1165 :param highestUSN: The highest USN modified by provision/upgradeprovision
1167 :param schema: A Schema object that represent the schema of the provision"""
1169 message(SIMPLE, "Starting update of samdb")
1170 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1173 message(SIMPLE, "Update of samdb finished")
1176 message(SIMPLE, "Update failed")
1180 def update_machine_account_password(samdb, secrets_ldb, names):
1181 """Update (change) the password of the current DC both in the SAM db and in
1184 :param samdb: An LDB object related to the sam.ldb file of a given provision
1185 :param secrets_ldb: An LDB object related to the secrets.ldb file of a given
1187 :param names: List of key provision parameters"""
1189 message(SIMPLE, "Update machine account")
1190 expression = "samAccountName=%s$" % names.netbiosname
1191 secrets_msg = secrets_ldb.search(expression=expression,
1192 attrs=["secureChannelType"])
1193 if int(secrets_msg[0]["secureChannelType"][0]) == SEC_CHAN_BDC:
1194 res = samdb.search(expression=expression, attrs=[])
1195 assert(len(res) == 1)
1197 msg = Message(res[0].dn)
1198 machinepass = samba.generate_random_password(128, 255)
1199 msg["userPassword"] = MessageElement(machinepass, FLAG_MOD_REPLACE,
1203 res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname),
1204 attrs=["msDs-keyVersionNumber"])
1205 assert(len(res) == 1)
1206 kvno = int(str(res[0]["msDs-keyVersionNumber"]))
1207 secChanType = int(secrets_msg[0]["secureChannelType"][0])
1209 secretsdb_self_join(secrets_ldb, domain=names.domain,
1210 realm=names.realm or sambaopts._lp.get('realm'),
1211 domainsid=names.domainsid,
1212 dnsdomain=names.dnsdomain,
1213 netbiosname=names.netbiosname,
1214 machinepass=machinepass,
1215 key_version_number=kvno,
1216 secure_channel_type=secChanType)
1218 raise ProvisioningError("Unable to find a Secure Channel"
1219 "of type SEC_CHAN_BDC")
1223 def setup_path(file):
1224 return os.path.join(setup_dir, file)
1226 # Synopsis for updateprovision
1227 # 1) get path related to provision to be update (called current)
1228 # 2) open current provision ldbs
1229 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1231 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1232 # by either upgradeprovision or provision
1233 # 5) creation of a new provision the latest version of provision script
1234 # (called reference)
1235 # 6) get reference provision paths
1236 # 7) open reference provision ldbs
1237 # 8) setup helpers data that will help the update process
1238 # 9) update the privilege ldb by copying the one of referecence provision to
1239 # the current provision
1240 # 10)get the oemInfo field, this field contains information about the different
1241 # provision that have been done
1242 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1243 # integer) or none of this the following things are done
1244 # A) When alpha9 or alphaxx is present
1245 # The base sam.ldb file is updated by looking at the difference between
1246 # referrence one and the current one. Everything is copied with the
1247 # exception of lastProvisionUSN attributes. The highest used USN
1248 # is fetched so that changed by upgradeprovision usn can be tracked
1249 # B) Other case (it reflect that that provision was done before alpha9)
1250 # The base sam.ldb of the reference provision is copied over
1251 # the current one, if necessary ldb related to partitions are moved
1253 # 12)A Schema object is created, it will be used to provide a complete
1254 # schema to current provision during update (as the schema of the
1255 # current provision might not be complete and so won't allow some
1256 # object to be created)
1257 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1258 # 14)The secrets db is updated by pull all the difference from the reference
1259 # provision into the current provision
1260 # 15)As the previous step has most probably modified the password stored in
1261 # in secret for the current DC, a new password is generated,
1262 # the kvno is bumped and the entry in samdb is also updated
1263 # 16)For current provision older than alpha9, we must fix the SD a little bit
1264 # administrator to update them because SD used to be generated with the
1265 # system account before alpha9.
1266 # 17)The highest usn modified so far is searched in the database it will be
1267 # the upper limit for usn modified during provision.
1268 # This is done before potential SD recalculation because we do not want
1269 # SD modified during recalculation to be marked as modified during provision
1270 # (and so possibly remplaced at next upgradeprovision)
1271 # 18)Rebuilt SD if the flag indicate to do so
1272 # 19)Check difference between SD of reference provision and those of the
1273 # current provision. The check is done by getting the sddl representation
1274 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1275 # Each part is verified separetly, for dacl and sacl ACL is splited into
1276 # ACEs and each ACE is verified separately (so that a permutation in ACE
1277 # didn't raise as an error).
1278 # 20)The oemInfo field is updated to add information about the fact that the
1279 # provision has been updated by the upgradeprovision version xxx
1280 # (the version is the one obtained when starting samba with the --version
1282 # 21)Check if the current provision has all the settings needed for dynamic
1283 # DNS update to work (that is to say the provision is newer than
1284 # january 2010). If not dns configuration file from reference provision
1285 # are copied in a sub folder and the administrator is invited to
1286 # do what is needed.
1287 # 22)If the lastProvisionUSN attribute was present it is updated to add
1288 # the range of usns modified by the current upgradeprovision
1291 # About updating the sam DB
1292 # The update takes place in update_partition function
1293 # This function read both current and reference provision and list all
1294 # the available DN of objects
1295 # If the string representation of a DN in reference provision is
1296 # equal to the string representation of a DN in current provision
1297 # (without taking care of case) then the object is flaged as being
1298 # present. If the object is not present in current provision the object
1299 # is being flaged as missing in current provision. Object present in current
1300 # provision but not in reference provision are ignored.
1301 # Once the list of objects present and missing is done, the deleted object
1302 # containers are created in the differents partitions (if missing)
1304 # Then the function add_missing_entries is called
1305 # This function will go through the list of missing entries by calling
1306 # add_missing_object for the given object. If this function returns 0
1307 # it means that the object needs some other object in order to be created
1308 # The object is reappended at the end of the list to be created later
1309 # (and preferably after all the needed object have been created)
1310 # The function keeps on looping on the list of object to be created until
1311 # it's empty or that the number of defered creation is equal to the number
1312 # of object that still needs to be created.
1314 # The function add_missing_object will first check if the object can be created.
1315 # That is to say that it didn't depends other not yet created objects
1316 # If requisit can't be fullfilled it exists with 0
1317 # Then it will try to create the missing entry by creating doing
1318 # an ldb_message_diff between the object in the reference provision and
1320 # This resulting object is filtered to remove all the back link attribute
1321 # (ie. memberOf) as they will be created by the other linked object (ie.
1322 # the one with the member attribute)
1323 # All attributes specified in the hashAttrNotCopied associative array are
1324 # also removed it's most of the time generated attributes
1326 # After missing entries have been added the update_partition function will
1327 # take care of object that exist but that need some update.
1328 # In order to do so the function update_present is called with the list
1329 # of object that are present in both provision and that might need an update.
1331 # This function handle first case mismatch so that the DN in the current
1332 # provision have the same case as in reference provision
1334 # It will then construct an associative array consiting of attributes as
1335 # key and invocationid as value( if the originating invocation id is
1336 # different from the invocation id of the current DC the value is -1 instead).
1338 # If the range of provision modified attributes is present, the function will
1339 # use the replMetadataProperty update method which is the following:
1340 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1341 # creationTime, msDs-KeyVersionNumber, oEMInformation
1342 # Check for each attribute if its usn is within one of the modified by
1343 # provision range and if its originating id is the invocation id of the
1344 # current DC, then validate the update from reference to current.
1345 # If not or if there is no replMetatdataProperty for this attribute then we
1347 # Otherwise (case the range of provision modified attribute is not present) it
1348 # use the following process:
1349 # All attributes that need to be added are accepted at the exeption of those
1350 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1351 # correct flags specified.
1352 # For attributes that need to be modified or removed, a check is performed
1353 # in OverwrittenAtt, if the attribute is present and the modification flag
1354 # (remove, delete) is one of those listed for this attribute then modification
1355 # is accepted. For complicated handling of attribute update, the control is passed
1356 # to handle_special_case
1360 if __name__ == '__main__':
1361 global defSDmodified
1362 defSDmodified = False
1363 # From here start the big steps of the program
1364 # 1) First get files paths
1365 paths = get_paths(param, smbconf=smbconf)
1366 paths.setup = setup_dir
1367 # Get ldbs with the system session, it is needed for searching
1368 # provision parameters
1369 session = system_session()
1371 # This variable will hold the last provision USN once if it exists.
1374 ldbs = get_ldbs(paths, creds, session, lp)
1375 ldbs.startTransactions()
1377 # 3) Guess all the needed names (variables in fact) from the current
1379 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1382 lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1383 if lastProvisionUSNs is not None:
1385 "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
1387 # Objects will be created with the admin session
1388 # (not anymore system session)
1389 adm_session = admin_session(lp, str(names.domainsid))
1390 # So we reget handle on objects
1391 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1393 if not sanitychecks(ldbs.sam, names):
1394 message(SIMPLE, "Sanity checks for the upgrade fails, checks messages"
1395 " and correct them before rerunning upgradeprovision")
1398 # Let's see provision parameters
1399 print_provision_key_parameters(names)
1401 # 5) With all this information let's create a fresh new provision used as
1403 message(SIMPLE, "Creating a reference provision")
1404 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1405 prefix="referenceprovision")
1406 newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
1411 # We need to get a list of object which SD is directly computed from
1412 # defaultSecurityDescriptor.
1413 # This will allow us to know which object we can rebuild the SD in case
1414 # of change of the parent's SD or of the defaultSD.
1415 # Get file paths of this new provision
1416 newpaths = get_paths(param, targetdir=provisiondir)
1417 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1418 new_ldbs.startTransactions()
1420 # 8) Populate some associative array to ease the update process
1421 # List of attribute which are link and backlink
1422 populate_links(new_ldbs.sam, names.schemadn)
1423 # List of attribute with ASN DN synthax)
1424 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1426 update_privilege(newpaths.private_dir, paths.private_dir)
1428 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1429 # Do some modification on sam.ldb
1430 ldbs.groupedCommit()
1432 if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1434 # Starting from alpha9 we can consider that the structure is quite ok
1435 # and that we should do only dela
1436 new_ldbs.groupedCommit()
1437 delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message)
1438 ldbs.startTransactions()
1439 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1440 new_ldbs.startTransactions()
1443 simple_update_basesamdb(newpaths, paths, names)
1444 ldbs = get_ldbs(paths, creds, session, lp)
1445 removeProvisionUSN(ldbs.sam)
1446 ldbs.startTransactions()
1449 schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
1450 serverdn=str(names.serverdn))
1453 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1455 message(SIMPLE, "Rollbacking every changes. Check the reason"
1457 message(SIMPLE, "In any case your system as it was before"
1459 ldbs.groupedRollback()
1460 new_ldbs.groupedRollback()
1461 shutil.rmtree(provisiondir)
1464 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1466 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1468 # 16) SD should be created with admin but as some previous acl were so wrong
1469 # that admin can't modify them we have first to recreate them with the good
1470 # form but with system account and then give the ownership to admin ...
1471 if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1472 message(SIMPLE, "Fixing old povision SD")
1473 fix_partition_sd(ldbs.sam, names)
1474 rebuild_sd(ldbs.sam, names)
1476 # We calculate the max USN before recalculating the SD because we might
1477 # touch object that have been modified after a provision and we do not
1478 # want that the next upgradeprovision thinks that it has a green light
1482 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1484 # 18) We rebuild SD only if defaultSecurityDescriptor is modified
1485 # But in fact we should do it also if one object has its SD modified as
1486 # child might need rebuild
1488 message(SIMPLE, "Updating SD")
1489 ldbs.sam.set_session_info(adm_session)
1490 # Alpha10 was a bit broken still
1491 if re.match(r'.*alpha(\d|10)', str(oem)):
1492 fix_partition_sd(ldbs.sam, names)
1493 rebuild_sd(ldbs.sam, names)
1496 # Now we are quite confident in the recalculate process of the SD, we make
1498 # Also the check must be done in a clever way as for the moment we just
1500 if opts.debugchangesd:
1501 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1504 updateOEMInfo(ldbs.sam, str(names.rootdn))
1506 check_for_DNS(newpaths.private_dir, paths.private_dir)
1508 if lastProvisionUSNs is not None:
1509 update_provision_usn(ldbs.sam, minUSN, maxUSN)
1510 if opts.full and (names.policyid is None or names.policyid_dc is None):
1511 update_policyids(names, ldbs.sam)
1512 if opts.full or opts.resetfileacl:
1514 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1515 except ProvisioningError, e:
1516 message(ERROR, "The policy for domain controller is missing,"
1517 " you should restart upgradeprovision with --full")
1520 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1521 except ProvisioningError, e:
1522 message(ERROR, "The policy for domain controller is missing,"
1523 " you should restart upgradeprovision with --full")
1524 ldbs.groupedCommit()
1525 new_ldbs.groupedCommit()
1526 message(SIMPLE, "Upgrade finished !")
1527 # remove reference provision now that everything is done !
1528 shutil.rmtree(provisiondir)