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