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")
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,
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, xattr
51 from samba.ndr import ndr_unpack
52 from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
53 find_provision_key_parameters, get_ldbs,
54 usn_in_range, identic_rename, get_diff_sddls,
55 update_secrets, CHANGE, ERROR, SIMPLE,
56 CHANGEALL, GUESS, CHANGESD, PROVISION,
57 updateOEMInfo, getOEMInfo, update_gpo,
58 delta_update_basesamdb, update_policyids,
59 update_machine_account_password)
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.setLevel(logging.INFO)
155 upgrade_logger.addHandler(handler)
157 provision_logger = logging.getLogger("provision")
158 provision_logger.addHandler(handler)
160 whatToLog = define_what_to_log(opts)
162 def message(what, text):
163 """Print a message if this message type has been selected to be printed
165 :param what: Category of the message
166 :param text: Message to print """
167 if (whatToLog & what) or what <= 0:
168 upgrade_logger.info("%s", text)
170 if len(sys.argv) == 1:
171 opts.interactive = True
172 lp = sambaopts.get_loadparm()
173 smbconf = lp.configfile
175 creds = credopts.get_credentials(lp)
176 creds.set_kerberos_state(DONT_USE_KERBEROS)
177 setup_dir = opts.setupdir
178 if setup_dir is None:
179 setup_dir = find_setup_dir()
183 def check_for_DNS(refprivate, private):
184 """Check if the provision has already the requirement for dynamic dns
186 :param refprivate: The path to the private directory of the reference
188 :param private: The path to the private directory of the upgraded
191 spnfile = "%s/spn_update_list" % private
192 namedfile = lp.get("dnsupdate:path")
195 namedfile = "%s/named.conf.update" % private
197 if not os.path.exists(spnfile):
198 shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
200 destdir = "%s/new_dns" % private
201 dnsdir = "%s/dns" % private
203 if not os.path.exists(namedfile):
204 if not os.path.exists(destdir):
206 if not os.path.exists(dnsdir):
208 shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
209 shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
210 message(SIMPLE, "It seems that you provision didn't integrate new rules "
211 "for dynamic dns update of domain related entries")
212 message(SIMPLE, "A copy of the new bind configuration files and "
213 "template as been put in %s, you should read them and configure dynamic "
214 " dns update" % destdir)
217 def populate_links(samdb, schemadn):
218 """Populate an array with all the back linked attributes
220 This attributes that are modified automaticaly when
221 front attibutes are changed
223 :param samdb: A LDB object for sam.ldb file
224 :param schemadn: DN of the schema for the partition"""
225 linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
226 backlinked.extend(linkedAttHash.values())
227 for t in linkedAttHash.keys():
231 def populate_dnsyntax(samdb, schemadn):
232 """Populate an array with all the attributes that have DN synthax
235 :param samdb: A LDB object for sam.ldb file
236 :param schemadn: DN of the schema for the partition"""
237 res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
238 str(schemadn)), scope=SCOPE_SUBTREE,
239 attrs=["lDAPDisplayName"])
241 dn_syntax_att.append(elem["lDAPDisplayName"])
244 def sanitychecks(samdb, names):
245 """Make some checks before trying to update
247 :param samdb: An LDB object opened on sam.ldb
248 :param names: list of key provision parameters
249 :return: Status of check (1 for Ok, 0 for not Ok) """
250 res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
251 scope=SCOPE_SUBTREE, attrs=["dn"],
252 controls=["search_options:1:2"])
254 print "No DC found, your provision is most probably hardly broken !"
257 print "Found %d domain controllers, for the moment upgradeprovision" \
258 "is not able to handle upgrade on domain with more than one DC, please demote" \
259 " the other(s) DC(s) before upgrading" % len(res)
265 def print_provision_key_parameters(names):
266 """Do a a pretty print of provision parameters
268 :param names: list of key provision parameters """
269 message(GUESS, "rootdn :" + str(names.rootdn))
270 message(GUESS, "configdn :" + str(names.configdn))
271 message(GUESS, "schemadn :" + str(names.schemadn))
272 message(GUESS, "serverdn :" + str(names.serverdn))
273 message(GUESS, "netbiosname :" + names.netbiosname)
274 message(GUESS, "defaultsite :" + names.sitename)
275 message(GUESS, "dnsdomain :" + names.dnsdomain)
276 message(GUESS, "hostname :" + names.hostname)
277 message(GUESS, "domain :" + names.domain)
278 message(GUESS, "realm :" + names.realm)
279 message(GUESS, "invocationid:" + names.invocation)
280 message(GUESS, "policyguid :" + names.policyid)
281 message(GUESS, "policyguiddc:" + str(names.policyid_dc))
282 message(GUESS, "domainsid :" + str(names.domainsid))
283 message(GUESS, "domainguid :" + names.domainguid)
284 message(GUESS, "ntdsguid :" + names.ntdsguid)
285 message(GUESS, "domainlevel :" + str(names.domainlevel))
288 def handle_special_case(att, delta, new, old, usn):
289 """Define more complicate update rules for some attributes
291 :param att: The attribute to be updated
292 :param delta: A messageElement object that correspond to the difference
293 between the updated object and the reference one
294 :param new: The reference object
295 :param old: The Updated object
296 :param usn: The highest usn modified by a previous (upgrade)provision
297 :return: True to indicate that the attribute should be kept, False for
300 flag = delta.get(att).flags()
301 # We do most of the special case handle if we do not have the
302 # highest usn as otherwise the replPropertyMetaData will guide us more
305 if (att == "member" and flag == FLAG_MOD_REPLACE):
309 for elem in old[0][att]:
310 hash[str(elem).lower()]=1
311 newval.append(str(elem))
313 for elem in new[0][att]:
314 if not hash.has_key(str(elem).lower()):
316 newval.append(str(elem))
318 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
323 if (att in ("gPLink", "gPCFileSysPath") and
324 flag == FLAG_MOD_REPLACE and
325 str(new[0].dn).lower() == str(old[0].dn).lower()):
329 if att == "forceLogoff":
330 ref=0x8000000000000000
331 oldval=int(old[0][att][0])
332 newval=int(new[0][att][0])
333 ref == old and ref == abs(new)
336 if att in ("adminDisplayName", "adminDescription"):
339 if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (names.schemadn)
340 and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
343 if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
344 att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
347 if (str(old[0].dn) == "%s" % (str(names.rootdn))
348 and att == "subRefs" and flag == FLAG_MOD_REPLACE):
351 if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
354 # This is a bit of special animal as we might have added
355 # already SPN entries to the list that has to be modified
356 # So we go in detail to try to find out what has to be added ...
357 if (att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):
361 for elem in old[0][att]:
363 newval.append(str(elem))
365 for elem in new[0][att]:
366 if not hash.has_key(str(elem)):
368 newval.append(str(elem))
370 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
377 def dump_denied_change(dn, att, flagtxt, current, reference):
378 """Print detailed information about why a changed is denied
380 :param dn: DN of the object which attribute is denied
381 :param att: Attribute that was supposed to be upgraded
382 :param flagtxt: Type of the update that should be performed
383 (add, change, remove, ...)
384 :param current: Value(s) of the current attribute
385 :param reference: Value(s) of the reference attribute"""
387 message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
388 +" is not allowed to be changed/removed, I discard this change")
389 if att != "objectSid" :
391 for e in range(0, len(current)):
392 message(CHANGE, "old %d : %s" % (i, str(current[e])))
394 if reference is not None:
396 for e in range(0, len(reference)):
397 message(CHANGE, "new %d : %s" % (i, str(reference[e])))
400 message(CHANGE, "old : %s" % ndr_unpack(security.dom_sid, current[0]))
401 message(CHANGE, "new : %s" % ndr_unpack(security.dom_sid, reference[0]))
404 def handle_special_add(samdb, dn, names):
405 """Handle special operation (like remove) on some object needed during
408 This is mostly due to wrong creation of the object in previous provision.
409 :param samdb: An Ldb object representing the SAM database
410 :param dn: DN of the object to inspect
411 :param names: list of key provision parameters
415 objDn = Dn(samdb, "CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn)
417 #This entry was misplaced lets remove it if it exists
418 dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
421 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn)
423 #This entry was misplaced lets remove it if it exists
424 dntoremove = "CN=Certificate Service DCOM Access,"\
425 "CN=Users, %s" % names.rootdn
428 objDn = Dn(samdb, "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn)
430 #This entry was misplaced lets remove it if it exists
431 dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
433 objDn = Dn(samdb, "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn)
435 #This entry was misplaced lets remove it if it exists
436 dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
438 objDn = Dn(samdb,"CN=System,CN=WellKnown Security Principals,"
439 "CN=Configuration,%s" % names.rootdn)
441 oldDn = Dn(samdb,"CN=Well-Known-Security-Id-System,"
442 "CN=WellKnown Security Principals,"
443 "CN=Configuration,%s" % names.rootdn)
445 res = samdb.search(expression="(dn=%s)" % oldDn,
446 base=str(names.rootdn),
447 scope=SCOPE_SUBTREE, attrs=["dn"],
448 controls=["search_options:1:2"])
450 message(CHANGE, "Existing object %s must be replaced by %s,"
451 "Renaming old object" % (str(oldDn), str(dn)))
452 samdb.rename(oldDn, objDn)
456 if dntoremove is not None:
457 res = samdb.search(expression="(dn=%s)" % dntoremove,
458 base=str(names.rootdn),
459 scope=SCOPE_SUBTREE, attrs=["dn"],
460 controls=["search_options:1:2"])
462 message(CHANGE, "Existing object %s must be replaced by %s,"
463 "removing old object" % (dntoremove, str(dn)))
464 samdb.delete(res[0]["dn"])
468 def check_dn_nottobecreated(hash, index, listdn):
469 """Check if one of the DN present in the list has a creation order
470 greater than the current.
472 Hash is indexed by dn to be created, with each key
473 is associated the creation order.
475 First dn to be created has the creation order 0, second has 1, ...
476 Index contain the current creation order
478 :param hash: Hash holding the different DN of the object to be
480 :param index: Current creation order
481 :param listdn: List of DNs on which the current DN depends on
482 :return: None if the current object do not depend on other
483 object or if all object have been created before."""
487 key = str(dn).lower()
488 if hash.has_key(key) and hash[key] > index:
494 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
495 """Add a new object if the dependencies are satisfied
497 The function add the object if the object on which it depends are already
500 :param ref_samdb: Ldb object representing the SAM db of the reference
502 :param samdb: Ldb object representing the SAM db of the upgraded
504 :param dn: DN of the object to be added
505 :param names: List of key provision parameters
506 :param basedn: DN of the partition to be updated
507 :param hash: Hash holding the different DN of the object to be
509 :param index: Current creation order
510 :return: True if the object was created False otherwise"""
512 if handle_special_add(samdb, dn, names):
514 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
515 scope=SCOPE_SUBTREE, controls=["search_options:1:2"])
517 delta = samdb.msg_diff(empty, reference[0])
521 if str(reference[0].get("cn")) == "RID Set":
524 if delta.get("objectSid"):
525 sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
526 m = re.match(r".*-(\d+)$", sid)
527 if m and int(m.group(1))>999:
528 delta.remove("objectSid")
529 for att in hashAttrNotCopied.keys():
531 for att in backlinked:
533 depend_on_yettobecreated = None
534 for att in dn_syntax_att:
535 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
537 if depend_on_yet_tobecreated is not None:
538 message(CHANGE, "Object %s depends on %s in attribute %s,"
539 "delaying the creation" % (dn,
540 depend_on_yet_tobecreated, att))
545 message(CHANGE,"Object %s will be added" % dn)
546 samdb.add(delta, ["relax:0"])
548 message(CHANGE,"Object %s was skipped" % dn)
552 def gen_dn_index_hash(listMissing):
553 """Generate a hash associating the DN to its creation order
555 :param listMissing: List of DN
556 :return: Hash with DN as keys and creation order as values"""
558 for i in range(0, len(listMissing)):
559 hash[str(listMissing[i]).lower()] = i
562 def add_deletedobj_containers(ref_samdb, samdb, names):
563 """Add the object containter: CN=Deleted Objects
565 This function create the container for each partition that need one and
566 then reference the object into the root of the partition
568 :param ref_samdb: Ldb object representing the SAM db of the reference
570 :param samdb: Ldb object representing the SAM db of the upgraded provision
571 :param names: List of key provision parameters"""
574 wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
575 partitions = [str(names.rootdn), str(names.configdn)]
576 for part in partitions:
577 ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
578 base=part, scope=SCOPE_SUBTREE,
580 controls=["show_deleted:0"])
581 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
582 base=part, scope=SCOPE_SUBTREE,
584 controls=["show_deleted:0"])
585 if len(ref_delObjCnt) > len(delObjCnt):
586 reference = ref_samdb.search(expression="cn=Deleted Objects",
587 base=part, scope=SCOPE_SUBTREE,
588 controls=["show_deleted:0"])
590 delta = samdb.msg_diff(empty, reference[0])
592 delta.dn = Dn(samdb, str(reference[0]["dn"]))
593 for att in hashAttrNotCopied.keys():
598 res = samdb.search(expression="(objectClass=*)", base=part,
600 attrs=["dn", "wellKnownObjects"])
602 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
606 wko = res[0]["wellKnownObjects"]
608 # The wellKnownObject that we want to add.
610 if str(o) == targetWKO:
612 listwko.append(str(o))
615 listwko.append(targetWKO)
618 delta.dn = Dn(samdb, str(res[0]["dn"]))
619 delta["wellKnownObjects"] = MessageElement(listwko,
624 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
625 """Add the missing object whose DN is the list
627 The function add the object if the objects on which it depends are
630 :param ref_samdb: Ldb object representing the SAM db of the reference
632 :param samdb: Ldb object representing the SAM db of the upgraded
634 :param dn: DN of the object to be added
635 :param names: List of key provision parameters
636 :param basedn: DN of the partition to be updated
637 :param list: List of DN to be added in the upgraded provision"""
642 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
644 listMissing = listDefered
646 hashMissing = gen_dn_index_hash(listMissing)
647 for dn in listMissing:
648 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
652 # DN can't be created because it depends on some
653 # other DN in the list
654 listDefered.append(dn)
655 if len(listDefered) != 0:
656 raise ProvisioningError("Unable to insert missing elements:" \
657 "circular references")
659 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
660 """This function handle updates on links
662 :param samdb: An LDB object pointing to the updated provision
663 :param att: Attribute to update
664 :param basedn: The root DN of the provision
665 :param dn: The DN of the inspected object
666 :param value: The value of the attribute
667 :param ref_value: The value of this attribute in the reference provision
668 :param delta: The MessageElement object that will be applied for
669 transforming the current provision"""
671 res = samdb.search(expression="dn=%s" % dn, base=basedn,
672 controls=["search_options:1:2", "reveal:1"],
680 newlinklist.extend(value)
684 # for w2k domain level the reveal won't reveal anything ...
685 # it means that we can readd links that were removed on purpose ...
686 # Also this function in fact just accept add not removal
688 for e in res[0][att]:
689 if not hash.has_key(e):
690 # We put in the blacklist all the element that are in the "revealed"
691 # result and not in the "standard" result
692 # This element are links that were removed before and so that
693 # we don't wan't to readd
697 if not blacklist.has_key(e) and not hash.has_key(e):
698 newlinklist.append(str(e))
701 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
706 msg_elt_flag_strs = {
707 ldb.FLAG_MOD_ADD: "MOD_ADD",
708 ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
709 ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
712 def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
713 """ This function updates the object that are already present in the
716 :param ref_samdb: An LDB object pointing to the reference provision
717 :param samdb: An LDB object pointing to the updated provision
718 :param basedn: A string with the value of the base DN for the provision
720 :param listPresent: A list of object that is present in the provision
721 :param usns: A list of USN range modified by previous provision and
723 :param invocationid: The value of the invocationid for the current DC"""
726 # This hash is meant to speedup lookup of attribute name from an oid,
727 # it's for the replPropertyMetaData handling
729 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
730 controls=["search_options:1:2"], attrs=["attributeID",
734 strDisplay = str(e.get("lDAPDisplayName"))
735 hash_oid_name[str(e.get("attributeID"))] = strDisplay
737 msg = "Unable to insert missing elements: circular references"
738 raise ProvisioningError(msg)
741 controls = ["search_options:1:2", "sd_flags:1:2"]
742 for dn in listPresent:
743 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
746 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
747 scope=SCOPE_SUBTREE, controls=controls)
750 (str(current[0].dn) != str(reference[0].dn)) and
751 (str(current[0].dn).upper() == str(reference[0].dn).upper())
753 message(CHANGE, "Name are the same but case change,"\
754 "let's rename %s to %s" % (str(current[0].dn),
755 str(reference[0].dn)))
756 identic_rename(samdb, reference[0].dn)
757 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
759 controls=["search_options:1:2"])
761 delta = samdb.msg_diff(current[0], reference[0])
763 for att in hashAttrNotCopied.keys():
766 for att in backlinked:
771 if len(delta.items()) > 1 and usns is not None:
772 # Fetch the replPropertyMetaData
773 res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
774 scope=SCOPE_SUBTREE, controls=controls,
775 attrs=["replPropertyMetaData"])
776 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
777 str(res[0]["replPropertyMetaData"])).ctr
781 # We put in this hash only modification
782 # made on the current host
783 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
784 if str(o.originating_invocation_id) == str(invocationid):
785 # Note we could just use 1 here
786 hash_attr_usn[att] = o.originating_usn
788 hash_attr_usn[att] = -1
795 # We have updated by provision usn information so let's exploit
796 # replMetadataProperties
797 if att in forwardlinked:
798 handle_links(samdb, att, basedn, current[0]["dn"],
799 current[0][att], reference[0][att], delta)
801 if isFirst == 0 and len(delta.items())>1:
803 txt = "%s\n" % (str(dn))
805 # There is always a dn attribute after a msg_diff
807 if att == "rIDAvailablePool":
810 if att == "objectSid":
813 if att == "creationTime":
816 if att == "oEMInformation":
819 if att == "msDs-KeyVersionNumber":
820 # This is the kvno of the computer/user it's a very bad
824 if handle_special_case(att, delta, reference, current, usns):
825 # This attribute is "complicated" to handle and handling
826 # was done in handle_special_case
828 attrUSN = hash_attr_usn.get(att)
829 if att == "forceLogoff" and attrUSN is None:
836 # This attribute was last modified by another DC forget
838 message(CHANGE, "%sAttribute: %s has been"
839 "created/modified/deleted by another DC,"
840 " do nothing" % (txt, att ))
844 elif not usn_in_range(int(attrUSN), usns):
845 message(CHANGE, "%sAttribute: %s has been"
846 "created/modified/deleted not during a"
847 " provision or upgradeprovision: current"
848 " usn %d , do nothing" % (txt, att, attrUSN))
853 if att == "defaultSecurityDescriptor":
856 message(CHANGE, "%sAttribute: %s will be modified"
857 "/deleted it was last modified"
858 "during a provision, current usn:"
859 "%d" % (txt, att, attrUSN))
862 message(CHANGE, "%sAttribute: %s will be added because"
863 " it hasn't existed before " % (txt, att))
868 # Old school way of handling things for pre alpha12 upgrade
870 msgElt = delta.get(att)
872 if att == "nTSecurityDescriptor":
879 if not hashOverwrittenAtt.has_key(att):
880 if msgElt.flags() != FLAG_MOD_ADD:
881 if not handle_special_case(att, delta, reference, current,
883 if opts.debugchange or opts.debugall:
885 dump_denied_change(dn, att,
886 msg_elt_flag_strs[msgElt.flags()],
887 current[0][att], reference[0][att])
889 dump_denied_change(dn, att,
890 msg_elt_flag_strs[msgElt.flags()],
891 current[0][att], None)
895 if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
897 elif hashOverwrittenAtt.get(att)==never:
902 if len(delta.items()) >1:
903 attributes=", ".join(delta.keys())
904 message(CHANGE, "%s is different from the reference one, changed"
905 " attributes: %s\n" % (dn, attributes))
911 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs):
912 """Check differences between the reference provision and the upgraded one.
914 It looks for all objects which base DN is name.
916 This function will also add the missing object and update existing object
917 to add or remove attributes that were missing.
919 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
921 :param samdb: An LDB object connected to the sam.ldb of the update
923 :param basedn: String value of the DN of the partition
924 :param names: List of key provision parameters
925 :param schema: A Schema object
926 :param provisionUSNs: The USNs modified by provision/upgradeprovision
936 # Connect to the reference provision and get all the attribute in the
937 # partition referred by name
938 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
939 scope=SCOPE_SUBTREE, attrs=["dn"],
940 controls=["search_options:1:2"])
942 current = samdb.search(expression="objectClass=*", base=basedn,
943 scope=SCOPE_SUBTREE, attrs=["dn"],
944 controls=["search_options:1:2"])
945 # Create a hash for speeding the search of new object
946 for i in range(0, len(reference)):
947 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
949 # Create a hash for speeding the search of existing object in the
951 for i in range(0, len(current)):
952 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
955 for k in hash_new.keys():
956 if not hash.has_key(k):
957 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
958 listMissing.append(hash_new[k])
960 listPresent.append(hash_new[k])
962 # Sort the missing object in order to have object of the lowest level
963 # first (which can be containers for higher level objects)
964 listMissing.sort(dn_sort)
965 listPresent.sort(dn_sort)
967 # The following lines is to load the up to
968 # date schema into our current LDB
969 # a complete schema is needed as the insertion of attributes
970 # and class is done against it
971 # and the schema is self validated
972 samdb.set_schema(schema)
974 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
975 add_deletedobj_containers(ref_samdb, samdb, names)
977 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
978 changed = update_present(ref_samdb, samdb, basedn, listPresent,
979 provisionUSNs, names.invocation)
980 message(SIMPLE, "There are %d changed objects" % (changed))
983 except StandardError, err:
984 message(ERROR, "Exception during upgrade of samdb:")
985 (typ, val, tb) = sys.exc_info()
986 traceback.print_exception(typ, val, tb)
990 def check_updated_sd(ref_sam, cur_sam, names):
991 """Check if the security descriptor in the upgraded provision are the same
994 :param ref_sam: A LDB object connected to the sam.ldb file used as
995 the reference provision
996 :param cur_sam: A LDB object connected to the sam.ldb file used as
998 :param names: List of key provision parameters"""
999 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1000 scope=SCOPE_SUBTREE,
1001 attrs=["dn", "nTSecurityDescriptor"],
1002 controls=["search_options:1:2"])
1003 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1004 scope=SCOPE_SUBTREE,
1005 attrs=["dn", "nTSecurityDescriptor"],
1006 controls=["search_options:1:2"])
1008 for i in range(0, len(reference)):
1009 refsd = ndr_unpack(security.descriptor,
1010 str(reference[i]["nTSecurityDescriptor"]))
1011 hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
1014 for i in range(0, len(current)):
1015 key = str(current[i]["dn"]).lower()
1016 if hash.has_key(key):
1017 cursd = ndr_unpack(security.descriptor,
1018 str(current[i]["nTSecurityDescriptor"]))
1019 sddl = cursd.as_sddl(names.domainsid)
1020 if sddl != hash[key]:
1021 txt = get_diff_sddls(hash[key], sddl)
1023 message(CHANGESD, "On object %s ACL is different"
1024 " \n%s" % (current[i]["dn"], txt))
1028 def fix_partition_sd(samdb, names):
1029 """This function fix the SD for partition containers (basedn, configdn, ...)
1030 This is needed because some provision use to have broken SD on containers
1032 :param samdb: An LDB object pointing to the sam of the current provision
1033 :param names: A list of key provision parameters
1035 # First update the SD for the rootdn
1036 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1037 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1038 controls=["search_options:1:2"])
1040 delta.dn = Dn(samdb, str(res[0]["dn"]))
1041 descr = get_domain_descriptor(names.domainsid)
1042 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1043 "nTSecurityDescriptor")
1044 samdb.modify(delta, ["recalculate_sd:0"])
1045 # Then the config dn
1046 res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1047 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1048 controls=["search_options:1:2"])
1050 delta.dn = Dn(samdb, str(res[0]["dn"]))
1051 descr = get_config_descriptor(names.domainsid)
1052 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1053 "nTSecurityDescriptor" )
1054 samdb.modify(delta, ["recalculate_sd:0"])
1055 # Then the schema dn
1056 res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1057 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1058 controls=["search_options:1:2"])
1061 delta.dn = Dn(samdb, str(res[0]["dn"]))
1062 descr = get_schema_descriptor(names.domainsid)
1063 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1064 "nTSecurityDescriptor" )
1065 samdb.modify(delta, ["recalculate_sd:0"])
1067 def rebuild_sd(samdb, names):
1068 """Rebuild security descriptor of the current provision from scratch
1070 During the different pre release of samba4 security descriptors (SD)
1071 were notarly broken (up to alpha11 included)
1072 This function allow to get them back in order, this function make the
1073 assumption that nobody has modified manualy an SD
1074 and so SD can be safely recalculated from scratch to get them right.
1076 :param names: List of key provision parameters"""
1080 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1081 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1082 controls=["search_options:1:2"])
1084 if not (str(obj["dn"]) == str(names.rootdn) or
1085 str(obj["dn"]) == str(names.configdn) or
1086 str(obj["dn"]) == str(names.schemadn)):
1087 hash[str(obj["dn"])] = obj["whenCreated"]
1089 listkeys = hash.keys()
1090 listkeys.sort(dn_sort)
1092 for key in listkeys:
1095 delta.dn = Dn(samdb, key)
1096 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1098 samdb.modify(delta, ["recalculate_sd:0"])
1100 # XXX: We should always catch an explicit exception.
1101 # What could go wrong here?
1102 samdb.transaction_cancel()
1103 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1104 scope=SCOPE_SUBTREE,
1105 attrs=["dn", "nTSecurityDescriptor"],
1106 controls=["search_options:1:2"])
1107 badsd = ndr_unpack(security.descriptor,
1108 str(res[0]["nTSecurityDescriptor"]))
1109 print "bad stuff %s" % badsd.as_sddl(names.domainsid)
1112 def removeProvisionUSN(samdb):
1113 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1114 entry = samdb.search(expression="dn=@PROVISION", base = "",
1115 scope=SCOPE_SUBTREE,
1116 controls=["search_options:1:2"],
1119 empty.dn = entry[0].dn
1120 delta = samdb.msg_diff(entry[0], empty)
1122 delta.dn = entry[0].dn
1125 def remove_stored_generated_attrs(paths, creds, session, lp):
1126 """Remove previously stored constructed attributes
1128 :param paths: List of paths for different provision objects
1129 from the upgraded provision
1130 :param creds: A credential object
1131 :param session: A session object
1132 :param lp: A line parser object
1133 :return: An associative array whose key are the different constructed
1134 attributes and the value the dn where this attributes were found.
1138 def simple_update_basesamdb(newpaths, paths, names):
1139 """Update the provision container db: sam.ldb
1140 This function is aimed at very old provision (before alpha9)
1142 :param newpaths: List of paths for different provision objects
1143 from the reference provision
1144 :param paths: List of paths for different provision objects
1145 from the upgraded provision
1146 :param names: List of key provision parameters"""
1148 message(SIMPLE, "Copy samdb")
1149 shutil.copy(newpaths.samdb, paths.samdb)
1151 message(SIMPLE, "Update partitions filename if needed")
1152 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1153 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1154 usersldb = os.path.join(paths.private_dir, "users.ldb")
1155 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1157 if not os.path.isdir(samldbdir):
1159 os.chmod(samldbdir, 0700)
1160 if os.path.isfile(schemaldb):
1161 shutil.copy(schemaldb, os.path.join(samldbdir,
1162 "%s.ldb"%str(names.schemadn).upper()))
1163 os.remove(schemaldb)
1164 if os.path.isfile(usersldb):
1165 shutil.copy(usersldb, os.path.join(samldbdir,
1166 "%s.ldb"%str(names.rootdn).upper()))
1168 if os.path.isfile(configldb):
1169 shutil.copy(configldb, os.path.join(samldbdir,
1170 "%s.ldb"%str(names.configdn).upper()))
1171 os.remove(configldb)
1174 def update_privilege(ref_private_path, cur_private_path):
1175 """Update the privilege database
1177 :param ref_private_path: Path to the private directory of the reference
1179 :param cur_private_path: Path to the private directory of the current
1180 (and to be updated) provision."""
1181 message(SIMPLE, "Copy privilege")
1182 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1183 os.path.join(cur_private_path, "privilege.ldb"))
1186 def update_samdb(ref_samdb, samdb, names, highestUSN, schema):
1187 """Upgrade the SAM DB contents for all the provision partitions
1189 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1191 :param samdb: An LDB object connected to the sam.ldb of the update
1193 :param names: List of key provision parameters
1194 :param highestUSN: The highest USN modified by provision/upgradeprovision
1196 :param schema: A Schema object that represent the schema of the provision"""
1198 message(SIMPLE, "Starting update of samdb")
1199 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1202 message(SIMPLE, "Update of samdb finished")
1205 message(SIMPLE, "Update failed")
1209 def copyxattrs(dir, refdir):
1210 """ Copy owner, groups, extended ACL and NT acls from
1211 a reference dir to a destination dir
1213 Both dir are supposed to hold the same files
1214 :param dir: Destination dir
1215 :param refdir: Reference directory"""
1218 for root, dirs, files in os.walk(dir, topdown=True):
1220 subdir=root[len(dir):]
1221 ref = os.path.join("%s%s" % (refdir, subdir), name)
1222 statsinfo = os.stat(ref)
1223 tgt = os.path.join(root, name)
1226 os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
1227 # Get the xattr attributes if any
1229 attribute = samba.xattr_native.wrap_getxattr(ref,
1230 xattr.XATTR_NTACL_NAME)
1231 samba.xattr_native.wrap_setxattr(tgt,
1232 xattr.XATTR_NTACL_NAME,
1236 attribute = samba.xattr_native.wrap_getxattr(ref,
1237 "system.posix_acl_access")
1238 samba.xattr_native.wrap_setxattr(tgt,
1239 "system.posix_acl_access",
1244 subdir=root[len(dir):]
1245 ref = os.path.join("%s%s" % (refdir, subdir), name)
1246 statsinfo = os.stat(ref)
1247 tgt = os.path.join(root, name)
1249 os.chown(os.path.join(root, name), statsinfo.st_uid,
1252 attribute = samba.xattr_native.wrap_getxattr(ref,
1253 xattr.XATTR_NTACL_NAME)
1254 samba.xattr_native.wrap_setxattr(tgt,
1255 xattr.XATTR_NTACL_NAME,
1259 attribute = samba.xattr_native.wrap_getxattr(ref,
1260 "system.posix_acl_access")
1261 samba.xattr_native.wrap_setxattr(tgt,
1262 "system.posix_acl_access",
1269 def backup_provision(paths, dir):
1270 """This function backup the provision files so that a rollback
1273 :param paths: Paths to different objects
1274 :param dir: Directory where to store the backup
1277 shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
1278 copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
1279 shutil.copy2(paths.samdb, dir)
1280 shutil.copy2(paths.secrets, dir)
1281 shutil.copy2(paths.idmapdb, dir)
1282 shutil.copy2(paths.privilege, dir)
1283 if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1284 shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1285 shutil.copy2(paths.smbconf, dir)
1286 shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1288 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1289 if not os.path.isdir(samldbdir):
1290 samldbdir = paths.private_dir
1291 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1292 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1293 usersldb = os.path.join(paths.private_dir, "users.ldb")
1294 shutil.copy2(schemaldb, dir)
1295 shutil.copy2(usersldb, dir)
1296 shutil.copy2(configldb, dir)
1298 shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1300 def setup_path(file):
1301 return os.path.join(setup_dir, file)
1303 # Synopsis for updateprovision
1304 # 1) get path related to provision to be update (called current)
1305 # 2) open current provision ldbs
1306 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1308 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1309 # by either upgradeprovision or provision
1310 # 5) creation of a new provision the latest version of provision script
1311 # (called reference)
1312 # 6) get reference provision paths
1313 # 7) open reference provision ldbs
1314 # 8) setup helpers data that will help the update process
1315 # 9) update the privilege ldb by copying the one of referecence provision to
1316 # the current provision
1317 # 10)get the oemInfo field, this field contains information about the different
1318 # provision that have been done
1319 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1320 # integer) or none of this the following things are done
1321 # A) When alpha9 or alphaxx is present
1322 # The base sam.ldb file is updated by looking at the difference between
1323 # referrence one and the current one. Everything is copied with the
1324 # exception of lastProvisionUSN attributes.
1325 # B) Other case (it reflect that that provision was done before alpha9)
1326 # The base sam.ldb of the reference provision is copied over
1327 # the current one, if necessary ldb related to partitions are moved
1329 # The highest used USN is fetched so that changed by upgradeprovision
1330 # usn can be tracked
1331 # 12)A Schema object is created, it will be used to provide a complete
1332 # schema to current provision during update (as the schema of the
1333 # current provision might not be complete and so won't allow some
1334 # object to be created)
1335 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1336 # 14)The secrets db is updated by pull all the difference from the reference
1337 # provision into the current provision
1338 # 15)As the previous step has most probably modified the password stored in
1339 # in secret for the current DC, a new password is generated,
1340 # the kvno is bumped and the entry in samdb is also updated
1341 # 16)For current provision older than alpha9, we must fix the SD a little bit
1342 # administrator to update them because SD used to be generated with the
1343 # system account before alpha9.
1344 # 17)The highest usn modified so far is searched in the database it will be
1345 # the upper limit for usn modified during provision.
1346 # This is done before potential SD recalculation because we do not want
1347 # SD modified during recalculation to be marked as modified during provision
1348 # (and so possibly remplaced at next upgradeprovision)
1349 # 18)Rebuilt SD if the flag indicate to do so
1350 # 19)Check difference between SD of reference provision and those of the
1351 # current provision. The check is done by getting the sddl representation
1352 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1353 # Each part is verified separetly, for dacl and sacl ACL is splited into
1354 # ACEs and each ACE is verified separately (so that a permutation in ACE
1355 # didn't raise as an error).
1356 # 20)The oemInfo field is updated to add information about the fact that the
1357 # provision has been updated by the upgradeprovision version xxx
1358 # (the version is the one obtained when starting samba with the --version
1360 # 21)Check if the current provision has all the settings needed for dynamic
1361 # DNS update to work (that is to say the provision is newer than
1362 # january 2010). If not dns configuration file from reference provision
1363 # are copied in a sub folder and the administrator is invited to
1364 # do what is needed.
1365 # 22)If the lastProvisionUSN attribute was present it is updated to add
1366 # the range of usns modified by the current upgradeprovision
1369 # About updating the sam DB
1370 # The update takes place in update_partition function
1371 # This function read both current and reference provision and list all
1372 # the available DN of objects
1373 # If the string representation of a DN in reference provision is
1374 # equal to the string representation of a DN in current provision
1375 # (without taking care of case) then the object is flaged as being
1376 # present. If the object is not present in current provision the object
1377 # is being flaged as missing in current provision. Object present in current
1378 # provision but not in reference provision are ignored.
1379 # Once the list of objects present and missing is done, the deleted object
1380 # containers are created in the differents partitions (if missing)
1382 # Then the function add_missing_entries is called
1383 # This function will go through the list of missing entries by calling
1384 # add_missing_object for the given object. If this function returns 0
1385 # it means that the object needs some other object in order to be created
1386 # The object is reappended at the end of the list to be created later
1387 # (and preferably after all the needed object have been created)
1388 # The function keeps on looping on the list of object to be created until
1389 # it's empty or that the number of defered creation is equal to the number
1390 # of object that still needs to be created.
1392 # The function add_missing_object will first check if the object can be created.
1393 # That is to say that it didn't depends other not yet created objects
1394 # If requisit can't be fullfilled it exists with 0
1395 # Then it will try to create the missing entry by creating doing
1396 # an ldb_message_diff between the object in the reference provision and
1398 # This resulting object is filtered to remove all the back link attribute
1399 # (ie. memberOf) as they will be created by the other linked object (ie.
1400 # the one with the member attribute)
1401 # All attributes specified in the hashAttrNotCopied associative array are
1402 # also removed it's most of the time generated attributes
1404 # After missing entries have been added the update_partition function will
1405 # take care of object that exist but that need some update.
1406 # In order to do so the function update_present is called with the list
1407 # of object that are present in both provision and that might need an update.
1409 # This function handle first case mismatch so that the DN in the current
1410 # provision have the same case as in reference provision
1412 # It will then construct an associative array consiting of attributes as
1413 # key and invocationid as value( if the originating invocation id is
1414 # different from the invocation id of the current DC the value is -1 instead).
1416 # If the range of provision modified attributes is present, the function will
1417 # use the replMetadataProperty update method which is the following:
1418 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1419 # creationTime, msDs-KeyVersionNumber, oEMInformation
1420 # Check for each attribute if its usn is within one of the modified by
1421 # provision range and if its originating id is the invocation id of the
1422 # current DC, then validate the update from reference to current.
1423 # If not or if there is no replMetatdataProperty for this attribute then we
1425 # Otherwise (case the range of provision modified attribute is not present) it
1426 # use the following process:
1427 # All attributes that need to be added are accepted at the exeption of those
1428 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1429 # correct flags specified.
1430 # For attributes that need to be modified or removed, a check is performed
1431 # in OverwrittenAtt, if the attribute is present and the modification flag
1432 # (remove, delete) is one of those listed for this attribute then modification
1433 # is accepted. For complicated handling of attribute update, the control is passed
1434 # to handle_special_case
1438 if __name__ == '__main__':
1439 global defSDmodified
1440 defSDmodified = False
1441 # From here start the big steps of the program
1442 # 1) First get files paths
1443 paths = get_paths(param, smbconf=smbconf)
1444 paths.setup = setup_dir
1445 # Get ldbs with the system session, it is needed for searching
1446 # provision parameters
1447 session = system_session()
1449 # This variable will hold the last provision USN once if it exists.
1452 ldbs = get_ldbs(paths, creds, session, lp)
1453 backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1454 prefix="backupprovision")
1455 backup_provision(paths, backupdir)
1457 ldbs.startTransactions()
1459 # 3) Guess all the needed names (variables in fact) from the current
1461 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1464 lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1465 if lastProvisionUSNs is not None:
1467 "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
1469 # Objects will be created with the admin session
1470 # (not anymore system session)
1471 adm_session = admin_session(lp, str(names.domainsid))
1472 # So we reget handle on objects
1473 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1475 if not sanitychecks(ldbs.sam, names):
1476 message(SIMPLE, "Sanity checks for the upgrade fails, checks messages"
1477 " and correct them before rerunning upgradeprovision")
1480 # Let's see provision parameters
1481 print_provision_key_parameters(names)
1483 # 5) With all this information let's create a fresh new provision used as
1485 message(SIMPLE, "Creating a reference provision")
1486 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1487 prefix="referenceprovision")
1488 newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
1493 # We need to get a list of object which SD is directly computed from
1494 # defaultSecurityDescriptor.
1495 # This will allow us to know which object we can rebuild the SD in case
1496 # of change of the parent's SD or of the defaultSD.
1497 # Get file paths of this new provision
1498 newpaths = get_paths(param, targetdir=provisiondir)
1499 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1500 new_ldbs.startTransactions()
1502 # 8) Populate some associative array to ease the update process
1503 # List of attribute which are link and backlink
1504 populate_links(new_ldbs.sam, names.schemadn)
1505 # List of attribute with ASN DN synthax)
1506 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1508 update_privilege(newpaths.private_dir, paths.private_dir)
1510 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1511 # Do some modification on sam.ldb
1512 ldbs.groupedCommit()
1513 new_ldbs.groupedCommit()
1516 if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1518 # Starting from alpha9 we can consider that the structure is quite ok
1519 # and that we should do only dela
1520 delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message)
1523 simple_update_basesamdb(newpaths, paths, names)
1524 ldbs = get_ldbs(paths, creds, session, lp)
1525 removeProvisionUSN(ldbs.sam)
1527 ldbs.startTransactions()
1528 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1529 new_ldbs.startTransactions()
1532 schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
1533 serverdn=str(names.serverdn))
1537 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1539 message(SIMPLE, "Rollbacking every changes. Check the reason"
1541 message(SIMPLE, "In any case your system as it was before"
1543 ldbs.groupedRollback()
1544 new_ldbs.groupedRollback()
1545 shutil.rmtree(provisiondir)
1548 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1550 message(SIMPLE, "Update machine account")
1551 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1553 # 16) SD should be created with admin but as some previous acl were so wrong
1554 # that admin can't modify them we have first to recreate them with the good
1555 # form but with system account and then give the ownership to admin ...
1556 if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1557 message(SIMPLE, "Fixing old povision SD")
1558 fix_partition_sd(ldbs.sam, names)
1559 rebuild_sd(ldbs.sam, names)
1561 # We calculate the max USN before recalculating the SD because we might
1562 # touch object that have been modified after a provision and we do not
1563 # want that the next upgradeprovision thinks that it has a green light
1567 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1569 # 18) We rebuild SD only if defaultSecurityDescriptor is modified
1570 # But in fact we should do it also if one object has its SD modified as
1571 # child might need rebuild
1573 message(SIMPLE, "Updating SD")
1574 ldbs.sam.set_session_info(adm_session)
1575 # Alpha10 was a bit broken still
1576 if re.match(r'.*alpha(\d|10)', str(oem)):
1577 fix_partition_sd(ldbs.sam, names)
1578 rebuild_sd(ldbs.sam, names)
1581 # Now we are quite confident in the recalculate process of the SD, we make
1583 # Also the check must be done in a clever way as for the moment we just
1585 if opts.debugchangesd:
1586 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1589 updateOEMInfo(ldbs.sam, str(names.rootdn))
1591 check_for_DNS(newpaths.private_dir, paths.private_dir)
1593 if lastProvisionUSNs is not None:
1594 update_provision_usn(ldbs.sam, minUSN, maxUSN)
1595 if opts.full and (names.policyid is None or names.policyid_dc is None):
1596 update_policyids(names, ldbs.sam)
1597 if opts.full or opts.resetfileacl:
1599 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1600 except ProvisioningError, e:
1601 message(ERROR, "The policy for domain controller is missing,"
1602 " you should restart upgradeprovision with --full")
1605 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1606 except ProvisioningError, e:
1607 message(ERROR, "The policy for domain controller is missing,"
1608 " you should restart upgradeprovision with --full")
1609 ldbs.groupedCommit()
1610 new_ldbs.groupedCommit()
1611 message(SIMPLE, "Upgrade finished !")
1612 # remove reference provision now that everything is done !
1613 shutil.rmtree(provisiondir)
1614 except StandardError, err:
1615 message(ERROR,"A problem has occured when trying to upgrade your provision,"
1616 " a full backup is located at %s" % backupdir)
1618 (typ, val, tb) = sys.exc_info()
1619 traceback.print_exception(typ, val, tb)