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