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
, xattr
54 from samba
.ndr
import ndr_unpack
55 from samba
.upgradehelpers
import (dn_sort
, get_paths
, newprovision
,
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
)
67 replace
=2**FLAG_MOD_REPLACE
69 delete
=2**FLAG_MOD_DELETE
73 # Will be modified during provision to tell if default sd has been modified
76 #Errors are always logged
78 __docformat__
= "restructuredText"
80 # Attributes that are never copied from the reference provision (even if they
81 # do not exist in the destination object).
82 # This is most probably because they are populated automatcally when object is
84 # This also apply to imported object from reference provision
85 replAttrNotCopied
= [ "dn", "whenCreated", "whenChanged", "objectGUID",
86 "parentGUID", "objectCategory", "distinguishedName",
87 "nTMixedDomain", "showInAdvancedViewOnly",
88 "instanceType", "msDS-Behavior-Version", "cn",
89 "lmPwdHistory", "pwdLastSet", "ntPwdHistory",
90 "unicodePwd", "dBCSPwd", "supplementalCredentials",
91 "gPCUserExtensionNames", "gPCMachineExtensionNames",
92 "maxPwdAge", "secret", "possibleInferiors", "privilege",
93 "sAMAccountType", "oEMInformation", "creationTime" ]
95 nonreplAttrNotCopied
= ["uSNCreated", "replPropertyMetaData", "uSNChanged",
96 "nextRid" ,"rIDNextRID", "rIDPreviousAllocationPool"]
98 nonDSDBAttrNotCopied
= ["msDS-KeyVersionNumber", "priorSecret", "priorWhenChanged"]
101 attrNotCopied
= replAttrNotCopied
102 attrNotCopied
.extend(nonreplAttrNotCopied
)
103 attrNotCopied
.extend(nonDSDBAttrNotCopied
)
104 # Usually for an object that already exists we do not overwrite attributes as
105 # they might have been changed for good reasons. Anyway for a few of them it's
106 # mandatory to replace them otherwise the provision will be broken somehow.
107 # But for attribute that are just missing we do not have to specify them as the default
108 # behavior is to add missing attribute
109 hashOverwrittenAtt
= { "prefixMap": replace
, "systemMayContain": replace
,
110 "systemOnly":replace
, "searchFlags":replace
,
111 "mayContain":replace
, "systemFlags":replace
+add
,
112 "description":replace
, "operatingSystemVersion":replace
,
113 "adminPropertyPages":replace
, "groupType":replace
,
114 "wellKnownObjects":replace
, "privilege":never
,
115 "defaultSecurityDescriptor": replace
,
116 "rIDAvailablePool": never
,
117 "versionNumber" : add
,
118 "rIDNextRID": add
, "rIDUsedPool": never
,
119 "defaultSecurityDescriptor": replace
+ add
,
120 "isMemberOfPartialAttributeSet": delete
,
121 "attributeDisplayNames": replace
+ add
,
122 "versionNumber": add
}
124 dnNotToRecalculate
= []
127 forwardlinked
= set()
130 def define_what_to_log(opts
):
134 if opts
.debugchangesd
:
135 what
= what | CHANGESD
138 if opts
.debugprovision
:
139 what
= what | PROVISION
141 what
= what | CHANGEALL
145 parser
= optparse
.OptionParser("provision [options]")
146 sambaopts
= options
.SambaOptions(parser
)
147 parser
.add_option_group(sambaopts
)
148 parser
.add_option_group(options
.VersionOptions(parser
))
149 credopts
= options
.CredentialsOptions(parser
)
150 parser
.add_option_group(credopts
)
151 parser
.add_option("--setupdir", type="string", metavar
="DIR",
152 help="directory with setup files")
153 parser
.add_option("--debugprovision", help="Debug provision", action
="store_true")
154 parser
.add_option("--debugguess", action
="store_true",
155 help="Print information on which values are guessed")
156 parser
.add_option("--debugchange", action
="store_true",
157 help="Print information on what is different but won't be changed")
158 parser
.add_option("--debugchangesd", action
="store_true",
159 help="Print security descriptor differences")
160 parser
.add_option("--debugall", action
="store_true",
161 help="Print all available information (very verbose)")
162 parser
.add_option("--resetfileacl", action
="store_true",
163 help="Force a reset on filesystem acls in sysvol / netlogon share")
164 parser
.add_option("--fixntacl", action
="store_true",
165 help="Only fix NT ACLs in sysvol / netlogon share")
166 parser
.add_option("--full", action
="store_true",
167 help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
169 opts
= parser
.parse_args()[0]
171 handler
= logging
.StreamHandler(sys
.stdout
)
172 upgrade_logger
= logging
.getLogger("upgradeprovision")
173 upgrade_logger
.setLevel(logging
.INFO
)
175 upgrade_logger
.addHandler(handler
)
177 provision_logger
= logging
.getLogger("provision")
178 provision_logger
.addHandler(handler
)
180 whatToLog
= define_what_to_log(opts
)
182 def message(what
, text
):
183 """Print a message if this message type has been selected to be printed
185 :param what: Category of the message
186 :param text: Message to print """
187 if (whatToLog
& what
) or what
<= 0:
188 upgrade_logger
.info("%s", text
)
190 if len(sys
.argv
) == 1:
191 opts
.interactive
= True
192 lp
= sambaopts
.get_loadparm()
193 smbconf
= lp
.configfile
195 creds
= credopts
.get_credentials(lp
)
196 creds
.set_kerberos_state(DONT_USE_KERBEROS
)
200 def check_for_DNS(refprivate
, private
):
201 """Check if the provision has already the requirement for dynamic dns
203 :param refprivate: The path to the private directory of the reference
205 :param private: The path to the private directory of the upgraded
208 spnfile
= "%s/spn_update_list" % private
209 dnsfile
= "%s/dns_update_list" % private
210 namedfile
= lp
.get("dnsupdate:path")
213 namedfile
= "%s/named.conf.update" % private
215 if not os
.path
.exists(spnfile
):
216 shutil
.copy("%s/spn_update_list" % refprivate
, "%s" % spnfile
)
218 if not os
.path
.exists(dnsfile
):
219 shutil
.copy("%s/dns_update_list" % refprivate
, "%s" % dnsfile
)
221 destdir
= "%s/new_dns" % private
222 dnsdir
= "%s/dns" % private
224 if not os
.path
.exists(namedfile
):
225 if not os
.path
.exists(destdir
):
227 if not os
.path
.exists(dnsdir
):
229 shutil
.copy("%s/named.conf" % refprivate
, "%s/named.conf" % destdir
)
230 shutil
.copy("%s/named.txt" % refprivate
, "%s/named.txt" % destdir
)
231 message(SIMPLE
, "It seems that your provision did not integrate "
232 "new rules for dynamic dns update of domain related entries")
233 message(SIMPLE
, "A copy of the new bind configuration files and "
234 "template has been put in %s, you should read them and "
235 "configure dynamic dns updates" % destdir
)
238 def populate_links(samdb
, schemadn
):
239 """Populate an array with all the back linked attributes
241 This attributes that are modified automaticaly when
242 front attibutes are changed
244 :param samdb: A LDB object for sam.ldb file
245 :param schemadn: DN of the schema for the partition"""
246 linkedAttHash
= get_linked_attributes(Dn(samdb
, str(schemadn
)), samdb
)
247 backlinked
.extend(linkedAttHash
.values())
248 for t
in linkedAttHash
.keys():
251 def isReplicated(att
):
252 """ Indicate if the attribute is replicated or not
254 :param att: Name of the attribute to be tested
255 :return: True is the attribute is replicated, False otherwise
258 return (att
not in not_replicated
)
260 def populateNotReplicated(samdb
, schemadn
):
261 """Populate an array with all the attributes that are not replicated
263 :param samdb: A LDB object for sam.ldb file
264 :param schemadn: DN of the schema for the partition"""
265 res
= samdb
.search(expression
="(&(objectclass=attributeSchema)(systemflags:1.2.840.113556.1.4.803:=1))", base
=Dn(samdb
,
266 str(schemadn
)), scope
=SCOPE_SUBTREE
,
267 attrs
=["lDAPDisplayName"])
269 not_replicated
.append(str(elem
["lDAPDisplayName"]))
272 def populate_dnsyntax(samdb
, schemadn
):
273 """Populate an array with all the attributes that have DN synthax
276 :param samdb: A LDB object for sam.ldb file
277 :param schemadn: DN of the schema for the partition"""
278 res
= samdb
.search(expression
="(attributeSyntax=2.5.5.1)", base
=Dn(samdb
,
279 str(schemadn
)), scope
=SCOPE_SUBTREE
,
280 attrs
=["lDAPDisplayName"])
282 dn_syntax_att
.append(elem
["lDAPDisplayName"])
285 def sanitychecks(samdb
, names
):
286 """Make some checks before trying to update
288 :param samdb: An LDB object opened on sam.ldb
289 :param names: list of key provision parameters
290 :return: Status of check (1 for Ok, 0 for not Ok) """
291 res
= samdb
.search(expression
="objectClass=ntdsdsa", base
=str(names
.configdn
),
292 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
293 controls
=["search_options:1:2"])
295 print "No DC found. Your provision is most probably broken!"
298 print "Found %d domain controllers. For the moment " \
299 "upgradeprovision is not able to handle an upgrade on a " \
300 "domain with more than one DC. Please demote the other " \
301 "DC(s) before upgrading" % len(res
)
307 def print_provision_key_parameters(names
):
308 """Do a a pretty print of provision parameters
310 :param names: list of key provision parameters """
311 message(GUESS
, "rootdn :" + str(names
.rootdn
))
312 message(GUESS
, "configdn :" + str(names
.configdn
))
313 message(GUESS
, "schemadn :" + str(names
.schemadn
))
314 message(GUESS
, "serverdn :" + str(names
.serverdn
))
315 message(GUESS
, "netbiosname :" + names
.netbiosname
)
316 message(GUESS
, "defaultsite :" + names
.sitename
)
317 message(GUESS
, "dnsdomain :" + names
.dnsdomain
)
318 message(GUESS
, "hostname :" + names
.hostname
)
319 message(GUESS
, "domain :" + names
.domain
)
320 message(GUESS
, "realm :" + names
.realm
)
321 message(GUESS
, "invocationid:" + names
.invocation
)
322 message(GUESS
, "policyguid :" + names
.policyid
)
323 message(GUESS
, "policyguiddc:" + str(names
.policyid_dc
))
324 message(GUESS
, "domainsid :" + str(names
.domainsid
))
325 message(GUESS
, "domainguid :" + names
.domainguid
)
326 message(GUESS
, "ntdsguid :" + names
.ntdsguid
)
327 message(GUESS
, "domainlevel :" + str(names
.domainlevel
))
330 def handle_special_case(att
, delta
, new
, old
, useReplMetadata
, basedn
, aldb
):
331 """Define more complicate update rules for some attributes
333 :param att: The attribute to be updated
334 :param delta: A messageElement object that correspond to the difference
335 between the updated object and the reference one
336 :param new: The reference object
337 :param old: The Updated object
338 :param useReplMetadata: A boolean that indicate if the update process
339 use replPropertyMetaData to decide what has to be updated.
340 :param basedn: The base DN of the provision
341 :param aldb: An ldb object used to build DN
342 :return: True to indicate that the attribute should be kept, False for
345 flag
= delta
.get(att
).flags()
346 # We do most of the special case handle if we do not have the
347 # highest usn as otherwise the replPropertyMetaData will guide us more
349 if not useReplMetadata
:
350 if (att
== "sPNMappings" and flag
== FLAG_MOD_REPLACE
and
351 ldb
.Dn(aldb
, "CN=Directory Service,CN=Windows NT,"
352 "CN=Services,CN=Configuration,%s" % basedn
)
355 if (att
== "userAccountControl" and flag
== FLAG_MOD_REPLACE
and
356 ldb
.Dn(aldb
, "CN=Administrator,CN=Users,%s" % basedn
)
358 message(SIMPLE
, "We suggest that you change the userAccountControl"
359 " for user Administrator from value %d to %d" %
360 (int(str(old
[0][att
])), int(str(new
[0][att
]))))
362 if (att
== "minPwdAge" and flag
== FLAG_MOD_REPLACE
):
363 if (long(str(old
[0][att
])) == 0):
364 delta
[att
] = MessageElement(new
[0][att
], FLAG_MOD_REPLACE
, att
)
367 if (att
== "member" and flag
== FLAG_MOD_REPLACE
):
371 for elem
in old
[0][att
]:
372 hash[str(elem
).lower()]=1
373 newval
.append(str(elem
))
375 for elem
in new
[0][att
]:
376 if not hash.has_key(str(elem
).lower()):
378 newval
.append(str(elem
))
380 delta
[att
] = MessageElement(newval
, FLAG_MOD_REPLACE
, att
)
385 if (att
in ("gPLink", "gPCFileSysPath") and
386 flag
== FLAG_MOD_REPLACE
and
387 str(new
[0].dn
).lower() == str(old
[0].dn
).lower()):
391 if att
== "forceLogoff":
392 ref
=0x8000000000000000
393 oldval
=int(old
[0][att
][0])
394 newval
=int(new
[0][att
][0])
395 ref
== old
and ref
== abs(new
)
398 if att
in ("adminDisplayName", "adminDescription"):
401 if (str(old
[0].dn
) == "CN=Samba4-Local-Domain, %s" % (names
.schemadn
)
402 and att
== "defaultObjectCategory" and flag
== FLAG_MOD_REPLACE
):
405 if (str(old
[0].dn
) == "CN=Title, %s" % (str(names
.schemadn
)) and
406 att
== "rangeUpper" and flag
== FLAG_MOD_REPLACE
):
409 if (str(old
[0].dn
) == "%s" % (str(names
.rootdn
))
410 and att
== "subRefs" and flag
== FLAG_MOD_REPLACE
):
412 #Allow to change revision of ForestUpdates objects
413 if (att
== "revision" or att
== "objectVersion"):
414 if str(delta
.dn
).lower().find("domainupdates") and str(delta
.dn
).lower().find("forestupdates") > 0:
416 if str(delta
.dn
).endswith("CN=DisplaySpecifiers, %s" % names
.configdn
):
419 # This is a bit of special animal as we might have added
420 # already SPN entries to the list that has to be modified
421 # So we go in detail to try to find out what has to be added ...
422 if (att
== "servicePrincipalName" and flag
== FLAG_MOD_REPLACE
):
426 for elem
in old
[0][att
]:
428 newval
.append(str(elem
))
430 for elem
in new
[0][att
]:
431 if not hash.has_key(str(elem
)):
433 newval
.append(str(elem
))
435 delta
[att
] = MessageElement(newval
, FLAG_MOD_REPLACE
, att
)
442 def dump_denied_change(dn
, att
, flagtxt
, current
, reference
):
443 """Print detailed information about why a change is denied
445 :param dn: DN of the object which attribute is denied
446 :param att: Attribute that was supposed to be upgraded
447 :param flagtxt: Type of the update that should be performed
448 (add, change, remove, ...)
449 :param current: Value(s) of the current attribute
450 :param reference: Value(s) of the reference attribute"""
452 message(CHANGE
, "dn= " + str(dn
)+" " + att
+" with flag " + flagtxt
453 + " must not be changed/removed. Discarding the change")
454 if att
== "objectSid" :
455 message(CHANGE
, "old : %s" % ndr_unpack(security
.dom_sid
, current
[0]))
456 message(CHANGE
, "new : %s" % ndr_unpack(security
.dom_sid
, reference
[0]))
457 elif att
== "rIDPreviousAllocationPool" or att
== "rIDAllocationPool":
458 message(CHANGE
, "old : %s" % int64range2str(current
[0]))
459 message(CHANGE
, "new : %s" % int64range2str(reference
[0]))
462 for e
in range(0, len(current
)):
463 message(CHANGE
, "old %d : %s" % (i
, str(current
[e
])))
465 if reference
is not None:
467 for e
in range(0, len(reference
)):
468 message(CHANGE
, "new %d : %s" % (i
, str(reference
[e
])))
471 def handle_special_add(samdb
, dn
, names
):
472 """Handle special operation (like remove) on some object needed during
475 This is mostly due to wrong creation of the object in previous provision.
476 :param samdb: An Ldb object representing the SAM database
477 :param dn: DN of the object to inspect
478 :param names: list of key provision parameters
482 objDn
= Dn(samdb
, "CN=IIS_IUSRS, CN=Builtin, %s" % names
.rootdn
)
484 #This entry was misplaced lets remove it if it exists
485 dntoremove
= "CN=IIS_IUSRS, CN=Users, %s" % names
.rootdn
488 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names
.rootdn
)
490 #This entry was misplaced lets remove it if it exists
491 dntoremove
= "CN=Certificate Service DCOM Access,"\
492 "CN=Users, %s" % names
.rootdn
494 objDn
= Dn(samdb
, "CN=Cryptographic Operators, CN=Builtin, %s" % names
.rootdn
)
496 #This entry was misplaced lets remove it if it exists
497 dntoremove
= "CN=Cryptographic Operators, CN=Users, %s" % names
.rootdn
499 objDn
= Dn(samdb
, "CN=Event Log Readers, CN=Builtin, %s" % names
.rootdn
)
501 #This entry was misplaced lets remove it if it exists
502 dntoremove
= "CN=Event Log Readers, CN=Users, %s" % names
.rootdn
504 objDn
= Dn(samdb
,"CN=System,CN=WellKnown Security Principals,"
505 "CN=Configuration,%s" % names
.rootdn
)
507 oldDn
= Dn(samdb
,"CN=Well-Known-Security-Id-System,"
508 "CN=WellKnown Security Principals,"
509 "CN=Configuration,%s" % names
.rootdn
)
511 res
= samdb
.search(expression
="(dn=%s)" % oldDn
,
512 base
=str(names
.rootdn
),
513 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
514 controls
=["search_options:1:2"])
516 res2
= samdb
.search(expression
="(dn=%s)" % dn
,
517 base
=str(names
.rootdn
),
518 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
519 controls
=["search_options:1:2"])
521 if len(res
) > 0 and len(res2
) == 0:
522 message(CHANGE
, "Existing object %s must be replaced by %s. "
523 "Renaming old object" % (str(oldDn
), str(dn
)))
524 samdb
.rename(oldDn
, objDn
, ["relax:0", "provision:0"])
528 if dntoremove
is not None:
529 res
= samdb
.search(expression
="(cn=RID Set)",
530 base
=str(names
.rootdn
),
531 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
532 controls
=["search_options:1:2"])
536 res
= samdb
.search(expression
="(dn=%s)" % dntoremove
,
537 base
=str(names
.rootdn
),
538 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
539 controls
=["search_options:1:2"])
541 message(CHANGE
, "Existing object %s must be replaced by %s. "
542 "Removing old object" % (dntoremove
, str(dn
)))
543 samdb
.delete(res
[0]["dn"])
549 def check_dn_nottobecreated(hash, index
, listdn
):
550 """Check if one of the DN present in the list has a creation order
551 greater than the current.
553 Hash is indexed by dn to be created, with each key
554 is associated the creation order.
556 First dn to be created has the creation order 0, second has 1, ...
557 Index contain the current creation order
559 :param hash: Hash holding the different DN of the object to be
561 :param index: Current creation order
562 :param listdn: List of DNs on which the current DN depends on
563 :return: None if the current object do not depend on other
564 object or if all object have been created before."""
568 key
= str(dn
).lower()
569 if hash.has_key(key
) and hash[key
] > index
:
575 def add_missing_object(ref_samdb
, samdb
, dn
, names
, basedn
, hash, index
):
576 """Add a new object if the dependencies are satisfied
578 The function add the object if the object on which it depends are already
581 :param ref_samdb: Ldb object representing the SAM db of the reference
583 :param samdb: Ldb object representing the SAM db of the upgraded
585 :param dn: DN of the object to be added
586 :param names: List of key provision parameters
587 :param basedn: DN of the partition to be updated
588 :param hash: Hash holding the different DN of the object to be
590 :param index: Current creation order
591 :return: True if the object was created False otherwise"""
593 ret
= handle_special_add(samdb
, dn
, names
)
602 reference
= ref_samdb
.search(expression
="dn=%s" % (str(dn
)), base
=basedn
,
603 scope
=SCOPE_SUBTREE
, controls
=["search_options:1:2"])
605 delta
= samdb
.msg_diff(empty
, reference
[0])
609 if str(reference
[0].get("cn")) == "RID Set":
610 for klass
in reference
[0].get("objectClass"):
611 if str(klass
).lower() == "ridset":
614 if delta
.get("objectSid"):
615 sid
= str(ndr_unpack(security
.dom_sid
, str(reference
[0]["objectSid"])))
616 m
= re
.match(r
".*-(\d+)$", sid
)
617 if m
and int(m
.group(1))>999:
618 delta
.remove("objectSid")
619 for att
in attrNotCopied
:
621 for att
in backlinked
:
623 depend_on_yettobecreated
= None
624 for att
in dn_syntax_att
:
625 depend_on_yet_tobecreated
= check_dn_nottobecreated(hash, index
,
627 if depend_on_yet_tobecreated
is not None:
628 message(CHANGE
, "Object %s depends on %s in attribute %s. "
629 "Delaying the creation" % (dn
,
630 depend_on_yet_tobecreated
, att
))
635 message(CHANGE
,"Object %s will be added" % dn
)
636 samdb
.add(delta
, ["relax:0", "provision:0"])
638 message(CHANGE
,"Object %s was skipped" % dn
)
642 def gen_dn_index_hash(listMissing
):
643 """Generate a hash associating the DN to its creation order
645 :param listMissing: List of DN
646 :return: Hash with DN as keys and creation order as values"""
648 for i
in range(0, len(listMissing
)):
649 hash[str(listMissing
[i
]).lower()] = i
652 def add_deletedobj_containers(ref_samdb
, samdb
, names
):
653 """Add the object containter: CN=Deleted Objects
655 This function create the container for each partition that need one and
656 then reference the object into the root of the partition
658 :param ref_samdb: Ldb object representing the SAM db of the reference
660 :param samdb: Ldb object representing the SAM db of the upgraded provision
661 :param names: List of key provision parameters"""
664 wkoPrefix
= "B:32:18E2EA80684F11D2B9AA00C04F79F805"
665 partitions
= [str(names
.rootdn
), str(names
.configdn
)]
666 for part
in partitions
:
667 ref_delObjCnt
= ref_samdb
.search(expression
="(cn=Deleted Objects)",
668 base
=part
, scope
=SCOPE_SUBTREE
,
670 controls
=["show_deleted:0",
672 delObjCnt
= samdb
.search(expression
="(cn=Deleted Objects)",
673 base
=part
, scope
=SCOPE_SUBTREE
,
675 controls
=["show_deleted:0",
677 if len(ref_delObjCnt
) > len(delObjCnt
):
678 reference
= ref_samdb
.search(expression
="cn=Deleted Objects",
679 base
=part
, scope
=SCOPE_SUBTREE
,
680 controls
=["show_deleted:0",
683 delta
= samdb
.msg_diff(empty
, reference
[0])
685 delta
.dn
= Dn(samdb
, str(reference
[0]["dn"]))
686 for att
in attrNotCopied
:
689 modcontrols
= ["relax:0", "provision:0"]
690 samdb
.add(delta
, modcontrols
)
693 res
= samdb
.search(expression
="(objectClass=*)", base
=part
,
695 attrs
=["dn", "wellKnownObjects"])
697 targetWKO
= "%s:%s" % (wkoPrefix
, str(reference
[0]["dn"]))
701 wko
= res
[0]["wellKnownObjects"]
703 # The wellKnownObject that we want to add.
705 if str(o
) == targetWKO
:
707 listwko
.append(str(o
))
710 listwko
.append(targetWKO
)
713 delta
.dn
= Dn(samdb
, str(res
[0]["dn"]))
714 delta
["wellKnownObjects"] = MessageElement(listwko
,
719 def add_missing_entries(ref_samdb
, samdb
, names
, basedn
, list):
720 """Add the missing object whose DN is the list
722 The function add the object if the objects on which it depends are
725 :param ref_samdb: Ldb object representing the SAM db of the reference
727 :param samdb: Ldb object representing the SAM db of the upgraded
729 :param dn: DN of the object to be added
730 :param names: List of key provision parameters
731 :param basedn: DN of the partition to be updated
732 :param list: List of DN to be added in the upgraded provision"""
737 while(len(listDefered
) != len(listMissing
) and len(listDefered
) > 0):
739 listMissing
= listDefered
741 hashMissing
= gen_dn_index_hash(listMissing
)
742 for dn
in listMissing
:
743 ret
= add_missing_object(ref_samdb
, samdb
, dn
, names
, basedn
,
747 # DN can't be created because it depends on some
748 # other DN in the list
749 listDefered
.append(dn
)
751 if len(listDefered
) != 0:
752 raise ProvisioningError("Unable to insert missing elements: "
753 "circular references")
755 def handle_links(samdb
, att
, basedn
, dn
, value
, ref_value
, delta
):
756 """This function handle updates on links
758 :param samdb: An LDB object pointing to the updated provision
759 :param att: Attribute to update
760 :param basedn: The root DN of the provision
761 :param dn: The DN of the inspected object
762 :param value: The value of the attribute
763 :param ref_value: The value of this attribute in the reference provision
764 :param delta: The MessageElement object that will be applied for
765 transforming the current provision"""
767 res
= samdb
.search(expression
="dn=%s" % dn
, base
=basedn
,
768 controls
=["search_options:1:2", "reveal:1"],
776 newlinklist
.extend(value
)
780 # for w2k domain level the reveal won't reveal anything ...
781 # it means that we can readd links that were removed on purpose ...
782 # Also this function in fact just accept add not removal
784 for e
in res
[0][att
]:
785 if not hash.has_key(e
):
786 # We put in the blacklist all the element that are in the "revealed"
787 # result and not in the "standard" result
788 # This element are links that were removed before and so that
789 # we don't wan't to readd
793 if not blacklist
.has_key(e
) and not hash.has_key(e
):
794 newlinklist
.append(str(e
))
797 delta
[att
] = MessageElement(newlinklist
, FLAG_MOD_REPLACE
, att
)
802 msg_elt_flag_strs
= {
803 ldb
.FLAG_MOD_ADD
: "MOD_ADD",
804 ldb
.FLAG_MOD_REPLACE
: "MOD_REPLACE",
805 ldb
.FLAG_MOD_DELETE
: "MOD_DELETE" }
807 def checkKeepAttributeOldMtd(delta
, att
, reference
, current
,
809 """ Check if we should keep the attribute modification or not.
810 This function didn't use replicationMetadata to take a decision.
812 :param delta: A message diff object
813 :param att: An attribute
814 :param reference: A message object for the current entry comming from
815 the reference provision.
816 :param current: A message object for the current entry commin from
817 the current provision.
818 :param basedn: The DN of the partition
819 :param samdb: A ldb connection to the sam database of the current provision.
821 :return: The modified message diff.
823 # Old school way of handling things for pre alpha12 upgrade
829 for att
in list(delta
):
830 msgElt
= delta
.get(att
)
832 if att
== "nTSecurityDescriptor":
840 if not hashOverwrittenAtt
.has_key(att
):
841 if msgElt
.flags() != FLAG_MOD_ADD
:
842 if not handle_special_case(att
, delta
, reference
, current
,
843 False, basedn
, samdb
):
844 if opts
.debugchange
or opts
.debugall
:
846 dump_denied_change(dn
, att
,
847 msg_elt_flag_strs
[msgElt
.flags()],
848 current
[0][att
], reference
[0][att
])
850 dump_denied_change(dn
, att
,
851 msg_elt_flag_strs
[msgElt
.flags()],
852 current
[0][att
], None)
856 if hashOverwrittenAtt
.get(att
)&2**msgElt
.flags() :
858 elif hashOverwrittenAtt
.get(att
)==never
:
864 def checkKeepAttributeWithMetadata(delta
, att
, message
, reference
, current
,
865 hash_attr_usn
, basedn
, usns
, samdb
):
866 """ Check if we should keep the attribute modification or not
868 :param delta: A message diff object
869 :param att: An attribute
870 :param message: A function to print messages
871 :param reference: A message object for the current entry comming from
872 the reference provision.
873 :param current: A message object for the current entry commin from
874 the current provision.
875 :param hash_attr_usn: A dictionnary with attribute name as keys,
876 USN and invocation id as values.
877 :param basedn: The DN of the partition
878 :param usns: A dictionnary with invocation ID as keys and USN ranges
880 :param samdb: A ldb object pointing to the sam DB
882 :return: The modified message diff.
889 for att
in list(delta
):
890 if att
in ["dn", "objectSid"]:
894 # We have updated by provision usn information so let's exploit
895 # replMetadataProperties
896 if att
in forwardlinked
:
897 curval
= current
[0].get(att
, ())
898 refval
= reference
[0].get(att
, ())
899 handle_links(samdb
, att
, basedn
, current
[0]["dn"],
900 curval
, refval
, delta
)
903 if isFirst
and len(delta
.items())>1:
905 txt
= "%s\n" % (str(dn
))
907 if handle_special_case(att
, delta
, reference
, current
, True, None, None):
908 # This attribute is "complicated" to handle and handling
909 # was done in handle_special_case
913 if hash_attr_usn
.get(att
):
914 [attrUSN
, attInvId
] = hash_attr_usn
.get(att
)
917 # If it's a replicated attribute and we don't have any USN
918 # information about it. It means that we never saw it before
920 # If it is a replicated attribute but we are not master on it
921 # (ie. not initially added in the provision we masterize).
923 if isReplicated(att
):
926 message(CHANGE
, "Non replicated attribute %s changed" % att
)
929 if att
== "nTSecurityDescriptor":
930 cursd
= ndr_unpack(security
.descriptor
,
931 str(current
[0]["nTSecurityDescriptor"]))
932 cursddl
= cursd
.as_sddl(names
.domainsid
)
933 refsd
= ndr_unpack(security
.descriptor
,
934 str(reference
[0]["nTSecurityDescriptor"]))
935 refsddl
= refsd
.as_sddl(names
.domainsid
)
937 diff
= get_diff_sddls(refsddl
, cursddl
)
939 # FIXME find a way to have it only with huge huge verbose mode
940 # message(CHANGE, "%ssd are identical" % txt)
946 message(CHANGESD
, "%ssd are not identical:\n%s" % (txt
, diff
))
949 message(CHANGESD
, "But the SD has been changed by someonelse "\
950 "so it's impossible to know if the difference"\
951 " cames from the modification or from a previous bug")
952 dnNotToRecalculate
.append(str(dn
))
954 dnToRecalculate
.append(str(dn
))
958 # This attribute was last modified by another DC forget
960 message(CHANGE
, "%sAttribute: %s has been "
961 "created/modified/deleted by another DC. "
962 "Doing nothing" % (txt
, att
))
966 elif not usn_in_range(int(attrUSN
), usns
.get(attInvId
)):
967 message(CHANGE
, "%sAttribute: %s was not "
968 "created/modified/deleted during a "
969 "provision or upgradeprovision. Current "
970 "usn: %d. Doing nothing" % (txt
, att
,
976 if att
== "defaultSecurityDescriptor":
979 message(CHANGE
, "%sAttribute: %s will be modified"
980 "/deleted it was last modified "
981 "during a provision. Current usn: "
982 "%d" % (txt
, att
, attrUSN
))
985 message(CHANGE
, "%sAttribute: %s will be added because "
986 "it did not exist before" % (txt
, att
))
992 def update_present(ref_samdb
, samdb
, basedn
, listPresent
, usns
):
993 """ This function updates the object that are already present in the
996 :param ref_samdb: An LDB object pointing to the reference provision
997 :param samdb: An LDB object pointing to the updated provision
998 :param basedn: A string with the value of the base DN for the provision
1000 :param listPresent: A list of object that is present in the provision
1001 :param usns: A list of USN range modified by previous provision and
1002 upgradeprovision grouped by invocation ID
1005 # This hash is meant to speedup lookup of attribute name from an oid,
1006 # it's for the replPropertyMetaData handling
1008 res
= samdb
.search(expression
="objectClass=attributeSchema", base
=basedn
,
1009 controls
=["search_options:1:2"], attrs
=["attributeID",
1013 strDisplay
= str(e
.get("lDAPDisplayName"))
1014 hash_oid_name
[str(e
.get("attributeID"))] = strDisplay
1016 msg
= "Unable to insert missing elements: circular references"
1017 raise ProvisioningError(msg
)
1020 controls
= ["search_options:1:2", "sd_flags:1:0"]
1021 if usns
is not None:
1022 message(CHANGE
, "Using replPropertyMetadata for change selection")
1023 for dn
in listPresent
:
1024 reference
= ref_samdb
.search(expression
="dn=%s" % (str(dn
)), base
=basedn
,
1025 scope
=SCOPE_SUBTREE
,
1027 current
= samdb
.search(expression
="dn=%s" % (str(dn
)), base
=basedn
,
1028 scope
=SCOPE_SUBTREE
, controls
=controls
)
1031 (str(current
[0].dn
) != str(reference
[0].dn
)) and
1032 (str(current
[0].dn
).upper() == str(reference
[0].dn
).upper())
1034 message(CHANGE
, "Names are the same except for the case. "
1035 "Renaming %s to %s" % (str(current
[0].dn
),
1036 str(reference
[0].dn
)))
1037 identic_rename(samdb
, reference
[0].dn
)
1038 current
= samdb
.search(expression
="dn=%s" % (str(dn
)), base
=basedn
,
1039 scope
=SCOPE_SUBTREE
,
1042 delta
= samdb
.msg_diff(current
[0], reference
[0])
1044 for att
in backlinked
:
1047 for att
in attrNotCopied
:
1050 delta
.remove("name")
1052 if len(delta
.items()) == 1:
1055 if len(delta
.items()) > 1 and usns
is not None:
1056 # Fetch the replPropertyMetaData
1057 res
= samdb
.search(expression
="dn=%s" % (str(dn
)), base
=basedn
,
1058 scope
=SCOPE_SUBTREE
, controls
=controls
,
1059 attrs
=["replPropertyMetaData"])
1060 ctr
= ndr_unpack(drsblobs
.replPropertyMetaDataBlob
,
1061 str(res
[0]["replPropertyMetaData"])).ctr
1065 # We put in this hash only modification
1066 # made on the current host
1067 att
= hash_oid_name
[samdb
.get_oid_from_attid(o
.attid
)]
1068 if str(o
.originating_invocation_id
) in usns
.keys():
1069 hash_attr_usn
[att
] = [o
.originating_usn
, str(o
.originating_invocation_id
)]
1071 hash_attr_usn
[att
] = [-1, None]
1073 if usns
is not None:
1074 delta
= checkKeepAttributeWithMetadata(delta
, att
, message
, reference
,
1075 current
, hash_attr_usn
,
1076 basedn
, usns
, samdb
)
1078 delta
= checkKeepAttributeOldMtd(delta
, att
, reference
, current
, basedn
, samdb
)
1081 if len(delta
.items()) >1:
1082 # Skip dn as the value is not really changed ...
1083 attributes
=", ".join(delta
.keys()[1:])
1085 relaxedatt
= ['iscriticalsystemobject', 'grouptype']
1086 # Let's try to reduce as much as possible the use of relax control
1087 for attr
in delta
.keys():
1088 if attr
.lower() in relaxedatt
:
1089 modcontrols
= ["relax:0", "provision:0"]
1090 message(CHANGE
, "%s is different from the reference one, changed"
1091 " attributes: %s\n" % (dn
, attributes
))
1093 samdb
.modify(delta
, modcontrols
)
1096 def reload_full_schema(samdb
, names
):
1097 """Load the updated schema with all the new and existing classes
1100 :param samdb: An LDB object connected to the sam.ldb of the update
1102 :param names: List of key provision parameters
1105 current
= samdb
.search(expression
="objectClass=*", base
=str(names
.schemadn
),
1106 scope
=SCOPE_SUBTREE
)
1111 schema_ldif
+= samdb
.write_ldif(ent
, ldb
.CHANGETYPE_NONE
)
1113 prefixmap_data
= open(setup_path("prefixMap.txt"), 'r').read()
1114 prefixmap_data
= b64encode(prefixmap_data
)
1116 # We don't actually add this ldif, just parse it
1117 prefixmap_ldif
= "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap_data
1119 dsdb
._dsdb
_set
_schema
_from
_ldif
(samdb
, prefixmap_ldif
, schema_ldif
)
1122 def update_partition(ref_samdb
, samdb
, basedn
, names
, schema
, provisionUSNs
, prereloadfunc
):
1123 """Check differences between the reference provision and the upgraded one.
1125 It looks for all objects which base DN is name.
1127 This function will also add the missing object and update existing object
1128 to add or remove attributes that were missing.
1130 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1132 :param samdb: An LDB object connected to the sam.ldb of the update
1134 :param basedn: String value of the DN of the partition
1135 :param names: List of key provision parameters
1136 :param schema: A Schema object
1137 :param provisionUSNs: A dictionnary with range of USN modified during provision
1138 or upgradeprovision. Ranges are grouped by invocationID.
1139 :param prereloadfunc: A function that must be executed just before the reload
1150 # Connect to the reference provision and get all the attribute in the
1151 # partition referred by name
1152 reference
= ref_samdb
.search(expression
="objectClass=*", base
=basedn
,
1153 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1154 controls
=["search_options:1:2"])
1156 current
= samdb
.search(expression
="objectClass=*", base
=basedn
,
1157 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1158 controls
=["search_options:1:2"])
1159 # Create a hash for speeding the search of new object
1160 for i
in range(0, len(reference
)):
1161 hash_new
[str(reference
[i
]["dn"]).lower()] = reference
[i
]["dn"]
1163 # Create a hash for speeding the search of existing object in the
1165 for i
in range(0, len(current
)):
1166 hash[str(current
[i
]["dn"]).lower()] = current
[i
]["dn"]
1169 for k
in hash_new
.keys():
1170 if not hash.has_key(k
):
1171 if not str(hash_new
[k
]) == "CN=Deleted Objects, %s" % names
.rootdn
:
1172 listMissing
.append(hash_new
[k
])
1174 listPresent
.append(hash_new
[k
])
1176 # Sort the missing object in order to have object of the lowest level
1177 # first (which can be containers for higher level objects)
1178 listMissing
.sort(dn_sort
)
1179 listPresent
.sort(dn_sort
)
1181 # The following lines is to load the up to
1182 # date schema into our current LDB
1183 # a complete schema is needed as the insertion of attributes
1184 # and class is done against it
1185 # and the schema is self validated
1186 samdb
.set_schema(schema
)
1188 message(SIMPLE
, "There are %d missing objects" % (len(listMissing
)))
1189 add_deletedobj_containers(ref_samdb
, samdb
, names
)
1191 add_missing_entries(ref_samdb
, samdb
, names
, basedn
, listMissing
)
1194 message(SIMPLE
, "Reloading a merged schema, which might trigger "
1195 "reindexing so please be patient")
1196 reload_full_schema(samdb
, names
)
1197 message(SIMPLE
, "Schema reloaded!")
1199 changed
= update_present(ref_samdb
, samdb
, basedn
, listPresent
,
1201 message(SIMPLE
, "There are %d changed objects" % (changed
))
1204 except StandardError, err
:
1205 message(ERROR
, "Exception during upgrade of samdb:")
1206 (typ
, val
, tb
) = sys
.exc_info()
1207 traceback
.print_exception(typ
, val
, tb
)
1211 def check_updated_sd(ref_sam
, cur_sam
, names
):
1212 """Check if the security descriptor in the upgraded provision are the same
1215 :param ref_sam: A LDB object connected to the sam.ldb file used as
1216 the reference provision
1217 :param cur_sam: A LDB object connected to the sam.ldb file used as
1219 :param names: List of key provision parameters"""
1220 reference
= ref_sam
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1221 scope
=SCOPE_SUBTREE
,
1222 attrs
=["dn", "nTSecurityDescriptor"],
1223 controls
=["search_options:1:2"])
1224 current
= cur_sam
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1225 scope
=SCOPE_SUBTREE
,
1226 attrs
=["dn", "nTSecurityDescriptor"],
1227 controls
=["search_options:1:2"])
1229 for i
in range(0, len(reference
)):
1230 refsd
= ndr_unpack(security
.descriptor
,
1231 str(reference
[i
]["nTSecurityDescriptor"]))
1232 hash[str(reference
[i
]["dn"]).lower()] = refsd
.as_sddl(names
.domainsid
)
1235 for i
in range(0, len(current
)):
1236 key
= str(current
[i
]["dn"]).lower()
1237 if hash.has_key(key
):
1238 cursd
= ndr_unpack(security
.descriptor
,
1239 str(current
[i
]["nTSecurityDescriptor"]))
1240 sddl
= cursd
.as_sddl(names
.domainsid
)
1241 if sddl
!= hash[key
]:
1242 txt
= get_diff_sddls(hash[key
], sddl
, False)
1244 message(CHANGESD
, "On object %s ACL is different"
1245 " \n%s" % (current
[i
]["dn"], txt
))
1249 def fix_partition_sd(samdb
, names
):
1250 """This function fix the SD for partition containers (basedn, configdn, ...)
1251 This is needed because some provision use to have broken SD on containers
1253 :param samdb: An LDB object pointing to the sam of the current provision
1254 :param names: A list of key provision parameters
1256 alwaysRecalculate
= False
1257 if len(dnToRecalculate
) == 0 and len(dnNotToRecalculate
) == 0:
1258 alwaysRecalculate
= True
1261 # NC's DN can't be both in dnToRecalculate and dnNotToRecalculate
1262 # First update the SD for the rootdn
1263 if alwaysRecalculate
or str(names
.rootdn
) in dnToRecalculate
:
1265 delta
.dn
= Dn(samdb
, str(names
.rootdn
))
1266 descr
= get_domain_descriptor(names
.domainsid
)
1267 delta
["nTSecurityDescriptor"] = MessageElement(descr
, FLAG_MOD_REPLACE
,
1268 "nTSecurityDescriptor")
1271 # Then the config dn
1272 if alwaysRecalculate
or str(names
.configdn
) in dnToRecalculate
:
1274 delta
.dn
= Dn(samdb
, str(names
.configdn
))
1275 descr
= get_config_descriptor(names
.domainsid
)
1276 delta
["nTSecurityDescriptor"] = MessageElement(descr
, FLAG_MOD_REPLACE
,
1277 "nTSecurityDescriptor" )
1280 # Then the schema dn
1281 if alwaysRecalculate
or str(names
.schemadn
) in dnToRecalculate
:
1283 delta
.dn
= Dn(samdb
, str(names
.schemadn
))
1284 descr
= get_schema_descriptor(names
.domainsid
)
1285 delta
["nTSecurityDescriptor"] = MessageElement(descr
, FLAG_MOD_REPLACE
,
1286 "nTSecurityDescriptor" )
1289 def rebuild_sd(samdb
, names
):
1290 """Rebuild security descriptor of the current provision from scratch
1292 During the different pre release of samba4 security descriptors (SD)
1293 were notarly broken (up to alpha11 included)
1294 This function allow to get them back in order, this function make the
1295 assumption that nobody has modified manualy an SD
1296 and so SD can be safely recalculated from scratch to get them right.
1298 :param names: List of key provision parameters"""
1300 fix_partition_sd(samdb
, names
)
1302 # List of namming contexts
1303 listNC
= [str(names
.rootdn
), str(names
.configdn
), str(names
.schemadn
)]
1305 if len(dnToRecalculate
) == 0:
1306 res
= samdb
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1307 scope
=SCOPE_SUBTREE
, attrs
=["dn", "whenCreated"],
1308 controls
=["search_options:1:2"])
1310 hash[str(obj
["dn"])] = obj
["whenCreated"]
1312 for dn
in dnToRecalculate
:
1313 if hash.has_key(dn
):
1315 # fetch each dn to recalculate and their child within the same partition
1316 res
= samdb
.search(expression
="objectClass=*", base
=dn
,
1317 scope
=SCOPE_SUBTREE
, attrs
=["dn", "whenCreated"])
1319 hash[str(obj
["dn"])] = obj
["whenCreated"]
1321 listKeys
= list(set(hash.keys()))
1322 listKeys
.sort(dn_sort
)
1324 if len(dnToRecalculate
) != 0:
1325 message(CHANGESD
, "%d DNs have been marked as needed to be recalculated"\
1326 ", recalculating %d due to inheritance"
1327 % (len(dnToRecalculate
), len(listKeys
)))
1329 for key
in listKeys
:
1330 if (key
in listNC
or
1331 key
in dnNotToRecalculate
):
1334 delta
.dn
= Dn(samdb
, key
)
1336 delta
["whenCreated"] = MessageElement(hash[key
], FLAG_MOD_REPLACE
,
1338 samdb
.modify(delta
, ["recalculate_sd:0","relax:0"])
1340 samdb
.transaction_cancel()
1341 res
= samdb
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1342 scope
=SCOPE_SUBTREE
,
1343 attrs
=["dn", "nTSecurityDescriptor"],
1344 controls
=["search_options:1:2"])
1345 badsd
= ndr_unpack(security
.descriptor
,
1346 str(res
[0]["nTSecurityDescriptor"]))
1347 message(ERROR
, "On %s bad stuff %s" % (str(delta
.dn
),badsd
.as_sddl(names
.domainsid
)))
1350 def removeProvisionUSN(samdb
):
1351 attrs
= [samba
.provision
.LAST_PROVISION_USN_ATTRIBUTE
, "dn"]
1352 entry
= samdb
.search(expression
="dn=@PROVISION", base
= "",
1353 scope
=SCOPE_SUBTREE
,
1356 empty
.dn
= entry
[0].dn
1357 delta
= samdb
.msg_diff(entry
[0], empty
)
1359 delta
.dn
= entry
[0].dn
1362 def remove_stored_generated_attrs(paths
, creds
, session
, lp
):
1363 """Remove previously stored constructed attributes
1365 :param paths: List of paths for different provision objects
1366 from the upgraded provision
1367 :param creds: A credential object
1368 :param session: A session object
1369 :param lp: A line parser object
1370 :return: An associative array whose key are the different constructed
1371 attributes and the value the dn where this attributes were found.
1375 def simple_update_basesamdb(newpaths
, paths
, names
):
1376 """Update the provision container db: sam.ldb
1377 This function is aimed at very old provision (before alpha9)
1379 :param newpaths: List of paths for different provision objects
1380 from the reference provision
1381 :param paths: List of paths for different provision objects
1382 from the upgraded provision
1383 :param names: List of key provision parameters"""
1385 message(SIMPLE
, "Copy samdb")
1386 shutil
.copy(newpaths
.samdb
, paths
.samdb
)
1388 message(SIMPLE
, "Update partitions filename if needed")
1389 schemaldb
= os
.path
.join(paths
.private_dir
, "schema.ldb")
1390 configldb
= os
.path
.join(paths
.private_dir
, "configuration.ldb")
1391 usersldb
= os
.path
.join(paths
.private_dir
, "users.ldb")
1392 samldbdir
= os
.path
.join(paths
.private_dir
, "sam.ldb.d")
1394 if not os
.path
.isdir(samldbdir
):
1396 os
.chmod(samldbdir
, 0700)
1397 if os
.path
.isfile(schemaldb
):
1398 shutil
.copy(schemaldb
, os
.path
.join(samldbdir
,
1399 "%s.ldb"%str
(names
.schemadn
).upper()))
1400 os
.remove(schemaldb
)
1401 if os
.path
.isfile(usersldb
):
1402 shutil
.copy(usersldb
, os
.path
.join(samldbdir
,
1403 "%s.ldb"%str
(names
.rootdn
).upper()))
1405 if os
.path
.isfile(configldb
):
1406 shutil
.copy(configldb
, os
.path
.join(samldbdir
,
1407 "%s.ldb"%str
(names
.configdn
).upper()))
1408 os
.remove(configldb
)
1411 def update_privilege(ref_private_path
, cur_private_path
):
1412 """Update the privilege database
1414 :param ref_private_path: Path to the private directory of the reference
1416 :param cur_private_path: Path to the private directory of the current
1417 (and to be updated) provision."""
1418 message(SIMPLE
, "Copy privilege")
1419 shutil
.copy(os
.path
.join(ref_private_path
, "privilege.ldb"),
1420 os
.path
.join(cur_private_path
, "privilege.ldb"))
1423 def update_samdb(ref_samdb
, samdb
, names
, provisionUSNs
, schema
, prereloadfunc
):
1424 """Upgrade the SAM DB contents for all the provision partitions
1426 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1428 :param samdb: An LDB object connected to the sam.ldb of the update
1430 :param names: List of key provision parameters
1431 :param provisionUSNs: A dictionnary with range of USN modified during provision
1432 or upgradeprovision. Ranges are grouped by invocationID.
1433 :param schema: A Schema object that represent the schema of the provision
1434 :param prereloadfunc: A function that must be executed just before the reload
1438 message(SIMPLE
, "Starting update of samdb")
1439 ret
= update_partition(ref_samdb
, samdb
, str(names
.rootdn
), names
,
1440 schema
, provisionUSNs
, prereloadfunc
)
1442 message(SIMPLE
, "Update of samdb finished")
1445 message(SIMPLE
, "Update failed")
1449 def copyxattrs(dir, refdir
):
1450 """ Copy owner, groups, extended ACL and NT acls from
1451 a reference dir to a destination dir
1453 Both dir are supposed to hold the same files
1454 :param dir: Destination dir
1455 :param refdir: Reference directory"""
1458 for root
, dirs
, files
in os
.walk(dir, topdown
=True):
1460 subdir
=root
[len(dir):]
1461 ref
= os
.path
.join("%s%s" % (refdir
, subdir
), name
)
1462 statsinfo
= os
.stat(ref
)
1463 tgt
= os
.path
.join(root
, name
)
1466 os
.chown(tgt
, statsinfo
.st_uid
, statsinfo
.st_gid
)
1467 # Get the xattr attributes if any
1469 attribute
= samba
.xattr_native
.wrap_getxattr(ref
,
1470 xattr
.XATTR_NTACL_NAME
)
1471 samba
.xattr_native
.wrap_setxattr(tgt
,
1472 xattr
.XATTR_NTACL_NAME
,
1476 attribute
= samba
.xattr_native
.wrap_getxattr(ref
,
1477 "system.posix_acl_access")
1478 samba
.xattr_native
.wrap_setxattr(tgt
,
1479 "system.posix_acl_access",
1484 subdir
=root
[len(dir):]
1485 ref
= os
.path
.join("%s%s" % (refdir
, subdir
), name
)
1486 statsinfo
= os
.stat(ref
)
1487 tgt
= os
.path
.join(root
, name
)
1489 os
.chown(os
.path
.join(root
, name
), statsinfo
.st_uid
,
1492 attribute
= samba
.xattr_native
.wrap_getxattr(ref
,
1493 xattr
.XATTR_NTACL_NAME
)
1494 samba
.xattr_native
.wrap_setxattr(tgt
,
1495 xattr
.XATTR_NTACL_NAME
,
1499 attribute
= samba
.xattr_native
.wrap_getxattr(ref
,
1500 "system.posix_acl_access")
1501 samba
.xattr_native
.wrap_setxattr(tgt
,
1502 "system.posix_acl_access",
1509 def backup_provision(paths
, dir):
1510 """This function backup the provision files so that a rollback
1513 :param paths: Paths to different objects
1514 :param dir: Directory where to store the backup
1517 shutil
.copytree(paths
.sysvol
, os
.path
.join(dir, "sysvol"))
1518 copyxattrs(os
.path
.join(dir, "sysvol"), paths
.sysvol
)
1519 shutil
.copy2(paths
.samdb
, dir)
1520 shutil
.copy2(paths
.secrets
, dir)
1521 shutil
.copy2(paths
.idmapdb
, dir)
1522 shutil
.copy2(paths
.privilege
, dir)
1523 if os
.path
.isfile(os
.path
.join(paths
.private_dir
,"eadb.tdb")):
1524 shutil
.copy2(os
.path
.join(paths
.private_dir
,"eadb.tdb"), dir)
1525 shutil
.copy2(paths
.smbconf
, dir)
1526 shutil
.copy2(os
.path
.join(paths
.private_dir
,"secrets.keytab"), dir)
1528 samldbdir
= os
.path
.join(paths
.private_dir
, "sam.ldb.d")
1529 if not os
.path
.isdir(samldbdir
):
1530 samldbdir
= paths
.private_dir
1531 schemaldb
= os
.path
.join(paths
.private_dir
, "schema.ldb")
1532 configldb
= os
.path
.join(paths
.private_dir
, "configuration.ldb")
1533 usersldb
= os
.path
.join(paths
.private_dir
, "users.ldb")
1534 shutil
.copy2(schemaldb
, dir)
1535 shutil
.copy2(usersldb
, dir)
1536 shutil
.copy2(configldb
, dir)
1538 shutil
.copytree(samldbdir
, os
.path
.join(dir, "sam.ldb.d"))
1543 def sync_calculated_attributes(samdb
, names
):
1544 """Synchronize attributes used for constructed ones, with the
1545 old constructed that were stored in the database.
1547 This apply for instance to msds-keyversionnumber that was
1548 stored and that is now constructed from replpropertymetadata.
1550 :param samdb: An LDB object attached to the currently upgraded samdb
1551 :param names: Various key parameter about current provision.
1553 listAttrs
= ["msDs-KeyVersionNumber"]
1554 hash = search_constructed_attrs_stored(samdb
, names
.rootdn
, listAttrs
)
1555 if hash.has_key("msDs-KeyVersionNumber"):
1556 increment_calculated_keyversion_number(samdb
, names
.rootdn
,
1557 hash["msDs-KeyVersionNumber"])
1559 # Synopsis for updateprovision
1560 # 1) get path related to provision to be update (called current)
1561 # 2) open current provision ldbs
1562 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1564 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1565 # by either upgradeprovision or provision
1566 # 5) creation of a new provision the latest version of provision script
1567 # (called reference)
1568 # 6) get reference provision paths
1569 # 7) open reference provision ldbs
1570 # 8) setup helpers data that will help the update process
1571 # 9) update the privilege ldb by copying the one of referecence provision to
1572 # the current provision
1573 # 10)get the oemInfo field, this field contains information about the different
1574 # provision that have been done
1575 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1576 # integer) or none of this the following things are done
1577 # A) When alpha9 or alphaxx is present
1578 # The base sam.ldb file is updated by looking at the difference between
1579 # referrence one and the current one. Everything is copied with the
1580 # exception of lastProvisionUSN attributes.
1581 # B) Other case (it reflect that that provision was done before alpha9)
1582 # The base sam.ldb of the reference provision is copied over
1583 # the current one, if necessary ldb related to partitions are moved
1585 # The highest used USN is fetched so that changed by upgradeprovision
1586 # usn can be tracked
1587 # 12)A Schema object is created, it will be used to provide a complete
1588 # schema to current provision during update (as the schema of the
1589 # current provision might not be complete and so won't allow some
1590 # object to be created)
1591 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1592 # 14)The secrets db is updated by pull all the difference from the reference
1593 # provision into the current provision
1594 # 15)As the previous step has most probably modified the password stored in
1595 # in secret for the current DC, a new password is generated,
1596 # the kvno is bumped and the entry in samdb is also updated
1597 # 16)For current provision older than alpha9, we must fix the SD a little bit
1598 # administrator to update them because SD used to be generated with the
1599 # system account before alpha9.
1600 # 17)The highest usn modified so far is searched in the database it will be
1601 # the upper limit for usn modified during provision.
1602 # This is done before potential SD recalculation because we do not want
1603 # SD modified during recalculation to be marked as modified during provision
1604 # (and so possibly remplaced at next upgradeprovision)
1605 # 18)Rebuilt SD if the flag indicate to do so
1606 # 19)Check difference between SD of reference provision and those of the
1607 # current provision. The check is done by getting the sddl representation
1608 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1609 # Each part is verified separetly, for dacl and sacl ACL is splited into
1610 # ACEs and each ACE is verified separately (so that a permutation in ACE
1611 # didn't raise as an error).
1612 # 20)The oemInfo field is updated to add information about the fact that the
1613 # provision has been updated by the upgradeprovision version xxx
1614 # (the version is the one obtained when starting samba with the --version
1616 # 21)Check if the current provision has all the settings needed for dynamic
1617 # DNS update to work (that is to say the provision is newer than
1618 # january 2010). If not dns configuration file from reference provision
1619 # are copied in a sub folder and the administrator is invited to
1620 # do what is needed.
1621 # 22)If the lastProvisionUSN attribute was present it is updated to add
1622 # the range of usns modified by the current upgradeprovision
1625 # About updating the sam DB
1626 # The update takes place in update_partition function
1627 # This function read both current and reference provision and list all
1628 # the available DN of objects
1629 # If the string representation of a DN in reference provision is
1630 # equal to the string representation of a DN in current provision
1631 # (without taking care of case) then the object is flaged as being
1632 # present. If the object is not present in current provision the object
1633 # is being flaged as missing in current provision. Object present in current
1634 # provision but not in reference provision are ignored.
1635 # Once the list of objects present and missing is done, the deleted object
1636 # containers are created in the differents partitions (if missing)
1638 # Then the function add_missing_entries is called
1639 # This function will go through the list of missing entries by calling
1640 # add_missing_object for the given object. If this function returns 0
1641 # it means that the object needs some other object in order to be created
1642 # The object is reappended at the end of the list to be created later
1643 # (and preferably after all the needed object have been created)
1644 # The function keeps on looping on the list of object to be created until
1645 # it's empty or that the number of defered creation is equal to the number
1646 # of object that still needs to be created.
1648 # The function add_missing_object will first check if the object can be created.
1649 # That is to say that it didn't depends other not yet created objects
1650 # If requisit can't be fullfilled it exists with 0
1651 # Then it will try to create the missing entry by creating doing
1652 # an ldb_message_diff between the object in the reference provision and
1654 # This resulting object is filtered to remove all the back link attribute
1655 # (ie. memberOf) as they will be created by the other linked object (ie.
1656 # the one with the member attribute)
1657 # All attributes specified in the attrNotCopied array are
1658 # also removed it's most of the time generated attributes
1660 # After missing entries have been added the update_partition function will
1661 # take care of object that exist but that need some update.
1662 # In order to do so the function update_present is called with the list
1663 # of object that are present in both provision and that might need an update.
1665 # This function handle first case mismatch so that the DN in the current
1666 # provision have the same case as in reference provision
1668 # It will then construct an associative array consiting of attributes as
1669 # key and invocationid as value( if the originating invocation id is
1670 # different from the invocation id of the current DC the value is -1 instead).
1672 # If the range of provision modified attributes is present, the function will
1673 # use the replMetadataProperty update method which is the following:
1674 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1675 # creationTime, msDs-KeyVersionNumber, oEMInformation
1676 # Check for each attribute if its usn is within one of the modified by
1677 # provision range and if its originating id is the invocation id of the
1678 # current DC, then validate the update from reference to current.
1679 # If not or if there is no replMetatdataProperty for this attribute then we
1681 # Otherwise (case the range of provision modified attribute is not present) it
1682 # use the following process:
1683 # All attributes that need to be added are accepted at the exeption of those
1684 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1685 # correct flags specified.
1686 # For attributes that need to be modified or removed, a check is performed
1687 # in OverwrittenAtt, if the attribute is present and the modification flag
1688 # (remove, delete) is one of those listed for this attribute then modification
1689 # is accepted. For complicated handling of attribute update, the control is passed
1690 # to handle_special_case
1694 if __name__
== '__main__':
1695 global defSDmodified
1696 defSDmodified
= False
1697 # From here start the big steps of the program
1698 # 1) First get files paths
1699 paths
= get_paths(param
, smbconf
=smbconf
)
1700 # Get ldbs with the system session, it is needed for searching
1701 # provision parameters
1702 session
= system_session()
1704 # This variable will hold the last provision USN once if it exists.
1707 ldbs
= get_ldbs(paths
, creds
, session
, lp
)
1708 backupdir
= tempfile
.mkdtemp(dir=paths
.private_dir
,
1709 prefix
="backupprovision")
1710 backup_provision(paths
, backupdir
)
1712 ldbs
.startTransactions()
1714 # 3) Guess all the needed names (variables in fact) from the current
1716 names
= find_provision_key_parameters(ldbs
.sam
, ldbs
.secrets
, ldbs
.idmap
,
1719 lastProvisionUSNs
= get_last_provision_usn(ldbs
.sam
)
1720 if lastProvisionUSNs
is not None:
1722 for k
in lastProvisionUSNs
.keys():
1723 for r
in lastProvisionUSNs
[k
]:
1727 "Find last provision USN, %d invocation(s) for a total of %d ranges" % \
1728 (len(lastProvisionUSNs
.keys()), v
/2 ))
1730 if lastProvisionUSNs
.get("default") != None:
1731 message(CHANGE
, "Old style for usn ranges used")
1732 lastProvisionUSNs
[str(names
.invocation
)] = lastProvisionUSNs
["default"]
1733 del lastProvisionUSNs
["default"]
1735 message(SIMPLE
, "Your provision lacks provision range information")
1736 if confirm("Do you want to run findprovisionusnranges to try to find them ?", False):
1737 ldbs
.groupedRollback()
1738 os
.system("%s %s %s %s %s" % (os
.path
.join(os
.path
.dirname(sys
.argv
[0]),
1739 "findprovisionusnranges"),
1744 message(SIMPLE
, "Once you applied/adapted the change(s) please restart the upgradeprovision script")
1747 # Objects will be created with the admin session
1748 # (not anymore system session)
1749 adm_session
= admin_session(lp
, str(names
.domainsid
))
1750 # So we reget handle on objects
1751 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1752 if not opts
.fixntacl
:
1753 if not sanitychecks(ldbs
.sam
, names
):
1754 message(SIMPLE
, "Sanity checks for the upgrade have failed. "
1755 "Check the messages and correct the errors "
1756 "before rerunning upgradeprovision")
1757 ldbs
.groupedRollback()
1760 # Let's see provision parameters
1761 print_provision_key_parameters(names
)
1763 # 5) With all this information let's create a fresh new provision used as
1765 message(SIMPLE
, "Creating a reference provision")
1766 provisiondir
= tempfile
.mkdtemp(dir=paths
.private_dir
,
1767 prefix
="referenceprovision")
1768 newprovision(names
, creds
, session
, smbconf
, provisiondir
,
1773 # We need to get a list of object which SD is directly computed from
1774 # defaultSecurityDescriptor.
1775 # This will allow us to know which object we can rebuild the SD in case
1776 # of change of the parent's SD or of the defaultSD.
1777 # Get file paths of this new provision
1778 newpaths
= get_paths(param
, targetdir
=provisiondir
)
1779 new_ldbs
= get_ldbs(newpaths
, creds
, session
, lp
)
1780 new_ldbs
.startTransactions()
1782 populateNotReplicated(new_ldbs
.sam
, names
.schemadn
)
1783 # 8) Populate some associative array to ease the update process
1784 # List of attribute which are link and backlink
1785 populate_links(new_ldbs
.sam
, names
.schemadn
)
1786 # List of attribute with ASN DN synthax)
1787 populate_dnsyntax(new_ldbs
.sam
, names
.schemadn
)
1789 update_privilege(newpaths
.private_dir
, paths
.private_dir
)
1791 oem
= getOEMInfo(ldbs
.sam
, str(names
.rootdn
))
1792 # Do some modification on sam.ldb
1793 ldbs
.groupedCommit()
1794 new_ldbs
.groupedCommit()
1797 if re
.match(".*alpha((9)|(\d\d+)).*", str(oem
)):
1799 # Starting from alpha9 we can consider that the structure is quite ok
1800 # and that we should do only dela
1801 deltaattr
= delta_update_basesamdb(newpaths
.samdb
,
1809 simple_update_basesamdb(newpaths
, paths
, names
)
1810 ldbs
= get_ldbs(paths
, creds
, session
, lp
)
1811 removeProvisionUSN(ldbs
.sam
)
1813 ldbs
.startTransactions()
1814 minUSN
= int(str(get_max_usn(ldbs
.sam
, str(names
.rootdn
)))) + 1
1815 new_ldbs
.startTransactions()
1818 schema
= Schema(names
.domainsid
, schemadn
=str(names
.schemadn
))
1819 # We create a closure that will be invoked just before schema reload
1820 def schemareloadclosure():
1821 basesam
= Ldb(paths
.samdb
, session_info
=session
, credentials
=creds
, lp
=lp
,
1822 options
=["modules:"])
1824 if deltaattr
is not None and len(deltaattr
) > 1:
1827 deltaattr
.remove("dn")
1828 for att
in deltaattr
:
1829 if att
.lower() == "dn":
1831 if (deltaattr
.get(att
) is not None
1832 and deltaattr
.get(att
).flags() != FLAG_MOD_ADD
):
1834 elif deltaattr
.get(att
) is None:
1837 message(CHANGE
, "Applying delta to @ATTRIBUTES")
1838 deltaattr
.dn
= ldb
.Dn(basesam
, "@ATTRIBUTES")
1839 basesam
.modify(deltaattr
)
1841 message(CHANGE
, "Not applying delta to @ATTRIBUTES because "
1842 "there is not only add")
1845 if not update_samdb(new_ldbs
.sam
, ldbs
.sam
, names
, lastProvisionUSNs
,
1846 schema
, schemareloadclosure
):
1847 message(SIMPLE
, "Rolling back all changes. Check the cause"
1849 message(SIMPLE
, "Your system is as it was before the upgrade")
1850 ldbs
.groupedRollback()
1851 new_ldbs
.groupedRollback()
1852 shutil
.rmtree(provisiondir
)
1855 # Try to reapply the change also when we do not change the sam
1856 # as the delta_upgrade
1857 schemareloadclosure()
1858 sync_calculated_attributes(ldbs
.sam
, names
)
1859 res
= ldbs
.sam
.search(expression
="(samaccountname=dns)",
1860 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1861 controls
=["search_options:1:2"])
1863 message(SIMPLE
, "You still have the old DNS object for managing "
1864 "dynamic DNS, but you didn't supply --full so "
1865 "a correct update can't be done")
1866 ldbs
.groupedRollback()
1867 new_ldbs
.groupedRollback()
1868 shutil
.rmtree(provisiondir
)
1871 update_secrets(new_ldbs
.secrets
, ldbs
.secrets
, message
)
1873 res
= ldbs
.sam
.search(expression
="(samaccountname=dns)",
1874 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1875 controls
=["search_options:1:2"])
1878 ldbs
.sam
.delete(res
[0]["dn"])
1879 res2
= ldbs
.secrets
.search(expression
="(samaccountname=dns)",
1880 scope
=SCOPE_SUBTREE
, attrs
=["dn"])
1881 update_dns_account_password(ldbs
.sam
, ldbs
.secrets
, names
)
1882 message(SIMPLE
, "IMPORTANT!!! "
1883 "If you were using Dynamic DNS before you need "
1884 "to update your configuration, so that the "
1885 "tkey-gssapi-credential has the following value: "
1886 "DNS/%s.%s" % (names
.netbiosname
.lower(),
1887 names
.realm
.lower()))
1889 message(SIMPLE
, "Update machine account")
1890 update_machine_account_password(ldbs
.sam
, ldbs
.secrets
, names
)
1892 dnToRecalculate
.sort(dn_sort
)
1893 # 16) SD should be created with admin but as some previous acl were so wrong
1894 # that admin can't modify them we have first to recreate them with the good
1895 # form but with system account and then give the ownership to admin ...
1896 if str(oem
) != "" and not re
.match(r
'.*alpha(9|\d\d+)', str(oem
)):
1897 message(SIMPLE
, "Fixing very old provision SD")
1898 rebuild_sd(ldbs
.sam
, names
)
1900 # We calculate the max USN before recalculating the SD because we might
1901 # touch object that have been modified after a provision and we do not
1902 # want that the next upgradeprovision thinks that it has a green light
1906 maxUSN
= get_max_usn(ldbs
.sam
, str(names
.rootdn
))
1908 # 18) We rebuild SD if a we have a list of DN to recalculate or if the
1909 # defSDmodified is set.
1910 if defSDmodified
or len(dnToRecalculate
) >0:
1911 message(SIMPLE
, "Some defaultSecurityDescriptors and/or"
1912 "securityDescriptor have changed, recalculating SD ")
1913 ldbs
.sam
.set_session_info(adm_session
)
1914 rebuild_sd(ldbs
.sam
, names
)
1917 # Now we are quite confident in the recalculate process of the SD, we make
1918 # it optional. And we don't do it if there is DN that we must touch
1919 # as we are assured that on this DNs we will have differences !
1920 # Also the check must be done in a clever way as for the moment we just
1922 if len(dnNotToRecalculate
) == 0 and (opts
.debugchangesd
or opts
.debugall
):
1923 message(CHANGESD
, "Checking recalculated SDs")
1924 check_updated_sd(new_ldbs
.sam
, ldbs
.sam
, names
)
1927 updateOEMInfo(ldbs
.sam
, str(names
.rootdn
))
1929 check_for_DNS(newpaths
.private_dir
, paths
.private_dir
)
1931 if lastProvisionUSNs
is not None:
1932 update_provision_usn(ldbs
.sam
, minUSN
, maxUSN
, names
.invocation
)
1933 if opts
.full
and (names
.policyid
is None or names
.policyid_dc
is None):
1934 update_policyids(names
, ldbs
.sam
)
1935 if opts
.full
or opts
.resetfileacl
or opts
.fixntacl
:
1937 update_gpo(paths
, ldbs
.sam
, names
, lp
, message
, 1)
1938 except ProvisioningError
, e
:
1939 message(ERROR
, "The policy for domain controller is missing. "
1940 "You should restart upgradeprovision with --full")
1942 message(ERROR
, "Setting ACL not supported on your filesystem")
1945 update_gpo(paths
, ldbs
.sam
, names
, lp
, message
, 0)
1946 except ProvisioningError
, e
:
1947 message(ERROR
, "The policy for domain controller is missing. "
1948 "You should restart upgradeprovision with --full")
1949 if not opts
.fixntacl
:
1950 ldbs
.groupedCommit()
1951 new_ldbs
.groupedCommit()
1952 message(SIMPLE
, "Upgrade finished!")
1953 # remove reference provision now that everything is done !
1954 # So we have reindexed first if need when the merged schema was reloaded
1955 # (as new attributes could have quick in)
1956 # But the second part of the update (when we update existing objects
1957 # can also have an influence on indexing as some attribute might have their
1958 # searchflag modificated
1959 message(SIMPLE
, "Reopenning samdb to trigger reindexing if needed "
1960 "after modification")
1961 samdb
= Ldb(paths
.samdb
, session_info
=session
, credentials
=creds
, lp
=lp
)
1962 message(SIMPLE
, "Reindexing finished")
1964 shutil
.rmtree(provisiondir
)
1966 ldbs
.groupedRollback()
1967 message(SIMPLE
, "ACLs fixed !")
1968 except StandardError, err
:
1969 message(ERROR
, "A problem occurred while trying to upgrade your "
1970 "provision. A full backup is located at %s" % backupdir
)
1971 if opts
.debugall
or opts
.debugchange
:
1972 (typ
, val
, tb
) = sys
.exc_info()
1973 traceback
.print_exception(typ
, val
, tb
)