s4:setup: remove standalone 'provision'
[Samba/gebeck_regimport.git] / source4 / scripting / bin / upgradeprovision
blob344d7f56c2a5791de4d4adeb089a50adb10ee8f7
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, LdbError)
46 from samba import param, dsdb, Ldb
47 from samba.common import confirm
48 from samba.provision import (get_domain_descriptor, find_provision_key_parameters,
49 get_config_descriptor,
50 ProvisioningError, get_last_provision_usn,
51 get_max_usn, update_provision_usn, setup_path)
52 from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
53 from samba.dcerpc import security, drsblobs
54 from samba.ndr import ndr_unpack
55 from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
56 get_ldbs, findprovisionrange,
57 usn_in_range, identic_rename, get_diff_sddls,
58 update_secrets, CHANGE, ERROR, SIMPLE,
59 CHANGEALL, GUESS, CHANGESD, PROVISION,
60 updateOEMInfo, getOEMInfo, update_gpo,
61 delta_update_basesamdb, update_policyids,
62 update_machine_account_password,
63 search_constructed_attrs_stored,
64 int64range2str, update_dns_account_password,
65 increment_calculated_keyversion_number,
66 print_provision_ranges)
67 from samba.xattr import copytree_with_xattrs
69 replace=2**FLAG_MOD_REPLACE
70 add=2**FLAG_MOD_ADD
71 delete=2**FLAG_MOD_DELETE
72 never=0
75 # Will be modified during provision to tell if default sd has been modified
76 # somehow ...
78 #Errors are always logged
80 __docformat__ = "restructuredText"
82 # Attributes that are never copied from the reference provision (even if they
83 # do not exist in the destination object).
84 # This is most probably because they are populated automatcally when object is
85 # created
86 # This also apply to imported object from reference provision
87 replAttrNotCopied = [ "dn", "whenCreated", "whenChanged", "objectGUID",
88 "parentGUID", "objectCategory", "distinguishedName",
89 "instanceType", "cn",
90 "lmPwdHistory", "pwdLastSet", "ntPwdHistory",
91 "unicodePwd", "dBCSPwd", "supplementalCredentials",
92 "gPCUserExtensionNames", "gPCMachineExtensionNames",
93 "maxPwdAge", "secret", "possibleInferiors", "privilege",
94 "sAMAccountType", "oEMInformation", "creationTime" ]
96 nonreplAttrNotCopied = ["uSNCreated", "replPropertyMetaData", "uSNChanged",
97 "nextRid" ,"rIDNextRID", "rIDPreviousAllocationPool"]
99 nonDSDBAttrNotCopied = ["msDS-KeyVersionNumber", "priorSecret", "priorWhenChanged"]
102 attrNotCopied = replAttrNotCopied
103 attrNotCopied.extend(nonreplAttrNotCopied)
104 attrNotCopied.extend(nonDSDBAttrNotCopied)
105 # Usually for an object that already exists we do not overwrite attributes as
106 # they might have been changed for good reasons. Anyway for a few of them it's
107 # mandatory to replace them otherwise the provision will be broken somehow.
108 # But for attribute that are just missing we do not have to specify them as the default
109 # behavior is to add missing attribute
110 hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace,
111 "systemOnly":replace, "searchFlags":replace,
112 "mayContain":replace, "systemFlags":replace+add,
113 "description":replace, "operatingSystemVersion":replace,
114 "adminPropertyPages":replace, "groupType":replace,
115 "wellKnownObjects":replace, "privilege":never,
116 "defaultSecurityDescriptor": replace,
117 "rIDAvailablePool": never,
118 "versionNumber" : add,
119 "rIDNextRID": add, "rIDUsedPool": never,
120 "defaultSecurityDescriptor": replace + add,
121 "isMemberOfPartialAttributeSet": delete,
122 "attributeDisplayNames": replace + add,
123 "versionNumber": add}
125 dnNotToRecalculate = []
126 dnToRecalculate = []
127 backlinked = []
128 forwardlinked = set()
129 dn_syntax_att = []
130 not_replicated = []
131 def define_what_to_log(opts):
132 what = 0
133 if opts.debugchange:
134 what = what | CHANGE
135 if opts.debugchangesd:
136 what = what | CHANGESD
137 if opts.debugguess:
138 what = what | GUESS
139 if opts.debugprovision:
140 what = what | PROVISION
141 if opts.debugall:
142 what = what | CHANGEALL
143 return what
146 parser = optparse.OptionParser("provision [options]")
147 sambaopts = options.SambaOptions(parser)
148 parser.add_option_group(sambaopts)
149 parser.add_option_group(options.VersionOptions(parser))
150 credopts = options.CredentialsOptions(parser)
151 parser.add_option_group(credopts)
152 parser.add_option("--setupdir", type="string", metavar="DIR",
153 help="directory with setup files")
154 parser.add_option("--debugprovision", help="Debug provision", action="store_true")
155 parser.add_option("--debugguess", action="store_true",
156 help="Print information on which values are guessed")
157 parser.add_option("--debugchange", action="store_true",
158 help="Print information on what is different but won't be changed")
159 parser.add_option("--debugchangesd", action="store_true",
160 help="Print security descriptor differences")
161 parser.add_option("--debugall", action="store_true",
162 help="Print all available information (very verbose)")
163 parser.add_option("--resetfileacl", action="store_true",
164 help="Force a reset on filesystem acls in sysvol / netlogon share")
165 parser.add_option("--nontaclfix", action="store_true",
166 help="In full upgrade mode do not try to upgrade sysvol / netlogon acls")
167 parser.add_option("--fixntacl", action="store_true",
168 help="Only fix NT ACLs in sysvol / netlogon share")
169 parser.add_option("--db_backup_only", action="store_true",
170 help="Do the backup of the database in the provision, skip the sysvol / netlogon shares")
171 parser.add_option("--full", action="store_true",
172 help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
174 opts = parser.parse_args()[0]
176 handler = logging.StreamHandler(sys.stdout)
177 upgrade_logger = logging.getLogger("upgradeprovision")
178 upgrade_logger.setLevel(logging.INFO)
180 upgrade_logger.addHandler(handler)
182 provision_logger = logging.getLogger("provision")
183 provision_logger.addHandler(handler)
185 whatToLog = define_what_to_log(opts)
187 def message(what, text):
188 """Print a message if this message type has been selected to be printed
190 :param what: Category of the message
191 :param text: Message to print """
192 if (whatToLog & what) or what <= 0:
193 upgrade_logger.info("%s", text)
195 if len(sys.argv) == 1:
196 opts.interactive = True
197 lp = sambaopts.get_loadparm()
198 smbconf = lp.configfile
200 creds = credopts.get_credentials(lp)
201 creds.set_kerberos_state(DONT_USE_KERBEROS)
205 def check_for_DNS(refprivate, private):
206 """Check if the provision has already the requirement for dynamic dns
208 :param refprivate: The path to the private directory of the reference
209 provision
210 :param private: The path to the private directory of the upgraded
211 provision"""
213 spnfile = "%s/spn_update_list" % private
214 dnsfile = "%s/dns_update_list" % private
215 namedfile = lp.get("dnsupdate:path")
217 if not namedfile:
218 namedfile = "%s/named.conf.update" % private
220 if not os.path.exists(spnfile):
221 shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
223 if not os.path.exists(dnsfile):
224 shutil.copy("%s/dns_update_list" % refprivate, "%s" % dnsfile)
226 destdir = "%s/new_dns" % private
227 dnsdir = "%s/dns" % private
229 if not os.path.exists(namedfile):
230 if not os.path.exists(destdir):
231 os.mkdir(destdir)
232 if not os.path.exists(dnsdir):
233 os.mkdir(dnsdir)
234 shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
235 shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
236 message(SIMPLE, "It seems that your provision did not integrate "
237 "new rules for dynamic dns update of domain related entries")
238 message(SIMPLE, "A copy of the new bind configuration files and "
239 "template has been put in %s, you should read them and "
240 "configure dynamic dns updates" % destdir)
243 def populate_links(samdb, schemadn):
244 """Populate an array with all the back linked attributes
246 This attributes that are modified automaticaly when
247 front attibutes are changed
249 :param samdb: A LDB object for sam.ldb file
250 :param schemadn: DN of the schema for the partition"""
251 linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
252 backlinked.extend(linkedAttHash.values())
253 for t in linkedAttHash.keys():
254 forwardlinked.add(t)
256 def isReplicated(att):
257 """ Indicate if the attribute is replicated or not
259 :param att: Name of the attribute to be tested
260 :return: True is the attribute is replicated, False otherwise
263 return (att not in not_replicated)
265 def populateNotReplicated(samdb, schemadn):
266 """Populate an array with all the attributes that are not replicated
268 :param samdb: A LDB object for sam.ldb file
269 :param schemadn: DN of the schema for the partition"""
270 res = samdb.search(expression="(&(objectclass=attributeSchema)(systemflags:1.2.840.113556.1.4.803:=1))", base=Dn(samdb,
271 str(schemadn)), scope=SCOPE_SUBTREE,
272 attrs=["lDAPDisplayName"])
273 for elem in res:
274 not_replicated.append(str(elem["lDAPDisplayName"]))
277 def populate_dnsyntax(samdb, schemadn):
278 """Populate an array with all the attributes that have DN synthax
279 (oid 2.5.5.1)
281 :param samdb: A LDB object for sam.ldb file
282 :param schemadn: DN of the schema for the partition"""
283 res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
284 str(schemadn)), scope=SCOPE_SUBTREE,
285 attrs=["lDAPDisplayName"])
286 for elem in res:
287 dn_syntax_att.append(elem["lDAPDisplayName"])
290 def sanitychecks(samdb, names):
291 """Make some checks before trying to update
293 :param samdb: An LDB object opened on sam.ldb
294 :param names: list of key provision parameters
295 :return: Status of check (1 for Ok, 0 for not Ok) """
296 res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
297 scope=SCOPE_SUBTREE, attrs=["dn"],
298 controls=["search_options:1:2"])
299 if len(res) == 0:
300 print "No DC found. Your provision is most probably broken!"
301 return False
302 elif len(res) != 1:
303 print "Found %d domain controllers. For the moment " \
304 "upgradeprovision is not able to handle an upgrade on a " \
305 "domain with more than one DC. Please demote the other " \
306 "DC(s) before upgrading" % len(res)
307 return False
308 else:
309 return True
312 def print_provision_key_parameters(names):
313 """Do a a pretty print of provision parameters
315 :param names: list of key provision parameters """
316 message(GUESS, "rootdn :" + str(names.rootdn))
317 message(GUESS, "configdn :" + str(names.configdn))
318 message(GUESS, "schemadn :" + str(names.schemadn))
319 message(GUESS, "serverdn :" + str(names.serverdn))
320 message(GUESS, "netbiosname :" + names.netbiosname)
321 message(GUESS, "defaultsite :" + names.sitename)
322 message(GUESS, "dnsdomain :" + names.dnsdomain)
323 message(GUESS, "hostname :" + names.hostname)
324 message(GUESS, "domain :" + names.domain)
325 message(GUESS, "realm :" + names.realm)
326 message(GUESS, "invocationid:" + names.invocation)
327 message(GUESS, "policyguid :" + names.policyid)
328 message(GUESS, "policyguiddc:" + str(names.policyid_dc))
329 message(GUESS, "domainsid :" + str(names.domainsid))
330 message(GUESS, "domainguid :" + names.domainguid)
331 message(GUESS, "ntdsguid :" + names.ntdsguid)
332 message(GUESS, "domainlevel :" + str(names.domainlevel))
335 def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb):
336 """Define more complicate update rules for some attributes
338 :param att: The attribute to be updated
339 :param delta: A messageElement object that correspond to the difference
340 between the updated object and the reference one
341 :param new: The reference object
342 :param old: The Updated object
343 :param useReplMetadata: A boolean that indicate if the update process
344 use replPropertyMetaData to decide what has to be updated.
345 :param basedn: The base DN of the provision
346 :param aldb: An ldb object used to build DN
347 :return: True to indicate that the attribute should be kept, False for
348 discarding it"""
350 # We do most of the special case handle if we do not have the
351 # highest usn as otherwise the replPropertyMetaData will guide us more
352 # correctly
353 if not useReplMetadata:
354 flag = delta.get(att).flags()
355 if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and
356 ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT,"
357 "CN=Services,CN=Configuration,%s" % basedn)
358 == old[0].dn):
359 return True
360 if (att == "userAccountControl" and flag == FLAG_MOD_REPLACE and
361 ldb.Dn(aldb, "CN=Administrator,CN=Users,%s" % basedn)
362 == old[0].dn):
363 message(SIMPLE, "We suggest that you change the userAccountControl"
364 " for user Administrator from value %d to %d" %
365 (int(str(old[0][att])), int(str(new[0][att]))))
366 return False
367 if (att == "minPwdAge" and flag == FLAG_MOD_REPLACE):
368 if (long(str(old[0][att])) == 0):
369 delta[att] = MessageElement(new[0][att], FLAG_MOD_REPLACE, att)
370 return True
372 if (att == "member" and flag == FLAG_MOD_REPLACE):
373 hash = {}
374 newval = []
375 changeDelta=0
376 for elem in old[0][att]:
377 hash[str(elem).lower()]=1
378 newval.append(str(elem))
380 for elem in new[0][att]:
381 if not hash.has_key(str(elem).lower()):
382 changeDelta=1
383 newval.append(str(elem))
384 if changeDelta == 1:
385 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
386 else:
387 delta.remove(att)
388 return True
390 if (att in ("gPLink", "gPCFileSysPath") and
391 flag == FLAG_MOD_REPLACE and
392 str(new[0].dn).lower() == str(old[0].dn).lower()):
393 delta.remove(att)
394 return True
396 if att == "forceLogoff":
397 ref=0x8000000000000000
398 oldval=int(old[0][att][0])
399 newval=int(new[0][att][0])
400 ref == old and ref == abs(new)
401 return True
403 if att in ("adminDisplayName", "adminDescription"):
404 return True
406 if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (names.schemadn)
407 and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
408 return True
410 if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
411 att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
412 return True
414 if (str(old[0].dn) == "%s" % (str(names.rootdn))
415 and att == "subRefs" and flag == FLAG_MOD_REPLACE):
416 return True
417 #Allow to change revision of ForestUpdates objects
418 if (att == "revision" or att == "objectVersion"):
419 if str(delta.dn).lower().find("domainupdates") and str(delta.dn).lower().find("forestupdates") > 0:
420 return True
421 if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
422 return True
424 # This is a bit of special animal as we might have added
425 # already SPN entries to the list that has to be modified
426 # So we go in detail to try to find out what has to be added ...
427 if (att == "servicePrincipalName" and delta.get(att).flags() == FLAG_MOD_REPLACE):
428 hash = {}
429 newval = []
430 changeDelta = 0
431 for elem in old[0][att]:
432 hash[str(elem)]=1
433 newval.append(str(elem))
435 for elem in new[0][att]:
436 if not hash.has_key(str(elem)):
437 changeDelta = 1
438 newval.append(str(elem))
439 if changeDelta == 1:
440 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
441 else:
442 delta.remove(att)
443 return True
445 return False
447 def dump_denied_change(dn, att, flagtxt, current, reference):
448 """Print detailed information about why a change is denied
450 :param dn: DN of the object which attribute is denied
451 :param att: Attribute that was supposed to be upgraded
452 :param flagtxt: Type of the update that should be performed
453 (add, change, remove, ...)
454 :param current: Value(s) of the current attribute
455 :param reference: Value(s) of the reference attribute"""
457 message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
458 + " must not be changed/removed. Discarding the change")
459 if att == "objectSid" :
460 message(CHANGE, "old : %s" % ndr_unpack(security.dom_sid, current[0]))
461 message(CHANGE, "new : %s" % ndr_unpack(security.dom_sid, reference[0]))
462 elif att == "rIDPreviousAllocationPool" or att == "rIDAllocationPool":
463 message(CHANGE, "old : %s" % int64range2str(current[0]))
464 message(CHANGE, "new : %s" % int64range2str(reference[0]))
465 else:
466 i = 0
467 for e in range(0, len(current)):
468 message(CHANGE, "old %d : %s" % (i, str(current[e])))
469 i+=1
470 if reference is not None:
471 i = 0
472 for e in range(0, len(reference)):
473 message(CHANGE, "new %d : %s" % (i, str(reference[e])))
474 i+=1
476 def handle_special_add(samdb, dn, names):
477 """Handle special operation (like remove) on some object needed during
478 upgrade
480 This is mostly due to wrong creation of the object in previous provision.
481 :param samdb: An Ldb object representing the SAM database
482 :param dn: DN of the object to inspect
483 :param names: list of key provision parameters
486 dntoremove = None
487 objDn = Dn(samdb, "CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn)
488 if dn == objDn :
489 #This entry was misplaced lets remove it if it exists
490 dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
492 objDn = Dn(samdb,
493 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn)
494 if dn == objDn:
495 #This entry was misplaced lets remove it if it exists
496 dntoremove = "CN=Certificate Service DCOM Access,"\
497 "CN=Users, %s" % names.rootdn
499 objDn = Dn(samdb, "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn)
500 if dn == objDn:
501 #This entry was misplaced lets remove it if it exists
502 dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
504 objDn = Dn(samdb, "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn)
505 if dn == objDn:
506 #This entry was misplaced lets remove it if it exists
507 dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
509 objDn = Dn(samdb,"CN=System,CN=WellKnown Security Principals,"
510 "CN=Configuration,%s" % names.rootdn)
511 if dn == objDn:
512 oldDn = Dn(samdb,"CN=Well-Known-Security-Id-System,"
513 "CN=WellKnown Security Principals,"
514 "CN=Configuration,%s" % names.rootdn)
516 res = samdb.search(expression="(distinguishedName=%s)" % oldDn,
517 base=str(names.rootdn),
518 scope=SCOPE_SUBTREE, attrs=["dn"],
519 controls=["search_options:1:2"])
521 res2 = samdb.search(expression="(distinguishedName=%s)" % dn,
522 base=str(names.rootdn),
523 scope=SCOPE_SUBTREE, attrs=["dn"],
524 controls=["search_options:1:2"])
526 if len(res) > 0 and len(res2) == 0:
527 message(CHANGE, "Existing object %s must be replaced by %s. "
528 "Renaming old object" % (str(oldDn), str(dn)))
529 samdb.rename(oldDn, objDn, ["relax:0", "provision:0"])
531 return 0
533 if dntoremove is not None:
534 res = samdb.search(expression="(cn=RID Set)",
535 base=str(names.rootdn),
536 scope=SCOPE_SUBTREE, attrs=["dn"],
537 controls=["search_options:1:2"])
539 if len(res) == 0:
540 return 2
541 res = samdb.search(expression="(distinguishedName=%s)" % dntoremove,
542 base=str(names.rootdn),
543 scope=SCOPE_SUBTREE, attrs=["dn"],
544 controls=["search_options:1:2"])
545 if len(res) > 0:
546 message(CHANGE, "Existing object %s must be replaced by %s. "
547 "Removing old object" % (dntoremove, str(dn)))
548 samdb.delete(res[0]["dn"])
549 return 0
551 return 1
554 def check_dn_nottobecreated(hash, index, listdn):
555 """Check if one of the DN present in the list has a creation order
556 greater than the current.
558 Hash is indexed by dn to be created, with each key
559 is associated the creation order.
561 First dn to be created has the creation order 0, second has 1, ...
562 Index contain the current creation order
564 :param hash: Hash holding the different DN of the object to be
565 created as key
566 :param index: Current creation order
567 :param listdn: List of DNs on which the current DN depends on
568 :return: None if the current object do not depend on other
569 object or if all object have been created before."""
570 if listdn is None:
571 return None
572 for dn in listdn:
573 key = str(dn).lower()
574 if hash.has_key(key) and hash[key] > index:
575 return str(dn)
576 return None
580 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
581 """Add a new object if the dependencies are satisfied
583 The function add the object if the object on which it depends are already
584 created
586 :param ref_samdb: Ldb object representing the SAM db of the reference
587 provision
588 :param samdb: Ldb object representing the SAM db of the upgraded
589 provision
590 :param dn: DN of the object to be added
591 :param names: List of key provision parameters
592 :param basedn: DN of the partition to be updated
593 :param hash: Hash holding the different DN of the object to be
594 created as key
595 :param index: Current creation order
596 :return: True if the object was created False otherwise"""
598 ret = handle_special_add(samdb, dn, names)
600 if ret == 2:
601 return False
603 if ret == 0:
604 return True
607 reference = ref_samdb.search(expression="(distinguishedName=%s)" % (str(dn)),
608 base=basedn, scope=SCOPE_SUBTREE,
609 controls=["search_options:1:2"])
610 empty = Message()
611 delta = samdb.msg_diff(empty, reference[0])
612 delta.dn
613 skip = False
614 try:
615 if str(reference[0].get("cn")) == "RID Set":
616 for klass in reference[0].get("objectClass"):
617 if str(klass).lower() == "ridset":
618 skip = True
619 finally:
620 if delta.get("objectSid"):
621 sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
622 m = re.match(r".*-(\d+)$", sid)
623 if m and int(m.group(1))>999:
624 delta.remove("objectSid")
625 for att in attrNotCopied:
626 delta.remove(att)
627 for att in backlinked:
628 delta.remove(att)
629 depend_on_yettobecreated = None
630 for att in dn_syntax_att:
631 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
632 delta.get(str(att)))
633 if depend_on_yet_tobecreated is not None:
634 message(CHANGE, "Object %s depends on %s in attribute %s. "
635 "Delaying the creation" % (dn,
636 depend_on_yet_tobecreated, att))
637 return False
639 delta.dn = dn
640 if not skip:
641 message(CHANGE,"Object %s will be added" % dn)
642 samdb.add(delta, ["relax:0", "provision:0"])
643 else:
644 message(CHANGE,"Object %s was skipped" % dn)
646 return True
648 def gen_dn_index_hash(listMissing):
649 """Generate a hash associating the DN to its creation order
651 :param listMissing: List of DN
652 :return: Hash with DN as keys and creation order as values"""
653 hash = {}
654 for i in range(0, len(listMissing)):
655 hash[str(listMissing[i]).lower()] = i
656 return hash
658 def add_deletedobj_containers(ref_samdb, samdb, names):
659 """Add the object containter: CN=Deleted Objects
661 This function create the container for each partition that need one and
662 then reference the object into the root of the partition
664 :param ref_samdb: Ldb object representing the SAM db of the reference
665 provision
666 :param samdb: Ldb object representing the SAM db of the upgraded provision
667 :param names: List of key provision parameters"""
670 wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
671 partitions = [str(names.rootdn), str(names.configdn)]
672 for part in partitions:
673 ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
674 base=part, scope=SCOPE_SUBTREE,
675 attrs=["dn"],
676 controls=["show_deleted:0",
677 "show_recycled:0"])
678 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
679 base=part, scope=SCOPE_SUBTREE,
680 attrs=["dn"],
681 controls=["show_deleted:0",
682 "show_recycled:0"])
683 if len(ref_delObjCnt) > len(delObjCnt):
684 reference = ref_samdb.search(expression="cn=Deleted Objects",
685 base=part, scope=SCOPE_SUBTREE,
686 controls=["show_deleted:0",
687 "show_recycled:0"])
688 empty = Message()
689 delta = samdb.msg_diff(empty, reference[0])
691 delta.dn = Dn(samdb, str(reference[0]["dn"]))
692 for att in attrNotCopied:
693 delta.remove(att)
695 modcontrols = ["relax:0", "provision:0"]
696 samdb.add(delta, modcontrols)
698 listwko = []
699 res = samdb.search(expression="(objectClass=*)", base=part,
700 scope=SCOPE_BASE,
701 attrs=["dn", "wellKnownObjects"])
703 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
704 found = False
706 if len(res[0]) > 0:
707 wko = res[0]["wellKnownObjects"]
709 # The wellKnownObject that we want to add.
710 for o in wko:
711 if str(o) == targetWKO:
712 found = True
713 listwko.append(str(o))
715 if not found:
716 listwko.append(targetWKO)
718 delta = Message()
719 delta.dn = Dn(samdb, str(res[0]["dn"]))
720 delta["wellKnownObjects"] = MessageElement(listwko,
721 FLAG_MOD_REPLACE,
722 "wellKnownObjects" )
723 samdb.modify(delta)
725 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
726 """Add the missing object whose DN is the list
728 The function add the object if the objects on which it depends are
729 already created.
731 :param ref_samdb: Ldb object representing the SAM db of the reference
732 provision
733 :param samdb: Ldb object representing the SAM db of the upgraded
734 provision
735 :param dn: DN of the object to be added
736 :param names: List of key provision parameters
737 :param basedn: DN of the partition to be updated
738 :param list: List of DN to be added in the upgraded provision"""
740 listMissing = []
741 listDefered = list
743 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
744 index = 0
745 listMissing = listDefered
746 listDefered = []
747 hashMissing = gen_dn_index_hash(listMissing)
748 for dn in listMissing:
749 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
750 hashMissing, index)
751 index = index + 1
752 if ret == 0:
753 # DN can't be created because it depends on some
754 # other DN in the list
755 listDefered.append(dn)
757 if len(listDefered) != 0:
758 raise ProvisioningError("Unable to insert missing elements: "
759 "circular references")
761 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
762 """This function handle updates on links
764 :param samdb: An LDB object pointing to the updated provision
765 :param att: Attribute to update
766 :param basedn: The root DN of the provision
767 :param dn: The DN of the inspected object
768 :param value: The value of the attribute
769 :param ref_value: The value of this attribute in the reference provision
770 :param delta: The MessageElement object that will be applied for
771 transforming the current provision"""
773 res = samdb.search(base=dn, controls=["search_options:1:2", "reveal:1"],
774 attrs=[att])
776 blacklist = {}
777 hash = {}
778 newlinklist = []
779 changed = False
781 for v in value:
782 newlinklist.append(str(v))
784 for e in value:
785 hash[e] = 1
786 # for w2k domain level the reveal won't reveal anything ...
787 # it means that we can readd links that were removed on purpose ...
788 # Also this function in fact just accept add not removal
790 for e in res[0][att]:
791 if not hash.has_key(e):
792 # We put in the blacklist all the element that are in the "revealed"
793 # result and not in the "standard" result
794 # This element are links that were removed before and so that
795 # we don't wan't to readd
796 blacklist[e] = 1
798 for e in ref_value:
799 if not blacklist.has_key(e) and not hash.has_key(e):
800 newlinklist.append(str(e))
801 changed = True
802 if changed:
803 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
804 else:
805 delta.remove(att)
807 return delta
810 msg_elt_flag_strs = {
811 ldb.FLAG_MOD_ADD: "MOD_ADD",
812 ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
813 ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
815 def checkKeepAttributeOldMtd(delta, att, reference, current,
816 basedn, samdb):
817 """ Check if we should keep the attribute modification or not.
818 This function didn't use replicationMetadata to take a decision.
820 :param delta: A message diff object
821 :param att: An attribute
822 :param reference: A message object for the current entry comming from
823 the reference provision.
824 :param current: A message object for the current entry commin from
825 the current provision.
826 :param basedn: The DN of the partition
827 :param samdb: A ldb connection to the sam database of the current provision.
829 :return: The modified message diff.
831 # Old school way of handling things for pre alpha12 upgrade
832 global defSDmodified
833 isFirst = False
834 txt = ""
835 dn = current[0].dn
837 for att in list(delta):
838 msgElt = delta.get(att)
840 if att == "nTSecurityDescriptor":
841 defSDmodified = True
842 delta.remove(att)
843 continue
845 if att == "dn":
846 continue
848 if not hashOverwrittenAtt.has_key(att):
849 if msgElt.flags() != FLAG_MOD_ADD:
850 if not handle_special_case(att, delta, reference, current,
851 False, basedn, samdb):
852 if opts.debugchange or opts.debugall:
853 try:
854 dump_denied_change(dn, att,
855 msg_elt_flag_strs[msgElt.flags()],
856 current[0][att], reference[0][att])
857 except KeyError:
858 dump_denied_change(dn, att,
859 msg_elt_flag_strs[msgElt.flags()],
860 current[0][att], None)
861 delta.remove(att)
862 continue
863 else:
864 if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
865 continue
866 elif hashOverwrittenAtt.get(att)==never:
867 delta.remove(att)
868 continue
870 return delta
872 def checkKeepAttributeWithMetadata(delta, att, message, reference, current,
873 hash_attr_usn, basedn, usns, samdb):
874 """ Check if we should keep the attribute modification or not
876 :param delta: A message diff object
877 :param att: An attribute
878 :param message: A function to print messages
879 :param reference: A message object for the current entry comming from
880 the reference provision.
881 :param current: A message object for the current entry commin from
882 the current provision.
883 :param hash_attr_usn: A dictionnary with attribute name as keys,
884 USN and invocation id as values.
885 :param basedn: The DN of the partition
886 :param usns: A dictionnary with invocation ID as keys and USN ranges
887 as values.
888 :param samdb: A ldb object pointing to the sam DB
890 :return: The modified message diff.
892 global defSDmodified
893 isFirst = True
894 txt = ""
895 dn = current[0].dn
897 for att in list(delta):
898 if att in ["dn", "objectSid"]:
899 delta.remove(att)
900 continue
902 # We have updated by provision usn information so let's exploit
903 # replMetadataProperties
904 if att in forwardlinked:
905 curval = current[0].get(att, ())
906 refval = reference[0].get(att, ())
907 delta = handle_links(samdb, att, basedn, current[0]["dn"],
908 curval, refval, delta)
909 continue
912 if isFirst and len(list(delta)) > 1:
913 isFirst = False
914 txt = "%s\n" % (str(dn))
916 if handle_special_case(att, delta, reference, current, True, None, None):
917 # This attribute is "complicated" to handle and handling
918 # was done in handle_special_case
919 continue
921 attrUSN = None
922 if hash_attr_usn.get(att):
923 [attrUSN, attInvId] = hash_attr_usn.get(att)
925 if attrUSN is None:
926 # If it's a replicated attribute and we don't have any USN
927 # information about it. It means that we never saw it before
928 # so let's add it !
929 # If it is a replicated attribute but we are not master on it
930 # (ie. not initially added in the provision we masterize).
931 # attrUSN will be -1
932 if isReplicated(att):
933 continue
934 else:
935 message(CHANGE, "Non replicated attribute %s changed" % att)
936 continue
938 if att == "nTSecurityDescriptor":
939 cursd = ndr_unpack(security.descriptor,
940 str(current[0]["nTSecurityDescriptor"]))
941 cursddl = cursd.as_sddl(names.domainsid)
942 refsd = ndr_unpack(security.descriptor,
943 str(reference[0]["nTSecurityDescriptor"]))
944 refsddl = refsd.as_sddl(names.domainsid)
946 diff = get_diff_sddls(refsddl, cursddl)
947 if diff == "":
948 # FIXME find a way to have it only with huge huge verbose mode
949 # message(CHANGE, "%ssd are identical" % txt)
950 # txt = ""
951 delta.remove(att)
952 continue
953 else:
954 delta.remove(att)
955 message(CHANGESD, "%ssd are not identical:\n%s" % (txt, diff))
956 txt = ""
957 if attrUSN == -1:
958 message(CHANGESD, "But the SD has been changed by someonelse "\
959 "so it's impossible to know if the difference"\
960 " cames from the modification or from a previous bug")
961 dnNotToRecalculate.append(str(dn))
962 else:
963 dnToRecalculate.append(str(dn))
964 continue
966 if attrUSN == -1:
967 # This attribute was last modified by another DC forget
968 # about it
969 message(CHANGE, "%sAttribute: %s has been "
970 "created/modified/deleted by another DC. "
971 "Doing nothing" % (txt, att))
972 txt = ""
973 delta.remove(att)
974 continue
975 elif not usn_in_range(int(attrUSN), usns.get(attInvId)):
976 message(CHANGE, "%sAttribute: %s was not "
977 "created/modified/deleted during a "
978 "provision or upgradeprovision. Current "
979 "usn: %d. Doing nothing" % (txt, att,
980 attrUSN))
981 txt = ""
982 delta.remove(att)
983 continue
984 else:
985 if att == "defaultSecurityDescriptor":
986 defSDmodified = True
987 if attrUSN:
988 message(CHANGE, "%sAttribute: %s will be modified"
989 "/deleted it was last modified "
990 "during a provision. Current usn: "
991 "%d" % (txt, att, attrUSN))
992 txt = ""
993 else:
994 message(CHANGE, "%sAttribute: %s will be added because "
995 "it did not exist before" % (txt, att))
996 txt = ""
997 continue
999 return delta
1001 def update_present(ref_samdb, samdb, basedn, listPresent, usns):
1002 """ This function updates the object that are already present in the
1003 provision
1005 :param ref_samdb: An LDB object pointing to the reference provision
1006 :param samdb: An LDB object pointing to the updated provision
1007 :param basedn: A string with the value of the base DN for the provision
1008 (ie. DC=foo, DC=bar)
1009 :param listPresent: A list of object that is present in the provision
1010 :param usns: A list of USN range modified by previous provision and
1011 upgradeprovision grouped by invocation ID
1014 # This hash is meant to speedup lookup of attribute name from an oid,
1015 # it's for the replPropertyMetaData handling
1016 hash_oid_name = {}
1017 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
1018 controls=["search_options:1:2"], attrs=["attributeID",
1019 "lDAPDisplayName"])
1020 if len(res) > 0:
1021 for e in res:
1022 strDisplay = str(e.get("lDAPDisplayName"))
1023 hash_oid_name[str(e.get("attributeID"))] = strDisplay
1024 else:
1025 msg = "Unable to insert missing elements: circular references"
1026 raise ProvisioningError(msg)
1028 changed = 0
1029 controls = ["search_options:1:2", "sd_flags:1:0"]
1030 if usns is not None:
1031 message(CHANGE, "Using replPropertyMetadata for change selection")
1032 for dn in listPresent:
1033 reference = ref_samdb.search(expression="(distinguishedName=%s)" % (str(dn)), base=basedn,
1034 scope=SCOPE_SUBTREE,
1035 controls=controls)
1036 current = samdb.search(expression="(distinguishedName=%s)" % (str(dn)), base=basedn,
1037 scope=SCOPE_SUBTREE, controls=controls)
1039 if (
1040 (str(current[0].dn) != str(reference[0].dn)) and
1041 (str(current[0].dn).upper() == str(reference[0].dn).upper())
1043 message(CHANGE, "Names are the same except for the case. "
1044 "Renaming %s to %s" % (str(current[0].dn),
1045 str(reference[0].dn)))
1046 identic_rename(samdb, reference[0].dn)
1047 current = samdb.search(expression="(distinguishedName=%s)" % (str(dn)), base=basedn,
1048 scope=SCOPE_SUBTREE,
1049 controls=controls)
1051 delta = samdb.msg_diff(current[0], reference[0])
1053 for att in backlinked:
1054 delta.remove(att)
1056 for att in attrNotCopied:
1057 delta.remove(att)
1059 delta.remove("name")
1061 nb_items = len(list(delta))
1063 if nb_items == 1:
1064 continue
1066 if nb_items > 1 and usns is not None:
1067 # Fetch the replPropertyMetaData
1068 res = samdb.search(expression="(distinguishedName=%s)" % (str(dn)), base=basedn,
1069 scope=SCOPE_SUBTREE, controls=controls,
1070 attrs=["replPropertyMetaData"])
1071 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1072 str(res[0]["replPropertyMetaData"])).ctr
1074 hash_attr_usn = {}
1075 for o in ctr.array:
1076 # We put in this hash only modification
1077 # made on the current host
1078 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
1079 if str(o.originating_invocation_id) in usns.keys():
1080 hash_attr_usn[att] = [o.originating_usn, str(o.originating_invocation_id)]
1081 else:
1082 hash_attr_usn[att] = [-1, None]
1084 if usns is not None:
1085 delta = checkKeepAttributeWithMetadata(delta, att, message, reference,
1086 current, hash_attr_usn,
1087 basedn, usns, samdb)
1088 else:
1089 delta = checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb)
1091 delta.dn = dn
1094 if len(delta) >1:
1095 # Skip dn as the value is not really changed ...
1096 attributes=", ".join(delta.keys()[1:])
1097 modcontrols = []
1098 relaxedatt = ['iscriticalsystemobject', 'grouptype']
1099 # Let's try to reduce as much as possible the use of relax control
1100 for attr in delta.keys():
1101 if attr.lower() in relaxedatt:
1102 modcontrols = ["relax:0", "provision:0"]
1103 message(CHANGE, "%s is different from the reference one, changed"
1104 " attributes: %s\n" % (dn, attributes))
1105 changed += 1
1106 samdb.modify(delta, modcontrols)
1107 return changed
1109 def reload_full_schema(samdb, names):
1110 """Load the updated schema with all the new and existing classes
1111 and attributes.
1113 :param samdb: An LDB object connected to the sam.ldb of the update
1114 provision
1115 :param names: List of key provision parameters
1118 schemadn = str(names.schemadn)
1119 current = samdb.search(expression="objectClass=*", base=schemadn,
1120 scope=SCOPE_SUBTREE)
1121 schema_ldif = ""
1122 prefixmap_data = ""
1124 for ent in current:
1125 schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
1127 prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
1128 prefixmap_data = b64encode(prefixmap_data)
1130 # We don't actually add this ldif, just parse it
1131 prefixmap_ldif = "dn: %s\nprefixMap:: %s\n\n" % (schemadn, prefixmap_data)
1133 dsdb._dsdb_set_schema_from_ldif(samdb, prefixmap_ldif, schema_ldif, schemadn)
1136 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, prereloadfunc):
1137 """Check differences between the reference provision and the upgraded one.
1139 It looks for all objects which base DN is name.
1141 This function will also add the missing object and update existing object
1142 to add or remove attributes that were missing.
1144 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1145 reference provision
1146 :param samdb: An LDB object connected to the sam.ldb of the update
1147 provision
1148 :param basedn: String value of the DN of the partition
1149 :param names: List of key provision parameters
1150 :param schema: A Schema object
1151 :param provisionUSNs: A dictionnary with range of USN modified during provision
1152 or upgradeprovision. Ranges are grouped by invocationID.
1153 :param prereloadfunc: A function that must be executed just before the reload
1154 of the schema
1157 hash_new = {}
1158 hash = {}
1159 listMissing = []
1160 listPresent = []
1161 reference = []
1162 current = []
1164 # Connect to the reference provision and get all the attribute in the
1165 # partition referred by name
1166 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
1167 scope=SCOPE_SUBTREE, attrs=["dn"],
1168 controls=["search_options:1:2"])
1170 current = samdb.search(expression="objectClass=*", base=basedn,
1171 scope=SCOPE_SUBTREE, attrs=["dn"],
1172 controls=["search_options:1:2"])
1173 # Create a hash for speeding the search of new object
1174 for i in range(0, len(reference)):
1175 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
1177 # Create a hash for speeding the search of existing object in the
1178 # current provision
1179 for i in range(0, len(current)):
1180 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
1183 for k in hash_new.keys():
1184 if not hash.has_key(k):
1185 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
1186 listMissing.append(hash_new[k])
1187 else:
1188 listPresent.append(hash_new[k])
1190 # Sort the missing object in order to have object of the lowest level
1191 # first (which can be containers for higher level objects)
1192 listMissing.sort(dn_sort)
1193 listPresent.sort(dn_sort)
1195 # The following lines is to load the up to
1196 # date schema into our current LDB
1197 # a complete schema is needed as the insertion of attributes
1198 # and class is done against it
1199 # and the schema is self validated
1200 samdb.set_schema(schema)
1201 try:
1202 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
1203 add_deletedobj_containers(ref_samdb, samdb, names)
1205 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
1207 prereloadfunc()
1208 message(SIMPLE, "Reloading a merged schema, which might trigger "
1209 "reindexing so please be patient")
1210 reload_full_schema(samdb, names)
1211 message(SIMPLE, "Schema reloaded!")
1213 changed = update_present(ref_samdb, samdb, basedn, listPresent,
1214 provisionUSNs)
1215 message(SIMPLE, "There are %d changed objects" % (changed))
1216 return 1
1218 except StandardError, err:
1219 message(ERROR, "Exception during upgrade of samdb:")
1220 (typ, val, tb) = sys.exc_info()
1221 traceback.print_exception(typ, val, tb)
1222 return 0
1225 def check_updated_sd(ref_sam, cur_sam, names):
1226 """Check if the security descriptor in the upgraded provision are the same
1227 as the reference
1229 :param ref_sam: A LDB object connected to the sam.ldb file used as
1230 the reference provision
1231 :param cur_sam: A LDB object connected to the sam.ldb file used as
1232 upgraded provision
1233 :param names: List of key provision parameters"""
1234 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1235 scope=SCOPE_SUBTREE,
1236 attrs=["dn", "nTSecurityDescriptor"],
1237 controls=["search_options:1:2"])
1238 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1239 scope=SCOPE_SUBTREE,
1240 attrs=["dn", "nTSecurityDescriptor"],
1241 controls=["search_options:1:2"])
1242 hash = {}
1243 for i in range(0, len(reference)):
1244 refsd = ndr_unpack(security.descriptor,
1245 str(reference[i]["nTSecurityDescriptor"]))
1246 hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
1249 for i in range(0, len(current)):
1250 key = str(current[i]["dn"]).lower()
1251 if hash.has_key(key):
1252 cursd = ndr_unpack(security.descriptor,
1253 str(current[i]["nTSecurityDescriptor"]))
1254 sddl = cursd.as_sddl(names.domainsid)
1255 if sddl != hash[key]:
1256 txt = get_diff_sddls(hash[key], sddl, False)
1257 if txt != "":
1258 message(CHANGESD, "On object %s ACL is different"
1259 " \n%s" % (current[i]["dn"], txt))
1263 def fix_partition_sd(samdb, names):
1264 """This function fix the SD for partition containers (basedn, configdn, ...)
1265 This is needed because some provision use to have broken SD on containers
1267 :param samdb: An LDB object pointing to the sam of the current provision
1268 :param names: A list of key provision parameters
1270 alwaysRecalculate = False
1271 if len(dnToRecalculate) == 0 and len(dnNotToRecalculate) == 0:
1272 alwaysRecalculate = True
1275 # NC's DN can't be both in dnToRecalculate and dnNotToRecalculate
1276 # First update the SD for the rootdn
1277 if alwaysRecalculate or str(names.rootdn) in dnToRecalculate:
1278 delta = Message()
1279 delta.dn = Dn(samdb, str(names.rootdn))
1280 descr = get_domain_descriptor(names.domainsid)
1281 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1282 "nTSecurityDescriptor")
1283 samdb.modify(delta)
1285 # Then the config dn
1286 if alwaysRecalculate or str(names.configdn) in dnToRecalculate:
1287 delta = Message()
1288 delta.dn = Dn(samdb, str(names.configdn))
1289 descr = get_config_descriptor(names.domainsid)
1290 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1291 "nTSecurityDescriptor" )
1292 samdb.modify(delta)
1294 # Then the schema dn
1295 if alwaysRecalculate or str(names.schemadn) in dnToRecalculate:
1296 delta = Message()
1297 delta.dn = Dn(samdb, str(names.schemadn))
1298 descr = get_schema_descriptor(names.domainsid)
1299 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1300 "nTSecurityDescriptor" )
1301 samdb.modify(delta)
1303 def rebuild_sd(samdb, names):
1304 """Rebuild security descriptor of the current provision from scratch
1306 During the different pre release of samba4 security descriptors (SD)
1307 were notarly broken (up to alpha11 included)
1308 This function allow to get them back in order, this function make the
1309 assumption that nobody has modified manualy an SD
1310 and so SD can be safely recalculated from scratch to get them right.
1312 :param names: List of key provision parameters"""
1314 fix_partition_sd(samdb, names)
1316 # List of namming contexts
1317 listNC = [str(names.rootdn), str(names.configdn), str(names.schemadn)]
1318 hash = {}
1319 if len(dnToRecalculate) == 0:
1320 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1321 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1322 controls=["search_options:1:2"])
1323 for obj in res:
1324 hash[str(obj["dn"])] = obj["whenCreated"]
1325 else:
1326 for dn in dnToRecalculate:
1327 if hash.has_key(dn):
1328 continue
1329 # fetch each dn to recalculate and their child within the same partition
1330 res = samdb.search(expression="objectClass=*", base=dn,
1331 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"])
1332 for obj in res:
1333 hash[str(obj["dn"])] = obj["whenCreated"]
1335 listKeys = list(set(hash.keys()))
1336 listKeys.sort(dn_sort)
1338 if len(dnToRecalculate) != 0:
1339 message(CHANGESD, "%d DNs have been marked as needed to be recalculated"\
1340 ", recalculating %d due to inheritance"
1341 % (len(dnToRecalculate), len(listKeys)))
1343 for key in listKeys:
1344 if (key in listNC or
1345 key in dnNotToRecalculate):
1346 continue
1347 delta = Message()
1348 delta.dn = Dn(samdb, key)
1349 try:
1350 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1351 "whenCreated" )
1352 samdb.modify(delta, ["recalculate_sd:0","relax:0"])
1353 except LdbError, e:
1354 samdb.transaction_cancel()
1355 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1356 scope=SCOPE_SUBTREE,
1357 attrs=["dn", "nTSecurityDescriptor"],
1358 controls=["search_options:1:2"])
1359 badsd = ndr_unpack(security.descriptor,
1360 str(res[0]["nTSecurityDescriptor"]))
1361 message(ERROR, "On %s bad stuff %s" % (str(delta.dn),badsd.as_sddl(names.domainsid)))
1362 return
1364 def hasATProvision(samdb):
1365 entry = samdb.search(expression="(distinguishedName=@PROVISION)", base = "",
1366 scope=SCOPE_BASE,
1367 attrs=["dn"])
1369 if entry != None and len(entry) == 1:
1370 return True
1371 else:
1372 return False
1374 def removeProvisionUSN(samdb):
1375 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1376 entry = samdb.search(expression="(distinguishedName=@PROVISION)", base = "",
1377 scope=SCOPE_BASE,
1378 attrs=attrs)
1379 empty = Message()
1380 empty.dn = entry[0].dn
1381 delta = samdb.msg_diff(entry[0], empty)
1382 delta.remove("dn")
1383 delta.dn = entry[0].dn
1384 samdb.modify(delta)
1386 def remove_stored_generated_attrs(paths, creds, session, lp):
1387 """Remove previously stored constructed attributes
1389 :param paths: List of paths for different provision objects
1390 from the upgraded provision
1391 :param creds: A credential object
1392 :param session: A session object
1393 :param lp: A line parser object
1394 :return: An associative array whose key are the different constructed
1395 attributes and the value the dn where this attributes were found.
1399 def simple_update_basesamdb(newpaths, paths, names):
1400 """Update the provision container db: sam.ldb
1401 This function is aimed at very old provision (before alpha9)
1403 :param newpaths: List of paths for different provision objects
1404 from the reference provision
1405 :param paths: List of paths for different provision objects
1406 from the upgraded provision
1407 :param names: List of key provision parameters"""
1409 message(SIMPLE, "Copy samdb")
1410 shutil.copy(newpaths.samdb, paths.samdb)
1412 message(SIMPLE, "Update partitions filename if needed")
1413 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1414 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1415 usersldb = os.path.join(paths.private_dir, "users.ldb")
1416 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1418 if not os.path.isdir(samldbdir):
1419 os.mkdir(samldbdir)
1420 os.chmod(samldbdir, 0700)
1421 if os.path.isfile(schemaldb):
1422 shutil.copy(schemaldb, os.path.join(samldbdir,
1423 "%s.ldb"%str(names.schemadn).upper()))
1424 os.remove(schemaldb)
1425 if os.path.isfile(usersldb):
1426 shutil.copy(usersldb, os.path.join(samldbdir,
1427 "%s.ldb"%str(names.rootdn).upper()))
1428 os.remove(usersldb)
1429 if os.path.isfile(configldb):
1430 shutil.copy(configldb, os.path.join(samldbdir,
1431 "%s.ldb"%str(names.configdn).upper()))
1432 os.remove(configldb)
1435 def update_privilege(ref_private_path, cur_private_path):
1436 """Update the privilege database
1438 :param ref_private_path: Path to the private directory of the reference
1439 provision.
1440 :param cur_private_path: Path to the private directory of the current
1441 (and to be updated) provision."""
1442 message(SIMPLE, "Copy privilege")
1443 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1444 os.path.join(cur_private_path, "privilege.ldb"))
1447 def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):
1448 """Upgrade the SAM DB contents for all the provision partitions
1450 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1451 provision
1452 :param samdb: An LDB object connected to the sam.ldb of the update
1453 provision
1454 :param names: List of key provision parameters
1455 :param provisionUSNs: A dictionnary with range of USN modified during provision
1456 or upgradeprovision. Ranges are grouped by invocationID.
1457 :param schema: A Schema object that represent the schema of the provision
1458 :param prereloadfunc: A function that must be executed just before the reload
1459 of the schema
1462 message(SIMPLE, "Starting update of samdb")
1463 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1464 schema, provisionUSNs, prereloadfunc)
1465 if ret:
1466 message(SIMPLE, "Update of samdb finished")
1467 return 1
1468 else:
1469 message(SIMPLE, "Update failed")
1470 return 0
1473 def backup_provision(paths, dir, only_db):
1474 """This function backup the provision files so that a rollback
1475 is possible
1477 :param paths: Paths to different objects
1478 :param dir: Directory where to store the backup
1479 :param only_db: Skip sysvol for users with big sysvol
1481 if paths.sysvol and not only_db:
1482 copytree_with_xattrs(paths.sysvol, os.path.join(dir, "sysvol"))
1483 shutil.copy2(paths.samdb, dir)
1484 shutil.copy2(paths.secrets, dir)
1485 shutil.copy2(paths.idmapdb, dir)
1486 shutil.copy2(paths.privilege, dir)
1487 if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1488 shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1489 shutil.copy2(paths.smbconf, dir)
1490 shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1492 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1493 if not os.path.isdir(samldbdir):
1494 samldbdir = paths.private_dir
1495 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1496 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1497 usersldb = os.path.join(paths.private_dir, "users.ldb")
1498 shutil.copy2(schemaldb, dir)
1499 shutil.copy2(usersldb, dir)
1500 shutil.copy2(configldb, dir)
1501 else:
1502 shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1505 def sync_calculated_attributes(samdb, names):
1506 """Synchronize attributes used for constructed ones, with the
1507 old constructed that were stored in the database.
1509 This apply for instance to msds-keyversionnumber that was
1510 stored and that is now constructed from replpropertymetadata.
1512 :param samdb: An LDB object attached to the currently upgraded samdb
1513 :param names: Various key parameter about current provision.
1515 listAttrs = ["msDs-KeyVersionNumber"]
1516 hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
1517 if hash.has_key("msDs-KeyVersionNumber"):
1518 increment_calculated_keyversion_number(samdb, names.rootdn,
1519 hash["msDs-KeyVersionNumber"])
1521 # Synopsis for updateprovision
1522 # 1) get path related to provision to be update (called current)
1523 # 2) open current provision ldbs
1524 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1525 # of the DC ....)
1526 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1527 # by either upgradeprovision or provision
1528 # 5) creation of a new provision the latest version of provision script
1529 # (called reference)
1530 # 6) get reference provision paths
1531 # 7) open reference provision ldbs
1532 # 8) setup helpers data that will help the update process
1533 # 9) update the privilege ldb by copying the one of referecence provision to
1534 # the current provision
1535 # 10)get the oemInfo field, this field contains information about the different
1536 # provision that have been done
1537 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1538 # integer) or none of this the following things are done
1539 # A) When alpha9 or alphaxx is present
1540 # The base sam.ldb file is updated by looking at the difference between
1541 # referrence one and the current one. Everything is copied with the
1542 # exception of lastProvisionUSN attributes.
1543 # B) Other case (it reflect that that provision was done before alpha9)
1544 # The base sam.ldb of the reference provision is copied over
1545 # the current one, if necessary ldb related to partitions are moved
1546 # and renamed
1547 # The highest used USN is fetched so that changed by upgradeprovision
1548 # usn can be tracked
1549 # 12)A Schema object is created, it will be used to provide a complete
1550 # schema to current provision during update (as the schema of the
1551 # current provision might not be complete and so won't allow some
1552 # object to be created)
1553 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1554 # 14)The secrets db is updated by pull all the difference from the reference
1555 # provision into the current provision
1556 # 15)As the previous step has most probably modified the password stored in
1557 # in secret for the current DC, a new password is generated,
1558 # the kvno is bumped and the entry in samdb is also updated
1559 # 16)For current provision older than alpha9, we must fix the SD a little bit
1560 # administrator to update them because SD used to be generated with the
1561 # system account before alpha9.
1562 # 17)The highest usn modified so far is searched in the database it will be
1563 # the upper limit for usn modified during provision.
1564 # This is done before potential SD recalculation because we do not want
1565 # SD modified during recalculation to be marked as modified during provision
1566 # (and so possibly remplaced at next upgradeprovision)
1567 # 18)Rebuilt SD if the flag indicate to do so
1568 # 19)Check difference between SD of reference provision and those of the
1569 # current provision. The check is done by getting the sddl representation
1570 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1571 # Each part is verified separetly, for dacl and sacl ACL is splited into
1572 # ACEs and each ACE is verified separately (so that a permutation in ACE
1573 # didn't raise as an error).
1574 # 20)The oemInfo field is updated to add information about the fact that the
1575 # provision has been updated by the upgradeprovision version xxx
1576 # (the version is the one obtained when starting samba with the --version
1577 # parameter)
1578 # 21)Check if the current provision has all the settings needed for dynamic
1579 # DNS update to work (that is to say the provision is newer than
1580 # january 2010). If not dns configuration file from reference provision
1581 # are copied in a sub folder and the administrator is invited to
1582 # do what is needed.
1583 # 22)If the lastProvisionUSN attribute was present it is updated to add
1584 # the range of usns modified by the current upgradeprovision
1587 # About updating the sam DB
1588 # The update takes place in update_partition function
1589 # This function read both current and reference provision and list all
1590 # the available DN of objects
1591 # If the string representation of a DN in reference provision is
1592 # equal to the string representation of a DN in current provision
1593 # (without taking care of case) then the object is flaged as being
1594 # present. If the object is not present in current provision the object
1595 # is being flaged as missing in current provision. Object present in current
1596 # provision but not in reference provision are ignored.
1597 # Once the list of objects present and missing is done, the deleted object
1598 # containers are created in the differents partitions (if missing)
1600 # Then the function add_missing_entries is called
1601 # This function will go through the list of missing entries by calling
1602 # add_missing_object for the given object. If this function returns 0
1603 # it means that the object needs some other object in order to be created
1604 # The object is reappended at the end of the list to be created later
1605 # (and preferably after all the needed object have been created)
1606 # The function keeps on looping on the list of object to be created until
1607 # it's empty or that the number of defered creation is equal to the number
1608 # of object that still needs to be created.
1610 # The function add_missing_object will first check if the object can be created.
1611 # That is to say that it didn't depends other not yet created objects
1612 # If requisit can't be fullfilled it exists with 0
1613 # Then it will try to create the missing entry by creating doing
1614 # an ldb_message_diff between the object in the reference provision and
1615 # an empty object.
1616 # This resulting object is filtered to remove all the back link attribute
1617 # (ie. memberOf) as they will be created by the other linked object (ie.
1618 # the one with the member attribute)
1619 # All attributes specified in the attrNotCopied array are
1620 # also removed it's most of the time generated attributes
1622 # After missing entries have been added the update_partition function will
1623 # take care of object that exist but that need some update.
1624 # In order to do so the function update_present is called with the list
1625 # of object that are present in both provision and that might need an update.
1627 # This function handle first case mismatch so that the DN in the current
1628 # provision have the same case as in reference provision
1630 # It will then construct an associative array consiting of attributes as
1631 # key and invocationid as value( if the originating invocation id is
1632 # different from the invocation id of the current DC the value is -1 instead).
1634 # If the range of provision modified attributes is present, the function will
1635 # use the replMetadataProperty update method which is the following:
1636 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1637 # creationTime, msDs-KeyVersionNumber, oEMInformation
1638 # Check for each attribute if its usn is within one of the modified by
1639 # provision range and if its originating id is the invocation id of the
1640 # current DC, then validate the update from reference to current.
1641 # If not or if there is no replMetatdataProperty for this attribute then we
1642 # do not update it.
1643 # Otherwise (case the range of provision modified attribute is not present) it
1644 # use the following process:
1645 # All attributes that need to be added are accepted at the exeption of those
1646 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1647 # correct flags specified.
1648 # For attributes that need to be modified or removed, a check is performed
1649 # in OverwrittenAtt, if the attribute is present and the modification flag
1650 # (remove, delete) is one of those listed for this attribute then modification
1651 # is accepted. For complicated handling of attribute update, the control is passed
1652 # to handle_special_case
1656 if __name__ == '__main__':
1657 global defSDmodified
1658 defSDmodified = False
1660 if opts.nontaclfix and opts.fixntacl:
1661 message(SIMPLE, "nontaclfix and fixntacl are mutally exclusive")
1662 # From here start the big steps of the program
1663 # 1) First get files paths
1664 paths = get_paths(param, smbconf=smbconf)
1665 # Get ldbs with the system session, it is needed for searching
1666 # provision parameters
1667 session = system_session()
1669 # This variable will hold the last provision USN once if it exists.
1670 minUSN = 0
1671 # 2)
1672 ldbs = get_ldbs(paths, creds, session, lp)
1673 backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1674 prefix="backupprovision")
1675 backup_provision(paths, backupdir, opts.db_backup_only)
1676 try:
1677 ldbs.startTransactions()
1679 # 3) Guess all the needed names (variables in fact) from the current
1680 # provision.
1681 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1682 paths, smbconf, lp)
1683 # 4)
1684 lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1685 if lastProvisionUSNs is not None:
1686 v = 0
1687 for k in lastProvisionUSNs.keys():
1688 for r in lastProvisionUSNs[k]:
1689 v = v + 1
1691 message(CHANGE,
1692 "Find last provision USN, %d invocation(s) for a total of %d ranges" % \
1693 (len(lastProvisionUSNs.keys()), v /2 ))
1695 if lastProvisionUSNs.get("default") != None:
1696 message(CHANGE, "Old style for usn ranges used")
1697 lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"]
1698 del lastProvisionUSNs["default"]
1699 else:
1700 message(SIMPLE, "Your provision lacks provision range information")
1701 if confirm("Do you want to run findprovisionusnranges to try to find them ?", False):
1702 ldbs.groupedRollback()
1703 minobj = 5
1704 (hash_id, nb_obj) = findprovisionrange(ldbs.sam, ldb.Dn(ldbs.sam, str(names.rootdn)))
1705 message(SIMPLE, "Here is a list of changes that modified more than %d objects in 1 minute." % minobj)
1706 message(SIMPLE, "Usually changes made by provision and upgradeprovision are those who affect a couple"\
1707 " of hundred of objects or more")
1708 message(SIMPLE, "Total number of objects: %d" % nb_obj)
1709 message(SIMPLE, "")
1711 print_provision_ranges(hash_id, minobj, None, str(paths.samdb), str(names.invocation))
1713 message(SIMPLE, "Once you applied/adapted the change(s) please restart the upgradeprovision script")
1714 sys.exit(0)
1716 # Objects will be created with the admin session
1717 # (not anymore system session)
1718 adm_session = admin_session(lp, str(names.domainsid))
1719 # So we reget handle on objects
1720 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1721 if not opts.fixntacl:
1722 if not sanitychecks(ldbs.sam, names):
1723 message(SIMPLE, "Sanity checks for the upgrade have failed. "
1724 "Check the messages and correct the errors "
1725 "before rerunning upgradeprovision")
1726 ldbs.groupedRollback()
1727 sys.exit(1)
1729 # Let's see provision parameters
1730 print_provision_key_parameters(names)
1732 # 5) With all this information let's create a fresh new provision used as
1733 # reference
1734 message(SIMPLE, "Creating a reference provision")
1735 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1736 prefix="referenceprovision")
1737 result = newprovision(names, creds, session, smbconf, provisiondir,
1738 provision_logger)
1739 result.report_logger(provision_logger)
1741 # TODO
1742 # 6) and 7)
1743 # We need to get a list of object which SD is directly computed from
1744 # defaultSecurityDescriptor.
1745 # This will allow us to know which object we can rebuild the SD in case
1746 # of change of the parent's SD or of the defaultSD.
1747 # Get file paths of this new provision
1748 newpaths = get_paths(param, targetdir=provisiondir)
1749 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1750 new_ldbs.startTransactions()
1752 populateNotReplicated(new_ldbs.sam, names.schemadn)
1753 # 8) Populate some associative array to ease the update process
1754 # List of attribute which are link and backlink
1755 populate_links(new_ldbs.sam, names.schemadn)
1756 # List of attribute with ASN DN synthax)
1757 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1758 # 9)
1759 update_privilege(newpaths.private_dir, paths.private_dir)
1760 # 10)
1761 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1762 # Do some modification on sam.ldb
1763 ldbs.groupedCommit()
1764 new_ldbs.groupedCommit()
1765 deltaattr = None
1766 # 11)
1767 message(GUESS, oem)
1768 if oem is None or hasATProvision(ldbs.sam) or re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1769 # 11) A
1770 # Starting from alpha9 we can consider that the structure is quite ok
1771 # and that we should do only dela
1772 deltaattr = delta_update_basesamdb(newpaths.samdb,
1773 paths.samdb,
1774 creds,
1775 session,
1777 message)
1778 else:
1779 # 11) B
1780 simple_update_basesamdb(newpaths, paths, names)
1781 ldbs = get_ldbs(paths, creds, session, lp)
1782 removeProvisionUSN(ldbs.sam)
1784 ldbs.startTransactions()
1785 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1786 new_ldbs.startTransactions()
1788 # 12)
1789 schema = Schema(names.domainsid, schemadn=str(names.schemadn))
1790 # We create a closure that will be invoked just before schema reload
1791 def schemareloadclosure():
1792 basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
1793 options=["modules:"])
1794 doit = False
1795 if deltaattr is not None and len(deltaattr) > 1:
1796 doit = True
1797 if doit:
1798 deltaattr.remove("dn")
1799 for att in deltaattr:
1800 if att.lower() == "dn":
1801 continue
1802 if (deltaattr.get(att) is not None
1803 and deltaattr.get(att).flags() != FLAG_MOD_ADD):
1804 doit = False
1805 elif deltaattr.get(att) is None:
1806 doit = False
1807 if doit:
1808 message(CHANGE, "Applying delta to @ATTRIBUTES")
1809 deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
1810 basesam.modify(deltaattr)
1811 else:
1812 message(CHANGE, "Not applying delta to @ATTRIBUTES because "
1813 "there is not only add")
1814 # 13)
1815 if opts.full:
1816 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1817 schema, schemareloadclosure):
1818 message(SIMPLE, "Rolling back all changes. Check the cause"
1819 " of the problem")
1820 message(SIMPLE, "Your system is as it was before the upgrade")
1821 ldbs.groupedRollback()
1822 new_ldbs.groupedRollback()
1823 shutil.rmtree(provisiondir)
1824 sys.exit(1)
1825 else:
1826 # Try to reapply the change also when we do not change the sam
1827 # as the delta_upgrade
1828 schemareloadclosure()
1829 sync_calculated_attributes(ldbs.sam, names)
1830 res = ldbs.sam.search(expression="(samaccountname=dns)",
1831 scope=SCOPE_SUBTREE, attrs=["dn"],
1832 controls=["search_options:1:2"])
1833 if len(res) > 0:
1834 message(SIMPLE, "You still have the old DNS object for managing "
1835 "dynamic DNS, but you didn't supply --full so "
1836 "a correct update can't be done")
1837 ldbs.groupedRollback()
1838 new_ldbs.groupedRollback()
1839 shutil.rmtree(provisiondir)
1840 sys.exit(1)
1841 # 14)
1842 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1843 # 14bis)
1844 res = ldbs.sam.search(expression="(samaccountname=dns)",
1845 scope=SCOPE_SUBTREE, attrs=["dn"],
1846 controls=["search_options:1:2"])
1848 if (len(res) == 1):
1849 ldbs.sam.delete(res[0]["dn"])
1850 res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
1851 scope=SCOPE_SUBTREE, attrs=["dn"])
1852 update_dns_account_password(ldbs.sam, ldbs.secrets, names)
1853 message(SIMPLE, "IMPORTANT!!! "
1854 "If you were using Dynamic DNS before you need "
1855 "to update your configuration, so that the "
1856 "tkey-gssapi-credential has the following value: "
1857 "DNS/%s.%s" % (names.netbiosname.lower(),
1858 names.realm.lower()))
1859 # 15)
1860 message(SIMPLE, "Update machine account")
1861 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1863 dnToRecalculate.sort(dn_sort)
1864 # 16) SD should be created with admin but as some previous acl were so wrong
1865 # that admin can't modify them we have first to recreate them with the good
1866 # form but with system account and then give the ownership to admin ...
1867 if str(oem) != "" and not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1868 message(SIMPLE, "Fixing very old provision SD")
1869 rebuild_sd(ldbs.sam, names)
1871 # We calculate the max USN before recalculating the SD because we might
1872 # touch object that have been modified after a provision and we do not
1873 # want that the next upgradeprovision thinks that it has a green light
1874 # to modify them
1876 # 17)
1877 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1879 # 18) We rebuild SD if a we have a list of DN to recalculate or if the
1880 # defSDmodified is set.
1881 if defSDmodified or len(dnToRecalculate) >0:
1882 message(SIMPLE, "Some (default) security descriptors (SDs) have "
1883 "changed, recalculating them")
1884 ldbs.sam.set_session_info(adm_session)
1885 rebuild_sd(ldbs.sam, names)
1887 # 19)
1888 # Now we are quite confident in the recalculate process of the SD, we make
1889 # it optional. And we don't do it if there is DN that we must touch
1890 # as we are assured that on this DNs we will have differences !
1891 # Also the check must be done in a clever way as for the moment we just
1892 # compare SDDL
1893 if len(dnNotToRecalculate) == 0 and (opts.debugchangesd or opts.debugall):
1894 message(CHANGESD, "Checking recalculated SDs")
1895 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1897 # 20)
1898 updateOEMInfo(ldbs.sam, str(names.rootdn))
1899 # 21)
1900 check_for_DNS(newpaths.private_dir, paths.private_dir)
1901 # 22)
1902 if lastProvisionUSNs is not None:
1903 update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
1904 if opts.full and (names.policyid is None or names.policyid_dc is None):
1905 update_policyids(names, ldbs.sam)
1906 if opts.nontaclfix:
1907 if opts.full or opts.resetfileacl or opts.fixntacl:
1908 try:
1909 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1910 except ProvisioningError, e:
1911 message(ERROR, "The policy for domain controller is missing. "
1912 "You should restart upgradeprovision with --full")
1913 except IOError, e:
1914 message(ERROR, "Setting ACL not supported on your filesystem")
1915 else:
1916 try:
1917 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1918 except ProvisioningError, e:
1919 message(ERROR, "The policy for domain controller is missing. "
1920 "You should restart upgradeprovision with --full")
1921 if not opts.fixntacl:
1922 ldbs.groupedCommit()
1923 new_ldbs.groupedCommit()
1924 message(SIMPLE, "Upgrade finished!")
1925 # remove reference provision now that everything is done !
1926 # So we have reindexed first if need when the merged schema was reloaded
1927 # (as new attributes could have quick in)
1928 # But the second part of the update (when we update existing objects
1929 # can also have an influence on indexing as some attribute might have their
1930 # searchflag modificated
1931 message(SIMPLE, "Reopening samdb to trigger reindexing if needed "
1932 "after modification")
1933 samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
1934 message(SIMPLE, "Reindexing finished")
1936 shutil.rmtree(provisiondir)
1937 else:
1938 ldbs.groupedRollback()
1939 message(SIMPLE, "ACLs fixed !")
1940 except StandardError, err:
1941 message(ERROR, "A problem occurred while trying to upgrade your "
1942 "provision. A full backup is located at %s" % backupdir)
1943 if opts.debugall or opts.debugchange:
1944 (typ, val, tb) = sys.exc_info()
1945 traceback.print_exception(typ, val, tb)
1946 sys.exit(1)