scripting: Move samba.provision.descriptor to samba.descriptor
[Samba/gebeck_regimport.git] / source4 / scripting / bin / samba_upgradeprovision
blobc0ce040801baa74841699514f5db8dcfab7098f4
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.descriptor import get_wellknown_sds, get_empty_descriptor
50 from samba.provision import (find_provision_key_parameters,
51 ProvisioningError, get_last_provision_usn,
52 get_max_usn, update_provision_usn, setup_path)
53 from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
54 from samba.dcerpc import security, drsblobs
55 from samba.dcerpc.security import (
56 SECINFO_OWNER, SECINFO_GROUP, SECINFO_DACL, SECINFO_SACL)
57 from samba.ndr import ndr_unpack
58 from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
59 get_ldbs, findprovisionrange,
60 usn_in_range, identic_rename, get_diff_sds,
61 update_secrets, CHANGE, ERROR, SIMPLE,
62 CHANGEALL, GUESS, CHANGESD, PROVISION,
63 updateOEMInfo, getOEMInfo, update_gpo,
64 delta_update_basesamdb, update_policyids,
65 update_machine_account_password,
66 search_constructed_attrs_stored,
67 int64range2str, update_dns_account_password,
68 increment_calculated_keyversion_number,
69 print_provision_ranges)
70 from samba.xattr import copytree_with_xattrs
72 # make sure the script dies immediately when hitting control-C,
73 # rather than raising KeyboardInterrupt. As we do all database
74 # operations using transactions, this is safe.
75 import signal
76 signal.signal(signal.SIGINT, signal.SIG_DFL)
78 replace=2**FLAG_MOD_REPLACE
79 add=2**FLAG_MOD_ADD
80 delete=2**FLAG_MOD_DELETE
81 never=0
84 # Will be modified during provision to tell if default sd has been modified
85 # somehow ...
87 #Errors are always logged
89 __docformat__ = "restructuredText"
91 # Attributes that are never copied from the reference provision (even if they
92 # do not exist in the destination object).
93 # This is most probably because they are populated automatcally when object is
94 # created
95 # This also apply to imported object from reference provision
96 replAttrNotCopied = [ "dn", "whenCreated", "whenChanged", "objectGUID",
97 "parentGUID", "objectCategory", "distinguishedName",
98 "instanceType", "cn",
99 "lmPwdHistory", "pwdLastSet", "ntPwdHistory",
100 "unicodePwd", "dBCSPwd", "supplementalCredentials",
101 "gPCUserExtensionNames", "gPCMachineExtensionNames",
102 "maxPwdAge", "secret", "possibleInferiors", "privilege",
103 "sAMAccountType", "oEMInformation", "creationTime" ]
105 nonreplAttrNotCopied = ["uSNCreated", "replPropertyMetaData", "uSNChanged",
106 "nextRid" ,"rIDNextRID", "rIDPreviousAllocationPool"]
108 nonDSDBAttrNotCopied = ["msDS-KeyVersionNumber", "priorSecret", "priorWhenChanged"]
111 attrNotCopied = replAttrNotCopied
112 attrNotCopied.extend(nonreplAttrNotCopied)
113 attrNotCopied.extend(nonDSDBAttrNotCopied)
114 # Usually for an object that already exists we do not overwrite attributes as
115 # they might have been changed for good reasons. Anyway for a few of them it's
116 # mandatory to replace them otherwise the provision will be broken somehow.
117 # But for attribute that are just missing we do not have to specify them as the default
118 # behavior is to add missing attribute
119 hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace,
120 "systemOnly":replace, "searchFlags":replace,
121 "mayContain":replace, "systemFlags":replace+add,
122 "description":replace, "operatingSystemVersion":replace,
123 "adminPropertyPages":replace, "groupType":replace,
124 "wellKnownObjects":replace, "privilege":never,
125 "defaultSecurityDescriptor": replace,
126 "rIDAvailablePool": never,
127 "versionNumber" : add,
128 "rIDNextRID": add, "rIDUsedPool": never,
129 "defaultSecurityDescriptor": replace + add,
130 "isMemberOfPartialAttributeSet": delete,
131 "attributeDisplayNames": replace + add,
132 "versionNumber": add}
134 dnNotToRecalculateFound = False
135 dnToRecalculate = []
136 backlinked = []
137 forwardlinked = set()
138 dn_syntax_att = []
139 not_replicated = []
140 def define_what_to_log(opts):
141 what = 0
142 if opts.debugchange:
143 what = what | CHANGE
144 if opts.debugchangesd:
145 what = what | CHANGESD
146 if opts.debugguess:
147 what = what | GUESS
148 if opts.debugprovision:
149 what = what | PROVISION
150 if opts.debugall:
151 what = what | CHANGEALL
152 return what
155 parser = optparse.OptionParser("provision [options]")
156 sambaopts = options.SambaOptions(parser)
157 parser.add_option_group(sambaopts)
158 parser.add_option_group(options.VersionOptions(parser))
159 credopts = options.CredentialsOptions(parser)
160 parser.add_option_group(credopts)
161 parser.add_option("--setupdir", type="string", metavar="DIR",
162 help="directory with setup files")
163 parser.add_option("--debugprovision", help="Debug provision", action="store_true")
164 parser.add_option("--debugguess", action="store_true",
165 help="Print information on which values are guessed")
166 parser.add_option("--debugchange", action="store_true",
167 help="Print information on what is different but won't be changed")
168 parser.add_option("--debugchangesd", action="store_true",
169 help="Print security descriptor differences")
170 parser.add_option("--debugall", action="store_true",
171 help="Print all available information (very verbose)")
172 parser.add_option("--db_backup_only", action="store_true",
173 help="Do the backup of the database in the provision, skip the sysvol / netlogon shares")
174 parser.add_option("--full", action="store_true",
175 help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
176 parser.add_option("--very-old-pre-alpha9", action="store_true",
177 help="Perform additional forced SD resets required for a database from before Samba 4.0.0alpha9.")
179 opts = parser.parse_args()[0]
181 handler = logging.StreamHandler(sys.stdout)
182 upgrade_logger = logging.getLogger("upgradeprovision")
183 upgrade_logger.setLevel(logging.INFO)
185 upgrade_logger.addHandler(handler)
187 provision_logger = logging.getLogger("provision")
188 provision_logger.addHandler(handler)
190 whatToLog = define_what_to_log(opts)
192 def message(what, text):
193 """Print a message if this message type has been selected to be printed
195 :param what: Category of the message
196 :param text: Message to print """
197 if (whatToLog & what) or what <= 0:
198 upgrade_logger.info("%s", text)
200 if len(sys.argv) == 1:
201 opts.interactive = True
202 lp = sambaopts.get_loadparm()
203 smbconf = lp.configfile
205 creds = credopts.get_credentials(lp)
206 creds.set_kerberos_state(DONT_USE_KERBEROS)
210 def check_for_DNS(refprivate, private, dns_backend):
211 """Check if the provision has already the requirement for dynamic dns
213 :param refprivate: The path to the private directory of the reference
214 provision
215 :param private: The path to the private directory of the upgraded
216 provision"""
218 spnfile = "%s/spn_update_list" % private
219 dnsfile = "%s/dns_update_list" % private
221 if not os.path.exists(spnfile):
222 shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
224 if not os.path.exists(dnsfile):
225 shutil.copy("%s/dns_update_list" % refprivate, "%s" % dnsfile)
227 if dns_backend not in ['BIND9_DLZ', 'BIND9_FLATFILE']:
228 return
230 namedfile = lp.get("dnsupdate:path")
231 if not namedfile:
232 namedfile = "%s/named.conf.update" % private
233 if not os.path.exists(namedfile):
234 destdir = "%s/new_dns" % private
235 dnsdir = "%s/dns" % private
237 if not os.path.exists(destdir):
238 os.mkdir(destdir)
239 if not os.path.exists(dnsdir):
240 os.mkdir(dnsdir)
241 shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
242 shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
243 message(SIMPLE, "It seems that your provision did not integrate "
244 "new rules for dynamic dns update of domain related entries")
245 message(SIMPLE, "A copy of the new bind configuration files and "
246 "template has been put in %s, you should read them and "
247 "configure dynamic dns updates" % destdir)
250 def populate_links(samdb, schemadn):
251 """Populate an array with all the back linked attributes
253 This attributes that are modified automaticaly when
254 front attibutes are changed
256 :param samdb: A LDB object for sam.ldb file
257 :param schemadn: DN of the schema for the partition"""
258 linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
259 backlinked.extend(linkedAttHash.values())
260 for t in linkedAttHash.keys():
261 forwardlinked.add(t)
263 def isReplicated(att):
264 """ Indicate if the attribute is replicated or not
266 :param att: Name of the attribute to be tested
267 :return: True is the attribute is replicated, False otherwise
270 return (att not in not_replicated)
272 def populateNotReplicated(samdb, schemadn):
273 """Populate an array with all the attributes that are not replicated
275 :param samdb: A LDB object for sam.ldb file
276 :param schemadn: DN of the schema for the partition"""
277 res = samdb.search(expression="(&(objectclass=attributeSchema)(systemflags:1.2.840.113556.1.4.803:=1))", base=Dn(samdb,
278 str(schemadn)), scope=SCOPE_SUBTREE,
279 attrs=["lDAPDisplayName"])
280 for elem in res:
281 not_replicated.append(str(elem["lDAPDisplayName"]))
284 def populate_dnsyntax(samdb, schemadn):
285 """Populate an array with all the attributes that have DN synthax
286 (oid 2.5.5.1)
288 :param samdb: A LDB object for sam.ldb file
289 :param schemadn: DN of the schema for the partition"""
290 res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
291 str(schemadn)), scope=SCOPE_SUBTREE,
292 attrs=["lDAPDisplayName"])
293 for elem in res:
294 dn_syntax_att.append(elem["lDAPDisplayName"])
297 def sanitychecks(samdb, names):
298 """Make some checks before trying to update
300 :param samdb: An LDB object opened on sam.ldb
301 :param names: list of key provision parameters
302 :return: Status of check (1 for Ok, 0 for not Ok) """
303 res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
304 scope=SCOPE_SUBTREE, attrs=["dn"],
305 controls=["search_options:1:2"])
306 if len(res) == 0:
307 print "No DC found. Your provision is most probably broken!"
308 return False
309 elif len(res) != 1:
310 print "Found %d domain controllers. For the moment " \
311 "upgradeprovision is not able to handle an upgrade on a " \
312 "domain with more than one DC. Please demote the other " \
313 "DC(s) before upgrading" % len(res)
314 return False
315 else:
316 return True
319 def print_provision_key_parameters(names):
320 """Do a a pretty print of provision parameters
322 :param names: list of key provision parameters """
323 message(GUESS, "rootdn :" + str(names.rootdn))
324 message(GUESS, "configdn :" + str(names.configdn))
325 message(GUESS, "schemadn :" + str(names.schemadn))
326 message(GUESS, "serverdn :" + str(names.serverdn))
327 message(GUESS, "netbiosname :" + names.netbiosname)
328 message(GUESS, "defaultsite :" + names.sitename)
329 message(GUESS, "dnsdomain :" + names.dnsdomain)
330 message(GUESS, "hostname :" + names.hostname)
331 message(GUESS, "domain :" + names.domain)
332 message(GUESS, "realm :" + names.realm)
333 message(GUESS, "invocationid:" + names.invocation)
334 message(GUESS, "policyguid :" + names.policyid)
335 message(GUESS, "policyguiddc:" + str(names.policyid_dc))
336 message(GUESS, "domainsid :" + str(names.domainsid))
337 message(GUESS, "domainguid :" + names.domainguid)
338 message(GUESS, "ntdsguid :" + names.ntdsguid)
339 message(GUESS, "domainlevel :" + str(names.domainlevel))
342 def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb):
343 """Define more complicate update rules for some attributes
345 :param att: The attribute to be updated
346 :param delta: A messageElement object that correspond to the difference
347 between the updated object and the reference one
348 :param new: The reference object
349 :param old: The Updated object
350 :param useReplMetadata: A boolean that indicate if the update process
351 use replPropertyMetaData to decide what has to be updated.
352 :param basedn: The base DN of the provision
353 :param aldb: An ldb object used to build DN
354 :return: True to indicate that the attribute should be kept, False for
355 discarding it"""
357 # We do most of the special case handle if we do not have the
358 # highest usn as otherwise the replPropertyMetaData will guide us more
359 # correctly
360 if not useReplMetadata:
361 flag = delta.get(att).flags()
362 if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and
363 ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT,"
364 "CN=Services,CN=Configuration,%s" % basedn)
365 == old[0].dn):
366 return True
367 if (att == "userAccountControl" and flag == FLAG_MOD_REPLACE and
368 ldb.Dn(aldb, "CN=Administrator,CN=Users,%s" % basedn)
369 == old[0].dn):
370 message(SIMPLE, "We suggest that you change the userAccountControl"
371 " for user Administrator from value %d to %d" %
372 (int(str(old[0][att])), int(str(new[0][att]))))
373 return False
374 if (att == "minPwdAge" and flag == FLAG_MOD_REPLACE):
375 if (long(str(old[0][att])) == 0):
376 delta[att] = MessageElement(new[0][att], FLAG_MOD_REPLACE, att)
377 return True
379 if (att == "member" and flag == FLAG_MOD_REPLACE):
380 hash = {}
381 newval = []
382 changeDelta=0
383 for elem in old[0][att]:
384 hash[str(elem).lower()]=1
385 newval.append(str(elem))
387 for elem in new[0][att]:
388 if not hash.has_key(str(elem).lower()):
389 changeDelta=1
390 newval.append(str(elem))
391 if changeDelta == 1:
392 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
393 else:
394 delta.remove(att)
395 return True
397 if (att in ("gPLink", "gPCFileSysPath") and
398 flag == FLAG_MOD_REPLACE and
399 str(new[0].dn).lower() == str(old[0].dn).lower()):
400 delta.remove(att)
401 return True
403 if att == "forceLogoff":
404 ref=0x8000000000000000
405 oldval=int(old[0][att][0])
406 newval=int(new[0][att][0])
407 ref == old and ref == abs(new)
408 return True
410 if att in ("adminDisplayName", "adminDescription"):
411 return True
413 if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (names.schemadn)
414 and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
415 return True
417 if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
418 att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
419 return True
421 if (str(old[0].dn) == "%s" % (str(names.rootdn))
422 and att == "subRefs" and flag == FLAG_MOD_REPLACE):
423 return True
424 #Allow to change revision of ForestUpdates objects
425 if (att == "revision" or att == "objectVersion"):
426 if str(delta.dn).lower().find("domainupdates") and str(delta.dn).lower().find("forestupdates") > 0:
427 return True
428 if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
429 return True
431 # This is a bit of special animal as we might have added
432 # already SPN entries to the list that has to be modified
433 # So we go in detail to try to find out what has to be added ...
434 if (att == "servicePrincipalName" and delta.get(att).flags() == FLAG_MOD_REPLACE):
435 hash = {}
436 newval = []
437 changeDelta = 0
438 for elem in old[0][att]:
439 hash[str(elem)]=1
440 newval.append(str(elem))
442 for elem in new[0][att]:
443 if not hash.has_key(str(elem)):
444 changeDelta = 1
445 newval.append(str(elem))
446 if changeDelta == 1:
447 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
448 else:
449 delta.remove(att)
450 return True
452 return False
454 def dump_denied_change(dn, att, flagtxt, current, reference):
455 """Print detailed information about why a change is denied
457 :param dn: DN of the object which attribute is denied
458 :param att: Attribute that was supposed to be upgraded
459 :param flagtxt: Type of the update that should be performed
460 (add, change, remove, ...)
461 :param current: Value(s) of the current attribute
462 :param reference: Value(s) of the reference attribute"""
464 message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
465 + " must not be changed/removed. Discarding the change")
466 if att == "objectSid" :
467 message(CHANGE, "old : %s" % ndr_unpack(security.dom_sid, current[0]))
468 message(CHANGE, "new : %s" % ndr_unpack(security.dom_sid, reference[0]))
469 elif att == "rIDPreviousAllocationPool" or att == "rIDAllocationPool":
470 message(CHANGE, "old : %s" % int64range2str(current[0]))
471 message(CHANGE, "new : %s" % int64range2str(reference[0]))
472 else:
473 i = 0
474 for e in range(0, len(current)):
475 message(CHANGE, "old %d : %s" % (i, str(current[e])))
476 i+=1
477 if reference is not None:
478 i = 0
479 for e in range(0, len(reference)):
480 message(CHANGE, "new %d : %s" % (i, str(reference[e])))
481 i+=1
483 def handle_special_add(samdb, dn, names):
484 """Handle special operation (like remove) on some object needed during
485 upgrade
487 This is mostly due to wrong creation of the object in previous provision.
488 :param samdb: An Ldb object representing the SAM database
489 :param dn: DN of the object to inspect
490 :param names: list of key provision parameters
493 dntoremove = None
494 objDn = Dn(samdb, "CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn)
495 if dn == objDn :
496 #This entry was misplaced lets remove it if it exists
497 dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
499 objDn = Dn(samdb,
500 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn)
501 if dn == objDn:
502 #This entry was misplaced lets remove it if it exists
503 dntoremove = "CN=Certificate Service DCOM Access,"\
504 "CN=Users, %s" % names.rootdn
506 objDn = Dn(samdb, "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn)
507 if dn == objDn:
508 #This entry was misplaced lets remove it if it exists
509 dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
511 objDn = Dn(samdb, "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn)
512 if dn == objDn:
513 #This entry was misplaced lets remove it if it exists
514 dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
516 objDn = Dn(samdb,"CN=System,CN=WellKnown Security Principals,"
517 "CN=Configuration,%s" % names.rootdn)
518 if dn == objDn:
519 oldDn = Dn(samdb,"CN=Well-Known-Security-Id-System,"
520 "CN=WellKnown Security Principals,"
521 "CN=Configuration,%s" % names.rootdn)
523 res = samdb.search(expression="(distinguishedName=%s)" % oldDn,
524 base=str(names.rootdn),
525 scope=SCOPE_SUBTREE, attrs=["dn"],
526 controls=["search_options:1:2"])
528 res2 = samdb.search(expression="(distinguishedName=%s)" % dn,
529 base=str(names.rootdn),
530 scope=SCOPE_SUBTREE, attrs=["dn"],
531 controls=["search_options:1:2"])
533 if len(res) > 0 and len(res2) == 0:
534 message(CHANGE, "Existing object %s must be replaced by %s. "
535 "Renaming old object" % (str(oldDn), str(dn)))
536 samdb.rename(oldDn, objDn, ["relax:0", "provision:0"])
538 return 0
540 if dntoremove is not None:
541 res = samdb.search(expression="(cn=RID Set)",
542 base=str(names.rootdn),
543 scope=SCOPE_SUBTREE, attrs=["dn"],
544 controls=["search_options:1:2"])
546 if len(res) == 0:
547 return 2
548 res = samdb.search(expression="(distinguishedName=%s)" % dntoremove,
549 base=str(names.rootdn),
550 scope=SCOPE_SUBTREE, attrs=["dn"],
551 controls=["search_options:1:2"])
552 if len(res) > 0:
553 message(CHANGE, "Existing object %s must be replaced by %s. "
554 "Removing old object" % (dntoremove, str(dn)))
555 samdb.delete(res[0]["dn"])
556 return 0
558 return 1
561 def check_dn_nottobecreated(hash, index, listdn):
562 """Check if one of the DN present in the list has a creation order
563 greater than the current.
565 Hash is indexed by dn to be created, with each key
566 is associated the creation order.
568 First dn to be created has the creation order 0, second has 1, ...
569 Index contain the current creation order
571 :param hash: Hash holding the different DN of the object to be
572 created as key
573 :param index: Current creation order
574 :param listdn: List of DNs on which the current DN depends on
575 :return: None if the current object do not depend on other
576 object or if all object have been created before."""
577 if listdn is None:
578 return None
579 for dn in listdn:
580 key = str(dn).lower()
581 if hash.has_key(key) and hash[key] > index:
582 return str(dn)
583 return None
587 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
588 """Add a new object if the dependencies are satisfied
590 The function add the object if the object on which it depends are already
591 created
593 :param ref_samdb: Ldb object representing the SAM db of the reference
594 provision
595 :param samdb: Ldb object representing the SAM db of the upgraded
596 provision
597 :param dn: DN of the object to be added
598 :param names: List of key provision parameters
599 :param basedn: DN of the partition to be updated
600 :param hash: Hash holding the different DN of the object to be
601 created as key
602 :param index: Current creation order
603 :return: True if the object was created False otherwise"""
605 ret = handle_special_add(samdb, dn, names)
607 if ret == 2:
608 return False
610 if ret == 0:
611 return True
614 reference = ref_samdb.search(expression="(distinguishedName=%s)" % (str(dn)),
615 base=basedn, scope=SCOPE_SUBTREE,
616 controls=["search_options:1:2"])
617 empty = Message()
618 delta = samdb.msg_diff(empty, reference[0])
619 delta.dn
620 skip = False
621 try:
622 if str(reference[0].get("cn")) == "RID Set":
623 for klass in reference[0].get("objectClass"):
624 if str(klass).lower() == "ridset":
625 skip = True
626 finally:
627 if delta.get("objectSid"):
628 sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
629 m = re.match(r".*-(\d+)$", sid)
630 if m and int(m.group(1))>999:
631 delta.remove("objectSid")
632 for att in attrNotCopied:
633 delta.remove(att)
634 for att in backlinked:
635 delta.remove(att)
636 depend_on_yettobecreated = None
637 for att in dn_syntax_att:
638 depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
639 delta.get(str(att)))
640 if depend_on_yet_tobecreated is not None:
641 message(CHANGE, "Object %s depends on %s in attribute %s. "
642 "Delaying the creation" % (dn,
643 depend_on_yet_tobecreated, att))
644 return False
646 delta.dn = dn
647 if not skip:
648 message(CHANGE,"Object %s will be added" % dn)
649 samdb.add(delta, ["relax:0", "provision:0"])
650 else:
651 message(CHANGE,"Object %s was skipped" % dn)
653 return True
655 def gen_dn_index_hash(listMissing):
656 """Generate a hash associating the DN to its creation order
658 :param listMissing: List of DN
659 :return: Hash with DN as keys and creation order as values"""
660 hash = {}
661 for i in range(0, len(listMissing)):
662 hash[str(listMissing[i]).lower()] = i
663 return hash
665 def add_deletedobj_containers(ref_samdb, samdb, names):
666 """Add the object containter: CN=Deleted Objects
668 This function create the container for each partition that need one and
669 then reference the object into the root of the partition
671 :param ref_samdb: Ldb object representing the SAM db of the reference
672 provision
673 :param samdb: Ldb object representing the SAM db of the upgraded provision
674 :param names: List of key provision parameters"""
677 wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
678 partitions = [str(names.rootdn), str(names.configdn)]
679 for part in partitions:
680 ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
681 base=part, scope=SCOPE_SUBTREE,
682 attrs=["dn"],
683 controls=["show_deleted:0",
684 "show_recycled:0"])
685 delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
686 base=part, scope=SCOPE_SUBTREE,
687 attrs=["dn"],
688 controls=["show_deleted:0",
689 "show_recycled:0"])
690 if len(ref_delObjCnt) > len(delObjCnt):
691 reference = ref_samdb.search(expression="cn=Deleted Objects",
692 base=part, scope=SCOPE_SUBTREE,
693 controls=["show_deleted:0",
694 "show_recycled:0"])
695 empty = Message()
696 delta = samdb.msg_diff(empty, reference[0])
698 delta.dn = Dn(samdb, str(reference[0]["dn"]))
699 for att in attrNotCopied:
700 delta.remove(att)
702 modcontrols = ["relax:0", "provision:0"]
703 samdb.add(delta, modcontrols)
705 listwko = []
706 res = samdb.search(expression="(objectClass=*)", base=part,
707 scope=SCOPE_BASE,
708 attrs=["dn", "wellKnownObjects"])
710 targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
711 found = False
713 if len(res[0]) > 0:
714 wko = res[0]["wellKnownObjects"]
716 # The wellKnownObject that we want to add.
717 for o in wko:
718 if str(o) == targetWKO:
719 found = True
720 listwko.append(str(o))
722 if not found:
723 listwko.append(targetWKO)
725 delta = Message()
726 delta.dn = Dn(samdb, str(res[0]["dn"]))
727 delta["wellKnownObjects"] = MessageElement(listwko,
728 FLAG_MOD_REPLACE,
729 "wellKnownObjects" )
730 samdb.modify(delta)
732 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
733 """Add the missing object whose DN is the list
735 The function add the object if the objects on which it depends are
736 already created.
738 :param ref_samdb: Ldb object representing the SAM db of the reference
739 provision
740 :param samdb: Ldb object representing the SAM db of the upgraded
741 provision
742 :param dn: DN of the object to be added
743 :param names: List of key provision parameters
744 :param basedn: DN of the partition to be updated
745 :param list: List of DN to be added in the upgraded provision"""
747 listMissing = []
748 listDefered = list
750 while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
751 index = 0
752 listMissing = listDefered
753 listDefered = []
754 hashMissing = gen_dn_index_hash(listMissing)
755 for dn in listMissing:
756 ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
757 hashMissing, index)
758 index = index + 1
759 if ret == 0:
760 # DN can't be created because it depends on some
761 # other DN in the list
762 listDefered.append(dn)
764 if len(listDefered) != 0:
765 raise ProvisioningError("Unable to insert missing elements: "
766 "circular references")
768 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
769 """This function handle updates on links
771 :param samdb: An LDB object pointing to the updated provision
772 :param att: Attribute to update
773 :param basedn: The root DN of the provision
774 :param dn: The DN of the inspected object
775 :param value: The value of the attribute
776 :param ref_value: The value of this attribute in the reference provision
777 :param delta: The MessageElement object that will be applied for
778 transforming the current provision"""
780 res = samdb.search(base=dn, controls=["search_options:1:2", "reveal:1"],
781 attrs=[att])
783 blacklist = {}
784 hash = {}
785 newlinklist = []
786 changed = False
788 for v in value:
789 newlinklist.append(str(v))
791 for e in value:
792 hash[e] = 1
793 # for w2k domain level the reveal won't reveal anything ...
794 # it means that we can readd links that were removed on purpose ...
795 # Also this function in fact just accept add not removal
797 for e in res[0][att]:
798 if not hash.has_key(e):
799 # We put in the blacklist all the element that are in the "revealed"
800 # result and not in the "standard" result
801 # This element are links that were removed before and so that
802 # we don't wan't to readd
803 blacklist[e] = 1
805 for e in ref_value:
806 if not blacklist.has_key(e) and not hash.has_key(e):
807 newlinklist.append(str(e))
808 changed = True
809 if changed:
810 delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
811 else:
812 delta.remove(att)
814 return delta
817 def checkKeepAttributeWithMetadata(delta, att, message, reference, current,
818 hash_attr_usn, basedn, usns, samdb):
819 """ Check if we should keep the attribute modification or not
821 :param delta: A message diff object
822 :param att: An attribute
823 :param message: A function to print messages
824 :param reference: A message object for the current entry comming from
825 the reference provision.
826 :param current: A message object for the current entry commin from
827 the current provision.
828 :param hash_attr_usn: A dictionnary with attribute name as keys,
829 USN and invocation id as values.
830 :param basedn: The DN of the partition
831 :param usns: A dictionnary with invocation ID as keys and USN ranges
832 as values.
833 :param samdb: A ldb object pointing to the sam DB
835 :return: The modified message diff.
837 global defSDmodified
838 isFirst = True
839 txt = ""
840 dn = current[0].dn
842 for att in list(delta):
843 if att in ["dn", "objectSid"]:
844 delta.remove(att)
845 continue
847 # We have updated by provision usn information so let's exploit
848 # replMetadataProperties
849 if att in forwardlinked:
850 curval = current[0].get(att, ())
851 refval = reference[0].get(att, ())
852 delta = handle_links(samdb, att, basedn, current[0]["dn"],
853 curval, refval, delta)
854 continue
857 if isFirst and len(list(delta)) > 1:
858 isFirst = False
859 txt = "%s\n" % (str(dn))
861 if handle_special_case(att, delta, reference, current, True, None, None):
862 # This attribute is "complicated" to handle and handling
863 # was done in handle_special_case
864 continue
866 attrUSN = None
867 if hash_attr_usn.get(att):
868 [attrUSN, attInvId] = hash_attr_usn.get(att)
870 if attrUSN is None:
871 # If it's a replicated attribute and we don't have any USN
872 # information about it. It means that we never saw it before
873 # so let's add it !
874 # If it is a replicated attribute but we are not master on it
875 # (ie. not initially added in the provision we masterize).
876 # attrUSN will be -1
877 if isReplicated(att):
878 continue
879 else:
880 message(CHANGE, "Non replicated attribute %s changed" % att)
881 continue
883 if att == "nTSecurityDescriptor":
884 cursd = ndr_unpack(security.descriptor,
885 str(current[0]["nTSecurityDescriptor"]))
886 refsd = ndr_unpack(security.descriptor,
887 str(reference[0]["nTSecurityDescriptor"]))
889 diff = get_diff_sds(refsd, cursd, names.domainsid)
890 if diff == "":
891 # FIXME find a way to have it only with huge huge verbose mode
892 # message(CHANGE, "%ssd are identical" % txt)
893 # txt = ""
894 delta.remove(att)
895 continue
896 else:
897 delta.remove(att)
898 message(CHANGESD, "%ssd are not identical:\n%s" % (txt, diff))
899 txt = ""
900 if attrUSN == -1:
901 message(CHANGESD, "But the SD has been changed by someonelse "
902 "so it's impossible to know if the difference"
903 " cames from the modification or from a previous bug")
904 dnNotToRecalculateFound = True
905 else:
906 dnToRecalculate.append(dn)
907 continue
909 if attrUSN == -1:
910 # This attribute was last modified by another DC forget
911 # about it
912 message(CHANGE, "%sAttribute: %s has been "
913 "created/modified/deleted by another DC. "
914 "Doing nothing" % (txt, att))
915 txt = ""
916 delta.remove(att)
917 continue
918 elif not usn_in_range(int(attrUSN), usns.get(attInvId)):
919 message(CHANGE, "%sAttribute: %s was not "
920 "created/modified/deleted during a "
921 "provision or upgradeprovision. Current "
922 "usn: %d. Doing nothing" % (txt, att,
923 attrUSN))
924 txt = ""
925 delta.remove(att)
926 continue
927 else:
928 if att == "defaultSecurityDescriptor":
929 defSDmodified = True
930 if attrUSN:
931 message(CHANGE, "%sAttribute: %s will be modified"
932 "/deleted it was last modified "
933 "during a provision. Current usn: "
934 "%d" % (txt, att, attrUSN))
935 txt = ""
936 else:
937 message(CHANGE, "%sAttribute: %s will be added because "
938 "it did not exist before" % (txt, att))
939 txt = ""
940 continue
942 return delta
944 def update_present(ref_samdb, samdb, basedn, listPresent, usns):
945 """ This function updates the object that are already present in the
946 provision
948 :param ref_samdb: An LDB object pointing to the reference provision
949 :param samdb: An LDB object pointing to the updated provision
950 :param basedn: A string with the value of the base DN for the provision
951 (ie. DC=foo, DC=bar)
952 :param listPresent: A list of object that is present in the provision
953 :param usns: A list of USN range modified by previous provision and
954 upgradeprovision grouped by invocation ID
957 # This hash is meant to speedup lookup of attribute name from an oid,
958 # it's for the replPropertyMetaData handling
959 hash_oid_name = {}
960 res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
961 controls=["search_options:1:2"], attrs=["attributeID",
962 "lDAPDisplayName"])
963 if len(res) > 0:
964 for e in res:
965 strDisplay = str(e.get("lDAPDisplayName"))
966 hash_oid_name[str(e.get("attributeID"))] = strDisplay
967 else:
968 msg = "Unable to insert missing elements: circular references"
969 raise ProvisioningError(msg)
971 changed = 0
972 sd_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL | SECINFO_SACL
973 controls = ["search_options:1:2", "sd_flags:1:%d" % sd_flags]
974 message(CHANGE, "Using replPropertyMetadata for change selection")
975 for dn in listPresent:
976 reference = ref_samdb.search(expression="(distinguishedName=%s)" % (str(dn)), base=basedn,
977 scope=SCOPE_SUBTREE,
978 controls=controls)
979 current = samdb.search(expression="(distinguishedName=%s)" % (str(dn)), base=basedn,
980 scope=SCOPE_SUBTREE, controls=controls)
982 if (
983 (str(current[0].dn) != str(reference[0].dn)) and
984 (str(current[0].dn).upper() == str(reference[0].dn).upper())
986 message(CHANGE, "Names are the same except for the case. "
987 "Renaming %s to %s" % (str(current[0].dn),
988 str(reference[0].dn)))
989 identic_rename(samdb, reference[0].dn)
990 current = samdb.search(expression="(distinguishedName=%s)" % (str(dn)), base=basedn,
991 scope=SCOPE_SUBTREE,
992 controls=controls)
994 delta = samdb.msg_diff(current[0], reference[0])
996 for att in backlinked:
997 delta.remove(att)
999 for att in attrNotCopied:
1000 delta.remove(att)
1002 delta.remove("name")
1004 nb_items = len(list(delta))
1006 if nb_items == 1:
1007 continue
1009 if nb_items > 1:
1010 # Fetch the replPropertyMetaData
1011 res = samdb.search(expression="(distinguishedName=%s)" % (str(dn)), base=basedn,
1012 scope=SCOPE_SUBTREE, controls=controls,
1013 attrs=["replPropertyMetaData"])
1014 ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1015 str(res[0]["replPropertyMetaData"])).ctr
1017 hash_attr_usn = {}
1018 for o in ctr.array:
1019 # We put in this hash only modification
1020 # made on the current host
1021 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
1022 if str(o.originating_invocation_id) in usns.keys():
1023 hash_attr_usn[att] = [o.originating_usn, str(o.originating_invocation_id)]
1024 else:
1025 hash_attr_usn[att] = [-1, None]
1027 delta = checkKeepAttributeWithMetadata(delta, att, message, reference,
1028 current, hash_attr_usn,
1029 basedn, usns, samdb)
1031 delta.dn = dn
1034 if len(delta) >1:
1035 # Skip dn as the value is not really changed ...
1036 attributes=", ".join(delta.keys()[1:])
1037 modcontrols = []
1038 relaxedatt = ['iscriticalsystemobject', 'grouptype']
1039 # Let's try to reduce as much as possible the use of relax control
1040 for attr in delta.keys():
1041 if attr.lower() in relaxedatt:
1042 modcontrols = ["relax:0", "provision:0"]
1043 message(CHANGE, "%s is different from the reference one, changed"
1044 " attributes: %s\n" % (dn, attributes))
1045 changed += 1
1046 samdb.modify(delta, modcontrols)
1047 return changed
1049 def reload_full_schema(samdb, names):
1050 """Load the updated schema with all the new and existing classes
1051 and attributes.
1053 :param samdb: An LDB object connected to the sam.ldb of the update
1054 provision
1055 :param names: List of key provision parameters
1058 schemadn = str(names.schemadn)
1059 current = samdb.search(expression="objectClass=*", base=schemadn,
1060 scope=SCOPE_SUBTREE)
1061 schema_ldif = ""
1062 prefixmap_data = ""
1064 for ent in current:
1065 schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
1067 prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
1068 prefixmap_data = b64encode(prefixmap_data)
1070 # We don't actually add this ldif, just parse it
1071 prefixmap_ldif = "dn: %s\nprefixMap:: %s\n\n" % (schemadn, prefixmap_data)
1073 dsdb._dsdb_set_schema_from_ldif(samdb, prefixmap_ldif, schema_ldif, schemadn)
1076 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, prereloadfunc):
1077 """Check differences between the reference provision and the upgraded one.
1079 It looks for all objects which base DN is name.
1081 This function will also add the missing object and update existing object
1082 to add or remove attributes that were missing.
1084 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1085 reference provision
1086 :param samdb: An LDB object connected to the sam.ldb of the update
1087 provision
1088 :param basedn: String value of the DN of the partition
1089 :param names: List of key provision parameters
1090 :param schema: A Schema object
1091 :param provisionUSNs: A dictionnary with range of USN modified during provision
1092 or upgradeprovision. Ranges are grouped by invocationID.
1093 :param prereloadfunc: A function that must be executed just before the reload
1094 of the schema
1097 hash_new = {}
1098 hash = {}
1099 listMissing = []
1100 listPresent = []
1101 reference = []
1102 current = []
1104 # Connect to the reference provision and get all the attribute in the
1105 # partition referred by name
1106 reference = ref_samdb.search(expression="objectClass=*", base=basedn,
1107 scope=SCOPE_SUBTREE, attrs=["dn"],
1108 controls=["search_options:1:2"])
1110 current = samdb.search(expression="objectClass=*", base=basedn,
1111 scope=SCOPE_SUBTREE, attrs=["dn"],
1112 controls=["search_options:1:2"])
1113 # Create a hash for speeding the search of new object
1114 for i in range(0, len(reference)):
1115 hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
1117 # Create a hash for speeding the search of existing object in the
1118 # current provision
1119 for i in range(0, len(current)):
1120 hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
1123 for k in hash_new.keys():
1124 if not hash.has_key(k):
1125 if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
1126 listMissing.append(hash_new[k])
1127 else:
1128 listPresent.append(hash_new[k])
1130 # Sort the missing object in order to have object of the lowest level
1131 # first (which can be containers for higher level objects)
1132 listMissing.sort(dn_sort)
1133 listPresent.sort(dn_sort)
1135 # The following lines is to load the up to
1136 # date schema into our current LDB
1137 # a complete schema is needed as the insertion of attributes
1138 # and class is done against it
1139 # and the schema is self validated
1140 samdb.set_schema(schema)
1141 try:
1142 message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
1143 add_deletedobj_containers(ref_samdb, samdb, names)
1145 add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
1147 prereloadfunc()
1148 message(SIMPLE, "Reloading a merged schema, which might trigger "
1149 "reindexing so please be patient")
1150 reload_full_schema(samdb, names)
1151 message(SIMPLE, "Schema reloaded!")
1153 changed = update_present(ref_samdb, samdb, basedn, listPresent,
1154 provisionUSNs)
1155 message(SIMPLE, "There are %d changed objects" % (changed))
1156 return 1
1158 except StandardError, err:
1159 message(ERROR, "Exception during upgrade of samdb:")
1160 (typ, val, tb) = sys.exc_info()
1161 traceback.print_exception(typ, val, tb)
1162 return 0
1165 def check_updated_sd(ref_sam, cur_sam, names):
1166 """Check if the security descriptor in the upgraded provision are the same
1167 as the reference
1169 :param ref_sam: A LDB object connected to the sam.ldb file used as
1170 the reference provision
1171 :param cur_sam: A LDB object connected to the sam.ldb file used as
1172 upgraded provision
1173 :param names: List of key provision parameters"""
1174 reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1175 scope=SCOPE_SUBTREE,
1176 attrs=["dn", "nTSecurityDescriptor"],
1177 controls=["search_options:1:2"])
1178 current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1179 scope=SCOPE_SUBTREE,
1180 attrs=["dn", "nTSecurityDescriptor"],
1181 controls=["search_options:1:2"])
1182 hash = {}
1183 for i in range(0, len(reference)):
1184 refsd_blob = str(reference[i]["nTSecurityDescriptor"])
1185 hash[str(reference[i]["dn"]).lower()] = refsd_blob
1188 for i in range(0, len(current)):
1189 key = str(current[i]["dn"]).lower()
1190 if hash.has_key(key):
1191 cursd_blob = str(current[i]["nTSecurityDescriptor"])
1192 cursd = ndr_unpack(security.descriptor,
1193 cursd_blob)
1194 if cursd_blob != hash[key]:
1195 refsd = ndr_unpack(security.descriptor,
1196 hash[key])
1197 txt = get_diff_sds(refsd, cursd, names.domainsid, False)
1198 if txt != "":
1199 message(CHANGESD, "On object %s ACL is different"
1200 " \n%s" % (current[i]["dn"], txt))
1204 def fix_wellknown_sd(samdb, names):
1205 """This function fix the SD for partition/wellknown containers (basedn, configdn, ...)
1206 This is needed because some provision use to have broken SD on containers
1208 :param samdb: An LDB object pointing to the sam of the current provision
1209 :param names: A list of key provision parameters
1212 list_wellknown_dns = []
1214 subcontainers = get_wellknown_sds(names, samdb)
1216 for [dn, descriptor_fn] in subcontainers:
1217 list_wellknown_dns.append(dn)
1218 if dn in dnToRecalculate:
1219 delta = Message()
1220 delta.dn = dn
1221 descr = descriptor_fn(names.domainsid, name_map=names.name_map)
1222 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1223 "nTSecurityDescriptor" )
1224 samdb.modify(delta)
1225 message(CHANGESD, "nTSecurityDescriptor updated on wellknown DN: %s" % delta.dn)
1227 return list_wellknown_dns
1229 def rebuild_sd(samdb, names):
1230 """Rebuild security descriptor of the current provision from scratch
1232 During the different pre release of samba4 security descriptors
1233 (SD) were notarly broken (up to alpha11 included)
1235 This function allows to get them back in order, this function works
1236 only after the database comparison that --full mode uses and which
1237 populates the dnToRecalculate and dnNotToRecalculate lists.
1239 The idea is that the SD can be safely recalculated from scratch to get it right.
1241 :param names: List of key provision parameters"""
1243 listWellknown = fix_wellknown_sd(samdb, names)
1245 if len(dnToRecalculate) != 0:
1246 message(CHANGESD, "%d DNs have been marked as needed to be recalculated"
1247 % (len(dnToRecalculate)))
1249 for dn in dnToRecalculate:
1250 # well known SDs have already been reset
1251 if dn in listWellknown:
1252 continue
1253 delta = Message()
1254 delta.dn = dn
1255 sd_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL | SECINFO_SACL
1256 try:
1257 descr = get_empty_descriptor(names.domainsid)
1258 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1259 "nTSecurityDescriptor")
1260 samdb.modify(delta, ["sd_flags:1:%d" % sd_flags,"relax:0","local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK])
1261 except LdbError, e:
1262 samdb.transaction_cancel()
1263 res = samdb.search(expression="objectClass=*", base=str(delta.dn),
1264 scope=SCOPE_BASE,
1265 attrs=["nTSecurityDescriptor"],
1266 controls=["sd_flags:1:%d" % sd_flags])
1267 badsd = ndr_unpack(security.descriptor,
1268 str(res[0]["nTSecurityDescriptor"]))
1269 message(ERROR, "On %s bad stuff %s" % (str(delta.dn),badsd.as_sddl(names.domainsid)))
1270 return
1272 def hasATProvision(samdb):
1273 entry = samdb.search(expression="(distinguishedName=@PROVISION)", base = "",
1274 scope=SCOPE_BASE,
1275 attrs=["dn"])
1277 if entry is not None and len(entry) == 1:
1278 return True
1279 else:
1280 return False
1282 def removeProvisionUSN(samdb):
1283 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1284 entry = samdb.search(expression="(distinguishedName=@PROVISION)", base = "",
1285 scope=SCOPE_BASE,
1286 attrs=attrs)
1287 empty = Message()
1288 empty.dn = entry[0].dn
1289 delta = samdb.msg_diff(entry[0], empty)
1290 delta.remove("dn")
1291 delta.dn = entry[0].dn
1292 samdb.modify(delta)
1294 def remove_stored_generated_attrs(paths, creds, session, lp):
1295 """Remove previously stored constructed attributes
1297 :param paths: List of paths for different provision objects
1298 from the upgraded provision
1299 :param creds: A credential object
1300 :param session: A session object
1301 :param lp: A line parser object
1302 :return: An associative array whose key are the different constructed
1303 attributes and the value the dn where this attributes were found.
1307 def simple_update_basesamdb(newpaths, paths, names):
1308 """Update the provision container db: sam.ldb
1309 This function is aimed at very old provision (before alpha9)
1311 :param newpaths: List of paths for different provision objects
1312 from the reference provision
1313 :param paths: List of paths for different provision objects
1314 from the upgraded provision
1315 :param names: List of key provision parameters"""
1317 message(SIMPLE, "Copy samdb")
1318 tdb_util.tdb_copy(newpaths.samdb, paths.samdb)
1320 message(SIMPLE, "Update partitions filename if needed")
1321 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1322 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1323 usersldb = os.path.join(paths.private_dir, "users.ldb")
1324 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1326 if not os.path.isdir(samldbdir):
1327 os.mkdir(samldbdir)
1328 os.chmod(samldbdir, 0700)
1329 if os.path.isfile(schemaldb):
1330 tdb_util.tdb_copy(schemaldb, os.path.join(samldbdir,
1331 "%s.ldb"%str(names.schemadn).upper()))
1332 os.remove(schemaldb)
1333 if os.path.isfile(usersldb):
1334 tdb_util.tdb_copy(usersldb, os.path.join(samldbdir,
1335 "%s.ldb"%str(names.rootdn).upper()))
1336 os.remove(usersldb)
1337 if os.path.isfile(configldb):
1338 tdb_util.tdb_copy(configldb, os.path.join(samldbdir,
1339 "%s.ldb"%str(names.configdn).upper()))
1340 os.remove(configldb)
1343 def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):
1344 """Upgrade the SAM DB contents for all the provision partitions
1346 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1347 provision
1348 :param samdb: An LDB object connected to the sam.ldb of the update
1349 provision
1350 :param names: List of key provision parameters
1351 :param provisionUSNs: A dictionnary with range of USN modified during provision
1352 or upgradeprovision. Ranges are grouped by invocationID.
1353 :param schema: A Schema object that represent the schema of the provision
1354 :param prereloadfunc: A function that must be executed just before the reload
1355 of the schema
1358 message(SIMPLE, "Starting update of samdb")
1359 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1360 schema, provisionUSNs, prereloadfunc)
1361 if ret:
1362 message(SIMPLE, "Update of samdb finished")
1363 return 1
1364 else:
1365 message(SIMPLE, "Update failed")
1366 return 0
1369 def backup_provision(paths, dir, only_db):
1370 """This function backup the provision files so that a rollback
1371 is possible
1373 :param paths: Paths to different objects
1374 :param dir: Directory where to store the backup
1375 :param only_db: Skip sysvol for users with big sysvol
1377 if paths.sysvol and not only_db:
1378 copytree_with_xattrs(paths.sysvol, os.path.join(dir, "sysvol"))
1379 tdb_util.tdb_copy(paths.samdb, os.path.join(dir, os.path.basename(paths.samdb)))
1380 tdb_util.tdb_copy(paths.secrets, os.path.join(dir, os.path.basename(paths.secrets)))
1381 tdb_util.tdb_copy(paths.idmapdb, os.path.join(dir, os.path.basename(paths.idmapdb)))
1382 tdb_util.tdb_copy(paths.privilege, os.path.join(dir, os.path.basename(paths.privilege)))
1383 if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1384 tdb_util.tdb_copy(os.path.join(paths.private_dir,"eadb.tdb"), os.path.join(dir, "eadb.tdb"))
1385 shutil.copy2(paths.smbconf, dir)
1386 shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1388 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1389 if not os.path.isdir(samldbdir):
1390 samldbdir = paths.private_dir
1391 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1392 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1393 usersldb = os.path.join(paths.private_dir, "users.ldb")
1394 tdb_util.tdb_copy(schemaldb, os.path.join(dir, "schema.ldb"))
1395 tdb_util.tdb_copy(usersldb, os.path.join(dir, "configuration.ldb"))
1396 tdb_util.tdb_copy(configldb, os.path.join(dir, "users.ldb"))
1397 else:
1398 os.mkdir(os.path.join(dir, "sam.ldb.d"), 0700)
1400 for ldb in os.listdir(samldbdir):
1401 tdb_util.tdb_copy(os.path.join(samldbdir, ldb),
1402 os.path.join(dir, "sam.ldb.d", ldb))
1405 def sync_calculated_attributes(samdb, names):
1406 """Synchronize attributes used for constructed ones, with the
1407 old constructed that were stored in the database.
1409 This apply for instance to msds-keyversionnumber that was
1410 stored and that is now constructed from replpropertymetadata.
1412 :param samdb: An LDB object attached to the currently upgraded samdb
1413 :param names: Various key parameter about current provision.
1415 listAttrs = ["msDs-KeyVersionNumber"]
1416 hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
1417 if hash.has_key("msDs-KeyVersionNumber"):
1418 increment_calculated_keyversion_number(samdb, names.rootdn,
1419 hash["msDs-KeyVersionNumber"])
1421 # Synopsis for updateprovision
1422 # 1) get path related to provision to be update (called current)
1423 # 2) open current provision ldbs
1424 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1425 # of the DC ....)
1426 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1427 # by either upgradeprovision or provision
1428 # 5) creation of a new provision the latest version of provision script
1429 # (called reference)
1430 # 6) get reference provision paths
1431 # 7) open reference provision ldbs
1432 # 8) setup helpers data that will help the update process
1433 # 9) (SKIPPED) we no longer update the privilege ldb by copying the one of referecence provision to
1434 # the current provision, because a shutil.copy would break the transaction locks both databases are under
1435 # and this database has not changed between 2009 and Samba 4.0.3 in Feb 2013 (at least)
1436 # 10)get the oemInfo field, this field contains information about the different
1437 # provision that have been done
1438 # 11)Depending on if the --very-old-pre-alpha9 flag is set the following things are done
1439 # A) When alpha9 or alphaxx not specified (default)
1440 # The base sam.ldb file is updated by looking at the difference between
1441 # referrence one and the current one. Everything is copied with the
1442 # exception of lastProvisionUSN attributes.
1443 # B) Other case (it reflect that that provision was done before alpha9)
1444 # The base sam.ldb of the reference provision is copied over
1445 # the current one, if necessary ldb related to partitions are moved
1446 # and renamed
1447 # The highest used USN is fetched so that changed by upgradeprovision
1448 # usn can be tracked
1449 # 12)A Schema object is created, it will be used to provide a complete
1450 # schema to current provision during update (as the schema of the
1451 # current provision might not be complete and so won't allow some
1452 # object to be created)
1453 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1454 # 14)The secrets db is updated by pull all the difference from the reference
1455 # provision into the current provision
1456 # 15)As the previous step has most probably modified the password stored in
1457 # in secret for the current DC, a new password is generated,
1458 # the kvno is bumped and the entry in samdb is also updated
1459 # 16)For current provision older than alpha9, we must fix the SD a little bit
1460 # administrator to update them because SD used to be generated with the
1461 # system account before alpha9.
1462 # 17)The highest usn modified so far is searched in the database it will be
1463 # the upper limit for usn modified during provision.
1464 # This is done before potential SD recalculation because we do not want
1465 # SD modified during recalculation to be marked as modified during provision
1466 # (and so possibly remplaced at next upgradeprovision)
1467 # 18)Rebuilt SD if the flag indicate to do so
1468 # 19)Check difference between SD of reference provision and those of the
1469 # current provision. The check is done by getting the sddl representation
1470 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1471 # Each part is verified separetly, for dacl and sacl ACL is splited into
1472 # ACEs and each ACE is verified separately (so that a permutation in ACE
1473 # didn't raise as an error).
1474 # 20)The oemInfo field is updated to add information about the fact that the
1475 # provision has been updated by the upgradeprovision version xxx
1476 # (the version is the one obtained when starting samba with the --version
1477 # parameter)
1478 # 21)Check if the current provision has all the settings needed for dynamic
1479 # DNS update to work (that is to say the provision is newer than
1480 # january 2010). If not dns configuration file from reference provision
1481 # are copied in a sub folder and the administrator is invited to
1482 # do what is needed.
1483 # 22)If the lastProvisionUSN attribute was present it is updated to add
1484 # the range of usns modified by the current upgradeprovision
1487 # About updating the sam DB
1488 # The update takes place in update_partition function
1489 # This function read both current and reference provision and list all
1490 # the available DN of objects
1491 # If the string representation of a DN in reference provision is
1492 # equal to the string representation of a DN in current provision
1493 # (without taking care of case) then the object is flaged as being
1494 # present. If the object is not present in current provision the object
1495 # is being flaged as missing in current provision. Object present in current
1496 # provision but not in reference provision are ignored.
1497 # Once the list of objects present and missing is done, the deleted object
1498 # containers are created in the differents partitions (if missing)
1500 # Then the function add_missing_entries is called
1501 # This function will go through the list of missing entries by calling
1502 # add_missing_object for the given object. If this function returns 0
1503 # it means that the object needs some other object in order to be created
1504 # The object is reappended at the end of the list to be created later
1505 # (and preferably after all the needed object have been created)
1506 # The function keeps on looping on the list of object to be created until
1507 # it's empty or that the number of defered creation is equal to the number
1508 # of object that still needs to be created.
1510 # The function add_missing_object will first check if the object can be created.
1511 # That is to say that it didn't depends other not yet created objects
1512 # If requisit can't be fullfilled it exists with 0
1513 # Then it will try to create the missing entry by creating doing
1514 # an ldb_message_diff between the object in the reference provision and
1515 # an empty object.
1516 # This resulting object is filtered to remove all the back link attribute
1517 # (ie. memberOf) as they will be created by the other linked object (ie.
1518 # the one with the member attribute)
1519 # All attributes specified in the attrNotCopied array are
1520 # also removed it's most of the time generated attributes
1522 # After missing entries have been added the update_partition function will
1523 # take care of object that exist but that need some update.
1524 # In order to do so the function update_present is called with the list
1525 # of object that are present in both provision and that might need an update.
1527 # This function handle first case mismatch so that the DN in the current
1528 # provision have the same case as in reference provision
1530 # It will then construct an associative array consiting of attributes as
1531 # key and invocationid as value( if the originating invocation id is
1532 # different from the invocation id of the current DC the value is -1 instead).
1534 # If the range of provision modified attributes is present, the function will
1535 # use the replMetadataProperty update method which is the following:
1536 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1537 # creationTime, msDs-KeyVersionNumber, oEMInformation
1538 # Check for each attribute if its usn is within one of the modified by
1539 # provision range and if its originating id is the invocation id of the
1540 # current DC, then validate the update from reference to current.
1541 # If not or if there is no replMetatdataProperty for this attribute then we
1542 # do not update it.
1543 # Otherwise (case the range of provision modified attribute is not present) it
1544 # use the following process:
1545 # All attributes that need to be added are accepted at the exeption of those
1546 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1547 # correct flags specified.
1548 # For attributes that need to be modified or removed, a check is performed
1549 # in OverwrittenAtt, if the attribute is present and the modification flag
1550 # (remove, delete) is one of those listed for this attribute then modification
1551 # is accepted. For complicated handling of attribute update, the control is passed
1552 # to handle_special_case
1556 if __name__ == '__main__':
1557 global defSDmodified
1558 defSDmodified = False
1560 # From here start the big steps of the program
1561 # 1) First get files paths
1562 paths = get_paths(param, smbconf=smbconf)
1563 # Get ldbs with the system session, it is needed for searching
1564 # provision parameters
1565 session = system_session()
1567 # This variable will hold the last provision USN once if it exists.
1568 minUSN = 0
1569 # 2)
1570 ldbs = get_ldbs(paths, creds, session, lp)
1571 backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1572 prefix="backupprovision")
1573 backup_provision(paths, backupdir, opts.db_backup_only)
1574 try:
1575 ldbs.startTransactions()
1577 # 3) Guess all the needed names (variables in fact) from the current
1578 # provision.
1579 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1580 paths, smbconf, lp)
1581 # 4)
1582 lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1583 if lastProvisionUSNs is not None:
1584 v = 0
1585 for k in lastProvisionUSNs.keys():
1586 for r in lastProvisionUSNs[k]:
1587 v = v + 1
1589 message(CHANGE,
1590 "Find last provision USN, %d invocation(s) for a total of %d ranges" %
1591 (len(lastProvisionUSNs.keys()), v /2 ))
1593 if lastProvisionUSNs.get("default") is not None:
1594 message(CHANGE, "Old style for usn ranges used")
1595 lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"]
1596 del lastProvisionUSNs["default"]
1597 else:
1598 message(SIMPLE, "Your provision lacks provision range information")
1599 if confirm("Do you want to run findprovisionusnranges to try to find them ?", False):
1600 ldbs.groupedRollback()
1601 minobj = 5
1602 (hash_id, nb_obj) = findprovisionrange(ldbs.sam, ldb.Dn(ldbs.sam, str(names.rootdn)))
1603 message(SIMPLE, "Here is a list of changes that modified more than %d objects in 1 minute." % minobj)
1604 message(SIMPLE, "Usually changes made by provision and upgradeprovision are those who affect a couple"
1605 " of hundred of objects or more")
1606 message(SIMPLE, "Total number of objects: %d" % nb_obj)
1607 message(SIMPLE, "")
1609 print_provision_ranges(hash_id, minobj, None, str(paths.samdb), str(names.invocation))
1611 message(SIMPLE, "Once you applied/adapted the change(s) please restart the upgradeprovision script")
1612 sys.exit(0)
1614 # Objects will be created with the admin session
1615 # (not anymore system session)
1616 adm_session = admin_session(lp, str(names.domainsid))
1617 # So we reget handle on objects
1618 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1620 if not sanitychecks(ldbs.sam, names):
1621 message(SIMPLE, "Sanity checks for the upgrade have failed. "
1622 "Check the messages and correct the errors "
1623 "before rerunning upgradeprovision")
1624 ldbs.groupedRollback()
1625 sys.exit(1)
1627 # Let's see provision parameters
1628 print_provision_key_parameters(names)
1630 # 5) With all this information let's create a fresh new provision used as
1631 # reference
1632 message(SIMPLE, "Creating a reference provision")
1633 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1634 prefix="referenceprovision")
1635 result = newprovision(names, creds, session, smbconf, provisiondir,
1636 provision_logger)
1637 result.report_logger(provision_logger)
1639 # TODO
1640 # 6) and 7)
1641 # We need to get a list of object which SD is directly computed from
1642 # defaultSecurityDescriptor.
1643 # This will allow us to know which object we can rebuild the SD in case
1644 # of change of the parent's SD or of the defaultSD.
1645 # Get file paths of this new provision
1646 newpaths = get_paths(param, targetdir=provisiondir)
1647 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1648 new_ldbs.startTransactions()
1650 populateNotReplicated(new_ldbs.sam, names.schemadn)
1651 # 8) Populate some associative array to ease the update process
1652 # List of attribute which are link and backlink
1653 populate_links(new_ldbs.sam, names.schemadn)
1654 # List of attribute with ASN DN synthax)
1655 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1656 # 9) (now skipped, was copy of privileges.ldb)
1657 # 10)
1658 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1659 # Do some modification on sam.ldb
1660 ldbs.groupedCommit()
1661 new_ldbs.groupedCommit()
1662 deltaattr = None
1663 # 11)
1664 message(GUESS, oem)
1665 if oem is None or hasATProvision(ldbs.sam) or not opts.very_old_pre_alpha9:
1666 # 11) A
1667 # Starting from alpha9 we can consider that the structure is quite ok
1668 # and that we should do only dela
1669 deltaattr = delta_update_basesamdb(newpaths.samdb,
1670 paths.samdb,
1671 creds,
1672 session,
1674 message)
1675 else:
1676 # 11) B
1677 simple_update_basesamdb(newpaths, paths, names)
1678 ldbs = get_ldbs(paths, creds, session, lp)
1679 removeProvisionUSN(ldbs.sam)
1681 ldbs.startTransactions()
1682 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1683 new_ldbs.startTransactions()
1685 # 12)
1686 schema = Schema(names.domainsid, schemadn=str(names.schemadn))
1687 # We create a closure that will be invoked just before schema reload
1688 def schemareloadclosure():
1689 basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
1690 options=["modules:"])
1691 doit = False
1692 if deltaattr is not None and len(deltaattr) > 1:
1693 doit = True
1694 if doit:
1695 deltaattr.remove("dn")
1696 for att in deltaattr:
1697 if att.lower() == "dn":
1698 continue
1699 if (deltaattr.get(att) is not None
1700 and deltaattr.get(att).flags() != FLAG_MOD_ADD):
1701 doit = False
1702 elif deltaattr.get(att) is None:
1703 doit = False
1704 if doit:
1705 message(CHANGE, "Applying delta to @ATTRIBUTES")
1706 deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
1707 basesam.modify(deltaattr)
1708 else:
1709 message(CHANGE, "Not applying delta to @ATTRIBUTES because "
1710 "there is not only add")
1711 # 13)
1712 if opts.full:
1713 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1714 schema, schemareloadclosure):
1715 message(SIMPLE, "Rolling back all changes. Check the cause"
1716 " of the problem")
1717 message(SIMPLE, "Your system is as it was before the upgrade")
1718 ldbs.groupedRollback()
1719 new_ldbs.groupedRollback()
1720 shutil.rmtree(provisiondir)
1721 sys.exit(1)
1722 else:
1723 # Try to reapply the change also when we do not change the sam
1724 # as the delta_upgrade
1725 schemareloadclosure()
1726 sync_calculated_attributes(ldbs.sam, names)
1727 res = ldbs.sam.search(expression="(samaccountname=dns)",
1728 scope=SCOPE_SUBTREE, attrs=["dn"],
1729 controls=["search_options:1:2"])
1730 if len(res) > 0:
1731 message(SIMPLE, "You still have the old DNS object for managing "
1732 "dynamic DNS, but you didn't supply --full so "
1733 "a correct update can't be done")
1734 ldbs.groupedRollback()
1735 new_ldbs.groupedRollback()
1736 shutil.rmtree(provisiondir)
1737 sys.exit(1)
1738 # 14)
1739 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1740 # 14bis)
1741 res = ldbs.sam.search(expression="(samaccountname=dns)",
1742 scope=SCOPE_SUBTREE, attrs=["dn"],
1743 controls=["search_options:1:2"])
1745 if (len(res) == 1):
1746 ldbs.sam.delete(res[0]["dn"])
1747 res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
1748 scope=SCOPE_SUBTREE, attrs=["dn"])
1749 update_dns_account_password(ldbs.sam, ldbs.secrets, names)
1750 message(SIMPLE, "IMPORTANT!!! "
1751 "If you were using Dynamic DNS before you need "
1752 "to update your configuration, so that the "
1753 "tkey-gssapi-credential has the following value: "
1754 "DNS/%s.%s" % (names.netbiosname.lower(),
1755 names.realm.lower()))
1756 # 15)
1757 message(SIMPLE, "Update machine account")
1758 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1760 # 16) SD should be created with admin but as some previous acl were so wrong
1761 # that admin can't modify them we have first to recreate them with the good
1762 # form but with system account and then give the ownership to admin ...
1763 if opts.very_old_pre_alpha9:
1764 message(SIMPLE, "Fixing very old provision SD")
1765 rebuild_sd(ldbs.sam, names)
1767 # We calculate the max USN before recalculating the SD because we might
1768 # touch object that have been modified after a provision and we do not
1769 # want that the next upgradeprovision thinks that it has a green light
1770 # to modify them
1772 # 17)
1773 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1775 # 18) We rebuild SD if a we have a list of DN to recalculate or if the
1776 # defSDmodified is set.
1777 if opts.full and (defSDmodified or len(dnToRecalculate) >0):
1778 message(SIMPLE, "Some (default) security descriptors (SDs) have "
1779 "changed, recalculating them")
1780 ldbs.sam.set_session_info(adm_session)
1781 rebuild_sd(ldbs.sam, names)
1783 # 19)
1784 # Now we are quite confident in the recalculate process of the SD, we make
1785 # it optional. And we don't do it if there is DN that we must touch
1786 # as we are assured that on this DNs we will have differences !
1787 # Also the check must be done in a clever way as for the moment we just
1788 # compare SDDL
1789 if dnNotToRecalculateFound == False and (opts.debugchangesd or opts.debugall):
1790 message(CHANGESD, "Checking recalculated SDs")
1791 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1793 # 20)
1794 updateOEMInfo(ldbs.sam, str(names.rootdn))
1795 # 21)
1796 check_for_DNS(newpaths.private_dir, paths.private_dir, names.dns_backend)
1797 # 22)
1798 update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
1799 if opts.full and (names.policyid is None or names.policyid_dc is None):
1800 update_policyids(names, ldbs.sam)
1802 if opts.full:
1803 try:
1804 update_gpo(paths, ldbs.sam, names, lp, message)
1805 except ProvisioningError, e:
1806 message(ERROR, "The policy for domain controller is missing. "
1807 "You should restart upgradeprovision with --full")
1809 ldbs.groupedCommit()
1810 new_ldbs.groupedCommit()
1811 message(SIMPLE, "Upgrade finished!")
1812 # remove reference provision now that everything is done !
1813 # So we have reindexed first if need when the merged schema was reloaded
1814 # (as new attributes could have quick in)
1815 # But the second part of the update (when we update existing objects
1816 # can also have an influence on indexing as some attribute might have their
1817 # searchflag modificated
1818 message(SIMPLE, "Reopening samdb to trigger reindexing if needed "
1819 "after modification")
1820 samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
1821 message(SIMPLE, "Reindexing finished")
1823 shutil.rmtree(provisiondir)
1824 except StandardError, err:
1825 message(ERROR, "A problem occurred while trying to upgrade your "
1826 "provision. A full backup is located at %s" % backupdir)
1827 if opts.debugall or opts.debugchange:
1828 (typ, val, tb) = sys.exc_info()
1829 traceback.print_exception(typ, val, tb)
1830 sys.exit(1)