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":
522 for klass in reference[0].get("objectClass"):
523 if str(klass).lower == "ridset":
526 if delta.get("objectSid"):
527 sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
528 m = re.match(r".*-(\d+)$", sid)
529 if m and int(m.group(1))>999:
530 delta.remove("objectSid")
531 for att in hashAttrNotCopied.keys():
533 for att in backlinked:
535 depend_on_yettobecreated = None
536 for att in dn_syntax_att:
537 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
539 if depend_on_yet_tobecreated is not None:
540 message(CHANGE, "Object %s depends on %s in attribute %s,"
541 "delaying the creation" % (dn,
542 depend_on_yet_tobecreated, att))
547 message(CHANGE,"Object %s will be added" % dn)
548 samdb.add(delta, ["relax:0"])
550 message(CHANGE,"Object %s was skipped" % dn)
554 def gen_dn_index_hash(listMissing):
555 """Generate a hash associating the DN to its creation order
557 :param listMissing: List of DN
558 :return: Hash with DN as keys and creation order as values"""
560 for i in range(0, len(listMissing)):
561 hash[str(listMissing[i]).lower()] = i
564 def add_deletedobj_containers(ref_samdb, samdb, names):
565 """Add the object containter: CN=Deleted Objects
567 This function create the container for each partition that need one and
568 then reference the object into the root of the partition
570 :param ref_samdb: Ldb object representing the SAM db of the reference
572 :param samdb: Ldb object representing the SAM db of the upgraded provision
573 :param names: List of key provision parameters"""
576 wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
577 partitions = [str(names.rootdn), str(names.configdn)]
578 for part in partitions:
579 ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
580 base=part, scope=SCOPE_SUBTREE,
582 controls=["show_deleted:0"])
583 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
584 base=part, scope=SCOPE_SUBTREE,
586 controls=["show_deleted:0"])
587 if len(ref_delObjCnt) > len(delObjCnt):
588 reference = ref_samdb.search(expression="cn=Deleted Objects",
589 base=part, scope=SCOPE_SUBTREE,
590 controls=["show_deleted:0"])
592 delta = samdb.msg_diff(empty, reference[0])
594 delta.dn = Dn(samdb, str(reference[0]["dn"]))
595 for att in hashAttrNotCopied.keys():
600 res = samdb.search(expression="(objectClass=*)", base=part,
602 attrs=["dn", "wellKnownObjects"])
604 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
608 wko = res[0]["wellKnownObjects"]
610 # The wellKnownObject that we want to add.
612 if str(o) == targetWKO:
614 listwko.append(str(o))
617 listwko.append(targetWKO)
620 delta.dn = Dn(samdb, str(res[0]["dn"]))
621 delta["wellKnownObjects"] = MessageElement(listwko,
626 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
627 """Add the missing object whose DN is the list
629 The function add the object if the objects on which it depends are
632 :param ref_samdb: Ldb object representing the SAM db of the reference
634 :param samdb: Ldb object representing the SAM db of the upgraded
636 :param dn: DN of the object to be added
637 :param names: List of key provision parameters
638 :param basedn: DN of the partition to be updated
639 :param list: List of DN to be added in the upgraded provision"""
644 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
646 listMissing = listDefered
648 hashMissing = gen_dn_index_hash(listMissing)
649 for dn in listMissing:
650 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
654 # DN can't be created because it depends on some
655 # other DN in the list
656 listDefered.append(dn)
657 if len(listDefered) != 0:
658 raise ProvisioningError("Unable to insert missing elements:" \
659 "circular references")
661 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
662 """This function handle updates on links
664 :param samdb: An LDB object pointing to the updated provision
665 :param att: Attribute to update
666 :param basedn: The root DN of the provision
667 :param dn: The DN of the inspected object
668 :param value: The value of the attribute
669 :param ref_value: The value of this attribute in the reference provision
670 :param delta: The MessageElement object that will be applied for
671 transforming the current provision"""
673 res = samdb.search(expression="dn=%s" % dn, base=basedn,
674 controls=["search_options:1:2", "reveal:1"],
682 newlinklist.extend(value)
686 # for w2k domain level the reveal won't reveal anything ...
687 # it means that we can readd links that were removed on purpose ...
688 # Also this function in fact just accept add not removal
690 for e in res[0][att]:
691 if not hash.has_key(e):
692 # We put in the blacklist all the element that are in the "revealed"
693 # result and not in the "standard" result
694 # This element are links that were removed before and so that
695 # we don't wan't to readd
699 if not blacklist.has_key(e) and not hash.has_key(e):
700 newlinklist.append(str(e))
703 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
708 msg_elt_flag_strs = {
709 ldb.FLAG_MOD_ADD: "MOD_ADD",
710 ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
711 ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
714 def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
715 """ This function updates the object that are already present in the
718 :param ref_samdb: An LDB object pointing to the reference provision
719 :param samdb: An LDB object pointing to the updated provision
720 :param basedn: A string with the value of the base DN for the provision
722 :param listPresent: A list of object that is present in the provision
723 :param usns: A list of USN range modified by previous provision and
725 :param invocationid: The value of the invocationid for the current DC"""
728 # This hash is meant to speedup lookup of attribute name from an oid,
729 # it's for the replPropertyMetaData handling
731 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
732 controls=["search_options:1:2"], attrs=["attributeID",
736 strDisplay = str(e.get("lDAPDisplayName"))
737 hash_oid_name[str(e.get("attributeID"))] = strDisplay
739 msg = "Unable to insert missing elements: circular references"
740 raise ProvisioningError(msg)
743 controls = ["search_options:1:2", "sd_flags:1:2"]
744 for dn in listPresent:
745 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
748 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
749 scope=SCOPE_SUBTREE, controls=controls)
752 (str(current[0].dn) != str(reference[0].dn)) and
753 (str(current[0].dn).upper() == str(reference[0].dn).upper())
755 message(CHANGE, "Name are the same but case change,"\
756 "let's rename %s to %s" % (str(current[0].dn),
757 str(reference[0].dn)))
758 identic_rename(samdb, reference[0].dn)
759 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
761 controls=["search_options:1:2"])
763 delta = samdb.msg_diff(current[0], reference[0])
765 for att in hashAttrNotCopied.keys():
768 for att in backlinked:
773 if len(delta.items()) > 1 and usns is not None:
774 # Fetch the replPropertyMetaData
775 res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
776 scope=SCOPE_SUBTREE, controls=controls,
777 attrs=["replPropertyMetaData"])
778 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
779 str(res[0]["replPropertyMetaData"])).ctr
783 # We put in this hash only modification
784 # made on the current host
785 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
786 if str(o.originating_invocation_id) == str(invocationid):
787 # Note we could just use 1 here
788 hash_attr_usn[att] = o.originating_usn
790 hash_attr_usn[att] = -1
797 # We have updated by provision usn information so let's exploit
798 # replMetadataProperties
799 if att in forwardlinked:
800 handle_links(samdb, att, basedn, current[0]["dn"],
801 current[0][att], reference[0][att], delta)
803 if isFirst == 0 and len(delta.items())>1:
805 txt = "%s\n" % (str(dn))
807 # There is always a dn attribute after a msg_diff
809 if att == "rIDAvailablePool":
812 if att == "objectSid":
815 if att == "creationTime":
818 if att == "oEMInformation":
821 if att == "msDs-KeyVersionNumber":
822 # This is the kvno of the computer/user it's a very bad
826 if handle_special_case(att, delta, reference, current, usns):
827 # This attribute is "complicated" to handle and handling
828 # was done in handle_special_case
830 attrUSN = hash_attr_usn.get(att)
831 if att == "forceLogoff" and attrUSN is None:
838 # This attribute was last modified by another DC forget
840 message(CHANGE, "%sAttribute: %s has been"
841 "created/modified/deleted by another DC,"
842 " do nothing" % (txt, att ))
846 elif not usn_in_range(int(attrUSN), usns):
847 message(CHANGE, "%sAttribute: %s has been"
848 "created/modified/deleted not during a"
849 " provision or upgradeprovision: current"
850 " usn %d , do nothing" % (txt, att, attrUSN))
855 if att == "defaultSecurityDescriptor":
858 message(CHANGE, "%sAttribute: %s will be modified"
859 "/deleted it was last modified"
860 "during a provision, current usn:"
861 "%d" % (txt, att, attrUSN))
864 message(CHANGE, "%sAttribute: %s will be added because"
865 " it hasn't existed before " % (txt, att))
870 # Old school way of handling things for pre alpha12 upgrade
872 msgElt = delta.get(att)
874 if att == "nTSecurityDescriptor":
881 if not hashOverwrittenAtt.has_key(att):
882 if msgElt.flags() != FLAG_MOD_ADD:
883 if not handle_special_case(att, delta, reference, current,
885 if opts.debugchange or opts.debugall:
887 dump_denied_change(dn, att,
888 msg_elt_flag_strs[msgElt.flags()],
889 current[0][att], reference[0][att])
891 dump_denied_change(dn, att,
892 msg_elt_flag_strs[msgElt.flags()],
893 current[0][att], None)
897 if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
899 elif hashOverwrittenAtt.get(att)==never:
904 if len(delta.items()) >1:
905 attributes=", ".join(delta.keys())
906 message(CHANGE, "%s is different from the reference one, changed"
907 " attributes: %s\n" % (dn, attributes))
913 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs):
914 """Check differences between the reference provision and the upgraded one.
916 It looks for all objects which base DN is name.
918 This function will also add the missing object and update existing object
919 to add or remove attributes that were missing.
921 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
923 :param samdb: An LDB object connected to the sam.ldb of the update
925 :param basedn: String value of the DN of the partition
926 :param names: List of key provision parameters
927 :param schema: A Schema object
928 :param provisionUSNs: The USNs modified by provision/upgradeprovision
938 # Connect to the reference provision and get all the attribute in the
939 # partition referred by name
940 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
941 scope=SCOPE_SUBTREE, attrs=["dn"],
942 controls=["search_options:1:2"])
944 current = samdb.search(expression="objectClass=*", base=basedn,
945 scope=SCOPE_SUBTREE, attrs=["dn"],
946 controls=["search_options:1:2"])
947 # Create a hash for speeding the search of new object
948 for i in range(0, len(reference)):
949 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
951 # Create a hash for speeding the search of existing object in the
953 for i in range(0, len(current)):
954 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
957 for k in hash_new.keys():
958 if not hash.has_key(k):
959 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
960 listMissing.append(hash_new[k])
962 listPresent.append(hash_new[k])
964 # Sort the missing object in order to have object of the lowest level
965 # first (which can be containers for higher level objects)
966 listMissing.sort(dn_sort)
967 listPresent.sort(dn_sort)
969 # The following lines is to load the up to
970 # date schema into our current LDB
971 # a complete schema is needed as the insertion of attributes
972 # and class is done against it
973 # and the schema is self validated
974 samdb.set_schema(schema)
976 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
977 add_deletedobj_containers(ref_samdb, samdb, names)
979 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
980 changed = update_present(ref_samdb, samdb, basedn, listPresent,
981 provisionUSNs, names.invocation)
982 message(SIMPLE, "There are %d changed objects" % (changed))
985 except StandardError, err:
986 message(ERROR, "Exception during upgrade of samdb:")
987 (typ, val, tb) = sys.exc_info()
988 traceback.print_exception(typ, val, tb)
992 def check_updated_sd(ref_sam, cur_sam, names):
993 """Check if the security descriptor in the upgraded provision are the same
996 :param ref_sam: A LDB object connected to the sam.ldb file used as
997 the reference provision
998 :param cur_sam: A LDB object connected to the sam.ldb file used as
1000 :param names: List of key provision parameters"""
1001 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1002 scope=SCOPE_SUBTREE,
1003 attrs=["dn", "nTSecurityDescriptor"],
1004 controls=["search_options:1:2"])
1005 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1006 scope=SCOPE_SUBTREE,
1007 attrs=["dn", "nTSecurityDescriptor"],
1008 controls=["search_options:1:2"])
1010 for i in range(0, len(reference)):
1011 refsd = ndr_unpack(security.descriptor,
1012 str(reference[i]["nTSecurityDescriptor"]))
1013 hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
1016 for i in range(0, len(current)):
1017 key = str(current[i]["dn"]).lower()
1018 if hash.has_key(key):
1019 cursd = ndr_unpack(security.descriptor,
1020 str(current[i]["nTSecurityDescriptor"]))
1021 sddl = cursd.as_sddl(names.domainsid)
1022 if sddl != hash[key]:
1023 txt = get_diff_sddls(hash[key], sddl)
1025 message(CHANGESD, "On object %s ACL is different"
1026 " \n%s" % (current[i]["dn"], txt))
1030 def fix_partition_sd(samdb, names):
1031 """This function fix the SD for partition containers (basedn, configdn, ...)
1032 This is needed because some provision use to have broken SD on containers
1034 :param samdb: An LDB object pointing to the sam of the current provision
1035 :param names: A list of key provision parameters
1037 # First update the SD for the rootdn
1038 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1039 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1040 controls=["search_options:1:2"])
1042 delta.dn = Dn(samdb, str(res[0]["dn"]))
1043 descr = get_domain_descriptor(names.domainsid)
1044 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1045 "nTSecurityDescriptor")
1046 samdb.modify(delta, ["recalculate_sd:0"])
1047 # Then the config dn
1048 res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1049 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1050 controls=["search_options:1:2"])
1052 delta.dn = Dn(samdb, str(res[0]["dn"]))
1053 descr = get_config_descriptor(names.domainsid)
1054 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1055 "nTSecurityDescriptor" )
1056 samdb.modify(delta, ["recalculate_sd:0"])
1057 # Then the schema dn
1058 res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1059 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1060 controls=["search_options:1:2"])
1063 delta.dn = Dn(samdb, str(res[0]["dn"]))
1064 descr = get_schema_descriptor(names.domainsid)
1065 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1066 "nTSecurityDescriptor" )
1067 samdb.modify(delta, ["recalculate_sd:0"])
1069 def rebuild_sd(samdb, names):
1070 """Rebuild security descriptor of the current provision from scratch
1072 During the different pre release of samba4 security descriptors (SD)
1073 were notarly broken (up to alpha11 included)
1074 This function allow to get them back in order, this function make the
1075 assumption that nobody has modified manualy an SD
1076 and so SD can be safely recalculated from scratch to get them right.
1078 :param names: List of key provision parameters"""
1082 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1083 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1084 controls=["search_options:1:2"])
1086 if not (str(obj["dn"]) == str(names.rootdn) or
1087 str(obj["dn"]) == str(names.configdn) or
1088 str(obj["dn"]) == str(names.schemadn)):
1089 hash[str(obj["dn"])] = obj["whenCreated"]
1091 listkeys = hash.keys()
1092 listkeys.sort(dn_sort)
1094 for key in listkeys:
1097 delta.dn = Dn(samdb, key)
1098 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1100 samdb.modify(delta, ["recalculate_sd:0"])
1102 # XXX: We should always catch an explicit exception.
1103 # What could go wrong here?
1104 samdb.transaction_cancel()
1105 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1106 scope=SCOPE_SUBTREE,
1107 attrs=["dn", "nTSecurityDescriptor"],
1108 controls=["search_options:1:2"])
1109 badsd = ndr_unpack(security.descriptor,
1110 str(res[0]["nTSecurityDescriptor"]))
1111 print "bad stuff %s" % badsd.as_sddl(names.domainsid)
1114 def removeProvisionUSN(samdb):
1115 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1116 entry = samdb.search(expression="dn=@PROVISION", base = "",
1117 scope=SCOPE_SUBTREE,
1118 controls=["search_options:1:2"],
1121 empty.dn = entry[0].dn
1122 delta = samdb.msg_diff(entry[0], empty)
1124 delta.dn = entry[0].dn
1127 def remove_stored_generated_attrs(paths, creds, session, lp):
1128 """Remove previously stored constructed attributes
1130 :param paths: List of paths for different provision objects
1131 from the upgraded provision
1132 :param creds: A credential object
1133 :param session: A session object
1134 :param lp: A line parser object
1135 :return: An associative array whose key are the different constructed
1136 attributes and the value the dn where this attributes were found.
1140 def simple_update_basesamdb(newpaths, paths, names):
1141 """Update the provision container db: sam.ldb
1142 This function is aimed at very old provision (before alpha9)
1144 :param newpaths: List of paths for different provision objects
1145 from the reference provision
1146 :param paths: List of paths for different provision objects
1147 from the upgraded provision
1148 :param names: List of key provision parameters"""
1150 message(SIMPLE, "Copy samdb")
1151 shutil.copy(newpaths.samdb, paths.samdb)
1153 message(SIMPLE, "Update partitions filename if needed")
1154 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1155 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1156 usersldb = os.path.join(paths.private_dir, "users.ldb")
1157 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1159 if not os.path.isdir(samldbdir):
1161 os.chmod(samldbdir, 0700)
1162 if os.path.isfile(schemaldb):
1163 shutil.copy(schemaldb, os.path.join(samldbdir,
1164 "%s.ldb"%str(names.schemadn).upper()))
1165 os.remove(schemaldb)
1166 if os.path.isfile(usersldb):
1167 shutil.copy(usersldb, os.path.join(samldbdir,
1168 "%s.ldb"%str(names.rootdn).upper()))
1170 if os.path.isfile(configldb):
1171 shutil.copy(configldb, os.path.join(samldbdir,
1172 "%s.ldb"%str(names.configdn).upper()))
1173 os.remove(configldb)
1176 def update_privilege(ref_private_path, cur_private_path):
1177 """Update the privilege database
1179 :param ref_private_path: Path to the private directory of the reference
1181 :param cur_private_path: Path to the private directory of the current
1182 (and to be updated) provision."""
1183 message(SIMPLE, "Copy privilege")
1184 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1185 os.path.join(cur_private_path, "privilege.ldb"))
1188 def update_samdb(ref_samdb, samdb, names, highestUSN, schema):
1189 """Upgrade the SAM DB contents for all the provision partitions
1191 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1193 :param samdb: An LDB object connected to the sam.ldb of the update
1195 :param names: List of key provision parameters
1196 :param highestUSN: The highest USN modified by provision/upgradeprovision
1198 :param schema: A Schema object that represent the schema of the provision"""
1200 message(SIMPLE, "Starting update of samdb")
1201 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1204 message(SIMPLE, "Update of samdb finished")
1207 message(SIMPLE, "Update failed")
1211 def copyxattrs(dir, refdir):
1212 """ Copy owner, groups, extended ACL and NT acls from
1213 a reference dir to a destination dir
1215 Both dir are supposed to hold the same files
1216 :param dir: Destination dir
1217 :param refdir: Reference directory"""
1220 for root, dirs, files in os.walk(dir, topdown=True):
1222 subdir=root[len(dir):]
1223 ref = os.path.join("%s%s" % (refdir, subdir), name)
1224 statsinfo = os.stat(ref)
1225 tgt = os.path.join(root, name)
1228 os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
1229 # Get the xattr attributes if any
1231 attribute = samba.xattr_native.wrap_getxattr(ref,
1232 xattr.XATTR_NTACL_NAME)
1233 samba.xattr_native.wrap_setxattr(tgt,
1234 xattr.XATTR_NTACL_NAME,
1238 attribute = samba.xattr_native.wrap_getxattr(ref,
1239 "system.posix_acl_access")
1240 samba.xattr_native.wrap_setxattr(tgt,
1241 "system.posix_acl_access",
1246 subdir=root[len(dir):]
1247 ref = os.path.join("%s%s" % (refdir, subdir), name)
1248 statsinfo = os.stat(ref)
1249 tgt = os.path.join(root, name)
1251 os.chown(os.path.join(root, name), statsinfo.st_uid,
1254 attribute = samba.xattr_native.wrap_getxattr(ref,
1255 xattr.XATTR_NTACL_NAME)
1256 samba.xattr_native.wrap_setxattr(tgt,
1257 xattr.XATTR_NTACL_NAME,
1261 attribute = samba.xattr_native.wrap_getxattr(ref,
1262 "system.posix_acl_access")
1263 samba.xattr_native.wrap_setxattr(tgt,
1264 "system.posix_acl_access",
1271 def backup_provision(paths, dir):
1272 """This function backup the provision files so that a rollback
1275 :param paths: Paths to different objects
1276 :param dir: Directory where to store the backup
1279 shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
1280 copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
1281 shutil.copy2(paths.samdb, dir)
1282 shutil.copy2(paths.secrets, dir)
1283 shutil.copy2(paths.idmapdb, dir)
1284 shutil.copy2(paths.privilege, dir)
1285 if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1286 shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1287 shutil.copy2(paths.smbconf, dir)
1288 shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1290 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1291 if not os.path.isdir(samldbdir):
1292 samldbdir = paths.private_dir
1293 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1294 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1295 usersldb = os.path.join(paths.private_dir, "users.ldb")
1296 shutil.copy2(schemaldb, dir)
1297 shutil.copy2(usersldb, dir)
1298 shutil.copy2(configldb, dir)
1300 shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1302 def setup_path(file):
1303 return os.path.join(setup_dir, file)
1305 # Synopsis for updateprovision
1306 # 1) get path related to provision to be update (called current)
1307 # 2) open current provision ldbs
1308 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1310 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1311 # by either upgradeprovision or provision
1312 # 5) creation of a new provision the latest version of provision script
1313 # (called reference)
1314 # 6) get reference provision paths
1315 # 7) open reference provision ldbs
1316 # 8) setup helpers data that will help the update process
1317 # 9) update the privilege ldb by copying the one of referecence provision to
1318 # the current provision
1319 # 10)get the oemInfo field, this field contains information about the different
1320 # provision that have been done
1321 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1322 # integer) or none of this the following things are done
1323 # A) When alpha9 or alphaxx is present
1324 # The base sam.ldb file is updated by looking at the difference between
1325 # referrence one and the current one. Everything is copied with the
1326 # exception of lastProvisionUSN attributes.
1327 # B) Other case (it reflect that that provision was done before alpha9)
1328 # The base sam.ldb of the reference provision is copied over
1329 # the current one, if necessary ldb related to partitions are moved
1331 # The highest used USN is fetched so that changed by upgradeprovision
1332 # usn can be tracked
1333 # 12)A Schema object is created, it will be used to provide a complete
1334 # schema to current provision during update (as the schema of the
1335 # current provision might not be complete and so won't allow some
1336 # object to be created)
1337 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1338 # 14)The secrets db is updated by pull all the difference from the reference
1339 # provision into the current provision
1340 # 15)As the previous step has most probably modified the password stored in
1341 # in secret for the current DC, a new password is generated,
1342 # the kvno is bumped and the entry in samdb is also updated
1343 # 16)For current provision older than alpha9, we must fix the SD a little bit
1344 # administrator to update them because SD used to be generated with the
1345 # system account before alpha9.
1346 # 17)The highest usn modified so far is searched in the database it will be
1347 # the upper limit for usn modified during provision.
1348 # This is done before potential SD recalculation because we do not want
1349 # SD modified during recalculation to be marked as modified during provision
1350 # (and so possibly remplaced at next upgradeprovision)
1351 # 18)Rebuilt SD if the flag indicate to do so
1352 # 19)Check difference between SD of reference provision and those of the
1353 # current provision. The check is done by getting the sddl representation
1354 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1355 # Each part is verified separetly, for dacl and sacl ACL is splited into
1356 # ACEs and each ACE is verified separately (so that a permutation in ACE
1357 # didn't raise as an error).
1358 # 20)The oemInfo field is updated to add information about the fact that the
1359 # provision has been updated by the upgradeprovision version xxx
1360 # (the version is the one obtained when starting samba with the --version
1362 # 21)Check if the current provision has all the settings needed for dynamic
1363 # DNS update to work (that is to say the provision is newer than
1364 # january 2010). If not dns configuration file from reference provision
1365 # are copied in a sub folder and the administrator is invited to
1366 # do what is needed.
1367 # 22)If the lastProvisionUSN attribute was present it is updated to add
1368 # the range of usns modified by the current upgradeprovision
1371 # About updating the sam DB
1372 # The update takes place in update_partition function
1373 # This function read both current and reference provision and list all
1374 # the available DN of objects
1375 # If the string representation of a DN in reference provision is
1376 # equal to the string representation of a DN in current provision
1377 # (without taking care of case) then the object is flaged as being
1378 # present. If the object is not present in current provision the object
1379 # is being flaged as missing in current provision. Object present in current
1380 # provision but not in reference provision are ignored.
1381 # Once the list of objects present and missing is done, the deleted object
1382 # containers are created in the differents partitions (if missing)
1384 # Then the function add_missing_entries is called
1385 # This function will go through the list of missing entries by calling
1386 # add_missing_object for the given object. If this function returns 0
1387 # it means that the object needs some other object in order to be created
1388 # The object is reappended at the end of the list to be created later
1389 # (and preferably after all the needed object have been created)
1390 # The function keeps on looping on the list of object to be created until
1391 # it's empty or that the number of defered creation is equal to the number
1392 # of object that still needs to be created.
1394 # The function add_missing_object will first check if the object can be created.
1395 # That is to say that it didn't depends other not yet created objects
1396 # If requisit can't be fullfilled it exists with 0
1397 # Then it will try to create the missing entry by creating doing
1398 # an ldb_message_diff between the object in the reference provision and
1400 # This resulting object is filtered to remove all the back link attribute
1401 # (ie. memberOf) as they will be created by the other linked object (ie.
1402 # the one with the member attribute)
1403 # All attributes specified in the hashAttrNotCopied associative array are
1404 # also removed it's most of the time generated attributes
1406 # After missing entries have been added the update_partition function will
1407 # take care of object that exist but that need some update.
1408 # In order to do so the function update_present is called with the list
1409 # of object that are present in both provision and that might need an update.
1411 # This function handle first case mismatch so that the DN in the current
1412 # provision have the same case as in reference provision
1414 # It will then construct an associative array consiting of attributes as
1415 # key and invocationid as value( if the originating invocation id is
1416 # different from the invocation id of the current DC the value is -1 instead).
1418 # If the range of provision modified attributes is present, the function will
1419 # use the replMetadataProperty update method which is the following:
1420 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1421 # creationTime, msDs-KeyVersionNumber, oEMInformation
1422 # Check for each attribute if its usn is within one of the modified by
1423 # provision range and if its originating id is the invocation id of the
1424 # current DC, then validate the update from reference to current.
1425 # If not or if there is no replMetatdataProperty for this attribute then we
1427 # Otherwise (case the range of provision modified attribute is not present) it
1428 # use the following process:
1429 # All attributes that need to be added are accepted at the exeption of those
1430 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1431 # correct flags specified.
1432 # For attributes that need to be modified or removed, a check is performed
1433 # in OverwrittenAtt, if the attribute is present and the modification flag
1434 # (remove, delete) is one of those listed for this attribute then modification
1435 # is accepted. For complicated handling of attribute update, the control is passed
1436 # to handle_special_case
1440 if __name__ == '__main__':
1441 global defSDmodified
1442 defSDmodified = False
1443 # From here start the big steps of the program
1444 # 1) First get files paths
1445 paths = get_paths(param, smbconf=smbconf)
1446 paths.setup = setup_dir
1447 # Get ldbs with the system session, it is needed for searching
1448 # provision parameters
1449 session = system_session()
1451 # This variable will hold the last provision USN once if it exists.
1454 ldbs = get_ldbs(paths, creds, session, lp)
1455 backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1456 prefix="backupprovision")
1457 backup_provision(paths, backupdir)
1459 ldbs.startTransactions()
1461 # 3) Guess all the needed names (variables in fact) from the current
1463 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1466 lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1467 if lastProvisionUSNs is not None:
1469 "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
1471 # Objects will be created with the admin session
1472 # (not anymore system session)
1473 adm_session = admin_session(lp, str(names.domainsid))
1474 # So we reget handle on objects
1475 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1477 if not sanitychecks(ldbs.sam, names):
1478 message(SIMPLE, "Sanity checks for the upgrade fails, checks messages"
1479 " and correct them before rerunning upgradeprovision")
1482 # Let's see provision parameters
1483 print_provision_key_parameters(names)
1485 # 5) With all this information let's create a fresh new provision used as
1487 message(SIMPLE, "Creating a reference provision")
1488 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1489 prefix="referenceprovision")
1490 newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
1495 # We need to get a list of object which SD is directly computed from
1496 # defaultSecurityDescriptor.
1497 # This will allow us to know which object we can rebuild the SD in case
1498 # of change of the parent's SD or of the defaultSD.
1499 # Get file paths of this new provision
1500 newpaths = get_paths(param, targetdir=provisiondir)
1501 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1502 new_ldbs.startTransactions()
1504 # 8) Populate some associative array to ease the update process
1505 # List of attribute which are link and backlink
1506 populate_links(new_ldbs.sam, names.schemadn)
1507 # List of attribute with ASN DN synthax)
1508 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1510 update_privilege(newpaths.private_dir, paths.private_dir)
1512 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1513 # Do some modification on sam.ldb
1514 ldbs.groupedCommit()
1515 new_ldbs.groupedCommit()
1518 if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1520 # Starting from alpha9 we can consider that the structure is quite ok
1521 # and that we should do only dela
1522 delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message)
1525 simple_update_basesamdb(newpaths, paths, names)
1526 ldbs = get_ldbs(paths, creds, session, lp)
1527 removeProvisionUSN(ldbs.sam)
1529 ldbs.startTransactions()
1530 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1531 new_ldbs.startTransactions()
1534 schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
1535 serverdn=str(names.serverdn))
1539 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1541 message(SIMPLE, "Rollbacking every changes. Check the reason"
1543 message(SIMPLE, "In any case your system as it was before"
1545 ldbs.groupedRollback()
1546 new_ldbs.groupedRollback()
1547 shutil.rmtree(provisiondir)
1550 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1552 message(SIMPLE, "Update machine account")
1553 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1555 # 16) SD should be created with admin but as some previous acl were so wrong
1556 # that admin can't modify them we have first to recreate them with the good
1557 # form but with system account and then give the ownership to admin ...
1558 if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1559 message(SIMPLE, "Fixing old povision SD")
1560 fix_partition_sd(ldbs.sam, names)
1561 rebuild_sd(ldbs.sam, names)
1563 # We calculate the max USN before recalculating the SD because we might
1564 # touch object that have been modified after a provision and we do not
1565 # want that the next upgradeprovision thinks that it has a green light
1569 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1571 # 18) We rebuild SD only if defaultSecurityDescriptor is modified
1572 # But in fact we should do it also if one object has its SD modified as
1573 # child might need rebuild
1575 message(SIMPLE, "Updating SD")
1576 ldbs.sam.set_session_info(adm_session)
1577 # Alpha10 was a bit broken still
1578 if re.match(r'.*alpha(\d|10)', str(oem)):
1579 fix_partition_sd(ldbs.sam, names)
1580 rebuild_sd(ldbs.sam, names)
1583 # Now we are quite confident in the recalculate process of the SD, we make
1585 # Also the check must be done in a clever way as for the moment we just
1587 if opts.debugchangesd:
1588 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1591 updateOEMInfo(ldbs.sam, str(names.rootdn))
1593 check_for_DNS(newpaths.private_dir, paths.private_dir)
1595 if lastProvisionUSNs is not None:
1596 update_provision_usn(ldbs.sam, minUSN, maxUSN)
1597 if opts.full and (names.policyid is None or names.policyid_dc is None):
1598 update_policyids(names, ldbs.sam)
1599 if opts.full or opts.resetfileacl:
1601 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1602 except ProvisioningError, e:
1603 message(ERROR, "The policy for domain controller is missing,"
1604 " you should restart upgradeprovision with --full")
1607 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1608 except ProvisioningError, e:
1609 message(ERROR, "The policy for domain controller is missing,"
1610 " you should restart upgradeprovision with --full")
1611 ldbs.groupedCommit()
1612 new_ldbs.groupedCommit()
1613 message(SIMPLE, "Upgrade finished !")
1614 # remove reference provision now that everything is done !
1615 shutil.rmtree(provisiondir)
1616 except StandardError, err:
1617 message(ERROR,"A problem has occured when trying to upgrade your provision,"
1618 " a full backup is located at %s" % backupdir)
1620 (typ, val, tb) = sys.exc_info()
1621 traceback.print_exception(typ, val, tb)