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