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,
60 search_constructed_attrs_stored,
61 increment_calculated_keyversion_number)
63 replace=2**FLAG_MOD_REPLACE
65 delete=2**FLAG_MOD_DELETE
69 # Will be modified during provision to tell if default sd has been modified
72 #Errors are always logged
74 __docformat__ = "restructuredText"
76 # Attributes that are never copied from the reference provision (even if they
77 # do not exist in the destination object).
78 # This is most probably because they are populated automatcally when object is
80 # This also apply to imported object from reference provision
81 hashAttrNotCopied = { "dn": 1, "whenCreated": 1, "whenChanged": 1,
82 "objectGUID": 1, "uSNCreated": 1,
83 "replPropertyMetaData": 1, "uSNChanged": 1,
84 "parentGUID": 1, "objectCategory": 1,
85 "distinguishedName": 1, "nTMixedDomain": 1,
86 "showInAdvancedViewOnly": 1, "instanceType": 1,
87 "msDS-Behavior-Version":1, "nextRid":1, "cn": 1,
88 "versionNumber":1, "lmPwdHistory":1, "pwdLastSet": 1,
89 "ntPwdHistory":1, "unicodePwd":1,"dBCSPwd":1,
90 "supplementalCredentials":1, "gPCUserExtensionNames":1,
91 "gPCMachineExtensionNames":1,"maxPwdAge":1, "secret":1,
92 "possibleInferiors":1, "privilege":1,
95 # Usually for an object that already exists we do not overwrite attributes as
96 # they might have been changed for good reasons. Anyway for a few of them it's
97 # mandatory to replace them otherwise the provision will be broken somehow.
98 # But for attribute that are just missing we do not have to specify them as the default
99 # behavior is to add missing attribute
100 hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace,
101 "systemOnly":replace, "searchFlags":replace,
102 "mayContain":replace, "systemFlags":replace+add,
103 "description":replace, "operatingSystemVersion":replace,
104 "adminPropertyPages":replace, "groupType":replace,
105 "wellKnownObjects":replace, "privilege":never,
106 "defaultSecurityDescriptor": replace,
107 "rIDAvailablePool": never,
108 "defaultSecurityDescriptor": replace + add }
112 forwardlinked = set()
114 def define_what_to_log(opts):
118 if opts.debugchangesd:
119 what = what | CHANGESD
122 if opts.debugprovision:
123 what = what | PROVISION
125 what = what | CHANGEALL
129 parser = optparse.OptionParser("provision [options]")
130 sambaopts = options.SambaOptions(parser)
131 parser.add_option_group(sambaopts)
132 parser.add_option_group(options.VersionOptions(parser))
133 credopts = options.CredentialsOptions(parser)
134 parser.add_option_group(credopts)
135 parser.add_option("--setupdir", type="string", metavar="DIR",
136 help="directory with setup files")
137 parser.add_option("--debugprovision", help="Debug provision", action="store_true")
138 parser.add_option("--debugguess", action="store_true",
139 help="Print information on what is different but won't be changed")
140 parser.add_option("--debugchange", action="store_true",
141 help="Print information on what is different but won't be changed")
142 parser.add_option("--debugchangesd", action="store_true",
143 help="Print information security descriptors differences")
144 parser.add_option("--debugall", action="store_true",
145 help="Print all available information (very verbose)")
146 parser.add_option("--resetfileacl", action="store_true",
147 help="Force a reset on filesystem acls in sysvol / netlogon share")
148 parser.add_option("--full", action="store_true",
149 help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
151 opts = parser.parse_args()[0]
153 handler = logging.StreamHandler(sys.stdout)
154 upgrade_logger = logging.getLogger("upgradeprovision")
155 upgrade_logger.setLevel(logging.INFO)
157 upgrade_logger.addHandler(handler)
159 provision_logger = logging.getLogger("provision")
160 provision_logger.addHandler(handler)
162 whatToLog = define_what_to_log(opts)
164 def message(what, text):
165 """Print a message if this message type has been selected to be printed
167 :param what: Category of the message
168 :param text: Message to print """
169 if (whatToLog & what) or what <= 0:
170 upgrade_logger.info("%s", text)
172 if len(sys.argv) == 1:
173 opts.interactive = True
174 lp = sambaopts.get_loadparm()
175 smbconf = lp.configfile
177 creds = credopts.get_credentials(lp)
178 creds.set_kerberos_state(DONT_USE_KERBEROS)
179 setup_dir = opts.setupdir
180 if setup_dir is None:
181 setup_dir = find_setup_dir()
185 def check_for_DNS(refprivate, private):
186 """Check if the provision has already the requirement for dynamic dns
188 :param refprivate: The path to the private directory of the reference
190 :param private: The path to the private directory of the upgraded
193 spnfile = "%s/spn_update_list" % private
194 namedfile = lp.get("dnsupdate:path")
197 namedfile = "%s/named.conf.update" % private
199 if not os.path.exists(spnfile):
200 shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
202 destdir = "%s/new_dns" % private
203 dnsdir = "%s/dns" % private
205 if not os.path.exists(namedfile):
206 if not os.path.exists(destdir):
208 if not os.path.exists(dnsdir):
210 shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
211 shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
212 message(SIMPLE, "It seems that you provision didn't integrate new rules "
213 "for dynamic dns update of domain related entries")
214 message(SIMPLE, "A copy of the new bind configuration files and "
215 "template as been put in %s, you should read them and configure dynamic "
216 " dns update" % destdir)
219 def populate_links(samdb, schemadn):
220 """Populate an array with all the back linked attributes
222 This attributes that are modified automaticaly when
223 front attibutes are changed
225 :param samdb: A LDB object for sam.ldb file
226 :param schemadn: DN of the schema for the partition"""
227 linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
228 backlinked.extend(linkedAttHash.values())
229 for t in linkedAttHash.keys():
233 def populate_dnsyntax(samdb, schemadn):
234 """Populate an array with all the attributes that have DN synthax
237 :param samdb: A LDB object for sam.ldb file
238 :param schemadn: DN of the schema for the partition"""
239 res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
240 str(schemadn)), scope=SCOPE_SUBTREE,
241 attrs=["lDAPDisplayName"])
243 dn_syntax_att.append(elem["lDAPDisplayName"])
246 def sanitychecks(samdb, names):
247 """Make some checks before trying to update
249 :param samdb: An LDB object opened on sam.ldb
250 :param names: list of key provision parameters
251 :return: Status of check (1 for Ok, 0 for not Ok) """
252 res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
253 scope=SCOPE_SUBTREE, attrs=["dn"],
254 controls=["search_options:1:2"])
256 print "No DC found, your provision is most probably hardly broken !"
259 print "Found %d domain controllers, for the moment upgradeprovision" \
260 "is not able to handle upgrade on domain with more than one DC, please demote" \
261 " the other(s) DC(s) before upgrading" % len(res)
267 def print_provision_key_parameters(names):
268 """Do a a pretty print of provision parameters
270 :param names: list of key provision parameters """
271 message(GUESS, "rootdn :" + str(names.rootdn))
272 message(GUESS, "configdn :" + str(names.configdn))
273 message(GUESS, "schemadn :" + str(names.schemadn))
274 message(GUESS, "serverdn :" + str(names.serverdn))
275 message(GUESS, "netbiosname :" + names.netbiosname)
276 message(GUESS, "defaultsite :" + names.sitename)
277 message(GUESS, "dnsdomain :" + names.dnsdomain)
278 message(GUESS, "hostname :" + names.hostname)
279 message(GUESS, "domain :" + names.domain)
280 message(GUESS, "realm :" + names.realm)
281 message(GUESS, "invocationid:" + names.invocation)
282 message(GUESS, "policyguid :" + names.policyid)
283 message(GUESS, "policyguiddc:" + str(names.policyid_dc))
284 message(GUESS, "domainsid :" + str(names.domainsid))
285 message(GUESS, "domainguid :" + names.domainguid)
286 message(GUESS, "ntdsguid :" + names.ntdsguid)
287 message(GUESS, "domainlevel :" + str(names.domainlevel))
290 def handle_special_case(att, delta, new, old, usn):
291 """Define more complicate update rules for some attributes
293 :param att: The attribute to be updated
294 :param delta: A messageElement object that correspond to the difference
295 between the updated object and the reference one
296 :param new: The reference object
297 :param old: The Updated object
298 :param usn: The highest usn modified by a previous (upgrade)provision
299 :return: True to indicate that the attribute should be kept, False for
302 flag = delta.get(att).flags()
303 # We do most of the special case handle if we do not have the
304 # highest usn as otherwise the replPropertyMetaData will guide us more
307 if (att == "member" and flag == FLAG_MOD_REPLACE):
311 for elem in old[0][att]:
312 hash[str(elem).lower()]=1
313 newval.append(str(elem))
315 for elem in new[0][att]:
316 if not hash.has_key(str(elem).lower()):
318 newval.append(str(elem))
320 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
325 if (att in ("gPLink", "gPCFileSysPath") and
326 flag == FLAG_MOD_REPLACE and
327 str(new[0].dn).lower() == str(old[0].dn).lower()):
331 if att == "forceLogoff":
332 ref=0x8000000000000000
333 oldval=int(old[0][att][0])
334 newval=int(new[0][att][0])
335 ref == old and ref == abs(new)
338 if att in ("adminDisplayName", "adminDescription"):
341 if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (names.schemadn)
342 and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
345 if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
346 att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
349 if (str(old[0].dn) == "%s" % (str(names.rootdn))
350 and att == "subRefs" and flag == FLAG_MOD_REPLACE):
353 if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
356 # This is a bit of special animal as we might have added
357 # already SPN entries to the list that has to be modified
358 # So we go in detail to try to find out what has to be added ...
359 if (att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):
363 for elem in old[0][att]:
365 newval.append(str(elem))
367 for elem in new[0][att]:
368 if not hash.has_key(str(elem)):
370 newval.append(str(elem))
372 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
379 def dump_denied_change(dn, att, flagtxt, current, reference):
380 """Print detailed information about why a changed is denied
382 :param dn: DN of the object which attribute is denied
383 :param att: Attribute that was supposed to be upgraded
384 :param flagtxt: Type of the update that should be performed
385 (add, change, remove, ...)
386 :param current: Value(s) of the current attribute
387 :param reference: Value(s) of the reference attribute"""
389 message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
390 +" is not allowed to be changed/removed, I discard this change")
391 if att != "objectSid" :
393 for e in range(0, len(current)):
394 message(CHANGE, "old %d : %s" % (i, str(current[e])))
396 if reference is not None:
398 for e in range(0, len(reference)):
399 message(CHANGE, "new %d : %s" % (i, str(reference[e])))
402 message(CHANGE, "old : %s" % ndr_unpack(security.dom_sid, current[0]))
403 message(CHANGE, "new : %s" % ndr_unpack(security.dom_sid, reference[0]))
406 def handle_special_add(samdb, dn, names):
407 """Handle special operation (like remove) on some object needed during
410 This is mostly due to wrong creation of the object in previous provision.
411 :param samdb: An Ldb object representing the SAM database
412 :param dn: DN of the object to inspect
413 :param names: list of key provision parameters
417 objDn = Dn(samdb, "CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn)
419 #This entry was misplaced lets remove it if it exists
420 dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
423 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn)
425 #This entry was misplaced lets remove it if it exists
426 dntoremove = "CN=Certificate Service DCOM Access,"\
427 "CN=Users, %s" % names.rootdn
430 objDn = Dn(samdb, "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn)
432 #This entry was misplaced lets remove it if it exists
433 dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
435 objDn = Dn(samdb, "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn)
437 #This entry was misplaced lets remove it if it exists
438 dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
440 objDn = Dn(samdb,"CN=System,CN=WellKnown Security Principals,"
441 "CN=Configuration,%s" % names.rootdn)
443 oldDn = Dn(samdb,"CN=Well-Known-Security-Id-System,"
444 "CN=WellKnown Security Principals,"
445 "CN=Configuration,%s" % names.rootdn)
447 res = samdb.search(expression="(dn=%s)" % oldDn,
448 base=str(names.rootdn),
449 scope=SCOPE_SUBTREE, attrs=["dn"],
450 controls=["search_options:1:2"])
452 message(CHANGE, "Existing object %s must be replaced by %s,"
453 "Renaming old object" % (str(oldDn), str(dn)))
454 samdb.rename(oldDn, objDn)
458 if dntoremove is not None:
459 res = samdb.search(expression="(dn=%s)" % dntoremove,
460 base=str(names.rootdn),
461 scope=SCOPE_SUBTREE, attrs=["dn"],
462 controls=["search_options:1:2"])
464 message(CHANGE, "Existing object %s must be replaced by %s,"
465 "removing old object" % (dntoremove, str(dn)))
466 samdb.delete(res[0]["dn"])
470 def check_dn_nottobecreated(hash, index, listdn):
471 """Check if one of the DN present in the list has a creation order
472 greater than the current.
474 Hash is indexed by dn to be created, with each key
475 is associated the creation order.
477 First dn to be created has the creation order 0, second has 1, ...
478 Index contain the current creation order
480 :param hash: Hash holding the different DN of the object to be
482 :param index: Current creation order
483 :param listdn: List of DNs on which the current DN depends on
484 :return: None if the current object do not depend on other
485 object or if all object have been created before."""
489 key = str(dn).lower()
490 if hash.has_key(key) and hash[key] > index:
496 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
497 """Add a new object if the dependencies are satisfied
499 The function add the object if the object on which it depends are already
502 :param ref_samdb: Ldb object representing the SAM db of the reference
504 :param samdb: Ldb object representing the SAM db of the upgraded
506 :param dn: DN of the object to be added
507 :param names: List of key provision parameters
508 :param basedn: DN of the partition to be updated
509 :param hash: Hash holding the different DN of the object to be
511 :param index: Current creation order
512 :return: True if the object was created False otherwise"""
514 if handle_special_add(samdb, dn, names):
516 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
517 scope=SCOPE_SUBTREE, controls=["search_options:1:2"])
519 delta = samdb.msg_diff(empty, reference[0])
523 if str(reference[0].get("cn")) == "RID Set":
524 for klass in reference[0].get("objectClass"):
525 if str(klass).lower == "ridset":
528 if delta.get("objectSid"):
529 sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
530 m = re.match(r".*-(\d+)$", sid)
531 if m and int(m.group(1))>999:
532 delta.remove("objectSid")
533 for att in hashAttrNotCopied.keys():
535 for att in backlinked:
537 depend_on_yettobecreated = None
538 for att in dn_syntax_att:
539 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
541 if depend_on_yet_tobecreated is not None:
542 message(CHANGE, "Object %s depends on %s in attribute %s,"
543 "delaying the creation" % (dn,
544 depend_on_yet_tobecreated, att))
549 message(CHANGE,"Object %s will be added" % dn)
550 samdb.add(delta, ["relax:0"])
552 message(CHANGE,"Object %s was skipped" % dn)
556 def gen_dn_index_hash(listMissing):
557 """Generate a hash associating the DN to its creation order
559 :param listMissing: List of DN
560 :return: Hash with DN as keys and creation order as values"""
562 for i in range(0, len(listMissing)):
563 hash[str(listMissing[i]).lower()] = i
566 def add_deletedobj_containers(ref_samdb, samdb, names):
567 """Add the object containter: CN=Deleted Objects
569 This function create the container for each partition that need one and
570 then reference the object into the root of the partition
572 :param ref_samdb: Ldb object representing the SAM db of the reference
574 :param samdb: Ldb object representing the SAM db of the upgraded provision
575 :param names: List of key provision parameters"""
578 wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
579 partitions = [str(names.rootdn), str(names.configdn)]
580 for part in partitions:
581 ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
582 base=part, scope=SCOPE_SUBTREE,
584 controls=["show_deleted:0"])
585 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
586 base=part, scope=SCOPE_SUBTREE,
588 controls=["show_deleted:0"])
589 if len(ref_delObjCnt) > len(delObjCnt):
590 reference = ref_samdb.search(expression="cn=Deleted Objects",
591 base=part, scope=SCOPE_SUBTREE,
592 controls=["show_deleted:0"])
594 delta = samdb.msg_diff(empty, reference[0])
596 delta.dn = Dn(samdb, str(reference[0]["dn"]))
597 for att in hashAttrNotCopied.keys():
602 res = samdb.search(expression="(objectClass=*)", base=part,
604 attrs=["dn", "wellKnownObjects"])
606 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
610 wko = res[0]["wellKnownObjects"]
612 # The wellKnownObject that we want to add.
614 if str(o) == targetWKO:
616 listwko.append(str(o))
619 listwko.append(targetWKO)
622 delta.dn = Dn(samdb, str(res[0]["dn"]))
623 delta["wellKnownObjects"] = MessageElement(listwko,
628 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
629 """Add the missing object whose DN is the list
631 The function add the object if the objects on which it depends are
634 :param ref_samdb: Ldb object representing the SAM db of the reference
636 :param samdb: Ldb object representing the SAM db of the upgraded
638 :param dn: DN of the object to be added
639 :param names: List of key provision parameters
640 :param basedn: DN of the partition to be updated
641 :param list: List of DN to be added in the upgraded provision"""
646 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
648 listMissing = listDefered
650 hashMissing = gen_dn_index_hash(listMissing)
651 for dn in listMissing:
652 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
656 # DN can't be created because it depends on some
657 # other DN in the list
658 listDefered.append(dn)
659 if len(listDefered) != 0:
660 raise ProvisioningError("Unable to insert missing elements:" \
661 "circular references")
663 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
664 """This function handle updates on links
666 :param samdb: An LDB object pointing to the updated provision
667 :param att: Attribute to update
668 :param basedn: The root DN of the provision
669 :param dn: The DN of the inspected object
670 :param value: The value of the attribute
671 :param ref_value: The value of this attribute in the reference provision
672 :param delta: The MessageElement object that will be applied for
673 transforming the current provision"""
675 res = samdb.search(expression="dn=%s" % dn, base=basedn,
676 controls=["search_options:1:2", "reveal:1"],
684 newlinklist.extend(value)
688 # for w2k domain level the reveal won't reveal anything ...
689 # it means that we can readd links that were removed on purpose ...
690 # Also this function in fact just accept add not removal
692 for e in res[0][att]:
693 if not hash.has_key(e):
694 # We put in the blacklist all the element that are in the "revealed"
695 # result and not in the "standard" result
696 # This element are links that were removed before and so that
697 # we don't wan't to readd
701 if not blacklist.has_key(e) and not hash.has_key(e):
702 newlinklist.append(str(e))
705 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
710 msg_elt_flag_strs = {
711 ldb.FLAG_MOD_ADD: "MOD_ADD",
712 ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
713 ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
716 def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
717 """ This function updates the object that are already present in the
720 :param ref_samdb: An LDB object pointing to the reference provision
721 :param samdb: An LDB object pointing to the updated provision
722 :param basedn: A string with the value of the base DN for the provision
724 :param listPresent: A list of object that is present in the provision
725 :param usns: A list of USN range modified by previous provision and
727 :param invocationid: The value of the invocationid for the current DC"""
730 # This hash is meant to speedup lookup of attribute name from an oid,
731 # it's for the replPropertyMetaData handling
733 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
734 controls=["search_options:1:2"], attrs=["attributeID",
738 strDisplay = str(e.get("lDAPDisplayName"))
739 hash_oid_name[str(e.get("attributeID"))] = strDisplay
741 msg = "Unable to insert missing elements: circular references"
742 raise ProvisioningError(msg)
745 controls = ["search_options:1:2", "sd_flags:1:2"]
746 for dn in listPresent:
747 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
750 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
751 scope=SCOPE_SUBTREE, controls=controls)
754 (str(current[0].dn) != str(reference[0].dn)) and
755 (str(current[0].dn).upper() == str(reference[0].dn).upper())
757 message(CHANGE, "Name are the same but case change,"\
758 "let's rename %s to %s" % (str(current[0].dn),
759 str(reference[0].dn)))
760 identic_rename(samdb, reference[0].dn)
761 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
763 controls=["search_options:1:2"])
765 delta = samdb.msg_diff(current[0], reference[0])
767 for att in hashAttrNotCopied.keys():
770 for att in backlinked:
775 if len(delta.items()) > 1 and usns is not None:
776 # Fetch the replPropertyMetaData
777 res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
778 scope=SCOPE_SUBTREE, controls=controls,
779 attrs=["replPropertyMetaData"])
780 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
781 str(res[0]["replPropertyMetaData"])).ctr
785 # We put in this hash only modification
786 # made on the current host
787 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
788 if str(o.originating_invocation_id) == str(invocationid):
789 # Note we could just use 1 here
790 hash_attr_usn[att] = o.originating_usn
792 hash_attr_usn[att] = -1
799 # We have updated by provision usn information so let's exploit
800 # replMetadataProperties
801 if att in forwardlinked:
802 handle_links(samdb, att, basedn, current[0]["dn"],
803 current[0][att], reference[0][att], delta)
805 if isFirst == 0 and len(delta.items())>1:
807 txt = "%s\n" % (str(dn))
809 # There is always a dn attribute after a msg_diff
811 if att == "rIDAvailablePool":
814 if att == "objectSid":
817 if att == "creationTime":
820 if att == "oEMInformation":
823 if att == "msDs-KeyVersionNumber":
824 # This is the kvno of the computer/user it's a very bad
828 if handle_special_case(att, delta, reference, current, usns):
829 # This attribute is "complicated" to handle and handling
830 # was done in handle_special_case
832 attrUSN = hash_attr_usn.get(att)
833 if att == "forceLogoff" and attrUSN is None:
840 # This attribute was last modified by another DC forget
842 message(CHANGE, "%sAttribute: %s has been"
843 "created/modified/deleted by another DC,"
844 " do nothing" % (txt, att ))
848 elif not usn_in_range(int(attrUSN), usns):
849 message(CHANGE, "%sAttribute: %s has been"
850 "created/modified/deleted not during a"
851 " provision or upgradeprovision: current"
852 " usn %d , do nothing" % (txt, att, attrUSN))
857 if att == "defaultSecurityDescriptor":
860 message(CHANGE, "%sAttribute: %s will be modified"
861 "/deleted it was last modified"
862 "during a provision, current usn:"
863 "%d" % (txt, att, attrUSN))
866 message(CHANGE, "%sAttribute: %s will be added because"
867 " it hasn't existed before " % (txt, att))
872 # Old school way of handling things for pre alpha12 upgrade
874 msgElt = delta.get(att)
876 if att == "nTSecurityDescriptor":
883 if not hashOverwrittenAtt.has_key(att):
884 if msgElt.flags() != FLAG_MOD_ADD:
885 if not handle_special_case(att, delta, reference, current,
887 if opts.debugchange or opts.debugall:
889 dump_denied_change(dn, att,
890 msg_elt_flag_strs[msgElt.flags()],
891 current[0][att], reference[0][att])
893 dump_denied_change(dn, att,
894 msg_elt_flag_strs[msgElt.flags()],
895 current[0][att], None)
899 if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
901 elif hashOverwrittenAtt.get(att)==never:
906 if len(delta.items()) >1:
907 attributes=", ".join(delta.keys())
908 message(CHANGE, "%s is different from the reference one, changed"
909 " attributes: %s\n" % (dn, attributes))
915 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs):
916 """Check differences between the reference provision and the upgraded one.
918 It looks for all objects which base DN is name.
920 This function will also add the missing object and update existing object
921 to add or remove attributes that were missing.
923 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
925 :param samdb: An LDB object connected to the sam.ldb of the update
927 :param basedn: String value of the DN of the partition
928 :param names: List of key provision parameters
929 :param schema: A Schema object
930 :param provisionUSNs: The USNs modified by provision/upgradeprovision
940 # Connect to the reference provision and get all the attribute in the
941 # partition referred by name
942 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
943 scope=SCOPE_SUBTREE, attrs=["dn"],
944 controls=["search_options:1:2"])
946 current = samdb.search(expression="objectClass=*", base=basedn,
947 scope=SCOPE_SUBTREE, attrs=["dn"],
948 controls=["search_options:1:2"])
949 # Create a hash for speeding the search of new object
950 for i in range(0, len(reference)):
951 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
953 # Create a hash for speeding the search of existing object in the
955 for i in range(0, len(current)):
956 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
959 for k in hash_new.keys():
960 if not hash.has_key(k):
961 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
962 listMissing.append(hash_new[k])
964 listPresent.append(hash_new[k])
966 # Sort the missing object in order to have object of the lowest level
967 # first (which can be containers for higher level objects)
968 listMissing.sort(dn_sort)
969 listPresent.sort(dn_sort)
971 # The following lines is to load the up to
972 # date schema into our current LDB
973 # a complete schema is needed as the insertion of attributes
974 # and class is done against it
975 # and the schema is self validated
976 samdb.set_schema(schema)
978 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
979 add_deletedobj_containers(ref_samdb, samdb, names)
981 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
982 changed = update_present(ref_samdb, samdb, basedn, listPresent,
983 provisionUSNs, names.invocation)
984 message(SIMPLE, "There are %d changed objects" % (changed))
987 except StandardError, err:
988 message(ERROR, "Exception during upgrade of samdb:")
989 (typ, val, tb) = sys.exc_info()
990 traceback.print_exception(typ, val, tb)
994 def check_updated_sd(ref_sam, cur_sam, names):
995 """Check if the security descriptor in the upgraded provision are the same
998 :param ref_sam: A LDB object connected to the sam.ldb file used as
999 the reference provision
1000 :param cur_sam: A LDB object connected to the sam.ldb file used as
1002 :param names: List of key provision parameters"""
1003 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1004 scope=SCOPE_SUBTREE,
1005 attrs=["dn", "nTSecurityDescriptor"],
1006 controls=["search_options:1:2"])
1007 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1008 scope=SCOPE_SUBTREE,
1009 attrs=["dn", "nTSecurityDescriptor"],
1010 controls=["search_options:1:2"])
1012 for i in range(0, len(reference)):
1013 refsd = ndr_unpack(security.descriptor,
1014 str(reference[i]["nTSecurityDescriptor"]))
1015 hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
1018 for i in range(0, len(current)):
1019 key = str(current[i]["dn"]).lower()
1020 if hash.has_key(key):
1021 cursd = ndr_unpack(security.descriptor,
1022 str(current[i]["nTSecurityDescriptor"]))
1023 sddl = cursd.as_sddl(names.domainsid)
1024 if sddl != hash[key]:
1025 txt = get_diff_sddls(hash[key], sddl)
1027 message(CHANGESD, "On object %s ACL is different"
1028 " \n%s" % (current[i]["dn"], txt))
1032 def fix_partition_sd(samdb, names):
1033 """This function fix the SD for partition containers (basedn, configdn, ...)
1034 This is needed because some provision use to have broken SD on containers
1036 :param samdb: An LDB object pointing to the sam of the current provision
1037 :param names: A list of key provision parameters
1039 # First update the SD for the rootdn
1040 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1041 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1042 controls=["search_options:1:2"])
1044 delta.dn = Dn(samdb, str(res[0]["dn"]))
1045 descr = get_domain_descriptor(names.domainsid)
1046 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1047 "nTSecurityDescriptor")
1048 samdb.modify(delta, ["recalculate_sd:0"])
1049 # Then the config dn
1050 res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1051 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1052 controls=["search_options:1:2"])
1054 delta.dn = Dn(samdb, str(res[0]["dn"]))
1055 descr = get_config_descriptor(names.domainsid)
1056 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1057 "nTSecurityDescriptor" )
1058 samdb.modify(delta, ["recalculate_sd:0"])
1059 # Then the schema dn
1060 res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1061 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1062 controls=["search_options:1:2"])
1065 delta.dn = Dn(samdb, str(res[0]["dn"]))
1066 descr = get_schema_descriptor(names.domainsid)
1067 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1068 "nTSecurityDescriptor" )
1069 samdb.modify(delta, ["recalculate_sd:0"])
1071 def rebuild_sd(samdb, names):
1072 """Rebuild security descriptor of the current provision from scratch
1074 During the different pre release of samba4 security descriptors (SD)
1075 were notarly broken (up to alpha11 included)
1076 This function allow to get them back in order, this function make the
1077 assumption that nobody has modified manualy an SD
1078 and so SD can be safely recalculated from scratch to get them right.
1080 :param names: List of key provision parameters"""
1084 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1085 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1086 controls=["search_options:1:2"])
1088 if not (str(obj["dn"]) == str(names.rootdn) or
1089 str(obj["dn"]) == str(names.configdn) or
1090 str(obj["dn"]) == str(names.schemadn)):
1091 hash[str(obj["dn"])] = obj["whenCreated"]
1093 listkeys = hash.keys()
1094 listkeys.sort(dn_sort)
1096 for key in listkeys:
1099 delta.dn = Dn(samdb, key)
1100 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1102 samdb.modify(delta, ["recalculate_sd:0"])
1104 # XXX: We should always catch an explicit exception.
1105 # What could go wrong here?
1106 samdb.transaction_cancel()
1107 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1108 scope=SCOPE_SUBTREE,
1109 attrs=["dn", "nTSecurityDescriptor"],
1110 controls=["search_options:1:2"])
1111 badsd = ndr_unpack(security.descriptor,
1112 str(res[0]["nTSecurityDescriptor"]))
1113 print "bad stuff %s" % badsd.as_sddl(names.domainsid)
1116 def removeProvisionUSN(samdb):
1117 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1118 entry = samdb.search(expression="dn=@PROVISION", base = "",
1119 scope=SCOPE_SUBTREE,
1120 controls=["search_options:1:2"],
1123 empty.dn = entry[0].dn
1124 delta = samdb.msg_diff(entry[0], empty)
1126 delta.dn = entry[0].dn
1129 def remove_stored_generated_attrs(paths, creds, session, lp):
1130 """Remove previously stored constructed attributes
1132 :param paths: List of paths for different provision objects
1133 from the upgraded provision
1134 :param creds: A credential object
1135 :param session: A session object
1136 :param lp: A line parser object
1137 :return: An associative array whose key are the different constructed
1138 attributes and the value the dn where this attributes were found.
1142 def simple_update_basesamdb(newpaths, paths, names):
1143 """Update the provision container db: sam.ldb
1144 This function is aimed at very old provision (before alpha9)
1146 :param newpaths: List of paths for different provision objects
1147 from the reference provision
1148 :param paths: List of paths for different provision objects
1149 from the upgraded provision
1150 :param names: List of key provision parameters"""
1152 message(SIMPLE, "Copy samdb")
1153 shutil.copy(newpaths.samdb, paths.samdb)
1155 message(SIMPLE, "Update partitions filename if needed")
1156 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1157 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1158 usersldb = os.path.join(paths.private_dir, "users.ldb")
1159 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1161 if not os.path.isdir(samldbdir):
1163 os.chmod(samldbdir, 0700)
1164 if os.path.isfile(schemaldb):
1165 shutil.copy(schemaldb, os.path.join(samldbdir,
1166 "%s.ldb"%str(names.schemadn).upper()))
1167 os.remove(schemaldb)
1168 if os.path.isfile(usersldb):
1169 shutil.copy(usersldb, os.path.join(samldbdir,
1170 "%s.ldb"%str(names.rootdn).upper()))
1172 if os.path.isfile(configldb):
1173 shutil.copy(configldb, os.path.join(samldbdir,
1174 "%s.ldb"%str(names.configdn).upper()))
1175 os.remove(configldb)
1178 def update_privilege(ref_private_path, cur_private_path):
1179 """Update the privilege database
1181 :param ref_private_path: Path to the private directory of the reference
1183 :param cur_private_path: Path to the private directory of the current
1184 (and to be updated) provision."""
1185 message(SIMPLE, "Copy privilege")
1186 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1187 os.path.join(cur_private_path, "privilege.ldb"))
1190 def update_samdb(ref_samdb, samdb, names, highestUSN, schema):
1191 """Upgrade the SAM DB contents for all the provision partitions
1193 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1195 :param samdb: An LDB object connected to the sam.ldb of the update
1197 :param names: List of key provision parameters
1198 :param highestUSN: The highest USN modified by provision/upgradeprovision
1200 :param schema: A Schema object that represent the schema of the provision"""
1202 message(SIMPLE, "Starting update of samdb")
1203 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1206 message(SIMPLE, "Update of samdb finished")
1209 message(SIMPLE, "Update failed")
1213 def copyxattrs(dir, refdir):
1214 """ Copy owner, groups, extended ACL and NT acls from
1215 a reference dir to a destination dir
1217 Both dir are supposed to hold the same files
1218 :param dir: Destination dir
1219 :param refdir: Reference directory"""
1222 for root, dirs, files in os.walk(dir, topdown=True):
1224 subdir=root[len(dir):]
1225 ref = os.path.join("%s%s" % (refdir, subdir), name)
1226 statsinfo = os.stat(ref)
1227 tgt = os.path.join(root, name)
1230 os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
1231 # Get the xattr attributes if any
1233 attribute = samba.xattr_native.wrap_getxattr(ref,
1234 xattr.XATTR_NTACL_NAME)
1235 samba.xattr_native.wrap_setxattr(tgt,
1236 xattr.XATTR_NTACL_NAME,
1240 attribute = samba.xattr_native.wrap_getxattr(ref,
1241 "system.posix_acl_access")
1242 samba.xattr_native.wrap_setxattr(tgt,
1243 "system.posix_acl_access",
1248 subdir=root[len(dir):]
1249 ref = os.path.join("%s%s" % (refdir, subdir), name)
1250 statsinfo = os.stat(ref)
1251 tgt = os.path.join(root, name)
1253 os.chown(os.path.join(root, name), statsinfo.st_uid,
1256 attribute = samba.xattr_native.wrap_getxattr(ref,
1257 xattr.XATTR_NTACL_NAME)
1258 samba.xattr_native.wrap_setxattr(tgt,
1259 xattr.XATTR_NTACL_NAME,
1263 attribute = samba.xattr_native.wrap_getxattr(ref,
1264 "system.posix_acl_access")
1265 samba.xattr_native.wrap_setxattr(tgt,
1266 "system.posix_acl_access",
1273 def backup_provision(paths, dir):
1274 """This function backup the provision files so that a rollback
1277 :param paths: Paths to different objects
1278 :param dir: Directory where to store the backup
1281 shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
1282 copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
1283 shutil.copy2(paths.samdb, dir)
1284 shutil.copy2(paths.secrets, dir)
1285 shutil.copy2(paths.idmapdb, dir)
1286 shutil.copy2(paths.privilege, dir)
1287 if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1288 shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1289 shutil.copy2(paths.smbconf, dir)
1290 shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1292 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1293 if not os.path.isdir(samldbdir):
1294 samldbdir = paths.private_dir
1295 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1296 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1297 usersldb = os.path.join(paths.private_dir, "users.ldb")
1298 shutil.copy2(schemaldb, dir)
1299 shutil.copy2(usersldb, dir)
1300 shutil.copy2(configldb, dir)
1302 shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1307 def sync_calculated_attributes(samdb, names):
1308 """Synchronize attributes used for constructed ones, with the
1309 old constructed that were stored in the database.
1311 This apply for instance to msds-keyversionnumber that was
1312 stored and that is now constructed from replpropertymetadata.
1314 :param samdb: An LDB object attached to the currently upgraded samdb
1315 :param names: Various key parameter about current provision.
1317 listAttrs = ["msDs-KeyVersionAttribute"]
1318 hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
1319 increment_calculated_keyversion_number(samdb, names.rootdn, hash)
1321 def setup_path(file):
1322 return os.path.join(setup_dir, file)
1324 # Synopsis for updateprovision
1325 # 1) get path related to provision to be update (called current)
1326 # 2) open current provision ldbs
1327 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1329 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1330 # by either upgradeprovision or provision
1331 # 5) creation of a new provision the latest version of provision script
1332 # (called reference)
1333 # 6) get reference provision paths
1334 # 7) open reference provision ldbs
1335 # 8) setup helpers data that will help the update process
1336 # 9) update the privilege ldb by copying the one of referecence provision to
1337 # the current provision
1338 # 10)get the oemInfo field, this field contains information about the different
1339 # provision that have been done
1340 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1341 # integer) or none of this the following things are done
1342 # A) When alpha9 or alphaxx is present
1343 # The base sam.ldb file is updated by looking at the difference between
1344 # referrence one and the current one. Everything is copied with the
1345 # exception of lastProvisionUSN attributes.
1346 # B) Other case (it reflect that that provision was done before alpha9)
1347 # The base sam.ldb of the reference provision is copied over
1348 # the current one, if necessary ldb related to partitions are moved
1350 # The highest used USN is fetched so that changed by upgradeprovision
1351 # usn can be tracked
1352 # 12)A Schema object is created, it will be used to provide a complete
1353 # schema to current provision during update (as the schema of the
1354 # current provision might not be complete and so won't allow some
1355 # object to be created)
1356 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1357 # 14)The secrets db is updated by pull all the difference from the reference
1358 # provision into the current provision
1359 # 15)As the previous step has most probably modified the password stored in
1360 # in secret for the current DC, a new password is generated,
1361 # the kvno is bumped and the entry in samdb is also updated
1362 # 16)For current provision older than alpha9, we must fix the SD a little bit
1363 # administrator to update them because SD used to be generated with the
1364 # system account before alpha9.
1365 # 17)The highest usn modified so far is searched in the database it will be
1366 # the upper limit for usn modified during provision.
1367 # This is done before potential SD recalculation because we do not want
1368 # SD modified during recalculation to be marked as modified during provision
1369 # (and so possibly remplaced at next upgradeprovision)
1370 # 18)Rebuilt SD if the flag indicate to do so
1371 # 19)Check difference between SD of reference provision and those of the
1372 # current provision. The check is done by getting the sddl representation
1373 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1374 # Each part is verified separetly, for dacl and sacl ACL is splited into
1375 # ACEs and each ACE is verified separately (so that a permutation in ACE
1376 # didn't raise as an error).
1377 # 20)The oemInfo field is updated to add information about the fact that the
1378 # provision has been updated by the upgradeprovision version xxx
1379 # (the version is the one obtained when starting samba with the --version
1381 # 21)Check if the current provision has all the settings needed for dynamic
1382 # DNS update to work (that is to say the provision is newer than
1383 # january 2010). If not dns configuration file from reference provision
1384 # are copied in a sub folder and the administrator is invited to
1385 # do what is needed.
1386 # 22)If the lastProvisionUSN attribute was present it is updated to add
1387 # the range of usns modified by the current upgradeprovision
1390 # About updating the sam DB
1391 # The update takes place in update_partition function
1392 # This function read both current and reference provision and list all
1393 # the available DN of objects
1394 # If the string representation of a DN in reference provision is
1395 # equal to the string representation of a DN in current provision
1396 # (without taking care of case) then the object is flaged as being
1397 # present. If the object is not present in current provision the object
1398 # is being flaged as missing in current provision. Object present in current
1399 # provision but not in reference provision are ignored.
1400 # Once the list of objects present and missing is done, the deleted object
1401 # containers are created in the differents partitions (if missing)
1403 # Then the function add_missing_entries is called
1404 # This function will go through the list of missing entries by calling
1405 # add_missing_object for the given object. If this function returns 0
1406 # it means that the object needs some other object in order to be created
1407 # The object is reappended at the end of the list to be created later
1408 # (and preferably after all the needed object have been created)
1409 # The function keeps on looping on the list of object to be created until
1410 # it's empty or that the number of defered creation is equal to the number
1411 # of object that still needs to be created.
1413 # The function add_missing_object will first check if the object can be created.
1414 # That is to say that it didn't depends other not yet created objects
1415 # If requisit can't be fullfilled it exists with 0
1416 # Then it will try to create the missing entry by creating doing
1417 # an ldb_message_diff between the object in the reference provision and
1419 # This resulting object is filtered to remove all the back link attribute
1420 # (ie. memberOf) as they will be created by the other linked object (ie.
1421 # the one with the member attribute)
1422 # All attributes specified in the hashAttrNotCopied associative array are
1423 # also removed it's most of the time generated attributes
1425 # After missing entries have been added the update_partition function will
1426 # take care of object that exist but that need some update.
1427 # In order to do so the function update_present is called with the list
1428 # of object that are present in both provision and that might need an update.
1430 # This function handle first case mismatch so that the DN in the current
1431 # provision have the same case as in reference provision
1433 # It will then construct an associative array consiting of attributes as
1434 # key and invocationid as value( if the originating invocation id is
1435 # different from the invocation id of the current DC the value is -1 instead).
1437 # If the range of provision modified attributes is present, the function will
1438 # use the replMetadataProperty update method which is the following:
1439 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1440 # creationTime, msDs-KeyVersionNumber, oEMInformation
1441 # Check for each attribute if its usn is within one of the modified by
1442 # provision range and if its originating id is the invocation id of the
1443 # current DC, then validate the update from reference to current.
1444 # If not or if there is no replMetatdataProperty for this attribute then we
1446 # Otherwise (case the range of provision modified attribute is not present) it
1447 # use the following process:
1448 # All attributes that need to be added are accepted at the exeption of those
1449 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1450 # correct flags specified.
1451 # For attributes that need to be modified or removed, a check is performed
1452 # in OverwrittenAtt, if the attribute is present and the modification flag
1453 # (remove, delete) is one of those listed for this attribute then modification
1454 # is accepted. For complicated handling of attribute update, the control is passed
1455 # to handle_special_case
1459 if __name__ == '__main__':
1460 global defSDmodified
1461 defSDmodified = False
1462 # From here start the big steps of the program
1463 # 1) First get files paths
1464 paths = get_paths(param, smbconf=smbconf)
1465 paths.setup = setup_dir
1466 # Get ldbs with the system session, it is needed for searching
1467 # provision parameters
1468 session = system_session()
1470 # This variable will hold the last provision USN once if it exists.
1473 ldbs = get_ldbs(paths, creds, session, lp)
1474 backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1475 prefix="backupprovision")
1476 backup_provision(paths, backupdir)
1478 ldbs.startTransactions()
1480 # 3) Guess all the needed names (variables in fact) from the current
1482 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1485 lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1486 if lastProvisionUSNs is not None:
1488 "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
1490 # Objects will be created with the admin session
1491 # (not anymore system session)
1492 adm_session = admin_session(lp, str(names.domainsid))
1493 # So we reget handle on objects
1494 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1496 if not sanitychecks(ldbs.sam, names):
1497 message(SIMPLE, "Sanity checks for the upgrade fails, checks messages"
1498 " and correct them before rerunning upgradeprovision")
1501 # Let's see provision parameters
1502 print_provision_key_parameters(names)
1504 # 5) With all this information let's create a fresh new provision used as
1506 message(SIMPLE, "Creating a reference provision")
1507 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1508 prefix="referenceprovision")
1509 newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
1514 # We need to get a list of object which SD is directly computed from
1515 # defaultSecurityDescriptor.
1516 # This will allow us to know which object we can rebuild the SD in case
1517 # of change of the parent's SD or of the defaultSD.
1518 # Get file paths of this new provision
1519 newpaths = get_paths(param, targetdir=provisiondir)
1520 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1521 new_ldbs.startTransactions()
1523 # 8) Populate some associative array to ease the update process
1524 # List of attribute which are link and backlink
1525 populate_links(new_ldbs.sam, names.schemadn)
1526 # List of attribute with ASN DN synthax)
1527 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1529 update_privilege(newpaths.private_dir, paths.private_dir)
1531 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1532 # Do some modification on sam.ldb
1533 ldbs.groupedCommit()
1534 new_ldbs.groupedCommit()
1537 if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1539 # Starting from alpha9 we can consider that the structure is quite ok
1540 # and that we should do only dela
1541 delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message)
1544 simple_update_basesamdb(newpaths, paths, names)
1545 ldbs = get_ldbs(paths, creds, session, lp)
1546 removeProvisionUSN(ldbs.sam)
1548 ldbs.startTransactions()
1549 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1550 new_ldbs.startTransactions()
1553 schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
1554 serverdn=str(names.serverdn))
1558 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1560 message(SIMPLE, "Rollbacking every changes. Check the reason"
1562 message(SIMPLE, "In any case your system as it was before"
1564 ldbs.groupedRollback()
1565 new_ldbs.groupedRollback()
1566 shutil.rmtree(provisiondir)
1569 sync_calculated_attributes(ldbs.sam, names)
1571 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1573 message(SIMPLE, "Update machine account")
1574 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1576 # 16) SD should be created with admin but as some previous acl were so wrong
1577 # that admin can't modify them we have first to recreate them with the good
1578 # form but with system account and then give the ownership to admin ...
1579 if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1580 message(SIMPLE, "Fixing old povision SD")
1581 fix_partition_sd(ldbs.sam, names)
1582 rebuild_sd(ldbs.sam, names)
1584 # We calculate the max USN before recalculating the SD because we might
1585 # touch object that have been modified after a provision and we do not
1586 # want that the next upgradeprovision thinks that it has a green light
1590 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1592 # 18) We rebuild SD only if defaultSecurityDescriptor is modified
1593 # But in fact we should do it also if one object has its SD modified as
1594 # child might need rebuild
1596 message(SIMPLE, "Updating SD")
1597 ldbs.sam.set_session_info(adm_session)
1598 # Alpha10 was a bit broken still
1599 if re.match(r'.*alpha(\d|10)', str(oem)):
1600 fix_partition_sd(ldbs.sam, names)
1601 rebuild_sd(ldbs.sam, names)
1604 # Now we are quite confident in the recalculate process of the SD, we make
1606 # Also the check must be done in a clever way as for the moment we just
1608 if opts.debugchangesd:
1609 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1612 updateOEMInfo(ldbs.sam, str(names.rootdn))
1614 check_for_DNS(newpaths.private_dir, paths.private_dir)
1616 if lastProvisionUSNs is not None:
1617 update_provision_usn(ldbs.sam, minUSN, maxUSN)
1618 if opts.full and (names.policyid is None or names.policyid_dc is None):
1619 update_policyids(names, ldbs.sam)
1620 if opts.full or opts.resetfileacl:
1622 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1623 except ProvisioningError, e:
1624 message(ERROR, "The policy for domain controller is missing,"
1625 " you should restart upgradeprovision with --full")
1628 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1629 except ProvisioningError, e:
1630 message(ERROR, "The policy for domain controller is missing,"
1631 " you should restart upgradeprovision with --full")
1632 ldbs.groupedCommit()
1633 new_ldbs.groupedCommit()
1634 message(SIMPLE, "Upgrade finished !")
1635 # remove reference provision now that everything is done !
1636 shutil.rmtree(provisiondir)
1637 except StandardError, err:
1638 message(ERROR,"A problem has occured when trying to upgrade your provision,"
1639 " a full backup is located at %s" % backupdir)
1641 (typ, val, tb) = sys.exc_info()
1642 traceback.print_exception(typ, val, tb)