s4-upgradeprovision: fix inverted logic and wrong flags on sd_flags control
[Samba/gbeck.git] / source4 / scripting / bin / upgradeprovision
blobb49ccf91c001b2a49104560161dac4cb8a657c75
1 #!/usr/bin/env python
2 # vim: expandtab
4 # Copyright (C) Matthieu Patou <mat@matws.net> 2009 - 2010
6 # Based on provision a Samba4 server by
7 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
8 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
26 import optparse
27 import os
28 import shutil
29 import sys
30 import tempfile
31 import re
32 import traceback
33 # Allow to run from s4 source directory (without installing samba)
34 sys.path.insert(0, "bin/python")
36 import ldb
37 import samba
38 import samba.getopt as options
40 from base64 import b64encode
41 from samba.credentials import DONT_USE_KERBEROS
42 from samba.auth import system_session, admin_session
43 from ldb import (SCOPE_SUBTREE, SCOPE_BASE,
44 FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE,
45 MessageElement, Message, Dn)
46 from samba import param, dsdb, Ldb
47 from samba.provision import (get_domain_descriptor, find_provision_key_parameters,
48 get_config_descriptor,
49 ProvisioningError, get_last_provision_usn,
50 get_max_usn, update_provision_usn, setup_path)
51 from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
52 from samba.dcerpc import security, drsblobs, xattr
53 from samba.ndr import ndr_unpack
54 from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
55 get_ldbs,
56 usn_in_range, identic_rename, get_diff_sddls,
57 update_secrets, CHANGE, ERROR, SIMPLE,
58 CHANGEALL, GUESS, CHANGESD, PROVISION,
59 updateOEMInfo, getOEMInfo, update_gpo,
60 delta_update_basesamdb, update_policyids,
61 update_machine_account_password,
62 search_constructed_attrs_stored,
63 int64range2str, update_dns_account_password,
64 increment_calculated_keyversion_number)
66 replace=2**FLAG_MOD_REPLACE
67 add=2**FLAG_MOD_ADD
68 delete=2**FLAG_MOD_DELETE
69 never=0
72 # Will be modified during provision to tell if default sd has been modified
73 # somehow ...
75 #Errors are always logged
77 __docformat__ = "restructuredText"
79 # Attributes that are never copied from the reference provision (even if they
80 # do not exist in the destination object).
81 # This is most probably because they are populated automatcally when object is
82 # created
83 # This also apply to imported object from reference provision
84 hashAttrNotCopied = { "dn": 1, "whenCreated": 1, "whenChanged": 1,
85 "objectGUID": 1, "uSNCreated": 1,
86 "replPropertyMetaData": 1, "uSNChanged": 1,
87 "parentGUID": 1, "objectCategory": 1,
88 "distinguishedName": 1, "nTMixedDomain": 1,
89 "showInAdvancedViewOnly": 1, "instanceType": 1,
90 "msDS-Behavior-Version":1, "nextRid":1, "cn": 1,
91 "lmPwdHistory":1, "pwdLastSet": 1,
92 "ntPwdHistory":1, "unicodePwd":1,"dBCSPwd":1,
93 "supplementalCredentials":1, "gPCUserExtensionNames":1,
94 "gPCMachineExtensionNames":1,"maxPwdAge":1, "secret":1,
95 "possibleInferiors":1, "privilege":1,
96 "sAMAccountType":1 }
98 # Usually for an object that already exists we do not overwrite attributes as
99 # they might have been changed for good reasons. Anyway for a few of them it's
100 # mandatory to replace them otherwise the provision will be broken somehow.
101 # But for attribute that are just missing we do not have to specify them as the default
102 # behavior is to add missing attribute
103 hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace,
104 "systemOnly":replace, "searchFlags":replace,
105 "mayContain":replace, "systemFlags":replace+add,
106 "description":replace, "operatingSystemVersion":replace,
107 "adminPropertyPages":replace, "groupType":replace,
108 "wellKnownObjects":replace, "privilege":never,
109 "defaultSecurityDescriptor": replace,
110 "rIDAvailablePool": never,
111 "rIDNextRID": add, "rIDUsedPool": never,
112 "defaultSecurityDescriptor": replace + add,
113 "isMemberOfPartialAttributeSet": delete,
114 "attributeDisplayNames": replace + add,
115 "versionNumber": add}
117 backlinked = []
118 forwardlinked = set()
119 dn_syntax_att = []
120 not_replicated = []
121 def define_what_to_log(opts):
122 what = 0
123 if opts.debugchange:
124 what = what | CHANGE
125 if opts.debugchangesd:
126 what = what | CHANGESD
127 if opts.debugguess:
128 what = what | GUESS
129 if opts.debugprovision:
130 what = what | PROVISION
131 if opts.debugall:
132 what = what | CHANGEALL
133 return what
136 parser = optparse.OptionParser("provision [options]")
137 sambaopts = options.SambaOptions(parser)
138 parser.add_option_group(sambaopts)
139 parser.add_option_group(options.VersionOptions(parser))
140 credopts = options.CredentialsOptions(parser)
141 parser.add_option_group(credopts)
142 parser.add_option("--setupdir", type="string", metavar="DIR",
143 help="directory with setup files")
144 parser.add_option("--debugprovision", help="Debug provision", action="store_true")
145 parser.add_option("--debugguess", action="store_true",
146 help="Print information on which values are guessed")
147 parser.add_option("--debugchange", action="store_true",
148 help="Print information on what is different but won't be changed")
149 parser.add_option("--debugchangesd", action="store_true",
150 help="Print security descriptor differences")
151 parser.add_option("--debugall", action="store_true",
152 help="Print all available information (very verbose)")
153 parser.add_option("--resetfileacl", action="store_true",
154 help="Force a reset on filesystem acls in sysvol / netlogon share")
155 parser.add_option("--fixntacl", action="store_true",
156 help="Only fix NT ACLs in sysvol / netlogon share")
157 parser.add_option("--full", action="store_true",
158 help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
160 opts = parser.parse_args()[0]
162 handler = logging.StreamHandler(sys.stdout)
163 upgrade_logger = logging.getLogger("upgradeprovision")
164 upgrade_logger.setLevel(logging.INFO)
166 upgrade_logger.addHandler(handler)
168 provision_logger = logging.getLogger("provision")
169 provision_logger.addHandler(handler)
171 whatToLog = define_what_to_log(opts)
173 def message(what, text):
174 """Print a message if this message type has been selected to be printed
176 :param what: Category of the message
177 :param text: Message to print """
178 if (whatToLog & what) or what <= 0:
179 upgrade_logger.info("%s", text)
181 if len(sys.argv) == 1:
182 opts.interactive = True
183 lp = sambaopts.get_loadparm()
184 smbconf = lp.configfile
186 creds = credopts.get_credentials(lp)
187 creds.set_kerberos_state(DONT_USE_KERBEROS)
191 def check_for_DNS(refprivate, private):
192 """Check if the provision has already the requirement for dynamic dns
194 :param refprivate: The path to the private directory of the reference
195 provision
196 :param private: The path to the private directory of the upgraded
197 provision"""
199 spnfile = "%s/spn_update_list" % private
200 dnsfile = "%s/dns_update_list" % private
201 namedfile = lp.get("dnsupdate:path")
203 if not namedfile:
204 namedfile = "%s/named.conf.update" % private
206 if not os.path.exists(spnfile):
207 shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
209 if not os.path.exists(dnsfile):
210 shutil.copy("%s/dns_update_list" % refprivate, "%s" % dnsfile)
212 destdir = "%s/new_dns" % private
213 dnsdir = "%s/dns" % private
215 if not os.path.exists(namedfile):
216 if not os.path.exists(destdir):
217 os.mkdir(destdir)
218 if not os.path.exists(dnsdir):
219 os.mkdir(dnsdir)
220 shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
221 shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
222 message(SIMPLE, "It seems that your provision did not integrate "
223 "new rules for dynamic dns update of domain related entries")
224 message(SIMPLE, "A copy of the new bind configuration files and "
225 "template has been put in %s, you should read them and "
226 "configure dynamic dns updates" % destdir)
229 def populate_links(samdb, schemadn):
230 """Populate an array with all the back linked attributes
232 This attributes that are modified automaticaly when
233 front attibutes are changed
235 :param samdb: A LDB object for sam.ldb file
236 :param schemadn: DN of the schema for the partition"""
237 linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
238 backlinked.extend(linkedAttHash.values())
239 for t in linkedAttHash.keys():
240 forwardlinked.add(t)
242 def isReplicated(att):
243 """ Indicate if the attribute is replicated or not
245 :param att: Name of the attribute to be tested
246 :return: True is the attribute is replicated, False otherwise
249 return (att not in not_replicated)
251 def populateNotReplicated(samdb, schemadn):
252 """Populate an array with all the attributes that are not replicated
254 :param samdb: A LDB object for sam.ldb file
255 :param schemadn: DN of the schema for the partition"""
256 res = samdb.search(expression="(&(objectclass=attributeSchema)(systemflags:1.2.840.113556.1.4.803:=1))", base=Dn(samdb,
257 str(schemadn)), scope=SCOPE_SUBTREE,
258 attrs=["lDAPDisplayName"])
259 for elem in res:
260 not_replicated.append(elem["lDAPDisplayName"])
262 def populate_dnsyntax(samdb, schemadn):
263 """Populate an array with all the attributes that have DN synthax
264 (oid 2.5.5.1)
266 :param samdb: A LDB object for sam.ldb file
267 :param schemadn: DN of the schema for the partition"""
268 res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
269 str(schemadn)), scope=SCOPE_SUBTREE,
270 attrs=["lDAPDisplayName"])
271 for elem in res:
272 dn_syntax_att.append(elem["lDAPDisplayName"])
275 def sanitychecks(samdb, names):
276 """Make some checks before trying to update
278 :param samdb: An LDB object opened on sam.ldb
279 :param names: list of key provision parameters
280 :return: Status of check (1 for Ok, 0 for not Ok) """
281 res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
282 scope=SCOPE_SUBTREE, attrs=["dn"],
283 controls=["search_options:1:2"])
284 if len(res) == 0:
285 print "No DC found. Your provision is most probably broken!"
286 return False
287 elif len(res) != 1:
288 print "Found %d domain controllers. For the moment " \
289 "upgradeprovision is not able to handle an upgrade on a " \
290 "domain with more than one DC. Please demote the other " \
291 "DC(s) before upgrading" % len(res)
292 return False
293 else:
294 return True
297 def print_provision_key_parameters(names):
298 """Do a a pretty print of provision parameters
300 :param names: list of key provision parameters """
301 message(GUESS, "rootdn :" + str(names.rootdn))
302 message(GUESS, "configdn :" + str(names.configdn))
303 message(GUESS, "schemadn :" + str(names.schemadn))
304 message(GUESS, "serverdn :" + str(names.serverdn))
305 message(GUESS, "netbiosname :" + names.netbiosname)
306 message(GUESS, "defaultsite :" + names.sitename)
307 message(GUESS, "dnsdomain :" + names.dnsdomain)
308 message(GUESS, "hostname :" + names.hostname)
309 message(GUESS, "domain :" + names.domain)
310 message(GUESS, "realm :" + names.realm)
311 message(GUESS, "invocationid:" + names.invocation)
312 message(GUESS, "policyguid :" + names.policyid)
313 message(GUESS, "policyguiddc:" + str(names.policyid_dc))
314 message(GUESS, "domainsid :" + str(names.domainsid))
315 message(GUESS, "domainguid :" + names.domainguid)
316 message(GUESS, "ntdsguid :" + names.ntdsguid)
317 message(GUESS, "domainlevel :" + str(names.domainlevel))
320 def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb):
321 """Define more complicate update rules for some attributes
323 :param att: The attribute to be updated
324 :param delta: A messageElement object that correspond to the difference
325 between the updated object and the reference one
326 :param new: The reference object
327 :param old: The Updated object
328 :param useReplMetadata: A boolean that indicate if the update process
329 use replPropertyMetaData to decide what has to be updated.
330 :param basedn: The base DN of the provision
331 :param aldb: An ldb object used to build DN
332 :return: True to indicate that the attribute should be kept, False for
333 discarding it"""
335 flag = delta.get(att).flags()
336 # We do most of the special case handle if we do not have the
337 # highest usn as otherwise the replPropertyMetaData will guide us more
338 # correctly
339 if not useReplMetadata:
340 if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and
341 ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT,"
342 "CN=Services,CN=Configuration,%s" % basedn)
343 == old[0].dn):
344 return True
345 if (att == "userAccountControl" and flag == FLAG_MOD_REPLACE and
346 ldb.Dn(aldb, "CN=Administrator,CN=Users,%s" % basedn)
347 == old[0].dn):
348 message(SIMPLE, "We suggest that you change the userAccountControl"
349 " for user Administrator from value %d to %d" %
350 (int(str(old[0][att])), int(str(new[0][att]))))
351 return False
352 if (att == "minPwdAge" and flag == FLAG_MOD_REPLACE):
353 if (long(str(old[0][att])) == 0):
354 delta[att] = MessageElement(new[0][att], FLAG_MOD_REPLACE, att)
355 return True
357 if (att == "member" and flag == FLAG_MOD_REPLACE):
358 hash = {}
359 newval = []
360 changeDelta=0
361 for elem in old[0][att]:
362 hash[str(elem).lower()]=1
363 newval.append(str(elem))
365 for elem in new[0][att]:
366 if not hash.has_key(str(elem).lower()):
367 changeDelta=1
368 newval.append(str(elem))
369 if changeDelta == 1:
370 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
371 else:
372 delta.remove(att)
373 return True
375 if (att in ("gPLink", "gPCFileSysPath") and
376 flag == FLAG_MOD_REPLACE and
377 str(new[0].dn).lower() == str(old[0].dn).lower()):
378 delta.remove(att)
379 return True
381 if att == "forceLogoff":
382 ref=0x8000000000000000
383 oldval=int(old[0][att][0])
384 newval=int(new[0][att][0])
385 ref == old and ref == abs(new)
386 return True
388 if att in ("adminDisplayName", "adminDescription"):
389 return True
391 if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (names.schemadn)
392 and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
393 return True
395 if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
396 att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
397 return True
399 if (str(old[0].dn) == "%s" % (str(names.rootdn))
400 and att == "subRefs" and flag == FLAG_MOD_REPLACE):
401 return True
402 #Allow to change revision of ForestUpdates objects
403 if (att == "revision" or att == "objectVersion"):
404 if str(delta.dn).lower().find("domainupdates") and str(delta.dn).lower().find("forestupdates") > 0:
405 return True
406 if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
407 return True
409 # This is a bit of special animal as we might have added
410 # already SPN entries to the list that has to be modified
411 # So we go in detail to try to find out what has to be added ...
412 if (att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):
413 hash = {}
414 newval = []
415 changeDelta = 0
416 for elem in old[0][att]:
417 hash[str(elem)]=1
418 newval.append(str(elem))
420 for elem in new[0][att]:
421 if not hash.has_key(str(elem)):
422 changeDelta = 1
423 newval.append(str(elem))
424 if changeDelta == 1:
425 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
426 else:
427 delta.remove(att)
428 return True
430 return False
432 def dump_denied_change(dn, att, flagtxt, current, reference):
433 """Print detailed information about why a change is denied
435 :param dn: DN of the object which attribute is denied
436 :param att: Attribute that was supposed to be upgraded
437 :param flagtxt: Type of the update that should be performed
438 (add, change, remove, ...)
439 :param current: Value(s) of the current attribute
440 :param reference: Value(s) of the reference attribute"""
442 message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
443 + " must not be changed/removed. Discarding the change")
444 if att == "objectSid" :
445 message(CHANGE, "old : %s" % ndr_unpack(security.dom_sid, current[0]))
446 message(CHANGE, "new : %s" % ndr_unpack(security.dom_sid, reference[0]))
447 elif att == "rIDPreviousAllocationPool" or att == "rIDAllocationPool":
448 message(CHANGE, "old : %s" % int64range2str(current[0]))
449 message(CHANGE, "new : %s" % int64range2str(reference[0]))
450 else:
451 i = 0
452 for e in range(0, len(current)):
453 message(CHANGE, "old %d : %s" % (i, str(current[e])))
454 i+=1
455 if reference is not None:
456 i = 0
457 for e in range(0, len(reference)):
458 message(CHANGE, "new %d : %s" % (i, str(reference[e])))
459 i+=1
461 def handle_special_add(samdb, dn, names):
462 """Handle special operation (like remove) on some object needed during
463 upgrade
465 This is mostly due to wrong creation of the object in previous provision.
466 :param samdb: An Ldb object representing the SAM database
467 :param dn: DN of the object to inspect
468 :param names: list of key provision parameters
471 dntoremove = None
472 objDn = Dn(samdb, "CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn)
473 if dn == objDn :
474 #This entry was misplaced lets remove it if it exists
475 dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
477 objDn = Dn(samdb,
478 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn)
479 if dn == objDn:
480 #This entry was misplaced lets remove it if it exists
481 dntoremove = "CN=Certificate Service DCOM Access,"\
482 "CN=Users, %s" % names.rootdn
484 objDn = Dn(samdb, "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn)
485 if dn == objDn:
486 #This entry was misplaced lets remove it if it exists
487 dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
489 objDn = Dn(samdb, "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn)
490 if dn == objDn:
491 #This entry was misplaced lets remove it if it exists
492 dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
494 objDn = Dn(samdb,"CN=System,CN=WellKnown Security Principals,"
495 "CN=Configuration,%s" % names.rootdn)
496 if dn == objDn:
497 oldDn = Dn(samdb,"CN=Well-Known-Security-Id-System,"
498 "CN=WellKnown Security Principals,"
499 "CN=Configuration,%s" % names.rootdn)
501 res = samdb.search(expression="(dn=%s)" % oldDn,
502 base=str(names.rootdn),
503 scope=SCOPE_SUBTREE, attrs=["dn"],
504 controls=["search_options:1:2"])
506 res2 = samdb.search(expression="(dn=%s)" % dn,
507 base=str(names.rootdn),
508 scope=SCOPE_SUBTREE, attrs=["dn"],
509 controls=["search_options:1:2"])
511 if len(res) > 0 and len(res2) == 0:
512 message(CHANGE, "Existing object %s must be replaced by %s. "
513 "Renaming old object" % (str(oldDn), str(dn)))
514 samdb.rename(oldDn, objDn, ["relax:0", "provision:0"])
516 return 0
518 if dntoremove is not None:
519 res = samdb.search(expression="(cn=RID Set)",
520 base=str(names.rootdn),
521 scope=SCOPE_SUBTREE, attrs=["dn"],
522 controls=["search_options:1:2"])
524 if len(res) == 0:
525 return 2
526 res = samdb.search(expression="(dn=%s)" % dntoremove,
527 base=str(names.rootdn),
528 scope=SCOPE_SUBTREE, attrs=["dn"],
529 controls=["search_options:1:2"])
530 if len(res) > 0:
531 message(CHANGE, "Existing object %s must be replaced by %s. "
532 "Removing old object" % (dntoremove, str(dn)))
533 samdb.delete(res[0]["dn"])
534 return 0
536 return 1
539 def check_dn_nottobecreated(hash, index, listdn):
540 """Check if one of the DN present in the list has a creation order
541 greater than the current.
543 Hash is indexed by dn to be created, with each key
544 is associated the creation order.
546 First dn to be created has the creation order 0, second has 1, ...
547 Index contain the current creation order
549 :param hash: Hash holding the different DN of the object to be
550 created as key
551 :param index: Current creation order
552 :param listdn: List of DNs on which the current DN depends on
553 :return: None if the current object do not depend on other
554 object or if all object have been created before."""
555 if listdn is None:
556 return None
557 for dn in listdn:
558 key = str(dn).lower()
559 if hash.has_key(key) and hash[key] > index:
560 return str(dn)
561 return None
565 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
566 """Add a new object if the dependencies are satisfied
568 The function add the object if the object on which it depends are already
569 created
571 :param ref_samdb: Ldb object representing the SAM db of the reference
572 provision
573 :param samdb: Ldb object representing the SAM db of the upgraded
574 provision
575 :param dn: DN of the object to be added
576 :param names: List of key provision parameters
577 :param basedn: DN of the partition to be updated
578 :param hash: Hash holding the different DN of the object to be
579 created as key
580 :param index: Current creation order
581 :return: True if the object was created False otherwise"""
583 ret = handle_special_add(samdb, dn, names)
585 if ret == 2:
586 return False
588 if ret == 0:
589 return True
592 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
593 scope=SCOPE_SUBTREE, controls=["search_options:1:2"])
594 empty = Message()
595 delta = samdb.msg_diff(empty, reference[0])
596 delta.dn
597 skip = False
598 try:
599 if str(reference[0].get("cn")) == "RID Set":
600 for klass in reference[0].get("objectClass"):
601 if str(klass).lower() == "ridset":
602 skip = True
603 finally:
604 if delta.get("objectSid"):
605 sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
606 m = re.match(r".*-(\d+)$", sid)
607 if m and int(m.group(1))>999:
608 delta.remove("objectSid")
609 for att in hashAttrNotCopied.keys():
610 delta.remove(att)
611 for att in backlinked:
612 delta.remove(att)
613 depend_on_yettobecreated = None
614 for att in dn_syntax_att:
615 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
616 delta.get(str(att)))
617 if depend_on_yet_tobecreated is not None:
618 message(CHANGE, "Object %s depends on %s in attribute %s. "
619 "Delaying the creation" % (dn,
620 depend_on_yet_tobecreated, att))
621 return False
623 delta.dn = dn
624 if not skip:
625 message(CHANGE,"Object %s will be added" % dn)
626 samdb.add(delta, ["relax:0", "provision:0"])
627 else:
628 message(CHANGE,"Object %s was skipped" % dn)
630 return True
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"""
637 hash = {}
638 for i in range(0, len(listMissing)):
639 hash[str(listMissing[i]).lower()] = i
640 return hash
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
649 provision
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,
659 attrs=["dn"],
660 controls=["show_deleted:0",
661 "show_recycled:0"])
662 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
663 base=part, scope=SCOPE_SUBTREE,
664 attrs=["dn"],
665 controls=["show_deleted:0",
666 "show_recycled:0"])
667 if len(ref_delObjCnt) > len(delObjCnt):
668 reference = ref_samdb.search(expression="cn=Deleted Objects",
669 base=part, scope=SCOPE_SUBTREE,
670 controls=["show_deleted:0",
671 "show_recycled:0"])
672 empty = Message()
673 delta = samdb.msg_diff(empty, reference[0])
675 delta.dn = Dn(samdb, str(reference[0]["dn"]))
676 for att in hashAttrNotCopied.keys():
677 delta.remove(att)
679 modcontrols = ["relax:0", "provision:0"]
680 samdb.add(delta, modcontrols)
682 listwko = []
683 res = samdb.search(expression="(objectClass=*)", base=part,
684 scope=SCOPE_BASE,
685 attrs=["dn", "wellKnownObjects"])
687 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
688 found = False
690 if len(res[0]) > 0:
691 wko = res[0]["wellKnownObjects"]
693 # The wellKnownObject that we want to add.
694 for o in wko:
695 if str(o) == targetWKO:
696 found = True
697 listwko.append(str(o))
699 if not found:
700 listwko.append(targetWKO)
702 delta = Message()
703 delta.dn = Dn(samdb, str(res[0]["dn"]))
704 delta["wellKnownObjects"] = MessageElement(listwko,
705 FLAG_MOD_REPLACE,
706 "wellKnownObjects" )
707 samdb.modify(delta)
709 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
710 """Add the missing object whose DN is the list
712 The function add the object if the objects on which it depends are
713 already created.
715 :param ref_samdb: Ldb object representing the SAM db of the reference
716 provision
717 :param samdb: Ldb object representing the SAM db of the upgraded
718 provision
719 :param dn: DN of the object to be added
720 :param names: List of key provision parameters
721 :param basedn: DN of the partition to be updated
722 :param list: List of DN to be added in the upgraded provision"""
724 listMissing = []
725 listDefered = list
727 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
728 index = 0
729 listMissing = listDefered
730 listDefered = []
731 hashMissing = gen_dn_index_hash(listMissing)
732 for dn in listMissing:
733 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
734 hashMissing, index)
735 index = index + 1
736 if ret == 0:
737 # DN can't be created because it depends on some
738 # other DN in the list
739 listDefered.append(dn)
741 if len(listDefered) != 0:
742 raise ProvisioningError("Unable to insert missing elements: "
743 "circular references")
745 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
746 """This function handle updates on links
748 :param samdb: An LDB object pointing to the updated provision
749 :param att: Attribute to update
750 :param basedn: The root DN of the provision
751 :param dn: The DN of the inspected object
752 :param value: The value of the attribute
753 :param ref_value: The value of this attribute in the reference provision
754 :param delta: The MessageElement object that will be applied for
755 transforming the current provision"""
757 res = samdb.search(expression="dn=%s" % dn, base=basedn,
758 controls=["search_options:1:2", "reveal:1"],
759 attrs=[att])
761 blacklist = {}
762 hash = {}
763 newlinklist = []
764 changed = False
766 newlinklist.extend(value)
768 for e in value:
769 hash[e] = 1
770 # for w2k domain level the reveal won't reveal anything ...
771 # it means that we can readd links that were removed on purpose ...
772 # Also this function in fact just accept add not removal
774 for e in res[0][att]:
775 if not hash.has_key(e):
776 # We put in the blacklist all the element that are in the "revealed"
777 # result and not in the "standard" result
778 # This element are links that were removed before and so that
779 # we don't wan't to readd
780 blacklist[e] = 1
782 for e in ref_value:
783 if not blacklist.has_key(e) and not hash.has_key(e):
784 newlinklist.append(str(e))
785 changed = True
786 if changed:
787 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
788 else:
789 delta.remove(att)
792 msg_elt_flag_strs = {
793 ldb.FLAG_MOD_ADD: "MOD_ADD",
794 ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
795 ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
797 def checkKeepAttributeOldMtd(delta, att, reference, current,
798 basedn, samdb):
799 """ Check if we should keep the attribute modification or not.
800 This function didn't use replicationMetadata to take a decision.
802 :param delta: A message diff object
803 :param att: An attribute
804 :param reference: A message object for the current entry comming from
805 the reference provision.
806 :param current: A message object for the current entry commin from
807 the current provision.
808 :param basedn: The DN of the partition
809 :param samdb: A ldb connection to the sam database of the current provision.
811 :return: The modified message diff.
813 # Old school way of handling things for pre alpha12 upgrade
814 global defSDmodified
815 isFirst = False
816 txt = ""
817 dn = current[0].dn
819 for att in list(delta):
820 defSDmodified = True
821 msgElt = delta.get(att)
823 if att == "nTSecurityDescriptor":
824 delta.remove(att)
825 continue
827 if att == "dn":
828 continue
830 if not hashOverwrittenAtt.has_key(att):
831 if msgElt.flags() != FLAG_MOD_ADD:
832 if not handle_special_case(att, delta, reference, current,
833 False, basedn, samdb):
834 if opts.debugchange or opts.debugall:
835 try:
836 dump_denied_change(dn, att,
837 msg_elt_flag_strs[msgElt.flags()],
838 current[0][att], reference[0][att])
839 except KeyError:
840 dump_denied_change(dn, att,
841 msg_elt_flag_strs[msgElt.flags()],
842 current[0][att], None)
843 delta.remove(att)
844 continue
845 else:
846 if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
847 continue
848 elif hashOverwrittenAtt.get(att)==never:
849 delta.remove(att)
850 continue
852 return delta
854 def checkKeepAttributeWithMetadata(delta, att, message, reference, current,
855 hash_attr_usn, basedn, usns, samdb):
856 """ Check if we should keep the attribute modification or not
858 :param delta: A message diff object
859 :param att: An attribute
860 :param message: A function to print messages
861 :param reference: A message object for the current entry comming from
862 the reference provision.
863 :param current: A message object for the current entry commin from
864 the current provision.
865 :param hash_attr_usn: A dictionnary with attribute name as keys,
866 USN and invocation id as values.
867 :param basedn: The DN of the partition
868 :param usns: A dictionnary with invocation ID as keys and USN ranges
869 as values.
870 :param samdb: A ldb object pointing to the sam DB
872 :return: The modified message diff.
874 global defSDmodified
875 isFirst = True
876 txt = ""
877 dn = current[0].dn
879 for att in list(delta):
880 # We have updated by provision usn information so let's exploit
881 # replMetadataProperties
882 if att in forwardlinked:
883 curval = current[0].get(att, ())
884 refval = reference[0].get(att, ())
885 handle_links(samdb, att, basedn, current[0]["dn"],
886 curval, refval, delta)
887 continue
889 if isFirst and len(delta.items())>1:
890 isFirst = False
891 txt = "%s\n" % (str(dn))
893 keptAttr = ["dn", "rIDAvailablePool", "objectSid", "creationTime", "oEMInformation", "msDs-KeyVersionNumber"]
894 if att in keptAttr:
895 delta.remove(att)
896 continue
898 if handle_special_case(att, delta, reference, current, True, None, None):
899 # This attribute is "complicated" to handle and handling
900 # was done in handle_special_case
901 continue
903 attrUSN = None
904 if hash_attr_usn.get(att):
905 [attrUSN, attInvId] = hash_attr_usn.get(att)
907 if attrUSN is None:
908 # If it's a replicated attribute and we don't have any USN
909 # information about it. It means that we never saw it before
910 # so let's add it !
911 # If it is a replicated attribute but we are not master on it
912 # (ie. not initially added in the provision we masterize).
913 # attrUSN will be -1
914 if isReplicated(att):
915 continue
916 elif att in hashAttrNotCopied.keys():
917 delta.remove(att)
918 else:
919 continue
921 if att == "nTSecurityDescriptor":
922 cursd = ndr_unpack(security.descriptor,
923 str(current[0]["nTSecurityDescriptor"]))
924 cursddl = cursd.as_sddl(names.domainsid)
925 refsd = ndr_unpack(security.descriptor,
926 str(reference[0]["nTSecurityDescriptor"]))
927 refsddl = refsd.as_sddl(names.domainsid)
929 if get_diff_sddls(refsddl, cursddl) == "":
930 message(CHANGE, "sd are identical")
931 else:
932 message(CHANGE, "sd are not identical")
934 if attrUSN == -1:
935 # This attribute was last modified by another DC forget
936 # about it
937 message(CHANGE, "%sAttribute: %s has been "
938 "created/modified/deleted by another DC. "
939 "Doing nothing" % (txt, att))
940 txt = ""
941 delta.remove(att)
942 continue
943 elif not usn_in_range(int(attrUSN), usns.get(attInvId)):
944 message(CHANGE, "%sAttribute: %s was not "
945 "created/modified/deleted during a "
946 "provision or upgradeprovision. Current "
947 "usn: %d. Doing nothing" % (txt, att,
948 attrUSN))
949 txt = ""
950 delta.remove(att)
951 continue
952 else:
953 if att == "defaultSecurityDescriptor":
954 defSDmodified = True
955 if attrUSN:
956 message(CHANGE, "%sAttribute: %s will be modified"
957 "/deleted it was last modified "
958 "during a provision. Current usn: "
959 "%d" % (txt, att, attrUSN))
960 txt = ""
961 else:
962 message(CHANGE, "%sAttribute: %s will be added because "
963 "it did not exist before" % (txt, att))
964 txt = ""
965 continue
967 return delta
969 def update_present(ref_samdb, samdb, basedn, listPresent, usns):
970 """ This function updates the object that are already present in the
971 provision
973 :param ref_samdb: An LDB object pointing to the reference provision
974 :param samdb: An LDB object pointing to the updated provision
975 :param basedn: A string with the value of the base DN for the provision
976 (ie. DC=foo, DC=bar)
977 :param listPresent: A list of object that is present in the provision
978 :param usns: A list of USN range modified by previous provision and
979 upgradeprovision grouped by invocation ID
982 # This hash is meant to speedup lookup of attribute name from an oid,
983 # it's for the replPropertyMetaData handling
984 hash_oid_name = {}
985 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
986 controls=["search_options:1:2"], attrs=["attributeID",
987 "lDAPDisplayName"])
988 if len(res) > 0:
989 for e in res:
990 strDisplay = str(e.get("lDAPDisplayName"))
991 hash_oid_name[str(e.get("attributeID"))] = strDisplay
992 else:
993 msg = "Unable to insert missing elements: circular references"
994 raise ProvisioningError(msg)
996 changed = 0
997 controls = ["search_options:1:2", "sd_flags:1:0"]
998 if usns is not None:
999 message(CHANGE, "Using replPropertyMetadata for change selection")
1000 for dn in listPresent:
1001 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1002 scope=SCOPE_SUBTREE,
1003 controls=controls)
1004 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1005 scope=SCOPE_SUBTREE, controls=controls)
1007 if (
1008 (str(current[0].dn) != str(reference[0].dn)) and
1009 (str(current[0].dn).upper() == str(reference[0].dn).upper())
1011 message(CHANGE, "Names are the same except for the case. "
1012 "Renaming %s to %s" % (str(current[0].dn),
1013 str(reference[0].dn)))
1014 identic_rename(samdb, reference[0].dn)
1015 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1016 scope=SCOPE_SUBTREE,
1017 controls=controls)
1019 delta = samdb.msg_diff(current[0], reference[0])
1021 for att in backlinked:
1022 delta.remove(att)
1024 delta.remove("name")
1026 if len(delta.items()) > 1 and usns is not None:
1027 # Fetch the replPropertyMetaData
1028 res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1029 scope=SCOPE_SUBTREE, controls=controls,
1030 attrs=["replPropertyMetaData"])
1031 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1032 str(res[0]["replPropertyMetaData"])).ctr
1034 hash_attr_usn = {}
1035 for o in ctr.array:
1036 # We put in this hash only modification
1037 # made on the current host
1038 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
1039 if str(o.originating_invocation_id) in usns.keys():
1040 hash_attr_usn[att] = [o.originating_usn, str(o.originating_invocation_id)]
1041 else:
1042 hash_attr_usn[att] = [-1, None]
1044 if usns is not None:
1045 delta = checkKeepAttributeWithMetadata(delta, att, message, reference,
1046 current, hash_attr_usn,
1047 basedn, usns, samdb)
1048 else:
1049 for att in hashAttrNotCopied.keys():
1050 delta.remove(att)
1051 delta = checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb)
1053 delta.dn = dn
1054 if len(delta.items()) >1:
1055 # Skip dn as the value is not really changed ...
1056 attributes=", ".join(delta.keys()[1:])
1057 modcontrols = []
1058 relaxedatt = ['iscriticalsystemobject', 'grouptype']
1059 # Let's try to reduce as much as possible the use of relax control
1060 #for checkedatt in relaxedatt:
1061 for attr in delta.keys():
1062 if attr.lower() in relaxedatt:
1063 modcontrols = ["relax:0", "provision:0"]
1064 message(CHANGE, "%s is different from the reference one, changed"
1065 " attributes: %s\n" % (dn, attributes))
1066 changed += 1
1067 samdb.modify(delta, modcontrols)
1068 return changed
1070 def reload_full_schema(samdb, names):
1071 """Load the updated schema with all the new and existing classes
1072 and attributes.
1074 :param samdb: An LDB object connected to the sam.ldb of the update
1075 provision
1076 :param names: List of key provision parameters
1079 current = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1080 scope=SCOPE_SUBTREE)
1081 schema_ldif = ""
1082 prefixmap_data = ""
1084 for ent in current:
1085 schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
1087 prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
1088 prefixmap_data = b64encode(prefixmap_data)
1090 # We don't actually add this ldif, just parse it
1091 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap_data
1093 dsdb._dsdb_set_schema_from_ldif(samdb, prefixmap_ldif, schema_ldif)
1096 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, prereloadfunc):
1097 """Check differences between the reference provision and the upgraded one.
1099 It looks for all objects which base DN is name.
1101 This function will also add the missing object and update existing object
1102 to add or remove attributes that were missing.
1104 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1105 reference provision
1106 :param samdb: An LDB object connected to the sam.ldb of the update
1107 provision
1108 :param basedn: String value of the DN of the partition
1109 :param names: List of key provision parameters
1110 :param schema: A Schema object
1111 :param provisionUSNs: A dictionnary with range of USN modified during provision
1112 or upgradeprovision. Ranges are grouped by invocationID.
1113 :param prereloadfunc: A function that must be executed just before the reload
1114 of the schema
1117 hash_new = {}
1118 hash = {}
1119 listMissing = []
1120 listPresent = []
1121 reference = []
1122 current = []
1124 # Connect to the reference provision and get all the attribute in the
1125 # partition referred by name
1126 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
1127 scope=SCOPE_SUBTREE, attrs=["dn"],
1128 controls=["search_options:1:2"])
1130 current = samdb.search(expression="objectClass=*", base=basedn,
1131 scope=SCOPE_SUBTREE, attrs=["dn"],
1132 controls=["search_options:1:2"])
1133 # Create a hash for speeding the search of new object
1134 for i in range(0, len(reference)):
1135 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
1137 # Create a hash for speeding the search of existing object in the
1138 # current provision
1139 for i in range(0, len(current)):
1140 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
1143 for k in hash_new.keys():
1144 if not hash.has_key(k):
1145 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
1146 listMissing.append(hash_new[k])
1147 else:
1148 listPresent.append(hash_new[k])
1150 # Sort the missing object in order to have object of the lowest level
1151 # first (which can be containers for higher level objects)
1152 listMissing.sort(dn_sort)
1153 listPresent.sort(dn_sort)
1155 # The following lines is to load the up to
1156 # date schema into our current LDB
1157 # a complete schema is needed as the insertion of attributes
1158 # and class is done against it
1159 # and the schema is self validated
1160 samdb.set_schema(schema)
1161 try:
1162 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
1163 add_deletedobj_containers(ref_samdb, samdb, names)
1165 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
1167 prereloadfunc()
1168 message(SIMPLE, "Reloading a merged schema, which might trigger "
1169 "reindexing so please be patient")
1170 reload_full_schema(samdb, names)
1171 message(SIMPLE, "Schema reloaded!")
1173 changed = update_present(ref_samdb, samdb, basedn, listPresent,
1174 provisionUSNs)
1175 message(SIMPLE, "There are %d changed objects" % (changed))
1176 return 1
1178 except StandardError, err:
1179 message(ERROR, "Exception during upgrade of samdb:")
1180 (typ, val, tb) = sys.exc_info()
1181 traceback.print_exception(typ, val, tb)
1182 return 0
1185 def check_updated_sd(ref_sam, cur_sam, names):
1186 """Check if the security descriptor in the upgraded provision are the same
1187 as the reference
1189 :param ref_sam: A LDB object connected to the sam.ldb file used as
1190 the reference provision
1191 :param cur_sam: A LDB object connected to the sam.ldb file used as
1192 upgraded provision
1193 :param names: List of key provision parameters"""
1194 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1195 scope=SCOPE_SUBTREE,
1196 attrs=["dn", "nTSecurityDescriptor"],
1197 controls=["search_options:1:2"])
1198 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1199 scope=SCOPE_SUBTREE,
1200 attrs=["dn", "nTSecurityDescriptor"],
1201 controls=["search_options:1:2"])
1202 hash = {}
1203 for i in range(0, len(reference)):
1204 refsd = ndr_unpack(security.descriptor,
1205 str(reference[i]["nTSecurityDescriptor"]))
1206 hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
1209 for i in range(0, len(current)):
1210 key = str(current[i]["dn"]).lower()
1211 if hash.has_key(key):
1212 cursd = ndr_unpack(security.descriptor,
1213 str(current[i]["nTSecurityDescriptor"]))
1214 sddl = cursd.as_sddl(names.domainsid)
1215 if sddl != hash[key]:
1216 txt = get_diff_sddls(hash[key], sddl)
1217 if txt != "":
1218 message(CHANGESD, "On object %s ACL is different"
1219 " \n%s" % (current[i]["dn"], txt))
1223 def fix_partition_sd(samdb, names):
1224 """This function fix the SD for partition containers (basedn, configdn, ...)
1225 This is needed because some provision use to have broken SD on containers
1227 :param samdb: An LDB object pointing to the sam of the current provision
1228 :param names: A list of key provision parameters
1230 # First update the SD for the rootdn
1231 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1232 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1233 controls=["search_options:1:2"])
1234 delta = Message()
1235 delta.dn = Dn(samdb, str(res[0]["dn"]))
1236 descr = get_domain_descriptor(names.domainsid)
1237 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1238 "nTSecurityDescriptor")
1239 samdb.modify(delta)
1240 # Then the config dn
1241 res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1242 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1243 controls=["search_options:1:2"])
1244 delta = Message()
1245 delta.dn = Dn(samdb, str(res[0]["dn"]))
1246 descr = get_config_descriptor(names.domainsid)
1247 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1248 "nTSecurityDescriptor" )
1249 samdb.modify(delta)
1250 # Then the schema dn
1251 res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1252 scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1253 controls=["search_options:1:2"])
1255 delta = Message()
1256 delta.dn = Dn(samdb, str(res[0]["dn"]))
1257 descr = get_schema_descriptor(names.domainsid)
1258 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1259 "nTSecurityDescriptor" )
1260 samdb.modify(delta)
1262 def rebuild_sd(samdb, names):
1263 """Rebuild security descriptor of the current provision from scratch
1265 During the different pre release of samba4 security descriptors (SD)
1266 were notarly broken (up to alpha11 included)
1267 This function allow to get them back in order, this function make the
1268 assumption that nobody has modified manualy an SD
1269 and so SD can be safely recalculated from scratch to get them right.
1271 :param names: List of key provision parameters"""
1274 hash = {}
1275 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1276 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1277 controls=["search_options:1:2"])
1278 for obj in res:
1279 if not (str(obj["dn"]) == str(names.rootdn) or
1280 str(obj["dn"]) == str(names.configdn) or
1281 str(obj["dn"]) == str(names.schemadn)):
1282 hash[str(obj["dn"])] = obj["whenCreated"]
1284 listkeys = hash.keys()
1285 listkeys.sort(dn_sort)
1287 for key in listkeys:
1288 try:
1289 delta = Message()
1290 delta.dn = Dn(samdb, key)
1291 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1292 "whenCreated" )
1293 samdb.modify(delta, ["recalculate_sd:0"])
1294 except:
1295 # XXX: We should always catch an explicit exception.
1296 # What could go wrong here?
1297 samdb.transaction_cancel()
1298 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1299 scope=SCOPE_SUBTREE,
1300 attrs=["dn", "nTSecurityDescriptor"],
1301 controls=["search_options:1:2"])
1302 badsd = ndr_unpack(security.descriptor,
1303 str(res[0]["nTSecurityDescriptor"]))
1304 print "bad stuff %s" % badsd.as_sddl(names.domainsid)
1305 return
1307 def removeProvisionUSN(samdb):
1308 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1309 entry = samdb.search(expression="dn=@PROVISION", base = "",
1310 scope=SCOPE_SUBTREE,
1311 attrs=attrs)
1312 empty = Message()
1313 empty.dn = entry[0].dn
1314 delta = samdb.msg_diff(entry[0], empty)
1315 delta.remove("dn")
1316 delta.dn = entry[0].dn
1317 samdb.modify(delta)
1319 def remove_stored_generated_attrs(paths, creds, session, lp):
1320 """Remove previously stored constructed attributes
1322 :param paths: List of paths for different provision objects
1323 from the upgraded provision
1324 :param creds: A credential object
1325 :param session: A session object
1326 :param lp: A line parser object
1327 :return: An associative array whose key are the different constructed
1328 attributes and the value the dn where this attributes were found.
1332 def simple_update_basesamdb(newpaths, paths, names):
1333 """Update the provision container db: sam.ldb
1334 This function is aimed at very old provision (before alpha9)
1336 :param newpaths: List of paths for different provision objects
1337 from the reference provision
1338 :param paths: List of paths for different provision objects
1339 from the upgraded provision
1340 :param names: List of key provision parameters"""
1342 message(SIMPLE, "Copy samdb")
1343 shutil.copy(newpaths.samdb, paths.samdb)
1345 message(SIMPLE, "Update partitions filename if needed")
1346 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1347 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1348 usersldb = os.path.join(paths.private_dir, "users.ldb")
1349 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1351 if not os.path.isdir(samldbdir):
1352 os.mkdir(samldbdir)
1353 os.chmod(samldbdir, 0700)
1354 if os.path.isfile(schemaldb):
1355 shutil.copy(schemaldb, os.path.join(samldbdir,
1356 "%s.ldb"%str(names.schemadn).upper()))
1357 os.remove(schemaldb)
1358 if os.path.isfile(usersldb):
1359 shutil.copy(usersldb, os.path.join(samldbdir,
1360 "%s.ldb"%str(names.rootdn).upper()))
1361 os.remove(usersldb)
1362 if os.path.isfile(configldb):
1363 shutil.copy(configldb, os.path.join(samldbdir,
1364 "%s.ldb"%str(names.configdn).upper()))
1365 os.remove(configldb)
1368 def update_privilege(ref_private_path, cur_private_path):
1369 """Update the privilege database
1371 :param ref_private_path: Path to the private directory of the reference
1372 provision.
1373 :param cur_private_path: Path to the private directory of the current
1374 (and to be updated) provision."""
1375 message(SIMPLE, "Copy privilege")
1376 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1377 os.path.join(cur_private_path, "privilege.ldb"))
1380 def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):
1381 """Upgrade the SAM DB contents for all the provision partitions
1383 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1384 provision
1385 :param samdb: An LDB object connected to the sam.ldb of the update
1386 provision
1387 :param names: List of key provision parameters
1388 :param provisionUSNs: A dictionnary with range of USN modified during provision
1389 or upgradeprovision. Ranges are grouped by invocationID.
1390 :param schema: A Schema object that represent the schema of the provision
1391 :param prereloadfunc: A function that must be executed just before the reload
1392 of the schema
1395 message(SIMPLE, "Starting update of samdb")
1396 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1397 schema, provisionUSNs, prereloadfunc)
1398 if ret:
1399 message(SIMPLE, "Update of samdb finished")
1400 return 1
1401 else:
1402 message(SIMPLE, "Update failed")
1403 return 0
1406 def copyxattrs(dir, refdir):
1407 """ Copy owner, groups, extended ACL and NT acls from
1408 a reference dir to a destination dir
1410 Both dir are supposed to hold the same files
1411 :param dir: Destination dir
1412 :param refdir: Reference directory"""
1414 noxattr = 0
1415 for root, dirs, files in os.walk(dir, topdown=True):
1416 for name in files:
1417 subdir=root[len(dir):]
1418 ref = os.path.join("%s%s" % (refdir, subdir), name)
1419 statsinfo = os.stat(ref)
1420 tgt = os.path.join(root, name)
1421 try:
1423 os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
1424 # Get the xattr attributes if any
1425 try:
1426 attribute = samba.xattr_native.wrap_getxattr(ref,
1427 xattr.XATTR_NTACL_NAME)
1428 samba.xattr_native.wrap_setxattr(tgt,
1429 xattr.XATTR_NTACL_NAME,
1430 attribute)
1431 except:
1432 noxattr = 1
1433 attribute = samba.xattr_native.wrap_getxattr(ref,
1434 "system.posix_acl_access")
1435 samba.xattr_native.wrap_setxattr(tgt,
1436 "system.posix_acl_access",
1437 attribute)
1438 except:
1439 continue
1440 for name in dirs:
1441 subdir=root[len(dir):]
1442 ref = os.path.join("%s%s" % (refdir, subdir), name)
1443 statsinfo = os.stat(ref)
1444 tgt = os.path.join(root, name)
1445 try:
1446 os.chown(os.path.join(root, name), statsinfo.st_uid,
1447 statsinfo.st_gid)
1448 try:
1449 attribute = samba.xattr_native.wrap_getxattr(ref,
1450 xattr.XATTR_NTACL_NAME)
1451 samba.xattr_native.wrap_setxattr(tgt,
1452 xattr.XATTR_NTACL_NAME,
1453 attribute)
1454 except:
1455 noxattr = 1
1456 attribute = samba.xattr_native.wrap_getxattr(ref,
1457 "system.posix_acl_access")
1458 samba.xattr_native.wrap_setxattr(tgt,
1459 "system.posix_acl_access",
1460 attribute)
1462 except:
1463 continue
1466 def backup_provision(paths, dir):
1467 """This function backup the provision files so that a rollback
1468 is possible
1470 :param paths: Paths to different objects
1471 :param dir: Directory where to store the backup
1474 shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
1475 copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
1476 shutil.copy2(paths.samdb, dir)
1477 shutil.copy2(paths.secrets, dir)
1478 shutil.copy2(paths.idmapdb, dir)
1479 shutil.copy2(paths.privilege, dir)
1480 if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1481 shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1482 shutil.copy2(paths.smbconf, dir)
1483 shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1485 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1486 if not os.path.isdir(samldbdir):
1487 samldbdir = paths.private_dir
1488 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1489 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1490 usersldb = os.path.join(paths.private_dir, "users.ldb")
1491 shutil.copy2(schemaldb, dir)
1492 shutil.copy2(usersldb, dir)
1493 shutil.copy2(configldb, dir)
1494 else:
1495 shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1500 def sync_calculated_attributes(samdb, names):
1501 """Synchronize attributes used for constructed ones, with the
1502 old constructed that were stored in the database.
1504 This apply for instance to msds-keyversionnumber that was
1505 stored and that is now constructed from replpropertymetadata.
1507 :param samdb: An LDB object attached to the currently upgraded samdb
1508 :param names: Various key parameter about current provision.
1510 listAttrs = ["msDs-KeyVersionNumber"]
1511 hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
1512 if hash.has_key("msDs-KeyVersionNumber"):
1513 increment_calculated_keyversion_number(samdb, names.rootdn,
1514 hash["msDs-KeyVersionNumber"])
1516 # Synopsis for updateprovision
1517 # 1) get path related to provision to be update (called current)
1518 # 2) open current provision ldbs
1519 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1520 # of the DC ....)
1521 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1522 # by either upgradeprovision or provision
1523 # 5) creation of a new provision the latest version of provision script
1524 # (called reference)
1525 # 6) get reference provision paths
1526 # 7) open reference provision ldbs
1527 # 8) setup helpers data that will help the update process
1528 # 9) update the privilege ldb by copying the one of referecence provision to
1529 # the current provision
1530 # 10)get the oemInfo field, this field contains information about the different
1531 # provision that have been done
1532 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1533 # integer) or none of this the following things are done
1534 # A) When alpha9 or alphaxx is present
1535 # The base sam.ldb file is updated by looking at the difference between
1536 # referrence one and the current one. Everything is copied with the
1537 # exception of lastProvisionUSN attributes.
1538 # B) Other case (it reflect that that provision was done before alpha9)
1539 # The base sam.ldb of the reference provision is copied over
1540 # the current one, if necessary ldb related to partitions are moved
1541 # and renamed
1542 # The highest used USN is fetched so that changed by upgradeprovision
1543 # usn can be tracked
1544 # 12)A Schema object is created, it will be used to provide a complete
1545 # schema to current provision during update (as the schema of the
1546 # current provision might not be complete and so won't allow some
1547 # object to be created)
1548 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1549 # 14)The secrets db is updated by pull all the difference from the reference
1550 # provision into the current provision
1551 # 15)As the previous step has most probably modified the password stored in
1552 # in secret for the current DC, a new password is generated,
1553 # the kvno is bumped and the entry in samdb is also updated
1554 # 16)For current provision older than alpha9, we must fix the SD a little bit
1555 # administrator to update them because SD used to be generated with the
1556 # system account before alpha9.
1557 # 17)The highest usn modified so far is searched in the database it will be
1558 # the upper limit for usn modified during provision.
1559 # This is done before potential SD recalculation because we do not want
1560 # SD modified during recalculation to be marked as modified during provision
1561 # (and so possibly remplaced at next upgradeprovision)
1562 # 18)Rebuilt SD if the flag indicate to do so
1563 # 19)Check difference between SD of reference provision and those of the
1564 # current provision. The check is done by getting the sddl representation
1565 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1566 # Each part is verified separetly, for dacl and sacl ACL is splited into
1567 # ACEs and each ACE is verified separately (so that a permutation in ACE
1568 # didn't raise as an error).
1569 # 20)The oemInfo field is updated to add information about the fact that the
1570 # provision has been updated by the upgradeprovision version xxx
1571 # (the version is the one obtained when starting samba with the --version
1572 # parameter)
1573 # 21)Check if the current provision has all the settings needed for dynamic
1574 # DNS update to work (that is to say the provision is newer than
1575 # january 2010). If not dns configuration file from reference provision
1576 # are copied in a sub folder and the administrator is invited to
1577 # do what is needed.
1578 # 22)If the lastProvisionUSN attribute was present it is updated to add
1579 # the range of usns modified by the current upgradeprovision
1582 # About updating the sam DB
1583 # The update takes place in update_partition function
1584 # This function read both current and reference provision and list all
1585 # the available DN of objects
1586 # If the string representation of a DN in reference provision is
1587 # equal to the string representation of a DN in current provision
1588 # (without taking care of case) then the object is flaged as being
1589 # present. If the object is not present in current provision the object
1590 # is being flaged as missing in current provision. Object present in current
1591 # provision but not in reference provision are ignored.
1592 # Once the list of objects present and missing is done, the deleted object
1593 # containers are created in the differents partitions (if missing)
1595 # Then the function add_missing_entries is called
1596 # This function will go through the list of missing entries by calling
1597 # add_missing_object for the given object. If this function returns 0
1598 # it means that the object needs some other object in order to be created
1599 # The object is reappended at the end of the list to be created later
1600 # (and preferably after all the needed object have been created)
1601 # The function keeps on looping on the list of object to be created until
1602 # it's empty or that the number of defered creation is equal to the number
1603 # of object that still needs to be created.
1605 # The function add_missing_object will first check if the object can be created.
1606 # That is to say that it didn't depends other not yet created objects
1607 # If requisit can't be fullfilled it exists with 0
1608 # Then it will try to create the missing entry by creating doing
1609 # an ldb_message_diff between the object in the reference provision and
1610 # an empty object.
1611 # This resulting object is filtered to remove all the back link attribute
1612 # (ie. memberOf) as they will be created by the other linked object (ie.
1613 # the one with the member attribute)
1614 # All attributes specified in the hashAttrNotCopied associative array are
1615 # also removed it's most of the time generated attributes
1617 # After missing entries have been added the update_partition function will
1618 # take care of object that exist but that need some update.
1619 # In order to do so the function update_present is called with the list
1620 # of object that are present in both provision and that might need an update.
1622 # This function handle first case mismatch so that the DN in the current
1623 # provision have the same case as in reference provision
1625 # It will then construct an associative array consiting of attributes as
1626 # key and invocationid as value( if the originating invocation id is
1627 # different from the invocation id of the current DC the value is -1 instead).
1629 # If the range of provision modified attributes is present, the function will
1630 # use the replMetadataProperty update method which is the following:
1631 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1632 # creationTime, msDs-KeyVersionNumber, oEMInformation
1633 # Check for each attribute if its usn is within one of the modified by
1634 # provision range and if its originating id is the invocation id of the
1635 # current DC, then validate the update from reference to current.
1636 # If not or if there is no replMetatdataProperty for this attribute then we
1637 # do not update it.
1638 # Otherwise (case the range of provision modified attribute is not present) it
1639 # use the following process:
1640 # All attributes that need to be added are accepted at the exeption of those
1641 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1642 # correct flags specified.
1643 # For attributes that need to be modified or removed, a check is performed
1644 # in OverwrittenAtt, if the attribute is present and the modification flag
1645 # (remove, delete) is one of those listed for this attribute then modification
1646 # is accepted. For complicated handling of attribute update, the control is passed
1647 # to handle_special_case
1651 if __name__ == '__main__':
1652 global defSDmodified
1653 defSDmodified = False
1654 # From here start the big steps of the program
1655 # 1) First get files paths
1656 paths = get_paths(param, smbconf=smbconf)
1657 # Get ldbs with the system session, it is needed for searching
1658 # provision parameters
1659 session = system_session()
1661 # This variable will hold the last provision USN once if it exists.
1662 minUSN = 0
1663 # 2)
1664 ldbs = get_ldbs(paths, creds, session, lp)
1665 backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1666 prefix="backupprovision")
1667 backup_provision(paths, backupdir)
1668 try:
1669 ldbs.startTransactions()
1671 # 3) Guess all the needed names (variables in fact) from the current
1672 # provision.
1673 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1674 paths, smbconf, lp)
1675 # 4)
1676 lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1677 if lastProvisionUSNs is not None:
1678 v = 0
1679 for k in lastProvisionUSNs.keys():
1680 for r in lastProvisionUSNs[k]:
1681 v = v + 1
1683 message(CHANGE,
1684 "Find last provision USN, %d invocation(s) for a total of %d ranges" % \
1685 (len(lastProvisionUSNs.keys()), v /2 ))
1687 if lastProvisionUSNs.get("default") != None:
1688 message(CHANGE, "Old style for usn ranges used")
1689 lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"]
1690 del lastProvisionUSNs["default"]
1691 # Objects will be created with the admin session
1692 # (not anymore system session)
1693 adm_session = admin_session(lp, str(names.domainsid))
1694 # So we reget handle on objects
1695 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1696 if not opts.fixntacl:
1697 if not sanitychecks(ldbs.sam, names):
1698 message(SIMPLE, "Sanity checks for the upgrade have failed. "
1699 "Check the messages and correct the errors "
1700 "before rerunning upgradeprovision")
1701 ldbs.groupedRollback()
1702 sys.exit(1)
1704 # Let's see provision parameters
1705 print_provision_key_parameters(names)
1707 # 5) With all this information let's create a fresh new provision used as
1708 # reference
1709 message(SIMPLE, "Creating a reference provision")
1710 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1711 prefix="referenceprovision")
1712 newprovision(names, creds, session, smbconf, provisiondir,
1713 provision_logger)
1715 # TODO
1716 # 6) and 7)
1717 # We need to get a list of object which SD is directly computed from
1718 # defaultSecurityDescriptor.
1719 # This will allow us to know which object we can rebuild the SD in case
1720 # of change of the parent's SD or of the defaultSD.
1721 # Get file paths of this new provision
1722 newpaths = get_paths(param, targetdir=provisiondir)
1723 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1724 new_ldbs.startTransactions()
1726 # 8) Populate some associative array to ease the update process
1727 # List of attribute which are link and backlink
1728 populate_links(new_ldbs.sam, names.schemadn)
1729 # List of attribute with ASN DN synthax)
1730 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1731 # 9)
1732 update_privilege(newpaths.private_dir, paths.private_dir)
1733 # 10)
1734 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1735 # Do some modification on sam.ldb
1736 ldbs.groupedCommit()
1737 new_ldbs.groupedCommit()
1738 deltaattr = None
1739 # 11)
1740 if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1741 # 11) A
1742 # Starting from alpha9 we can consider that the structure is quite ok
1743 # and that we should do only dela
1744 deltaattr = delta_update_basesamdb(newpaths.samdb,
1745 paths.samdb,
1746 creds,
1747 session,
1749 message)
1750 else:
1751 # 11) B
1752 simple_update_basesamdb(newpaths, paths, names)
1753 ldbs = get_ldbs(paths, creds, session, lp)
1754 removeProvisionUSN(ldbs.sam)
1756 ldbs.startTransactions()
1757 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1758 new_ldbs.startTransactions()
1760 # 12)
1761 schema = Schema(names.domainsid, schemadn=str(names.schemadn))
1762 # We create a closure that will be invoked just before schema reload
1763 def schemareloadclosure():
1764 basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
1765 options=["modules:"])
1766 doit = False
1767 if deltaattr is not None and len(deltaattr) > 1:
1768 doit = True
1769 if doit:
1770 deltaattr.remove("dn")
1771 for att in deltaattr:
1772 if att.lower() == "dn":
1773 continue
1774 if (deltaattr.get(att) is not None
1775 and deltaattr.get(att).flags() != FLAG_MOD_ADD):
1776 doit = False
1777 elif deltaattr.get(att) is None:
1778 doit = False
1779 if doit:
1780 message(CHANGE, "Applying delta to @ATTRIBUTES")
1781 deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
1782 basesam.modify(deltaattr)
1783 else:
1784 message(CHANGE, "Not applying delta to @ATTRIBUTES because "
1785 "there is not only add")
1786 # 13)
1787 if opts.full:
1788 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1789 schema, schemareloadclosure):
1790 message(SIMPLE, "Rolling back all changes. Check the cause"
1791 " of the problem")
1792 message(SIMPLE, "Your system is as it was before the upgrade")
1793 ldbs.groupedRollback()
1794 new_ldbs.groupedRollback()
1795 shutil.rmtree(provisiondir)
1796 sys.exit(1)
1797 else:
1798 # Try to reapply the change also when we do not change the sam
1799 # as the delta_upgrade
1800 schemareloadclosure()
1801 sync_calculated_attributes(ldbs.sam, names)
1802 res = ldbs.sam.search(expression="(samaccountname=dns)",
1803 scope=SCOPE_SUBTREE, attrs=["dn"],
1804 controls=["search_options:1:2"])
1805 if len(res) > 0:
1806 message(SIMPLE, "You still have the old DNS object for managing "
1807 "dynamic DNS, but you didn't supply --full so "
1808 "a correct update can't be done")
1809 ldbs.groupedRollback()
1810 new_ldbs.groupedRollback()
1811 shutil.rmtree(provisiondir)
1812 sys.exit(1)
1813 # 14)
1814 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1815 # 14bis)
1816 res = ldbs.sam.search(expression="(samaccountname=dns)",
1817 scope=SCOPE_SUBTREE, attrs=["dn"],
1818 controls=["search_options:1:2"])
1820 if (len(res) == 1):
1821 ldbs.sam.delete(res[0]["dn"])
1822 res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
1823 scope=SCOPE_SUBTREE, attrs=["dn"])
1824 update_dns_account_password(ldbs.sam, ldbs.secrets, names)
1825 message(SIMPLE, "IMPORTANT!!! "
1826 "If you were using Dynamic DNS before you need "
1827 "to update your configuration, so that the "
1828 "tkey-gssapi-credential has the following value: "
1829 "DNS/%s.%s" % (names.netbiosname.lower(),
1830 names.realm.lower()))
1831 # 15)
1832 message(SIMPLE, "Update machine account")
1833 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1835 # 16) SD should be created with admin but as some previous acl were so wrong
1836 # that admin can't modify them we have first to recreate them with the good
1837 # form but with system account and then give the ownership to admin ...
1838 if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1839 message(SIMPLE, "Fixing old povision SD")
1840 fix_partition_sd(ldbs.sam, names)
1841 rebuild_sd(ldbs.sam, names)
1843 # We calculate the max USN before recalculating the SD because we might
1844 # touch object that have been modified after a provision and we do not
1845 # want that the next upgradeprovision thinks that it has a green light
1846 # to modify them
1848 # 17)
1849 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1851 # 18) We rebuild SD only if defaultSecurityDescriptor is modified
1852 # But in fact we should do it also if one object has its SD modified as
1853 # child might need rebuild
1854 if defSDmodified:
1855 message(SIMPLE, "Updating SD")
1856 ldbs.sam.set_session_info(adm_session)
1857 # Alpha10 was a bit broken still
1858 if re.match(r'.*alpha(\d|10)', str(oem)):
1859 fix_partition_sd(ldbs.sam, names)
1860 rebuild_sd(ldbs.sam, names)
1862 # 19)
1863 # Now we are quite confident in the recalculate process of the SD, we make
1864 # it optional.
1865 # Also the check must be done in a clever way as for the moment we just
1866 # compare SDDL
1867 if opts.debugchangesd:
1868 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1870 # 20)
1871 updateOEMInfo(ldbs.sam, str(names.rootdn))
1872 # 21)
1873 check_for_DNS(newpaths.private_dir, paths.private_dir)
1874 # 22)
1875 if lastProvisionUSNs is not None:
1876 update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
1877 if opts.full and (names.policyid is None or names.policyid_dc is None):
1878 update_policyids(names, ldbs.sam)
1879 if opts.full or opts.resetfileacl or opts.fixntacl:
1880 try:
1881 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1882 except ProvisioningError, e:
1883 message(ERROR, "The policy for domain controller is missing. "
1884 "You should restart upgradeprovision with --full")
1885 except IOError, e:
1886 message(ERROR, "Setting ACL not supported on your filesystem")
1887 else:
1888 try:
1889 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1890 except ProvisioningError, e:
1891 message(ERROR, "The policy for domain controller is missing. "
1892 "You should restart upgradeprovision with --full")
1893 if not opts.fixntacl:
1894 ldbs.groupedCommit()
1895 new_ldbs.groupedCommit()
1896 message(SIMPLE, "Upgrade finished!")
1897 # remove reference provision now that everything is done !
1898 # So we have reindexed first if need when the merged schema was reloaded
1899 # (as new attributes could have quick in)
1900 # But the second part of the update (when we update existing objects
1901 # can also have an influence on indexing as some attribute might have their
1902 # searchflag modificated
1903 message(SIMPLE, "Reopenning samdb to trigger reindexing if needed "
1904 "after modification")
1905 samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
1906 message(SIMPLE, "Reindexing finished")
1908 shutil.rmtree(provisiondir)
1909 else:
1910 ldbs.groupedRollback()
1911 message(SIMPLE, "ACLs fixed !")
1912 except StandardError, err:
1913 message(ERROR, "A problem occurred while trying to upgrade your "
1914 "provision. A full backup is located at %s" % backupdir)
1915 if opts.debugall or opts.debugchange:
1916 (typ, val, tb) = sys.exc_info()
1917 traceback.print_exception(typ, val, tb)
1918 sys.exit(1)