4 # Copyright (C) Matthieu Patou <mat@matws.net> 2009
6 # Based on provision a Samba4 server by
7 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
8 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
33 # Allow to run from s4 source directory (without installing samba)
34 sys.path.insert(0, "bin/python")
37 import samba.getopt as options
38 from samba.credentials import DONT_USE_KERBEROS
39 from samba.auth import system_session, admin_session
40 from samba import Ldb, version
41 from ldb import SCOPE_ONELEVEL, 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.misc import messageEltFlagToString
46 from samba.provision import find_setup_dir, get_domain_descriptor,\
47 get_config_descriptor, secretsdb_self_join,\
48 set_gpo_acl, getpolicypath,create_gpo_struct,\
49 ProvisioningError, getLastProvisionUSN,\
50 get_max_usn, updateProvisionUSN
51 from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
52 from samba.dcerpc import security, drsblobs
53 from samba.ndr import ndr_unpack
54 from samba.dcerpc.misc import SEC_CHAN_BDC
55 from samba.upgradehelpers import dn_sort, get_paths, newprovision,\
56 find_provision_key_parameters, get_ldbs
58 replace=2**FLAG_MOD_REPLACE
60 delete=2**FLAG_MOD_DELETE
64 # Will be modified during provision to tell if default sd has been modified
67 #Errors are always logged
76 __docformat__ = "restructuredText"
78 # Attributes that are never copied from the reference provision (even if they
79 # do not exist in the destination object).
80 # This is most probably because they are populated automatcally when object is
82 # This also apply to imported object from reference provision
83 hashAttrNotCopied = { "dn": 1, "whenCreated": 1, "whenChanged": 1,
84 "objectGUID": 1, "uSNCreated": 1,
85 "replPropertyMetaData": 1, "uSNChanged": 1,
86 "parentGUID": 1, "objectCategory": 1,
87 "distinguishedName": 1, "nTMixedDomain": 1,
88 "showInAdvancedViewOnly": 1, "instanceType": 1,
89 "msDS-Behavior-Version":1, "nextRid":1, "cn": 1,
90 "versionNumber":1, "lmPwdHistory":1, "pwdLastSet": 1,
91 "ntPwdHistory":1, "unicodePwd":1,"dBCSPwd":1,
92 "supplementalCredentials":1, "gPCUserExtensionNames":1,
93 "gPCMachineExtensionNames":1,"maxPwdAge":1, "secret":1,
94 "possibleInferiors":1, "privilege":1,
97 # Usually for an object that already exists we do not overwrite attributes as
98 # they might have been changed for good reasons. Anyway for a few of them it's
99 # mandatory to replace them otherwise the provision will be broken somehow.
100 # But for attribute that are just missing we do not have to specify them as the default
101 # behavior is to add missing attribute
102 hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace,
103 "systemOnly":replace, "searchFlags":replace,
104 "mayContain":replace, "systemFlags":replace+add,
105 "description":replace, "operatingSystemVersion":replace,
106 "adminPropertyPages":replace, "groupType":replace,
107 "wellKnownObjects":replace, "privilege":never,
108 "defaultSecurityDescriptor": replace,
109 "rIDAvailablePool": never,
110 "defaultSecurityDescriptor": replace + add }
116 def define_what_to_log(opts):
120 if opts.debugchangesd:
121 what = what | CHANGESD
124 if opts.debugprovision:
125 what = what | PROVISION
127 what = what | CHANGEALL
131 parser = optparse.OptionParser("provision [options]")
132 sambaopts = options.SambaOptions(parser)
133 parser.add_option_group(sambaopts)
134 parser.add_option_group(options.VersionOptions(parser))
135 credopts = options.CredentialsOptions(parser)
136 parser.add_option_group(credopts)
137 parser.add_option("--setupdir", type="string", metavar="DIR",
138 help="directory with setup files")
139 parser.add_option("--debugprovision", help="Debug provision", action="store_true")
140 parser.add_option("--debugguess", action="store_true",
141 help="Print information on what is different but won't be changed")
142 parser.add_option("--debugchange", action="store_true",
143 help="Print information on what is different but won't be changed")
144 parser.add_option("--debugchangesd", action="store_true",
145 help="Print information security descriptors differences")
146 parser.add_option("--debugall", action="store_true",
147 help="Print all available information (very verbose)")
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.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()
182 def identic_rename(ldbobj,dn):
183 """Perform a back and forth rename to trigger renaming on attribute that can't be directly modified.
185 :param lbdobj: An Ldb Object
186 :param dn: DN of the object to manipulate """
188 (before, sep, after)=str(dn).partition('=')
189 ldbobj.rename(dn, Dn(ldbobj, "%s=foo%s" % (before, after)))
190 ldbobj.rename(Dn(ldbobj, "%s=foo%s" % (before, after)), dn)
192 def usn_in_range(usn, range):
193 """Check if the usn is in one of the range provided.
194 To do so, the value is checked to be between the lower bound and
195 higher bound of a range
197 :param usn: A integer value corresponding to the usn that we want to update
198 :param range: A list of integer representing ranges, lower bounds are in
199 the even indices, higher in odd indices
200 :return: 1 if the usn is in one of the range, 0 otherwise"""
206 if idx == len(range):
209 if usn < int(range[idx]):
213 if usn == int(range[idx]):
219 def check_for_DNS(refprivate, private):
220 """Check if the provision has already the requirement for dynamic dns
222 :param refprivate: The path to the private directory of the reference
224 :param private: The path to the private directory of the upgraded
227 spnfile = "%s/spn_update_list" % private
228 namedfile = lp.get("dnsupdate:path")
231 namedfile = "%s/named.conf.update" % private
233 if not os.path.exists(spnfile):
234 shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
236 destdir = "%s/new_dns" % private
237 dnsdir = "%s/dns" % private
239 if not os.path.exists(namedfile):
240 if not os.path.exists(destdir):
242 if not os.path.exists(dnsdir):
244 shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
245 shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
246 message(SIMPLE, "It seems that you provision didn't integrate new rules "
247 "for dynamic dns update of domain related entries")
248 message(SIMPLE, "A copy of the new bind configuration files and "
249 "template as been put in %s, you should read them and configure dynamic "
250 " dns update" % destdir)
256 def populate_links(samdb, schemadn):
257 """Populate an array with all the back linked attributes
259 This attributes that are modified automaticaly when
260 front attibutes are changed
262 :param samdb: A LDB object for sam.ldb file
263 :param schemadn: DN of the schema for the partition"""
264 linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
265 backlinked.extend(linkedAttHash.values())
266 for t in linkedAttHash.keys():
269 def populate_dnsyntax(samdb, schemadn):
270 """Populate an array with all the attributes that have DN synthax
273 :param samdb: A LDB object for sam.ldb file
274 :param schemadn: DN of the schema for the partition"""
275 res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
276 str(schemadn)), scope=SCOPE_SUBTREE,
277 attrs=["lDAPDisplayName"])
279 dn_syntax_att.append(elem["lDAPDisplayName"])
282 def sanitychecks(samdb, names):
283 """Make some checks before trying to update
285 :param samdb: An LDB object opened on sam.ldb
286 :param names: list of key provision parameters
287 :return: Status of check (1 for Ok, 0 for not Ok) """
288 res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
289 scope=SCOPE_SUBTREE, attrs=["dn"],
290 controls=["search_options:1:2"])
292 print "No DC found, your provision is most probably hardly broken !"
295 print "Found %d domain controllers, for the moment upgradeprovision" \
296 "is not able to handle upgrade on domain with more than one DC, please demote" \
297 " the other(s) DC(s) before upgrading" % len(res)
303 def print_provision_key_parameters(names):
304 """Do a a pretty print of provision parameters
306 :param names: list of key provision parameters """
307 message(GUESS, "rootdn :" + str(names.rootdn))
308 message(GUESS, "configdn :" + str(names.configdn))
309 message(GUESS, "schemadn :" + str(names.schemadn))
310 message(GUESS, "serverdn :" + str(names.serverdn))
311 message(GUESS, "netbiosname :" + names.netbiosname)
312 message(GUESS, "defaultsite :" + names.sitename)
313 message(GUESS, "dnsdomain :" + names.dnsdomain)
314 message(GUESS, "hostname :" + names.hostname)
315 message(GUESS, "domain :" + names.domain)
316 message(GUESS, "realm :" + names.realm)
317 message(GUESS, "invocationid:" + names.invocation)
318 message(GUESS, "policyguid :" + names.policyid)
319 message(GUESS, "policyguiddc:" + str(names.policyid_dc))
320 message(GUESS, "domainsid :" + str(names.domainsid))
321 message(GUESS, "domainguid :" + names.domainguid)
322 message(GUESS, "ntdsguid :" + names.ntdsguid)
323 message(GUESS, "domainlevel :" + str(names.domainlevel))
326 def handle_special_case(att, delta, new, old, usn):
327 """Define more complicate update rules for some attributes
329 :param att: The attribute to be updated
330 :param delta: A messageElement object that correspond to the difference
331 between the updated object and the reference one
332 :param new: The reference object
333 :param old: The Updated object
334 :param usn: The highest usn modified by a previous (upgrade)provision
335 :return: True to indicate that the attribute should be kept, False for
338 flag = delta.get(att).flags()
339 # We do most of the special case handle if we do not have the
340 # highest usn as otherwise the replPropertyMetaData will guide us more
343 if (att == "member" and flag == FLAG_MOD_REPLACE):
347 for elem in old[0][att]:
349 newval.append(str(elem))
351 for elem in new[0][att]:
352 if not hash.has_key(str(elem)):
354 newval.append(str(elem))
356 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
361 if (att == "gPLink" or att == "gPCFileSysPath") and \
362 flag == FLAG_MOD_REPLACE and\
363 str(new[0].dn).lower() == str(old[0].dn).lower():
367 if att == "forceLogoff":
368 ref=0x8000000000000000
369 oldval=int(old[0][att][0])
370 newval=int(new[0][att][0])
371 ref == old and ref == abs(new)
374 if (att == "adminDisplayName" or att == "adminDescription"):
377 if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (str(names.schemadn))\
378 and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
381 if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
382 att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
385 if (str(old[0].dn) == "%s" % (str(names.rootdn))
386 and att == "subRefs" and flag == FLAG_MOD_REPLACE):
389 if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
392 # This is a bit of special animal as we might have added
393 # already SPN entries to the list that has to be modified
394 # So we go in detail to try to find out what has to be added ...
395 if ( att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):
399 for elem in old[0][att]:
401 newval.append(str(elem))
403 for elem in new[0][att]:
404 if not hash.has_key(str(elem)):
406 newval.append(str(elem))
408 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
415 def update_secrets(newsecrets_ldb, secrets_ldb):
416 """Update secrets.ldb
418 :param newsecrets_ldb: An LDB object that is connected to the secrets.ldb
419 of the reference provision
420 :param secrets_ldb: An LDB object that is connected to the secrets.ldb
421 of the updated provision"""
423 message(SIMPLE, "update secrets.ldb")
424 reference = newsecrets_ldb.search(expression="dn=@MODULES", base="",
426 current = secrets_ldb.search(expression="dn=@MODULES", base="",
428 delta = secrets_ldb.msg_diff(current[0], reference[0])
429 delta.dn = current[0].dn
430 secrets_ldb.modify(delta)
432 reference = newsecrets_ldb.search(expression="objectClass=top", base="",
433 scope=SCOPE_SUBTREE, attrs=["dn"])
434 current = secrets_ldb.search(expression="objectClass=top", base="",
435 scope=SCOPE_SUBTREE, attrs=["dn"])
442 for i in range(0, len(reference)):
443 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
445 # Create a hash for speeding the search of existing object in the
447 for i in range(0, len(current)):
448 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
450 for k in hash_new.keys():
451 if not hash.has_key(k):
452 listMissing.append(hash_new[k])
454 listPresent.append(hash_new[k])
456 for entry in listMissing:
457 reference = newsecrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
458 current = secrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
459 delta = secrets_ldb.msg_diff(empty,reference[0])
460 for att in hashAttrNotCopied.keys():
462 message(CHANGE, "Entry %s is missing from secrets.ldb"%reference[0].dn)
464 message(CHANGE, " Adding attribute %s"%att)
465 delta.dn = reference[0].dn
466 secrets_ldb.add(delta)
468 for entry in listPresent:
469 reference = newsecrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
470 current = secrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
471 delta = secrets_ldb.msg_diff(current[0],reference[0])
472 for att in hashAttrNotCopied.keys():
476 message(CHANGE, "Found attribute name on %s, must rename the DN "%(current[0].dn))
477 identic_rename(secrets_ldb,reference[0].dn)
481 for entry in listPresent:
482 reference = newsecrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
483 current = secrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
484 delta = secrets_ldb.msg_diff(current[0],reference[0])
485 for att in hashAttrNotCopied.keys():
489 message(CHANGE, " Adding/Changing attribute %s to %s"%(att,current[0].dn))
491 delta.dn = current[0].dn
492 secrets_ldb.modify(delta)
495 def dump_denied_change(dn, att, flagtxt, current, reference):
496 """Print detailed information about why a changed is denied
498 :param dn: DN of the object which attribute is denied
499 :param att: Attribute that was supposed to be upgraded
500 :param flagtxt: Type of the update that should be performed
501 (add, change, remove, ...)
502 :param current: Value(s) of the current attribute
503 :param reference: Value(s) of the reference attribute"""
505 message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
506 +" is not allowed to be changed/removed, I discard this change")
507 if att != "objectSid" :
509 for e in range(0, len(current)):
510 message(CHANGE, "old %d : %s" % (i, str(current[e])))
512 if reference != None:
514 for e in range(0, len(reference)):
515 message(CHANGE, "new %d : %s" % (i, str(reference[e])))
518 message(CHANGE, "old : %s" % str(ndr_unpack( security.dom_sid, current[0])))
519 message(CHANGE, "new : %s" % str(ndr_unpack( security.dom_sid, reference[0])))
522 def handle_special_add(samdb, dn, names):
523 """Handle special operation (like remove) on some object needed during
526 This is mostly due to wrong creation of the object in previous provision.
527 :param samdb: An Ldb object representing the SAM database
528 :param dn: DN of the object to inspect
529 :param names: list of key provision parameters"""
532 if str(dn).lower() == ("CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn).lower():
533 #This entry was misplaced lets remove it if it exists
534 dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
536 objname = "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn
537 if str(dn).lower() == objname.lower():
538 #This entry was misplaced lets remove it if it exists
539 dntoremove = "CN=Certificate Service DCOM Access,"\
540 "CN=Users, %s" % names.rootdn
542 objname = "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn
543 if str(dn).lower() == objname.lower():
544 #This entry was misplaced lets remove it if it exists
545 dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
547 objname = "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn
548 if str(dn).lower() == objname.lower():
549 #This entry was misplaced lets remove it if it exists
550 dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
552 if dntoremove != None:
553 res = samdb.search(expression="(dn=%s)" % dntoremove,
554 base=str(names.rootdn),
555 scope=SCOPE_SUBTREE, attrs=["dn"],
556 controls=["search_options:1:2"])
558 message(CHANGE, "Existing object %s must be replaced by %s,"\
559 "removing old object" % (dntoremove, str(dn)))
560 samdb.delete(res[0]["dn"])
563 def check_dn_nottobecreated(hash, index, listdn):
564 """Check if one of the DN present in the list has a creation order
565 greater than the current.
567 Hash is indexed by dn to be created, with each key
568 is associated the creation order.
570 First dn to be created has the creation order 0, second has 1, ...
571 Index contain the current creation order
573 :param hash: Hash holding the different DN of the object to be
575 :param index: Current creation order
576 :param listdn: List of DNs on which the current DN depends on
577 :return: None if the current object do not depend on other
578 object or if all object have been created before."""
582 key = str(dn).lower()
583 if hash.has_key(key) and hash[key] > index:
589 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
590 """Add a new object if the dependencies are satisfied
592 The function add the object if the object on which it depends are already
595 :param ref_samdb: Ldb object representing the SAM db of the reference
597 :param samdb: Ldb object representing the SAM db of the upgraded
599 :param dn: DN of the object to be added
600 :param names: List of key provision parameters
601 :param basedn: DN of the partition to be updated
602 :param hash: Hash holding the different DN of the object to be
604 :param index: Current creation order
605 :return: True if the object was created False otherwise"""
607 handle_special_add(samdb, dn, names)
608 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
609 scope=SCOPE_SUBTREE, controls=["search_options:1:2"])
611 delta = samdb.msg_diff(empty, reference[0])
613 for att in hashAttrNotCopied.keys():
615 for att in backlinked:
617 depend_on_yettobecreated = None
618 for att in dn_syntax_att:
619 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
621 if depend_on_yet_tobecreated != None:
622 message(CHANGE, "Object %s depends on %s in attribute %s," \
623 "delaying the creation" % (str(dn), \
624 depend_on_yet_tobecreated, str(att)))
628 message(CHANGE,"Object %s will be added" % dn)
629 samdb.add(delta, ["relax:0"])
632 def gen_dn_index_hash(listMissing):
633 """Generate a hash associating the DN to its creation order
635 :param listMissing: List of DN
636 :return: Hash with DN as keys and creation order as values"""
638 for i in range(0, len(listMissing)):
639 hash[str(listMissing[i]).lower()] = i
642 def add_deletedobj_containers(ref_samdb, samdb, names):
643 """Add the object containter: CN=Deleted Objects
645 This function create the container for each partition that need one and
646 then reference the object into the root of the partition
648 :param ref_samdb: Ldb object representing the SAM db of the reference
650 :param samdb: Ldb object representing the SAM db of the upgraded provision
651 :param names: List of key provision parameters"""
654 wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
655 partitions = [str(names.rootdn), str(names.configdn)]
656 for part in partitions:
657 ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
658 base=part, scope=SCOPE_SUBTREE,
660 controls=["show_deleted:0"])
661 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
662 base=part, scope=SCOPE_SUBTREE,
664 controls=["show_deleted:0"])
665 if len(ref_delObjCnt) > len(delObjCnt):
666 reference = ref_samdb.search(expression="cn=Deleted Objects",
667 base=part, scope=SCOPE_SUBTREE,
668 controls=["show_deleted:0"])
670 delta = samdb.msg_diff(empty, reference[0])
672 delta.dn = Dn(samdb, str(reference[0]["dn"]))
673 for att in hashAttrNotCopied.keys():
678 res = samdb.search(expression="(objectClass=*)", base=part,
680 attrs=["dn", "wellKnownObjects"])
682 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
686 wko = res[0]["wellKnownObjects"]
688 # The wellKnownObject that we want to add.
690 if str(o) == targetWKO:
692 listwko.append(str(o))
695 listwko.append(targetWKO)
698 delta.dn = Dn(samdb, str(res[0]["dn"]))
699 delta["wellKnownObjects"] = MessageElement(listwko,
704 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
705 """Add the missing object whose DN is the list
707 The function add the object if the objects on which it depends are
710 :param ref_samdb: Ldb object representing the SAM db of the reference
712 :param samdb: Ldb object representing the SAM db of the upgraded
714 :param dn: DN of the object to be added
715 :param names: List of key provision parameters
716 :param basedn: DN of the partition to be updated
717 :param list: List of DN to be added in the upgraded provision"""
722 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
724 listMissing = listDefered
726 hashMissing = gen_dn_index_hash(listMissing)
727 for dn in listMissing:
728 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
732 # DN can't be created because it depends on some
733 # other DN in the list
734 listDefered.append(dn)
735 if len(listDefered) != 0:
736 raise ProvisioningError("Unable to insert missing elements:" \
737 "circular references")
739 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
740 """This function handle updates on links
742 :param samdb: An LDB object pointing to the updated provision
743 :param att: Attribute to update
744 :param basedn: The root DN of the provision
745 :param dn: The DN of the inspected object
746 :param value: The value of the attribute
747 :param ref_value: The value of this attribute in the reference provision
748 :param delta: The MessageElement object that will be applied for
749 transforming the current provision"""
751 res = samdb.search(expression="dn=%s" % dn, base=basedn,
752 controls=["search_options:1:2", "reveal:1"],
760 newlinklist.extend(value)
764 # for w2k domain level the reveal won't reveal anything ...
765 # it means that we can readd links that were removed on purpose ...
766 # Also this function in fact just accept add not removal
768 for e in res[0][att]:
769 if not hash.has_key(e):
770 # We put in the blacklist all the element that are in the "revealed"
771 # result and not in the "standard" result
772 # This element are links that were removed before and so that
773 # we don't wan't to readd
777 if not blacklist.has_key(e) and not hash.has_key(e):
778 newlinklist.append(str(e))
781 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
785 def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
786 """ This function updates the object that are already present in the
789 :param ref_samdb: An LDB object pointing to the reference provision
790 :param samdb: An LDB object pointing to the updated provision
791 :param basedn: A string with the value of the base DN for the provision
793 :param listPresent: A list of object that is present in the provision
794 :param usns: A list of USN range modified by previous provision and
796 :param invocationid: The value of the invocationid for the current DC"""
799 # This hash is meant to speedup lookup of attribute name from an oid,
800 # it's for the replPropertyMetaData handling
802 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
803 controls=["search_options:1:2"], attrs=["attributeID",
807 strDisplay = str(e.get("lDAPDisplayName"))
808 hash_oid_name[str(e.get("attributeID"))] = strDisplay
810 msg = "Unable to insert missing elements: circular references"
811 raise ProvisioningError(msg)
814 controls = ["search_options:1:2", "sd_flags:1:2"]
815 for dn in listPresent:
816 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
819 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
820 scope=SCOPE_SUBTREE, controls=controls)
823 (str(current[0].dn) != str(reference[0].dn)) and
824 (str(current[0].dn).upper() == str(reference[0].dn).upper())
826 message(CHANGE, "Name are the same but case change,"\
827 "let's rename %s to %s" % (str(current[0].dn),
828 str(reference[0].dn)))
829 identic_rename(samdb, reference[0].dn)
830 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
832 controls=["search_options:1:2"])
834 delta = samdb.msg_diff(current[0], reference[0])
836 for att in hashAttrNotCopied.keys():
839 for att in backlinked:
844 if len(delta.items()) > 1 and usns != None:
845 # Fetch the replPropertyMetaData
846 res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
847 scope=SCOPE_SUBTREE, controls=controls,
848 attrs=["replPropertyMetaData"])
849 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
850 str(res[0]["replPropertyMetaData"])).ctr
854 # We put in this hash only modification
855 # made on the current host
856 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
857 if str(o.originating_invocation_id) == str(invocationid):
858 hash_attr_usn[att] = o.originating_usn
860 hash_attr_usn[att] = -1
866 if forwardlinked.has_key(att):
867 handle_links(samdb, att, basedn, current[0]["dn"],
868 current[0][att], reference[0][att], delta)
870 if isFirst == 0 and len(delta.items())>1:
872 txt = "%s\n" % (str(dn))
875 if att == "rIDAvailablePool":
878 if att == "objectSid":
881 if att == "creationTime":
884 if att == "oEMInformation":
887 if att == "msDs-KeyVersionNumber":
890 if handle_special_case(att, delta, reference, current, usns):
891 # This attribute is "complicated" to handle and handling
892 # was done in handle_special_case
894 attrUSN = hash_attr_usn.get(att)
895 if att == "forceLogoff" and attrUSN == None:
902 # This attribute was last modified by another DC forget
904 message(CHANGE, "%sAttribute: %s has been" \
905 "created/modified/deleted by another DC,"
906 " do nothing" % (txt, att ))
910 elif usn_in_range(int(attrUSN), usns) == 0:
911 message(CHANGE, "%sAttribute: %s has been" \
912 "created/modified/deleted not during a" \
913 " provision or upgradeprovision: current" \
914 " usn %d , do nothing" % (txt, att, attrUSN))
919 if att == "defaultSecurityDescriptor":
922 message(CHANGE, "%sAttribute: %s will be modified" \
923 "/deleted it was last modified" \
924 "during a provision, current usn:" \
925 "%d" % (txt, att, attrUSN))
928 message(CHANGE, "%sAttribute: %s will be added because" \
929 " it hasn't existed before " % (txt, att))
934 # Old school way of handling things for pre alpha12 upgrade
936 msgElt = delta.get(att)
938 if att == "nTSecurityDescriptor":
945 if not hashOverwrittenAtt.has_key(att):
946 if msgElt.flags() != FLAG_MOD_ADD:
947 if not handle_special_case(att, delta, reference, current,
949 if opts.debugchange or opts.debugall:
951 dump_denied_change(dn, att,
952 messageEltFlagToString(msgElt.flags()),
953 current[0][att], reference[0][att])
955 dump_denied_change(dn, att,
956 messageEltFlagToString(msgElt.flags()),
957 current[0][att], None)
961 if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
963 elif hashOverwrittenAtt.get(att)==never:
968 if len(delta.items()) >1:
969 attributes=", ".join(delta.keys())
970 message(CHANGE, "%s is different from the reference one, changed" \
971 " attributes: %s\n" % (dn, attributes))
972 changed = changed + 1
976 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs):
977 """Check differences between the reference provision and the upgraded one.
979 It looks for all objects which base DN is name.
981 This function will also add the missing object and update existing object
982 to add or remove attributes that were missing.
984 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
986 :param samdb: An LDB object connected to the sam.ldb of the update
988 :param basedn: String value of the DN of the partition
989 :param names: List of key provision parameters
990 :param schema: A Schema object
991 :param provisionUSNs: The USNs modified by provision/upgradeprovision
1001 # Connect to the reference provision and get all the attribute in the
1002 # partition referred by name
1003 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
1004 scope=SCOPE_SUBTREE, attrs=["dn"],
1005 controls=["search_options:1:2"])
1007 current = samdb.search(expression="objectClass=*", base=basedn,
1008 scope=SCOPE_SUBTREE, attrs=["dn"],
1009 controls=["search_options:1:2"])
1010 # Create a hash for speeding the search of new object
1011 for i in range(0, len(reference)):
1012 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
1014 # Create a hash for speeding the search of existing object in the
1016 for i in range(0, len(current)):
1017 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
1020 for k in hash_new.keys():
1021 if not hash.has_key(k):
1022 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
1023 listMissing.append(hash_new[k])
1025 listPresent.append(hash_new[k])
1027 # Sort the missing object in order to have object of the lowest level
1028 # first (which can be containers for higher level objects)
1029 listMissing.sort(dn_sort)
1030 listPresent.sort(dn_sort)
1032 # The following lines is to load the up to
1033 # date schema into our current LDB
1034 # a complete schema is needed as the insertion of attributes
1035 # and class is done against it
1036 # and the schema is self validated
1037 samdb.set_schema_from_ldb(schema.ldb)
1039 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
1040 add_deletedobj_containers(ref_samdb, samdb, names)
1042 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
1043 changed = update_present(ref_samdb, samdb, basedn, listPresent,
1044 provisionUSNs, names.invocation)
1045 message(SIMPLE, "There are %d changed objects" % (changed))
1048 except StandardError, err:
1049 message(ERROR, "Exception during upgrade of samdb:")
1050 (typ, val, tb) = sys.exc_info()
1051 traceback.print_exception(typ, val, tb)
1054 def chunck_acl(acl):
1055 """Return separate ACE of an ACL
1057 :param acl: A string representing the ACL
1058 :return: A hash with different parts
1061 p = re.compile(r'(\w+)?(\(.*?\))')
1062 tab = p.findall(acl)
1068 hash["flags"] = e[0]
1069 hash["aces"].append(e[1])
1074 def chunck_sddl(sddl):
1075 """ Return separate parts of the SDDL (owner, group, ...)
1077 :param sddl: An string containing the SDDL to chunk
1078 :return: A hash with the different chunk
1081 p = re.compile(r'([OGDS]:)(.*?)(?=(?:[GDS]:|$))')
1082 tab = p.findall(sddl)
1087 hash["owner"] = e[1]
1089 hash["group"] = e[1]
1097 def check_updated_sd(ref_sam, cur_sam, names):
1098 """Check if the security descriptor in the upgraded provision are the same
1101 :param ref_sam: A LDB object connected to the sam.ldb file used as
1102 the reference provision
1103 :param cur_sam: A LDB object connected to the sam.ldb file used as
1105 :param names: List of key provision parameters"""
1106 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1107 scope=SCOPE_SUBTREE,
1108 attrs=["dn", "nTSecurityDescriptor"],
1109 controls=["search_options:1:2"])
1110 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1111 scope=SCOPE_SUBTREE,
1112 attrs=["dn", "nTSecurityDescriptor"],
1113 controls=["search_options:1:2"])
1115 for i in range(0,len(reference)):
1116 hash[str(reference[i]["dn"]).lower()] = ndr_unpack(security.descriptor,str(reference[i]["nTSecurityDescriptor"])).as_sddl(names.domainsid)
1118 for i in range(0,len(current)):
1119 key = str(current[i]["dn"]).lower()
1120 if hash.has_key(key):
1121 sddl = ndr_unpack(security.descriptor,str(current[i]["nTSecurityDescriptor"])).as_sddl(names.domainsid)
1122 if sddl != hash[key]:
1124 hash_new = chunck_sddl(sddl)
1125 hash_ref = chunck_sddl(hash[key])
1126 if hash_new["owner"] != hash_ref["owner"]:
1127 txt = "\tOwner mismatch: %s (in ref) %s (in current provision)\n" % (hash_ref["owner"], hash_new["owner"])
1129 if hash_new["group"] != hash_ref["group"]:
1130 txt = "%s\tGroup mismatch: %s (in ref) %s (in current provision)\n" % (txt, hash_ref["group"], hash_new["group"])
1132 for part in ["dacl","sacl"]:
1133 if hash_new.has_key(part) and hash_ref.has_key(part):
1135 # both are present, check if they contain the same ACE
1138 c_new = chunck_acl(hash_new[part])
1139 c_ref = chunck_acl(hash_ref[part])
1141 for elem in c_new["aces"]:
1144 for elem in c_ref["aces"]:
1147 for k in h_ref.keys():
1148 if h_new.has_key(k):
1152 if len(h_new.keys()) + len(h_ref.keys()) > 0:
1153 txt = "%s\tPart %s is different between reference and current provision here is the detail:\n" % (txt, part)
1155 for item in h_new.keys():
1156 txt = "%s\t\t%s ACE is not present in the reference provision\n" % (txt, item)
1158 for item in h_ref.keys():
1159 txt = "%s\t\t%s ACE is not present in the current provision\n" % (txt, item)
1161 elif hash_new.has_key(part) and not hash_ref.has_key(part):
1162 txt = "%s\tReference provision ACL hasn't a %s part\n" % (txt, part)
1163 elif not hash_new.has_key(part) and hash_ref.has_key(part):
1164 txt = "%s\tCurrent provision ACL hasn't a %s part\n" % (txt, part)
1167 print "On object %s ACL is different\n%s" % (current[i]["dn"], txt)
1171 def fix_partition_sd(samdb, names):
1172 """This function fix the SD for partition containers (basedn, configdn, ...)
1173 This is needed because some provision use to have broken SD on containers
1175 :param samdb: An LDB object pointing to the sam of the current provision
1176 :param names: A list of key provision parameters
1178 # First update the SD for the rootdn
1179 res = samdb.search(expression="objectClass=*", base=str(names.rootdn), scope=SCOPE_BASE,\
1180 attrs=["dn", "whenCreated"], controls=["search_options:1:2"])
1182 delta.dn = Dn(samdb,str(res[0]["dn"]))
1183 descr = get_domain_descriptor(names.domainsid)
1184 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, "nTSecurityDescriptor")
1185 samdb.modify(delta,["recalculate_sd:0"])
1186 # Then the config dn
1187 res = samdb.search(expression="objectClass=*",base=str(names.configdn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
1189 delta.dn = Dn(samdb,str(res[0]["dn"]))
1190 descr = get_config_descriptor(names.domainsid)
1191 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, "nTSecurityDescriptor" )
1192 samdb.modify(delta,["recalculate_sd:0"])
1193 # Then the schema dn
1194 res = samdb.search(expression="objectClass=*",base=str(names.schemadn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
1196 delta.dn = Dn(samdb,str(res[0]["dn"]))
1197 descr = get_schema_descriptor(names.domainsid)
1198 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, "nTSecurityDescriptor" )
1199 samdb.modify(delta,["recalculate_sd:0"])
1201 def rebuild_sd(samdb, names):
1202 """Rebuild security descriptor of the current provision from scratch
1204 During the different pre release of samba4 security descriptors (SD) were notarly broken (up to alpha11 included)
1205 This function allow to get them back in order, this function make the assumption that nobody has modified manualy an SD
1206 and so SD can be safely recalculated from scratch to get them right.
1208 :param names: List of key provision parameters"""
1212 res = samdb.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_SUBTREE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
1214 if not (str(obj["dn"]) == str(names.rootdn) or
1215 str(obj["dn"]) == str(names.configdn) or \
1216 str(obj["dn"]) == str(names.schemadn)):
1217 hash[str(obj["dn"])] = obj["whenCreated"]
1219 listkeys = hash.keys()
1220 listkeys.sort(dn_sort)
1222 for key in listkeys:
1225 delta.dn = Dn(samdb,key)
1226 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE, "whenCreated" )
1227 samdb.modify(delta,["recalculate_sd:0"])
1229 # XXX: We should always catch an explicit exception.
1230 # What could go wrong here?
1231 samdb.transaction_cancel()
1232 res = samdb.search(expression="objectClass=*", base=str(names.rootdn), scope=SCOPE_SUBTREE,\
1233 attrs=["dn","nTSecurityDescriptor"], controls=["search_options:1:2"])
1234 print "bad stuff" +ndr_unpack(security.descriptor,str(res[0]["nTSecurityDescriptor"])).as_sddl(names.domainsid)
1237 def removeProvisionUSN(samdb):
1238 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1239 entry = samdb.search(expression="dn=@PROVISION", base = "",
1240 scope=SCOPE_SUBTREE,
1241 controls=["search_options:1:2"],
1244 empty.dn = entry[0].dn
1245 delta = samdb.msg_diff(entry[0], empty)
1247 delta.dn = entry[0].dn
1250 def delta_update_basesamdb(refpaths, paths, creds, session, lp):
1251 """Update the provision container db: sam.ldb
1252 This function is aimed for alpha9 and newer;
1254 :param refpaths: An object holding the different importants paths for reference provision object
1255 :param paths: An object holding the different importants paths for upgraded provision object
1256 :param creds: Credential used for openning LDB files
1257 :param session: Session to use for openning LDB files
1258 :param lp: A loadparam object"""
1260 message(SIMPLE,"Update base samdb by searching difference with reference one")
1261 refsam = Ldb(refpaths.samdb, session_info=session, credentials=creds, lp=lp, options=["modules:"] )
1262 sam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp, options=["modules:"] )
1266 reference = refsam.search(expression="")
1268 for refentry in reference:
1269 entry = sam.search(expression="dn=%s" % refentry["dn"],scope=SCOPE_SUBTREE)
1270 if not len(entry[0]):
1271 message(CHANGE,"Adding %s to sam db" % str(delta.dn))
1272 delta = sam.msg_diff(empty,refentry)
1273 if str(refentry.dn) == "@PROVISION" and delta.get(samba.provision.LAST_PROVISION_USN_ATTRIBUTE):
1274 delta.remove(samba.provision.LAST_PROVISION_USN_ATTRIBUTE)
1275 delta.dn = refentry.dn
1278 delta = sam.msg_diff(entry[0],refentry)
1279 if str(refentry.dn) == "@PROVISION" and delta.get(samba.provision.LAST_PROVISION_USN_ATTRIBUTE):
1280 delta.remove(samba.provision.LAST_PROVISION_USN_ATTRIBUTE)
1281 if len(delta.items()) > 1:
1282 delta.dn = refentry.dn
1286 def simple_update_basesamdb(newpaths, paths, names):
1287 """Update the provision container db: sam.ldb
1288 This function is aimed at very old provision (before alpha9)
1290 :param newpaths: List of paths for different provision objects from the reference provision
1291 :param paths: List of paths for different provision objects from the upgraded provision
1292 :param names: List of key provision parameters"""
1294 message(SIMPLE, "Copy samdb")
1295 shutil.copy(newpaths.samdb,paths.samdb)
1297 message(SIMPLE, "Update partitions filename if needed")
1298 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1299 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1300 usersldb = os.path.join(paths.private_dir, "users.ldb")
1301 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1303 if not os.path.isdir(samldbdir):
1305 os.chmod(samldbdir,0700)
1306 if os.path.isfile(schemaldb):
1307 shutil.copy(schemaldb, os.path.join(samldbdir, "%s.ldb" % str(names.schemadn).upper()))
1308 os.remove(schemaldb)
1309 if os.path.isfile(usersldb):
1310 shutil.copy(usersldb, os.path.join(samldbdir, "%s.ldb" % str(names.rootdn).upper()))
1312 if os.path.isfile(configldb):
1313 shutil.copy(configldb, os.path.join(samldbdir, "%s.ldb" % str(names.configdn).upper()))
1314 os.remove(configldb)
1317 def update_privilege(ref_private_path, cur_private_path):
1318 """Update the privilege database
1320 :param ref_private_path: Path to the private directory of the reference provision.
1321 :param cur_private_path: Path to the private directory of the current (and to be updated) provision."""
1322 message(SIMPLE, "Copy privilege")
1323 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1324 os.path.join(cur_private_path, "privilege.ldb"))
1327 def update_samdb(ref_samdb, samdb, names, highestUSN, schema):
1328 """Upgrade the SAM DB contents for all the provision partitions
1330 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference provision
1331 :param samdb: An LDB object connected to the sam.ldb of the update provision
1332 :param names: List of key provision parameters
1333 :param highestUSN: The highest USN modified by provision/upgradeprovision last time
1334 :param schema: A Schema object that represent the schema of the provision"""
1336 message(SIMPLE, "Starting update of samdb")
1337 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names, schema, highestUSN)
1339 message(SIMPLE,"Update of samdb finished")
1342 message(SIMPLE,"Update failed")
1346 def update_machine_account_password(samdb, secrets_ldb, names):
1347 """Update (change) the password of the current DC both in the SAM db and in secret one
1349 :param samdb: An LDB object related to the sam.ldb file of a given provision
1350 :param secrets_ldb: An LDB object related to the secrets.ldb file of a given provision
1351 :param names: List of key provision parameters"""
1353 message(SIMPLE,"Update machine account")
1354 secrets_msg = secrets_ldb.search(expression=("samAccountName=%s$" % names.netbiosname), attrs=["secureChannelType"])
1355 if int(secrets_msg[0]["secureChannelType"][0]) == SEC_CHAN_BDC:
1356 res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname), attrs=[])
1357 assert(len(res) == 1)
1359 msg = Message(res[0].dn)
1360 machinepass = samba.generate_random_password(128, 255)
1361 msg["userPassword"] = MessageElement(machinepass, FLAG_MOD_REPLACE, "userPassword")
1364 res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname),
1365 attrs=["msDs-keyVersionNumber"])
1366 assert(len(res) == 1)
1367 kvno = int(str(res[0]["msDs-keyVersionNumber"]))
1369 secretsdb_self_join(secrets_ldb, domain=names.domain,
1370 realm=names.realm or sambaopts._lp.get('realm'),
1371 domainsid=names.domainsid,
1372 dnsdomain=names.dnsdomain,
1373 netbiosname=names.netbiosname,
1374 machinepass=machinepass,
1375 key_version_number=kvno,
1376 secure_channel_type=int(secrets_msg[0]["secureChannelType"][0]))
1378 raise ProvisioningError("Unable to find a Secure Channel of type SEC_CHAN_BDC")
1381 def update_gpo(paths,creds,session,names):
1382 """Create missing GPO file object if needed
1384 Set ACL correctly also.
1386 dir = getpolicypath(paths.sysvol,names.dnsdomain,names.policyid)
1387 if not os.path.isdir(dir):
1388 create_gpo_struct(dir)
1390 dir = getpolicypath(paths.sysvol,names.dnsdomain,names.policyid_dc)
1391 if not os.path.isdir(dir):
1392 create_gpo_struct(dir)
1393 samdb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp)
1394 set_gpo_acl(paths.sysvol, names.dnsdomain, names.domainsid,
1395 names.domaindn, samdb, lp)
1398 def getOEMInfo(samdb, rootdn):
1399 """Return OEM Information on the top level
1400 Samba4 use to store version info in this field
1402 :param samdb: An LDB object connect to sam.ldb
1403 :param rootdn: Root DN of the domain
1404 :return: The content of the field oEMInformation (if any)"""
1405 res = samdb.search(expression="(objectClass=*)", base=str(rootdn),
1406 scope=SCOPE_BASE, attrs=["dn", "oEMInformation"])
1408 info = res[0]["oEMInformation"]
1413 def updateOEMInfo(samdb, names):
1414 res = samdb.search(expression="(objectClass=*)", base=str(names.rootdn),
1415 scope=SCOPE_BASE, attrs=["dn", "oEMInformation"])
1417 info = res[0]["oEMInformation"]
1418 info = "%s, upgrade to %s" % (info, version)
1420 delta.dn = Dn(samdb, str(res[0]["dn"]))
1421 delta["oEMInformation"] = MessageElement(info, FLAG_MOD_REPLACE,
1426 def setup_path(file):
1427 return os.path.join(setup_dir, file)
1430 if __name__ == '__main__':
1431 global defSDmodified
1433 # From here start the big steps of the program
1434 # First get files paths
1435 paths = get_paths(param, smbconf=smbconf)
1436 paths.setup = setup_dir
1437 # Get ldbs with the system session, it is needed for searching provision parameters
1438 session = system_session()
1440 # This variable will hold the last provision USN once if it exists.
1443 ldbs = get_ldbs(paths, creds, session, lp)
1444 ldbs.startTransactions()
1446 # Guess all the needed names (variables in fact) from the current
1448 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, paths, smbconf, lp)
1449 lastProvisionUSNs = getLastProvisionUSN(ldbs.sam)
1450 if lastProvisionUSNs != None:
1452 "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
1454 # Objects will be created with the admin session (not anymore system session)
1455 adm_session = admin_session(lp, str(names.domainsid))
1456 # So we reget handle on objects
1457 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1459 if not sanitychecks(ldbs.sam, names):
1460 message(SIMPLE,"Sanity checks for the upgrade fails, checks messages and correct them before rerunning upgradeprovision")
1463 # Let's see provision parameters
1464 print_provision_key_parameters(names)
1466 # 5) With all this information let's create a fresh new provision used as
1468 message(SIMPLE, "Creating a reference provision")
1469 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1470 prefix="referenceprovision")
1471 newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
1475 # We need to get a list of object which SD is directly computed from
1476 # defaultSecurityDescriptor.
1477 # This will allow us to know which object we can rebuild the SD in case
1478 # of change of the parent's SD or of the defaultSD.
1479 # Get file paths of this new provision
1480 newpaths = get_paths(param, targetdir=provisiondir)
1481 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1482 new_ldbs.startTransactions()
1484 # Populate some associative array to ease the update process
1485 # List of attribute which are link and backlink
1486 populate_links(new_ldbs.sam, names.schemadn)
1487 # List of attribute with ASN DN synthax)
1488 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1490 update_privilege(newpaths.private_dir,paths.private_dir)
1491 oem = getOEMInfo(ldbs.sam, names.rootdn)
1492 # Do some modification on sam.ldb
1493 ldbs.groupedCommit()
1494 if re.match(".*alpha((9)|(\d\d+)).*",str(oem)):
1495 # Starting from alpha9 we can consider that the structure is quite ok and that we should do only dela
1496 new_ldbs.groupedCommit()
1497 delta_update_basesamdb(newpaths, paths, creds, session, lp)
1498 ldbs.startTransactions()
1499 minUSN = get_max_usn(ldbs.sam, str(names.rootdn)) + 1
1500 new_ldbs.startTransactions()
1502 simple_update_basesamdb(newpaths, paths, names)
1503 ldbs = get_ldbs(paths, creds, session, lp)
1504 ldbs.startTransactions()
1505 removeProvisionUSN(ldbs.sam)
1507 schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
1508 serverdn=str(names.serverdn))
1511 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1513 message(SIMPLE, "Rollbacking every changes. Check the reason\
1515 message(SIMPLE, "In any case your system as it was before\
1517 ldbs.groupedRollback()
1518 new_ldbs.groupedRollback()
1519 shutil.rmtree(provisiondir)
1522 update_secrets(new_ldbs.secrets,ldbs.secrets)
1523 update_machine_account_password(ldbs.sam,ldbs.secrets, names)
1525 # SD should be created with admin but as some previous acl were so wrong that admin can't modify them we have first
1526 # to recreate them with the good form but with system account and then give the ownership to admin ...
1527 if not re.match(r'.*alpha(9|\d\d+)',str(oem)):
1528 message(SIMPLE, "Fixing old povision SD")
1529 fix_partition_sd(ldbs.sam,names)
1530 rebuild_sd(ldbs.sam,names)
1532 # We calculate the max USN before recalculating the SD because we might
1533 # touch object that have been modified after a provision and we do not
1534 # want that the next upgradeprovision thinks that it has a green light
1537 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1539 # We rebuild SD only if defaultSecurityDescriptor is modified
1540 # But in fact we should do it also if one object has its SD modified as
1541 # child might need rebuild
1542 if defSDmodified == 1:
1543 message(SIMPLE, "Updating SD")
1544 ldbs.sam.set_session_info(adm_session)
1545 # Alpha10 was a bit broken still
1546 if re.match(r'.*alpha(\d|10)',str(oem)):
1547 fix_partition_sd(ldbs.sam,names)
1548 rebuild_sd(ldbs.sam, names)
1550 # Now we are quite confident in the recalculate process of the SD, we make it optional
1551 # Also the check must be done in a clever way as for the moment we just compare SDDL
1552 if opts.debugchangesd:
1553 check_updated_sd(new_ldbs.sam,ldbs.sam, names)
1555 updateOEMInfo(ldbs.sam,names)
1556 check_for_DNS(newpaths.private_dir, paths.private_dir)
1557 if lastProvisionUSNs != None:
1558 updateProvisionUSN(ldbs.sam, minUSN, maxUSN)
1559 ldbs.groupedCommit()
1560 new_ldbs.groupedCommit()
1561 message(SIMPLE, "Upgrade finished !")
1562 # remove reference provision now that everything is done !
1563 shutil.rmtree(provisiondir)