upgrade provision didn't run findprovisionrange anymore
[Samba/bb.git] / source4 / scripting / bin / upgradeprovision
blobe578c14b80480d6bfdd4c8ccdb957e27bcd9786d
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="(dn=%s)" % oldDn,
517 base=str(names.rootdn),
518 scope=SCOPE_SUBTREE, attrs=["dn"],
519 controls=["search_options:1:2"])
521 res2 = samdb.search(expression="(dn=%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="(dn=%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="dn=%s" % (str(dn)), base=basedn,
608 scope=SCOPE_SUBTREE, controls=["search_options:1:2"])
609 empty = Message()
610 delta = samdb.msg_diff(empty, reference[0])
611 delta.dn
612 skip = False
613 try:
614 if str(reference[0].get("cn")) == "RID Set":
615 for klass in reference[0].get("objectClass"):
616 if str(klass).lower() == "ridset":
617 skip = True
618 finally:
619 if delta.get("objectSid"):
620 sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
621 m = re.match(r".*-(\d+)$", sid)
622 if m and int(m.group(1))>999:
623 delta.remove("objectSid")
624 for att in attrNotCopied:
625 delta.remove(att)
626 for att in backlinked:
627 delta.remove(att)
628 depend_on_yettobecreated = None
629 for att in dn_syntax_att:
630 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
631 delta.get(str(att)))
632 if depend_on_yet_tobecreated is not None:
633 message(CHANGE, "Object %s depends on %s in attribute %s. "
634 "Delaying the creation" % (dn,
635 depend_on_yet_tobecreated, att))
636 return False
638 delta.dn = dn
639 if not skip:
640 message(CHANGE,"Object %s will be added" % dn)
641 samdb.add(delta, ["relax:0", "provision:0"])
642 else:
643 message(CHANGE,"Object %s was skipped" % dn)
645 return True
647 def gen_dn_index_hash(listMissing):
648 """Generate a hash associating the DN to its creation order
650 :param listMissing: List of DN
651 :return: Hash with DN as keys and creation order as values"""
652 hash = {}
653 for i in range(0, len(listMissing)):
654 hash[str(listMissing[i]).lower()] = i
655 return hash
657 def add_deletedobj_containers(ref_samdb, samdb, names):
658 """Add the object containter: CN=Deleted Objects
660 This function create the container for each partition that need one and
661 then reference the object into the root of the partition
663 :param ref_samdb: Ldb object representing the SAM db of the reference
664 provision
665 :param samdb: Ldb object representing the SAM db of the upgraded provision
666 :param names: List of key provision parameters"""
669 wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
670 partitions = [str(names.rootdn), str(names.configdn)]
671 for part in partitions:
672 ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
673 base=part, scope=SCOPE_SUBTREE,
674 attrs=["dn"],
675 controls=["show_deleted:0",
676 "show_recycled:0"])
677 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
678 base=part, scope=SCOPE_SUBTREE,
679 attrs=["dn"],
680 controls=["show_deleted:0",
681 "show_recycled:0"])
682 if len(ref_delObjCnt) > len(delObjCnt):
683 reference = ref_samdb.search(expression="cn=Deleted Objects",
684 base=part, scope=SCOPE_SUBTREE,
685 controls=["show_deleted:0",
686 "show_recycled:0"])
687 empty = Message()
688 delta = samdb.msg_diff(empty, reference[0])
690 delta.dn = Dn(samdb, str(reference[0]["dn"]))
691 for att in attrNotCopied:
692 delta.remove(att)
694 modcontrols = ["relax:0", "provision:0"]
695 samdb.add(delta, modcontrols)
697 listwko = []
698 res = samdb.search(expression="(objectClass=*)", base=part,
699 scope=SCOPE_BASE,
700 attrs=["dn", "wellKnownObjects"])
702 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
703 found = False
705 if len(res[0]) > 0:
706 wko = res[0]["wellKnownObjects"]
708 # The wellKnownObject that we want to add.
709 for o in wko:
710 if str(o) == targetWKO:
711 found = True
712 listwko.append(str(o))
714 if not found:
715 listwko.append(targetWKO)
717 delta = Message()
718 delta.dn = Dn(samdb, str(res[0]["dn"]))
719 delta["wellKnownObjects"] = MessageElement(listwko,
720 FLAG_MOD_REPLACE,
721 "wellKnownObjects" )
722 samdb.modify(delta)
724 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
725 """Add the missing object whose DN is the list
727 The function add the object if the objects on which it depends are
728 already created.
730 :param ref_samdb: Ldb object representing the SAM db of the reference
731 provision
732 :param samdb: Ldb object representing the SAM db of the upgraded
733 provision
734 :param dn: DN of the object to be added
735 :param names: List of key provision parameters
736 :param basedn: DN of the partition to be updated
737 :param list: List of DN to be added in the upgraded provision"""
739 listMissing = []
740 listDefered = list
742 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
743 index = 0
744 listMissing = listDefered
745 listDefered = []
746 hashMissing = gen_dn_index_hash(listMissing)
747 for dn in listMissing:
748 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
749 hashMissing, index)
750 index = index + 1
751 if ret == 0:
752 # DN can't be created because it depends on some
753 # other DN in the list
754 listDefered.append(dn)
756 if len(listDefered) != 0:
757 raise ProvisioningError("Unable to insert missing elements: "
758 "circular references")
760 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
761 """This function handle updates on links
763 :param samdb: An LDB object pointing to the updated provision
764 :param att: Attribute to update
765 :param basedn: The root DN of the provision
766 :param dn: The DN of the inspected object
767 :param value: The value of the attribute
768 :param ref_value: The value of this attribute in the reference provision
769 :param delta: The MessageElement object that will be applied for
770 transforming the current provision"""
772 res = samdb.search(base=dn, controls=["search_options:1:2", "reveal:1"],
773 attrs=[att])
775 blacklist = {}
776 hash = {}
777 newlinklist = []
778 changed = False
780 for v in value:
781 newlinklist.append(str(v))
783 for e in value:
784 hash[e] = 1
785 # for w2k domain level the reveal won't reveal anything ...
786 # it means that we can readd links that were removed on purpose ...
787 # Also this function in fact just accept add not removal
789 for e in res[0][att]:
790 if not hash.has_key(e):
791 # We put in the blacklist all the element that are in the "revealed"
792 # result and not in the "standard" result
793 # This element are links that were removed before and so that
794 # we don't wan't to readd
795 blacklist[e] = 1
797 for e in ref_value:
798 if not blacklist.has_key(e) and not hash.has_key(e):
799 newlinklist.append(str(e))
800 changed = True
801 if changed:
802 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
803 else:
804 delta.remove(att)
806 return delta
809 msg_elt_flag_strs = {
810 ldb.FLAG_MOD_ADD: "MOD_ADD",
811 ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
812 ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
814 def checkKeepAttributeOldMtd(delta, att, reference, current,
815 basedn, samdb):
816 """ Check if we should keep the attribute modification or not.
817 This function didn't use replicationMetadata to take a decision.
819 :param delta: A message diff object
820 :param att: An attribute
821 :param reference: A message object for the current entry comming from
822 the reference provision.
823 :param current: A message object for the current entry commin from
824 the current provision.
825 :param basedn: The DN of the partition
826 :param samdb: A ldb connection to the sam database of the current provision.
828 :return: The modified message diff.
830 # Old school way of handling things for pre alpha12 upgrade
831 global defSDmodified
832 isFirst = False
833 txt = ""
834 dn = current[0].dn
836 for att in list(delta):
837 msgElt = delta.get(att)
839 if att == "nTSecurityDescriptor":
840 defSDmodified = True
841 delta.remove(att)
842 continue
844 if att == "dn":
845 continue
847 if not hashOverwrittenAtt.has_key(att):
848 if msgElt.flags() != FLAG_MOD_ADD:
849 if not handle_special_case(att, delta, reference, current,
850 False, basedn, samdb):
851 if opts.debugchange or opts.debugall:
852 try:
853 dump_denied_change(dn, att,
854 msg_elt_flag_strs[msgElt.flags()],
855 current[0][att], reference[0][att])
856 except KeyError:
857 dump_denied_change(dn, att,
858 msg_elt_flag_strs[msgElt.flags()],
859 current[0][att], None)
860 delta.remove(att)
861 continue
862 else:
863 if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
864 continue
865 elif hashOverwrittenAtt.get(att)==never:
866 delta.remove(att)
867 continue
869 return delta
871 def checkKeepAttributeWithMetadata(delta, att, message, reference, current,
872 hash_attr_usn, basedn, usns, samdb):
873 """ Check if we should keep the attribute modification or not
875 :param delta: A message diff object
876 :param att: An attribute
877 :param message: A function to print messages
878 :param reference: A message object for the current entry comming from
879 the reference provision.
880 :param current: A message object for the current entry commin from
881 the current provision.
882 :param hash_attr_usn: A dictionnary with attribute name as keys,
883 USN and invocation id as values.
884 :param basedn: The DN of the partition
885 :param usns: A dictionnary with invocation ID as keys and USN ranges
886 as values.
887 :param samdb: A ldb object pointing to the sam DB
889 :return: The modified message diff.
891 global defSDmodified
892 isFirst = True
893 txt = ""
894 dn = current[0].dn
896 for att in list(delta):
897 if att in ["dn", "objectSid"]:
898 delta.remove(att)
899 continue
901 # We have updated by provision usn information so let's exploit
902 # replMetadataProperties
903 if att in forwardlinked:
904 curval = current[0].get(att, ())
905 refval = reference[0].get(att, ())
906 delta = handle_links(samdb, att, basedn, current[0]["dn"],
907 curval, refval, delta)
908 continue
911 if isFirst and len(list(delta)) > 1:
912 isFirst = False
913 txt = "%s\n" % (str(dn))
915 if handle_special_case(att, delta, reference, current, True, None, None):
916 # This attribute is "complicated" to handle and handling
917 # was done in handle_special_case
918 continue
920 attrUSN = None
921 if hash_attr_usn.get(att):
922 [attrUSN, attInvId] = hash_attr_usn.get(att)
924 if attrUSN is None:
925 # If it's a replicated attribute and we don't have any USN
926 # information about it. It means that we never saw it before
927 # so let's add it !
928 # If it is a replicated attribute but we are not master on it
929 # (ie. not initially added in the provision we masterize).
930 # attrUSN will be -1
931 if isReplicated(att):
932 continue
933 else:
934 message(CHANGE, "Non replicated attribute %s changed" % att)
935 continue
937 if att == "nTSecurityDescriptor":
938 cursd = ndr_unpack(security.descriptor,
939 str(current[0]["nTSecurityDescriptor"]))
940 cursddl = cursd.as_sddl(names.domainsid)
941 refsd = ndr_unpack(security.descriptor,
942 str(reference[0]["nTSecurityDescriptor"]))
943 refsddl = refsd.as_sddl(names.domainsid)
945 diff = get_diff_sddls(refsddl, cursddl)
946 if diff == "":
947 # FIXME find a way to have it only with huge huge verbose mode
948 # message(CHANGE, "%ssd are identical" % txt)
949 # txt = ""
950 delta.remove(att)
951 continue
952 else:
953 delta.remove(att)
954 message(CHANGESD, "%ssd are not identical:\n%s" % (txt, diff))
955 txt = ""
956 if attrUSN == -1:
957 message(CHANGESD, "But the SD has been changed by someonelse "\
958 "so it's impossible to know if the difference"\
959 " cames from the modification or from a previous bug")
960 dnNotToRecalculate.append(str(dn))
961 else:
962 dnToRecalculate.append(str(dn))
963 continue
965 if attrUSN == -1:
966 # This attribute was last modified by another DC forget
967 # about it
968 message(CHANGE, "%sAttribute: %s has been "
969 "created/modified/deleted by another DC. "
970 "Doing nothing" % (txt, att))
971 txt = ""
972 delta.remove(att)
973 continue
974 elif not usn_in_range(int(attrUSN), usns.get(attInvId)):
975 message(CHANGE, "%sAttribute: %s was not "
976 "created/modified/deleted during a "
977 "provision or upgradeprovision. Current "
978 "usn: %d. Doing nothing" % (txt, att,
979 attrUSN))
980 txt = ""
981 delta.remove(att)
982 continue
983 else:
984 if att == "defaultSecurityDescriptor":
985 defSDmodified = True
986 if attrUSN:
987 message(CHANGE, "%sAttribute: %s will be modified"
988 "/deleted it was last modified "
989 "during a provision. Current usn: "
990 "%d" % (txt, att, attrUSN))
991 txt = ""
992 else:
993 message(CHANGE, "%sAttribute: %s will be added because "
994 "it did not exist before" % (txt, att))
995 txt = ""
996 continue
998 return delta
1000 def update_present(ref_samdb, samdb, basedn, listPresent, usns):
1001 """ This function updates the object that are already present in the
1002 provision
1004 :param ref_samdb: An LDB object pointing to the reference provision
1005 :param samdb: An LDB object pointing to the updated provision
1006 :param basedn: A string with the value of the base DN for the provision
1007 (ie. DC=foo, DC=bar)
1008 :param listPresent: A list of object that is present in the provision
1009 :param usns: A list of USN range modified by previous provision and
1010 upgradeprovision grouped by invocation ID
1013 # This hash is meant to speedup lookup of attribute name from an oid,
1014 # it's for the replPropertyMetaData handling
1015 hash_oid_name = {}
1016 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
1017 controls=["search_options:1:2"], attrs=["attributeID",
1018 "lDAPDisplayName"])
1019 if len(res) > 0:
1020 for e in res:
1021 strDisplay = str(e.get("lDAPDisplayName"))
1022 hash_oid_name[str(e.get("attributeID"))] = strDisplay
1023 else:
1024 msg = "Unable to insert missing elements: circular references"
1025 raise ProvisioningError(msg)
1027 changed = 0
1028 controls = ["search_options:1:2", "sd_flags:1:0"]
1029 if usns is not None:
1030 message(CHANGE, "Using replPropertyMetadata for change selection")
1031 for dn in listPresent:
1032 reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1033 scope=SCOPE_SUBTREE,
1034 controls=controls)
1035 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1036 scope=SCOPE_SUBTREE, controls=controls)
1038 if (
1039 (str(current[0].dn) != str(reference[0].dn)) and
1040 (str(current[0].dn).upper() == str(reference[0].dn).upper())
1042 message(CHANGE, "Names are the same except for the case. "
1043 "Renaming %s to %s" % (str(current[0].dn),
1044 str(reference[0].dn)))
1045 identic_rename(samdb, reference[0].dn)
1046 current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1047 scope=SCOPE_SUBTREE,
1048 controls=controls)
1050 delta = samdb.msg_diff(current[0], reference[0])
1052 for att in backlinked:
1053 delta.remove(att)
1055 for att in attrNotCopied:
1056 delta.remove(att)
1058 delta.remove("name")
1060 nb_items = len(list(delta))
1062 if nb_items == 1:
1063 continue
1065 if nb_items > 1 and usns is not None:
1066 # Fetch the replPropertyMetaData
1067 res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1068 scope=SCOPE_SUBTREE, controls=controls,
1069 attrs=["replPropertyMetaData"])
1070 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1071 str(res[0]["replPropertyMetaData"])).ctr
1073 hash_attr_usn = {}
1074 for o in ctr.array:
1075 # We put in this hash only modification
1076 # made on the current host
1077 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
1078 if str(o.originating_invocation_id) in usns.keys():
1079 hash_attr_usn[att] = [o.originating_usn, str(o.originating_invocation_id)]
1080 else:
1081 hash_attr_usn[att] = [-1, None]
1083 if usns is not None:
1084 delta = checkKeepAttributeWithMetadata(delta, att, message, reference,
1085 current, hash_attr_usn,
1086 basedn, usns, samdb)
1087 else:
1088 delta = checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb)
1090 delta.dn = dn
1093 if len(delta) >1:
1094 # Skip dn as the value is not really changed ...
1095 attributes=", ".join(delta.keys()[1:])
1096 modcontrols = []
1097 relaxedatt = ['iscriticalsystemobject', 'grouptype']
1098 # Let's try to reduce as much as possible the use of relax control
1099 for attr in delta.keys():
1100 if attr.lower() in relaxedatt:
1101 modcontrols = ["relax:0", "provision:0"]
1102 message(CHANGE, "%s is different from the reference one, changed"
1103 " attributes: %s\n" % (dn, attributes))
1104 changed += 1
1105 samdb.modify(delta, modcontrols)
1106 return changed
1108 def reload_full_schema(samdb, names):
1109 """Load the updated schema with all the new and existing classes
1110 and attributes.
1112 :param samdb: An LDB object connected to the sam.ldb of the update
1113 provision
1114 :param names: List of key provision parameters
1117 schemadn = str(names.schemadn)
1118 current = samdb.search(expression="objectClass=*", base=schemadn,
1119 scope=SCOPE_SUBTREE)
1120 schema_ldif = ""
1121 prefixmap_data = ""
1123 for ent in current:
1124 schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
1126 prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
1127 prefixmap_data = b64encode(prefixmap_data)
1129 # We don't actually add this ldif, just parse it
1130 prefixmap_ldif = "dn: %s\nprefixMap:: %s\n\n" % (schemadn, prefixmap_data)
1132 dsdb._dsdb_set_schema_from_ldif(samdb, prefixmap_ldif, schema_ldif, schemadn)
1135 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, prereloadfunc):
1136 """Check differences between the reference provision and the upgraded one.
1138 It looks for all objects which base DN is name.
1140 This function will also add the missing object and update existing object
1141 to add or remove attributes that were missing.
1143 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1144 reference provision
1145 :param samdb: An LDB object connected to the sam.ldb of the update
1146 provision
1147 :param basedn: String value of the DN of the partition
1148 :param names: List of key provision parameters
1149 :param schema: A Schema object
1150 :param provisionUSNs: A dictionnary with range of USN modified during provision
1151 or upgradeprovision. Ranges are grouped by invocationID.
1152 :param prereloadfunc: A function that must be executed just before the reload
1153 of the schema
1156 hash_new = {}
1157 hash = {}
1158 listMissing = []
1159 listPresent = []
1160 reference = []
1161 current = []
1163 # Connect to the reference provision and get all the attribute in the
1164 # partition referred by name
1165 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
1166 scope=SCOPE_SUBTREE, attrs=["dn"],
1167 controls=["search_options:1:2"])
1169 current = samdb.search(expression="objectClass=*", base=basedn,
1170 scope=SCOPE_SUBTREE, attrs=["dn"],
1171 controls=["search_options:1:2"])
1172 # Create a hash for speeding the search of new object
1173 for i in range(0, len(reference)):
1174 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
1176 # Create a hash for speeding the search of existing object in the
1177 # current provision
1178 for i in range(0, len(current)):
1179 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
1182 for k in hash_new.keys():
1183 if not hash.has_key(k):
1184 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
1185 listMissing.append(hash_new[k])
1186 else:
1187 listPresent.append(hash_new[k])
1189 # Sort the missing object in order to have object of the lowest level
1190 # first (which can be containers for higher level objects)
1191 listMissing.sort(dn_sort)
1192 listPresent.sort(dn_sort)
1194 # The following lines is to load the up to
1195 # date schema into our current LDB
1196 # a complete schema is needed as the insertion of attributes
1197 # and class is done against it
1198 # and the schema is self validated
1199 samdb.set_schema(schema)
1200 try:
1201 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
1202 add_deletedobj_containers(ref_samdb, samdb, names)
1204 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
1206 prereloadfunc()
1207 message(SIMPLE, "Reloading a merged schema, which might trigger "
1208 "reindexing so please be patient")
1209 reload_full_schema(samdb, names)
1210 message(SIMPLE, "Schema reloaded!")
1212 changed = update_present(ref_samdb, samdb, basedn, listPresent,
1213 provisionUSNs)
1214 message(SIMPLE, "There are %d changed objects" % (changed))
1215 return 1
1217 except StandardError, err:
1218 message(ERROR, "Exception during upgrade of samdb:")
1219 (typ, val, tb) = sys.exc_info()
1220 traceback.print_exception(typ, val, tb)
1221 return 0
1224 def check_updated_sd(ref_sam, cur_sam, names):
1225 """Check if the security descriptor in the upgraded provision are the same
1226 as the reference
1228 :param ref_sam: A LDB object connected to the sam.ldb file used as
1229 the reference provision
1230 :param cur_sam: A LDB object connected to the sam.ldb file used as
1231 upgraded provision
1232 :param names: List of key provision parameters"""
1233 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1234 scope=SCOPE_SUBTREE,
1235 attrs=["dn", "nTSecurityDescriptor"],
1236 controls=["search_options:1:2"])
1237 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1238 scope=SCOPE_SUBTREE,
1239 attrs=["dn", "nTSecurityDescriptor"],
1240 controls=["search_options:1:2"])
1241 hash = {}
1242 for i in range(0, len(reference)):
1243 refsd = ndr_unpack(security.descriptor,
1244 str(reference[i]["nTSecurityDescriptor"]))
1245 hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
1248 for i in range(0, len(current)):
1249 key = str(current[i]["dn"]).lower()
1250 if hash.has_key(key):
1251 cursd = ndr_unpack(security.descriptor,
1252 str(current[i]["nTSecurityDescriptor"]))
1253 sddl = cursd.as_sddl(names.domainsid)
1254 if sddl != hash[key]:
1255 txt = get_diff_sddls(hash[key], sddl, False)
1256 if txt != "":
1257 message(CHANGESD, "On object %s ACL is different"
1258 " \n%s" % (current[i]["dn"], txt))
1262 def fix_partition_sd(samdb, names):
1263 """This function fix the SD for partition containers (basedn, configdn, ...)
1264 This is needed because some provision use to have broken SD on containers
1266 :param samdb: An LDB object pointing to the sam of the current provision
1267 :param names: A list of key provision parameters
1269 alwaysRecalculate = False
1270 if len(dnToRecalculate) == 0 and len(dnNotToRecalculate) == 0:
1271 alwaysRecalculate = True
1274 # NC's DN can't be both in dnToRecalculate and dnNotToRecalculate
1275 # First update the SD for the rootdn
1276 if alwaysRecalculate or str(names.rootdn) in dnToRecalculate:
1277 delta = Message()
1278 delta.dn = Dn(samdb, str(names.rootdn))
1279 descr = get_domain_descriptor(names.domainsid)
1280 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1281 "nTSecurityDescriptor")
1282 samdb.modify(delta)
1284 # Then the config dn
1285 if alwaysRecalculate or str(names.configdn) in dnToRecalculate:
1286 delta = Message()
1287 delta.dn = Dn(samdb, str(names.configdn))
1288 descr = get_config_descriptor(names.domainsid)
1289 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1290 "nTSecurityDescriptor" )
1291 samdb.modify(delta)
1293 # Then the schema dn
1294 if alwaysRecalculate or str(names.schemadn) in dnToRecalculate:
1295 delta = Message()
1296 delta.dn = Dn(samdb, str(names.schemadn))
1297 descr = get_schema_descriptor(names.domainsid)
1298 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1299 "nTSecurityDescriptor" )
1300 samdb.modify(delta)
1302 def rebuild_sd(samdb, names):
1303 """Rebuild security descriptor of the current provision from scratch
1305 During the different pre release of samba4 security descriptors (SD)
1306 were notarly broken (up to alpha11 included)
1307 This function allow to get them back in order, this function make the
1308 assumption that nobody has modified manualy an SD
1309 and so SD can be safely recalculated from scratch to get them right.
1311 :param names: List of key provision parameters"""
1313 fix_partition_sd(samdb, names)
1315 # List of namming contexts
1316 listNC = [str(names.rootdn), str(names.configdn), str(names.schemadn)]
1317 hash = {}
1318 if len(dnToRecalculate) == 0:
1319 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1320 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1321 controls=["search_options:1:2"])
1322 for obj in res:
1323 hash[str(obj["dn"])] = obj["whenCreated"]
1324 else:
1325 for dn in dnToRecalculate:
1326 if hash.has_key(dn):
1327 continue
1328 # fetch each dn to recalculate and their child within the same partition
1329 res = samdb.search(expression="objectClass=*", base=dn,
1330 scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"])
1331 for obj in res:
1332 hash[str(obj["dn"])] = obj["whenCreated"]
1334 listKeys = list(set(hash.keys()))
1335 listKeys.sort(dn_sort)
1337 if len(dnToRecalculate) != 0:
1338 message(CHANGESD, "%d DNs have been marked as needed to be recalculated"\
1339 ", recalculating %d due to inheritance"
1340 % (len(dnToRecalculate), len(listKeys)))
1342 for key in listKeys:
1343 if (key in listNC or
1344 key in dnNotToRecalculate):
1345 continue
1346 delta = Message()
1347 delta.dn = Dn(samdb, key)
1348 try:
1349 delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1350 "whenCreated" )
1351 samdb.modify(delta, ["recalculate_sd:0","relax:0"])
1352 except LdbError, e:
1353 samdb.transaction_cancel()
1354 res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1355 scope=SCOPE_SUBTREE,
1356 attrs=["dn", "nTSecurityDescriptor"],
1357 controls=["search_options:1:2"])
1358 badsd = ndr_unpack(security.descriptor,
1359 str(res[0]["nTSecurityDescriptor"]))
1360 message(ERROR, "On %s bad stuff %s" % (str(delta.dn),badsd.as_sddl(names.domainsid)))
1361 return
1363 def hasATProvision(samdb):
1364 entry = samdb.search(expression="dn=@PROVISION", base = "",
1365 scope=SCOPE_BASE,
1366 attrs=["dn"])
1368 if entry != None and len(entry) == 1:
1369 return True
1370 else:
1371 return False
1373 def removeProvisionUSN(samdb):
1374 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1375 entry = samdb.search(expression="dn=@PROVISION", base = "",
1376 scope=SCOPE_BASE,
1377 attrs=attrs)
1378 empty = Message()
1379 empty.dn = entry[0].dn
1380 delta = samdb.msg_diff(entry[0], empty)
1381 delta.remove("dn")
1382 delta.dn = entry[0].dn
1383 samdb.modify(delta)
1385 def remove_stored_generated_attrs(paths, creds, session, lp):
1386 """Remove previously stored constructed attributes
1388 :param paths: List of paths for different provision objects
1389 from the upgraded provision
1390 :param creds: A credential object
1391 :param session: A session object
1392 :param lp: A line parser object
1393 :return: An associative array whose key are the different constructed
1394 attributes and the value the dn where this attributes were found.
1398 def simple_update_basesamdb(newpaths, paths, names):
1399 """Update the provision container db: sam.ldb
1400 This function is aimed at very old provision (before alpha9)
1402 :param newpaths: List of paths for different provision objects
1403 from the reference provision
1404 :param paths: List of paths for different provision objects
1405 from the upgraded provision
1406 :param names: List of key provision parameters"""
1408 message(SIMPLE, "Copy samdb")
1409 shutil.copy(newpaths.samdb, paths.samdb)
1411 message(SIMPLE, "Update partitions filename if needed")
1412 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1413 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1414 usersldb = os.path.join(paths.private_dir, "users.ldb")
1415 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1417 if not os.path.isdir(samldbdir):
1418 os.mkdir(samldbdir)
1419 os.chmod(samldbdir, 0700)
1420 if os.path.isfile(schemaldb):
1421 shutil.copy(schemaldb, os.path.join(samldbdir,
1422 "%s.ldb"%str(names.schemadn).upper()))
1423 os.remove(schemaldb)
1424 if os.path.isfile(usersldb):
1425 shutil.copy(usersldb, os.path.join(samldbdir,
1426 "%s.ldb"%str(names.rootdn).upper()))
1427 os.remove(usersldb)
1428 if os.path.isfile(configldb):
1429 shutil.copy(configldb, os.path.join(samldbdir,
1430 "%s.ldb"%str(names.configdn).upper()))
1431 os.remove(configldb)
1434 def update_privilege(ref_private_path, cur_private_path):
1435 """Update the privilege database
1437 :param ref_private_path: Path to the private directory of the reference
1438 provision.
1439 :param cur_private_path: Path to the private directory of the current
1440 (and to be updated) provision."""
1441 message(SIMPLE, "Copy privilege")
1442 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1443 os.path.join(cur_private_path, "privilege.ldb"))
1446 def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):
1447 """Upgrade the SAM DB contents for all the provision partitions
1449 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1450 provision
1451 :param samdb: An LDB object connected to the sam.ldb of the update
1452 provision
1453 :param names: List of key provision parameters
1454 :param provisionUSNs: A dictionnary with range of USN modified during provision
1455 or upgradeprovision. Ranges are grouped by invocationID.
1456 :param schema: A Schema object that represent the schema of the provision
1457 :param prereloadfunc: A function that must be executed just before the reload
1458 of the schema
1461 message(SIMPLE, "Starting update of samdb")
1462 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1463 schema, provisionUSNs, prereloadfunc)
1464 if ret:
1465 message(SIMPLE, "Update of samdb finished")
1466 return 1
1467 else:
1468 message(SIMPLE, "Update failed")
1469 return 0
1472 def backup_provision(paths, dir, only_db):
1473 """This function backup the provision files so that a rollback
1474 is possible
1476 :param paths: Paths to different objects
1477 :param dir: Directory where to store the backup
1478 :param only_db: Skip sysvol for users with big sysvol
1480 if paths.sysvol and not only_db:
1481 copytree_with_xattrs(paths.sysvol, os.path.join(dir, "sysvol"))
1482 shutil.copy2(paths.samdb, dir)
1483 shutil.copy2(paths.secrets, dir)
1484 shutil.copy2(paths.idmapdb, dir)
1485 shutil.copy2(paths.privilege, dir)
1486 if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1487 shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1488 shutil.copy2(paths.smbconf, dir)
1489 shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1491 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1492 if not os.path.isdir(samldbdir):
1493 samldbdir = paths.private_dir
1494 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1495 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1496 usersldb = os.path.join(paths.private_dir, "users.ldb")
1497 shutil.copy2(schemaldb, dir)
1498 shutil.copy2(usersldb, dir)
1499 shutil.copy2(configldb, dir)
1500 else:
1501 shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1504 def sync_calculated_attributes(samdb, names):
1505 """Synchronize attributes used for constructed ones, with the
1506 old constructed that were stored in the database.
1508 This apply for instance to msds-keyversionnumber that was
1509 stored and that is now constructed from replpropertymetadata.
1511 :param samdb: An LDB object attached to the currently upgraded samdb
1512 :param names: Various key parameter about current provision.
1514 listAttrs = ["msDs-KeyVersionNumber"]
1515 hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
1516 if hash.has_key("msDs-KeyVersionNumber"):
1517 increment_calculated_keyversion_number(samdb, names.rootdn,
1518 hash["msDs-KeyVersionNumber"])
1520 # Synopsis for updateprovision
1521 # 1) get path related to provision to be update (called current)
1522 # 2) open current provision ldbs
1523 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1524 # of the DC ....)
1525 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1526 # by either upgradeprovision or provision
1527 # 5) creation of a new provision the latest version of provision script
1528 # (called reference)
1529 # 6) get reference provision paths
1530 # 7) open reference provision ldbs
1531 # 8) setup helpers data that will help the update process
1532 # 9) update the privilege ldb by copying the one of referecence provision to
1533 # the current provision
1534 # 10)get the oemInfo field, this field contains information about the different
1535 # provision that have been done
1536 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1537 # integer) or none of this the following things are done
1538 # A) When alpha9 or alphaxx is present
1539 # The base sam.ldb file is updated by looking at the difference between
1540 # referrence one and the current one. Everything is copied with the
1541 # exception of lastProvisionUSN attributes.
1542 # B) Other case (it reflect that that provision was done before alpha9)
1543 # The base sam.ldb of the reference provision is copied over
1544 # the current one, if necessary ldb related to partitions are moved
1545 # and renamed
1546 # The highest used USN is fetched so that changed by upgradeprovision
1547 # usn can be tracked
1548 # 12)A Schema object is created, it will be used to provide a complete
1549 # schema to current provision during update (as the schema of the
1550 # current provision might not be complete and so won't allow some
1551 # object to be created)
1552 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1553 # 14)The secrets db is updated by pull all the difference from the reference
1554 # provision into the current provision
1555 # 15)As the previous step has most probably modified the password stored in
1556 # in secret for the current DC, a new password is generated,
1557 # the kvno is bumped and the entry in samdb is also updated
1558 # 16)For current provision older than alpha9, we must fix the SD a little bit
1559 # administrator to update them because SD used to be generated with the
1560 # system account before alpha9.
1561 # 17)The highest usn modified so far is searched in the database it will be
1562 # the upper limit for usn modified during provision.
1563 # This is done before potential SD recalculation because we do not want
1564 # SD modified during recalculation to be marked as modified during provision
1565 # (and so possibly remplaced at next upgradeprovision)
1566 # 18)Rebuilt SD if the flag indicate to do so
1567 # 19)Check difference between SD of reference provision and those of the
1568 # current provision. The check is done by getting the sddl representation
1569 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1570 # Each part is verified separetly, for dacl and sacl ACL is splited into
1571 # ACEs and each ACE is verified separately (so that a permutation in ACE
1572 # didn't raise as an error).
1573 # 20)The oemInfo field is updated to add information about the fact that the
1574 # provision has been updated by the upgradeprovision version xxx
1575 # (the version is the one obtained when starting samba with the --version
1576 # parameter)
1577 # 21)Check if the current provision has all the settings needed for dynamic
1578 # DNS update to work (that is to say the provision is newer than
1579 # january 2010). If not dns configuration file from reference provision
1580 # are copied in a sub folder and the administrator is invited to
1581 # do what is needed.
1582 # 22)If the lastProvisionUSN attribute was present it is updated to add
1583 # the range of usns modified by the current upgradeprovision
1586 # About updating the sam DB
1587 # The update takes place in update_partition function
1588 # This function read both current and reference provision and list all
1589 # the available DN of objects
1590 # If the string representation of a DN in reference provision is
1591 # equal to the string representation of a DN in current provision
1592 # (without taking care of case) then the object is flaged as being
1593 # present. If the object is not present in current provision the object
1594 # is being flaged as missing in current provision. Object present in current
1595 # provision but not in reference provision are ignored.
1596 # Once the list of objects present and missing is done, the deleted object
1597 # containers are created in the differents partitions (if missing)
1599 # Then the function add_missing_entries is called
1600 # This function will go through the list of missing entries by calling
1601 # add_missing_object for the given object. If this function returns 0
1602 # it means that the object needs some other object in order to be created
1603 # The object is reappended at the end of the list to be created later
1604 # (and preferably after all the needed object have been created)
1605 # The function keeps on looping on the list of object to be created until
1606 # it's empty or that the number of defered creation is equal to the number
1607 # of object that still needs to be created.
1609 # The function add_missing_object will first check if the object can be created.
1610 # That is to say that it didn't depends other not yet created objects
1611 # If requisit can't be fullfilled it exists with 0
1612 # Then it will try to create the missing entry by creating doing
1613 # an ldb_message_diff between the object in the reference provision and
1614 # an empty object.
1615 # This resulting object is filtered to remove all the back link attribute
1616 # (ie. memberOf) as they will be created by the other linked object (ie.
1617 # the one with the member attribute)
1618 # All attributes specified in the attrNotCopied array are
1619 # also removed it's most of the time generated attributes
1621 # After missing entries have been added the update_partition function will
1622 # take care of object that exist but that need some update.
1623 # In order to do so the function update_present is called with the list
1624 # of object that are present in both provision and that might need an update.
1626 # This function handle first case mismatch so that the DN in the current
1627 # provision have the same case as in reference provision
1629 # It will then construct an associative array consiting of attributes as
1630 # key and invocationid as value( if the originating invocation id is
1631 # different from the invocation id of the current DC the value is -1 instead).
1633 # If the range of provision modified attributes is present, the function will
1634 # use the replMetadataProperty update method which is the following:
1635 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1636 # creationTime, msDs-KeyVersionNumber, oEMInformation
1637 # Check for each attribute if its usn is within one of the modified by
1638 # provision range and if its originating id is the invocation id of the
1639 # current DC, then validate the update from reference to current.
1640 # If not or if there is no replMetatdataProperty for this attribute then we
1641 # do not update it.
1642 # Otherwise (case the range of provision modified attribute is not present) it
1643 # use the following process:
1644 # All attributes that need to be added are accepted at the exeption of those
1645 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1646 # correct flags specified.
1647 # For attributes that need to be modified or removed, a check is performed
1648 # in OverwrittenAtt, if the attribute is present and the modification flag
1649 # (remove, delete) is one of those listed for this attribute then modification
1650 # is accepted. For complicated handling of attribute update, the control is passed
1651 # to handle_special_case
1655 if __name__ == '__main__':
1656 global defSDmodified
1657 defSDmodified = False
1659 if opts.nontaclfix and opts.fixntacl:
1660 message(SIMPLE, "nontaclfix and fixntacl are mutally exclusive")
1661 # From here start the big steps of the program
1662 # 1) First get files paths
1663 paths = get_paths(param, smbconf=smbconf)
1664 # Get ldbs with the system session, it is needed for searching
1665 # provision parameters
1666 session = system_session()
1668 # This variable will hold the last provision USN once if it exists.
1669 minUSN = 0
1670 # 2)
1671 ldbs = get_ldbs(paths, creds, session, lp)
1672 backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1673 prefix="backupprovision")
1674 backup_provision(paths, backupdir, opts.db_backup_only)
1675 try:
1676 ldbs.startTransactions()
1678 # 3) Guess all the needed names (variables in fact) from the current
1679 # provision.
1680 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1681 paths, smbconf, lp)
1682 # 4)
1683 lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1684 if lastProvisionUSNs is not None:
1685 v = 0
1686 for k in lastProvisionUSNs.keys():
1687 for r in lastProvisionUSNs[k]:
1688 v = v + 1
1690 message(CHANGE,
1691 "Find last provision USN, %d invocation(s) for a total of %d ranges" % \
1692 (len(lastProvisionUSNs.keys()), v /2 ))
1694 if lastProvisionUSNs.get("default") != None:
1695 message(CHANGE, "Old style for usn ranges used")
1696 lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"]
1697 del lastProvisionUSNs["default"]
1698 else:
1699 message(SIMPLE, "Your provision lacks provision range information")
1700 if confirm("Do you want to run findprovisionusnranges to try to find them ?", False):
1701 ldbs.groupedRollback()
1702 minobj = 5
1703 (hash_id, nb_obj) = findprovisionrange(ldbs.sam, ldb.Dn(ldbs.sam, str(names.rootdn)))
1704 message(SIMPLE, "Here is a list of changes that modified more than %d objects in 1 minute." % minobj)
1705 message(SIMPLE, "Usually changes made by provision and upgradeprovision are those who affect a couple"\
1706 " of hundred of objects or more")
1707 message(SIMPLE, "Total number of objects: %d" % nb_obj)
1708 message(SIMPLE, "")
1710 print_provision_ranges(hash_id, minobj, None, str(paths.samdb), str(names.invocation))
1712 message(SIMPLE, "Once you applied/adapted the change(s) please restart the upgradeprovision script")
1713 sys.exit(0)
1715 # Objects will be created with the admin session
1716 # (not anymore system session)
1717 adm_session = admin_session(lp, str(names.domainsid))
1718 # So we reget handle on objects
1719 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1720 if not opts.fixntacl:
1721 if not sanitychecks(ldbs.sam, names):
1722 message(SIMPLE, "Sanity checks for the upgrade have failed. "
1723 "Check the messages and correct the errors "
1724 "before rerunning upgradeprovision")
1725 ldbs.groupedRollback()
1726 sys.exit(1)
1728 # Let's see provision parameters
1729 print_provision_key_parameters(names)
1731 # 5) With all this information let's create a fresh new provision used as
1732 # reference
1733 message(SIMPLE, "Creating a reference provision")
1734 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1735 prefix="referenceprovision")
1736 result = newprovision(names, creds, session, smbconf, provisiondir,
1737 provision_logger)
1738 result.report_logger(provision_logger)
1740 # TODO
1741 # 6) and 7)
1742 # We need to get a list of object which SD is directly computed from
1743 # defaultSecurityDescriptor.
1744 # This will allow us to know which object we can rebuild the SD in case
1745 # of change of the parent's SD or of the defaultSD.
1746 # Get file paths of this new provision
1747 newpaths = get_paths(param, targetdir=provisiondir)
1748 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1749 new_ldbs.startTransactions()
1751 populateNotReplicated(new_ldbs.sam, names.schemadn)
1752 # 8) Populate some associative array to ease the update process
1753 # List of attribute which are link and backlink
1754 populate_links(new_ldbs.sam, names.schemadn)
1755 # List of attribute with ASN DN synthax)
1756 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1757 # 9)
1758 update_privilege(newpaths.private_dir, paths.private_dir)
1759 # 10)
1760 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1761 # Do some modification on sam.ldb
1762 ldbs.groupedCommit()
1763 new_ldbs.groupedCommit()
1764 deltaattr = None
1765 # 11)
1766 message(GUESS, oem)
1767 if oem is None or hasATProvision(ldbs.sam) or re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1768 # 11) A
1769 # Starting from alpha9 we can consider that the structure is quite ok
1770 # and that we should do only dela
1771 deltaattr = delta_update_basesamdb(newpaths.samdb,
1772 paths.samdb,
1773 creds,
1774 session,
1776 message)
1777 else:
1778 # 11) B
1779 simple_update_basesamdb(newpaths, paths, names)
1780 ldbs = get_ldbs(paths, creds, session, lp)
1781 removeProvisionUSN(ldbs.sam)
1783 ldbs.startTransactions()
1784 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1785 new_ldbs.startTransactions()
1787 # 12)
1788 schema = Schema(names.domainsid, schemadn=str(names.schemadn))
1789 # We create a closure that will be invoked just before schema reload
1790 def schemareloadclosure():
1791 basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
1792 options=["modules:"])
1793 doit = False
1794 if deltaattr is not None and len(deltaattr) > 1:
1795 doit = True
1796 if doit:
1797 deltaattr.remove("dn")
1798 for att in deltaattr:
1799 if att.lower() == "dn":
1800 continue
1801 if (deltaattr.get(att) is not None
1802 and deltaattr.get(att).flags() != FLAG_MOD_ADD):
1803 doit = False
1804 elif deltaattr.get(att) is None:
1805 doit = False
1806 if doit:
1807 message(CHANGE, "Applying delta to @ATTRIBUTES")
1808 deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
1809 basesam.modify(deltaattr)
1810 else:
1811 message(CHANGE, "Not applying delta to @ATTRIBUTES because "
1812 "there is not only add")
1813 # 13)
1814 if opts.full:
1815 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1816 schema, schemareloadclosure):
1817 message(SIMPLE, "Rolling back all changes. Check the cause"
1818 " of the problem")
1819 message(SIMPLE, "Your system is as it was before the upgrade")
1820 ldbs.groupedRollback()
1821 new_ldbs.groupedRollback()
1822 shutil.rmtree(provisiondir)
1823 sys.exit(1)
1824 else:
1825 # Try to reapply the change also when we do not change the sam
1826 # as the delta_upgrade
1827 schemareloadclosure()
1828 sync_calculated_attributes(ldbs.sam, names)
1829 res = ldbs.sam.search(expression="(samaccountname=dns)",
1830 scope=SCOPE_SUBTREE, attrs=["dn"],
1831 controls=["search_options:1:2"])
1832 if len(res) > 0:
1833 message(SIMPLE, "You still have the old DNS object for managing "
1834 "dynamic DNS, but you didn't supply --full so "
1835 "a correct update can't be done")
1836 ldbs.groupedRollback()
1837 new_ldbs.groupedRollback()
1838 shutil.rmtree(provisiondir)
1839 sys.exit(1)
1840 # 14)
1841 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1842 # 14bis)
1843 res = ldbs.sam.search(expression="(samaccountname=dns)",
1844 scope=SCOPE_SUBTREE, attrs=["dn"],
1845 controls=["search_options:1:2"])
1847 if (len(res) == 1):
1848 ldbs.sam.delete(res[0]["dn"])
1849 res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
1850 scope=SCOPE_SUBTREE, attrs=["dn"])
1851 update_dns_account_password(ldbs.sam, ldbs.secrets, names)
1852 message(SIMPLE, "IMPORTANT!!! "
1853 "If you were using Dynamic DNS before you need "
1854 "to update your configuration, so that the "
1855 "tkey-gssapi-credential has the following value: "
1856 "DNS/%s.%s" % (names.netbiosname.lower(),
1857 names.realm.lower()))
1858 # 15)
1859 message(SIMPLE, "Update machine account")
1860 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1862 dnToRecalculate.sort(dn_sort)
1863 # 16) SD should be created with admin but as some previous acl were so wrong
1864 # that admin can't modify them we have first to recreate them with the good
1865 # form but with system account and then give the ownership to admin ...
1866 if str(oem) != "" and not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1867 message(SIMPLE, "Fixing very old provision SD")
1868 rebuild_sd(ldbs.sam, names)
1870 # We calculate the max USN before recalculating the SD because we might
1871 # touch object that have been modified after a provision and we do not
1872 # want that the next upgradeprovision thinks that it has a green light
1873 # to modify them
1875 # 17)
1876 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1878 # 18) We rebuild SD if a we have a list of DN to recalculate or if the
1879 # defSDmodified is set.
1880 if defSDmodified or len(dnToRecalculate) >0:
1881 message(SIMPLE, "Some (default) security descriptors (SDs) have "
1882 "changed, recalculating them")
1883 ldbs.sam.set_session_info(adm_session)
1884 rebuild_sd(ldbs.sam, names)
1886 # 19)
1887 # Now we are quite confident in the recalculate process of the SD, we make
1888 # it optional. And we don't do it if there is DN that we must touch
1889 # as we are assured that on this DNs we will have differences !
1890 # Also the check must be done in a clever way as for the moment we just
1891 # compare SDDL
1892 if len(dnNotToRecalculate) == 0 and (opts.debugchangesd or opts.debugall):
1893 message(CHANGESD, "Checking recalculated SDs")
1894 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1896 # 20)
1897 updateOEMInfo(ldbs.sam, str(names.rootdn))
1898 # 21)
1899 check_for_DNS(newpaths.private_dir, paths.private_dir)
1900 # 22)
1901 if lastProvisionUSNs is not None:
1902 update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
1903 if opts.full and (names.policyid is None or names.policyid_dc is None):
1904 update_policyids(names, ldbs.sam)
1905 if opts.nontaclfix:
1906 if opts.full or opts.resetfileacl or opts.fixntacl:
1907 try:
1908 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1909 except ProvisioningError, e:
1910 message(ERROR, "The policy for domain controller is missing. "
1911 "You should restart upgradeprovision with --full")
1912 except IOError, e:
1913 message(ERROR, "Setting ACL not supported on your filesystem")
1914 else:
1915 try:
1916 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1917 except ProvisioningError, e:
1918 message(ERROR, "The policy for domain controller is missing. "
1919 "You should restart upgradeprovision with --full")
1920 if not opts.fixntacl:
1921 ldbs.groupedCommit()
1922 new_ldbs.groupedCommit()
1923 message(SIMPLE, "Upgrade finished!")
1924 # remove reference provision now that everything is done !
1925 # So we have reindexed first if need when the merged schema was reloaded
1926 # (as new attributes could have quick in)
1927 # But the second part of the update (when we update existing objects
1928 # can also have an influence on indexing as some attribute might have their
1929 # searchflag modificated
1930 message(SIMPLE, "Reopening samdb to trigger reindexing if needed "
1931 "after modification")
1932 samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
1933 message(SIMPLE, "Reindexing finished")
1935 shutil.rmtree(provisiondir)
1936 else:
1937 ldbs.groupedRollback()
1938 message(SIMPLE, "ACLs fixed !")
1939 except StandardError, err:
1940 message(ERROR, "A problem occurred while trying to upgrade your "
1941 "provision. A full backup is located at %s" % backupdir)
1942 if opts.debugall or opts.debugchange:
1943 (typ, val, tb) = sys.exc_info()
1944 traceback.print_exception(typ, val, tb)
1945 sys.exit(1)