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),
1180 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1181 controls=["search_options:1:2"])
1183 delta.dn = Dn(samdb, str(res[0]["dn"]))
1184 descr = get_domain_descriptor(names.domainsid)
1185 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1186 "nTSecurityDescriptor")
1187 samdb.modify(delta, ["recalculate_sd:0"])
1188 # Then the config dn
1189 res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1190 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1191 controls=["search_options:1:2"])
1193 delta.dn = Dn(samdb, str(res[0]["dn"]))
1194 descr = get_config_descriptor(names.domainsid)
1195 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1196 "nTSecurityDescriptor" )
1197 samdb.modify(delta, ["recalculate_sd:0"])
1198 # Then the schema dn
1199 res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1200 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1201 controls=["search_options:1:2"])
1204 delta.dn = Dn(samdb, str(res[0]["dn"]))
1205 descr = get_schema_descriptor(names.domainsid)
1206 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1207 "nTSecurityDescriptor" )
1208 samdb.modify(delta, ["recalculate_sd:0"])
1210 def rebuild_sd(samdb, names):
1211 """Rebuild security descriptor of the current provision from scratch
1213 During the different pre release of samba4 security descriptors (SD)
1214 were notarly broken (up to alpha11 included)
1215 This function allow to get them back in order, this function make the
1216 assumption that nobody has modified manualy an SD
1217 and so SD can be safely recalculated from scratch to get them right.
1219 :param names: List of key provision parameters"""
1223 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1224 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1225 controls=["search_options:1:2"])
1227 if not (str(obj["dn"]) == str(names.rootdn) or
1228 str(obj["dn"]) == str(names.configdn) or \
1229 str(obj["dn"]) == str(names.schemadn)):
1230 hash[str(obj["dn"])] = obj["whenCreated"]
1232 listkeys = hash.keys()
1233 listkeys.sort(dn_sort)
1235 for key in listkeys:
1238 delta.dn = Dn(samdb, key)
1239 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1241 samdb.modify(delta, ["recalculate_sd:0"])
1243 # XXX: We should always catch an explicit exception.
1244 # What could go wrong here?
1245 samdb.transaction_cancel()
1246 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1247 scope=SCOPE_SUBTREE,
1248 attrs=["dn", "nTSecurityDescriptor"],
1249 controls=["search_options:1:2"])
1250 badsd = ndr_unpack(security.descriptor,
1251 str(res[0]["nTSecurityDescriptor"]))
1252 print "bad stuff %s"%badsd.as_sddl(names.domainsid)
1255 def removeProvisionUSN(samdb):
1256 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1257 entry = samdb.search(expression="dn=@PROVISION", base = "",
1258 scope=SCOPE_SUBTREE,
1259 controls=["search_options:1:2"],
1262 empty.dn = entry[0].dn
1263 delta = samdb.msg_diff(entry[0], empty)
1265 delta.dn = entry[0].dn
1268 def delta_update_basesamdb(refpaths, paths, creds, session, lp):
1269 """Update the provision container db: sam.ldb
1270 This function is aimed for alpha9 and newer;
1272 :param refpaths: An object holding the different importants paths for
1273 reference provision object
1274 :param paths: An object holding the different importants paths for
1275 upgraded provision object
1276 :param creds: Credential used for openning LDB files
1277 :param session: Session to use for openning LDB files
1278 :param lp: A loadparam object"""
1281 "Update base samdb by searching difference with reference one")
1282 refsam = Ldb(refpaths.samdb, session_info=session, credentials=creds,
1283 lp=lp, options=["modules:"])
1284 sam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
1285 options=["modules:"])
1289 reference = refsam.search(expression="")
1291 for refentry in reference:
1292 entry = sam.search(expression="dn=%s" % refentry["dn"],
1293 scope=SCOPE_SUBTREE)
1295 message(CHANGE, "Adding %s to sam db" % str(delta.dn))
1296 delta = sam.msg_diff(empty, refentry)
1297 if str(refentry.dn) == "@PROVISION" and\
1298 delta.get(samba.provision.LAST_PROVISION_USN_ATTRIBUTE):
1299 delta.remove(samba.provision.LAST_PROVISION_USN_ATTRIBUTE)
1300 delta.dn = refentry.dn
1303 delta = sam.msg_diff(entry[0], refentry)
1304 if str(refentry.dn) == "@PROVISION" and\
1305 delta.get(samba.provision.LAST_PROVISION_USN_ATTRIBUTE):
1306 delta.remove(samba.provision.LAST_PROVISION_USN_ATTRIBUTE)
1307 if len(delta.items()) > 1:
1308 delta.dn = refentry.dn
1312 def simple_update_basesamdb(newpaths, paths, names):
1313 """Update the provision container db: sam.ldb
1314 This function is aimed at very old provision (before alpha9)
1316 :param newpaths: List of paths for different provision objects
1317 from the reference provision
1318 :param paths: List of paths for different provision objects
1319 from the upgraded provision
1320 :param names: List of key provision parameters"""
1322 message(SIMPLE, "Copy samdb")
1323 shutil.copy(newpaths.samdb, paths.samdb)
1325 message(SIMPLE, "Update partitions filename if needed")
1326 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1327 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1328 usersldb = os.path.join(paths.private_dir, "users.ldb")
1329 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1331 if not os.path.isdir(samldbdir):
1333 os.chmod(samldbdir, 0700)
1334 if os.path.isfile(schemaldb):
1335 shutil.copy(schemaldb, os.path.join(samldbdir,
1336 "%s.ldb"%str(names.schemadn).upper()))
1337 os.remove(schemaldb)
1338 if os.path.isfile(usersldb):
1339 shutil.copy(usersldb, os.path.join(samldbdir,
1340 "%s.ldb"%str(names.rootdn).upper()))
1342 if os.path.isfile(configldb):
1343 shutil.copy(configldb, os.path.join(samldbdir,
1344 "%s.ldb"%str(names.configdn).upper()))
1345 os.remove(configldb)
1348 def update_privilege(ref_private_path, cur_private_path):
1349 """Update the privilege database
1351 :param ref_private_path: Path to the private directory of the reference
1353 :param cur_private_path: Path to the private directory of the current
1354 (and to be updated) provision."""
1355 message(SIMPLE, "Copy privilege")
1356 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1357 os.path.join(cur_private_path, "privilege.ldb"))
1360 def update_samdb(ref_samdb, samdb, names, highestUSN, schema):
1361 """Upgrade the SAM DB contents for all the provision partitions
1363 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1365 :param samdb: An LDB object connected to the sam.ldb of the update
1367 :param names: List of key provision parameters
1368 :param highestUSN: The highest USN modified by provision/upgradeprovision
1370 :param schema: A Schema object that represent the schema of the provision"""
1372 message(SIMPLE, "Starting update of samdb")
1373 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1376 message(SIMPLE, "Update of samdb finished")
1379 message(SIMPLE, "Update failed")
1383 def update_machine_account_password(samdb, secrets_ldb, names):
1384 """Update (change) the password of the current DC both in the SAM db and in
1387 :param samdb: An LDB object related to the sam.ldb file of a given provision
1388 :param secrets_ldb: An LDB object related to the secrets.ldb file of a given
1390 :param names: List of key provision parameters"""
1392 message(SIMPLE, "Update machine account")
1393 expression = "samAccountName=%s$" % names.netbiosname
1394 secrets_msg = secrets_ldb.search(expression=expression,
1395 attrs=["secureChannelType"])
1396 if int(secrets_msg[0]["secureChannelType"][0]) == SEC_CHAN_BDC:
1397 res = samdb.search(expression=expression, attrs=[])
1398 assert(len(res) == 1)
1400 msg = Message(res[0].dn)
1401 machinepass = samba.generate_random_password(128, 255)
1402 msg["userPassword"] = MessageElement(machinepass, FLAG_MOD_REPLACE,
1406 res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname),
1407 attrs=["msDs-keyVersionNumber"])
1408 assert(len(res) == 1)
1409 kvno = int(str(res[0]["msDs-keyVersionNumber"]))
1410 secChanType = int(secrets_msg[0]["secureChannelType"][0])
1412 secretsdb_self_join(secrets_ldb, domain=names.domain,
1413 realm=names.realm or sambaopts._lp.get('realm'),
1414 domainsid=names.domainsid,
1415 dnsdomain=names.dnsdomain,
1416 netbiosname=names.netbiosname,
1417 machinepass=machinepass,
1418 key_version_number=kvno,
1419 secure_channel_type=secChanType)
1421 raise ProvisioningError("Unable to find a Secure Channel" \
1422 "of type SEC_CHAN_BDC")
1425 def update_gpo(paths, creds, session, names):
1426 """Create missing GPO file object if needed
1428 Set ACL correctly also.
1430 dir = getpolicypath(paths.sysvol, names.dnsdomain, names.policyid)
1431 if not os.path.isdir(dir):
1432 create_gpo_struct(dir)
1434 dir = getpolicypath(paths.sysvol, names.dnsdomain, names.policyid_dc)
1435 if not os.path.isdir(dir):
1436 create_gpo_struct(dir)
1437 samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
1438 set_gpo_acl(paths.sysvol, names.dnsdomain, names.domainsid,
1439 names.domaindn, samdb, lp)
1442 def getOEMInfo(samdb, rootdn):
1443 """Return OEM Information on the top level
1444 Samba4 use to store version info in this field
1446 :param samdb: An LDB object connect to sam.ldb
1447 :param rootdn: Root DN of the domain
1448 :return: The content of the field oEMInformation (if any)"""
1449 res = samdb.search(expression="(objectClass=*)", base=str(rootdn),
1450 scope=SCOPE_BASE, attrs=["dn", "oEMInformation"])
1452 info = res[0]["oEMInformation"]
1457 def updateOEMInfo(samdb, names):
1458 res = samdb.search(expression="(objectClass=*)", base=str(names.rootdn),
1459 scope=SCOPE_BASE, attrs=["dn", "oEMInformation"])
1461 info = res[0]["oEMInformation"]
1462 info = "%s, upgrade to %s" % (info, version)
1464 delta.dn = Dn(samdb, str(res[0]["dn"]))
1465 delta["oEMInformation"] = MessageElement(info, FLAG_MOD_REPLACE,
1470 def setup_path(file):
1471 return os.path.join(setup_dir, file)
1474 if __name__ == '__main__':
1475 global defSDmodified
1477 # From here start the big steps of the program
1478 # First get files paths
1479 paths = get_paths(param, smbconf=smbconf)
1480 paths.setup = setup_dir
1481 # Get ldbs with the system session, it is needed for searching
1482 # provision parameters
1483 session = system_session()
1485 # This variable will hold the last provision USN once if it exists.
1488 ldbs = get_ldbs(paths, creds, session, lp)
1489 ldbs.startTransactions()
1491 # Guess all the needed names (variables in fact) from the current
1493 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, paths,
1495 lastProvisionUSNs = getLastProvisionUSN(ldbs.sam)
1496 if lastProvisionUSNs != None:
1498 "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
1500 # Objects will be created with the admin session
1501 # (not anymore system session)
1502 adm_session = admin_session(lp, str(names.domainsid))
1503 # So we reget handle on objects
1504 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1506 if not sanitychecks(ldbs.sam, names):
1507 message(SIMPLE, "Sanity checks for the upgrade fails, checks messages" \
1508 " and correct them before rerunning upgradeprovision")
1511 # Let's see provision parameters
1512 print_provision_key_parameters(names)
1514 # 5) With all this information let's create a fresh new provision used as
1516 message(SIMPLE, "Creating a reference provision")
1517 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1518 prefix="referenceprovision")
1519 newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
1523 # We need to get a list of object which SD is directly computed from
1524 # defaultSecurityDescriptor.
1525 # This will allow us to know which object we can rebuild the SD in case
1526 # of change of the parent's SD or of the defaultSD.
1527 # Get file paths of this new provision
1528 newpaths = get_paths(param, targetdir=provisiondir)
1529 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1530 new_ldbs.startTransactions()
1532 # Populate some associative array to ease the update process
1533 # List of attribute which are link and backlink
1534 populate_links(new_ldbs.sam, names.schemadn)
1535 # List of attribute with ASN DN synthax)
1536 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1538 update_privilege(newpaths.private_dir, paths.private_dir)
1539 oem = getOEMInfo(ldbs.sam, names.rootdn)
1540 # Do some modification on sam.ldb
1541 ldbs.groupedCommit()
1542 if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1543 # Starting from alpha9 we can consider that the structure is quite ok
1544 # and that we should do only dela
1545 new_ldbs.groupedCommit()
1546 delta_update_basesamdb(newpaths, paths, creds, session, lp)
1547 ldbs.startTransactions()
1548 minUSN = get_max_usn(ldbs.sam, str(names.rootdn)) + 1
1549 new_ldbs.startTransactions()
1551 simple_update_basesamdb(newpaths, paths, names)
1552 ldbs = get_ldbs(paths, creds, session, lp)
1553 ldbs.startTransactions()
1554 removeProvisionUSN(ldbs.sam)
1556 schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
1557 serverdn=str(names.serverdn))
1560 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1562 message(SIMPLE, "Rollbacking every changes. Check the reason" \
1564 message(SIMPLE, "In any case your system as it was before" \
1566 ldbs.groupedRollback()
1567 new_ldbs.groupedRollback()
1568 shutil.rmtree(provisiondir)
1571 update_secrets(new_ldbs.secrets, ldbs.secrets)
1572 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1574 # SD should be created with admin but as some previous acl were so wrong
1575 # that admin can't modify them we have first to recreate them with the good
1576 # form but with system account and then give the ownership to admin ...
1577 if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1578 message(SIMPLE, "Fixing old povision SD")
1579 fix_partition_sd(ldbs.sam, names)
1580 rebuild_sd(ldbs.sam, names)
1582 # We calculate the max USN before recalculating the SD because we might
1583 # touch object that have been modified after a provision and we do not
1584 # want that the next upgradeprovision thinks that it has a green light
1587 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1589 # We rebuild SD only if defaultSecurityDescriptor is modified
1590 # But in fact we should do it also if one object has its SD modified as
1591 # child might need rebuild
1592 if defSDmodified == 1:
1593 message(SIMPLE, "Updating SD")
1594 ldbs.sam.set_session_info(adm_session)
1595 # Alpha10 was a bit broken still
1596 if re.match(r'.*alpha(\d|10)', str(oem)):
1597 fix_partition_sd(ldbs.sam, names)
1598 rebuild_sd(ldbs.sam, names)
1600 # Now we are quite confident in the recalculate process of the SD, we make
1602 # Also the check must be done in a clever way as for the moment we just
1604 if opts.debugchangesd:
1605 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1607 updateOEMInfo(ldbs.sam, names)
1608 check_for_DNS(newpaths.private_dir, paths.private_dir)
1609 if lastProvisionUSNs != None:
1610 updateProvisionUSN(ldbs.sam, minUSN, maxUSN)
1611 ldbs.groupedCommit()
1612 new_ldbs.groupedCommit()
1613 message(SIMPLE, "Upgrade finished !")
1614 # remove reference provision now that everything is done !
1615 shutil.rmtree(provisiondir)