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"]
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(elem
["lDAPDisplayName"])
271 def populate_dnsyntax(samdb
, schemadn
):
272 """Populate an array with all the attributes that have DN synthax
275 :param samdb: A LDB object for sam.ldb file
276 :param schemadn: DN of the schema for the partition"""
277 res
= samdb
.search(expression
="(attributeSyntax=2.5.5.1)", base
=Dn(samdb
,
278 str(schemadn
)), scope
=SCOPE_SUBTREE
,
279 attrs
=["lDAPDisplayName"])
281 dn_syntax_att
.append(elem
["lDAPDisplayName"])
284 def sanitychecks(samdb
, names
):
285 """Make some checks before trying to update
287 :param samdb: An LDB object opened on sam.ldb
288 :param names: list of key provision parameters
289 :return: Status of check (1 for Ok, 0 for not Ok) """
290 res
= samdb
.search(expression
="objectClass=ntdsdsa", base
=str(names
.configdn
),
291 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
292 controls
=["search_options:1:2"])
294 print "No DC found. Your provision is most probably broken!"
297 print "Found %d domain controllers. For the moment " \
298 "upgradeprovision is not able to handle an upgrade on a " \
299 "domain with more than one DC. Please demote the other " \
300 "DC(s) before upgrading" % len(res
)
306 def print_provision_key_parameters(names
):
307 """Do a a pretty print of provision parameters
309 :param names: list of key provision parameters """
310 message(GUESS
, "rootdn :" + str(names
.rootdn
))
311 message(GUESS
, "configdn :" + str(names
.configdn
))
312 message(GUESS
, "schemadn :" + str(names
.schemadn
))
313 message(GUESS
, "serverdn :" + str(names
.serverdn
))
314 message(GUESS
, "netbiosname :" + names
.netbiosname
)
315 message(GUESS
, "defaultsite :" + names
.sitename
)
316 message(GUESS
, "dnsdomain :" + names
.dnsdomain
)
317 message(GUESS
, "hostname :" + names
.hostname
)
318 message(GUESS
, "domain :" + names
.domain
)
319 message(GUESS
, "realm :" + names
.realm
)
320 message(GUESS
, "invocationid:" + names
.invocation
)
321 message(GUESS
, "policyguid :" + names
.policyid
)
322 message(GUESS
, "policyguiddc:" + str(names
.policyid_dc
))
323 message(GUESS
, "domainsid :" + str(names
.domainsid
))
324 message(GUESS
, "domainguid :" + names
.domainguid
)
325 message(GUESS
, "ntdsguid :" + names
.ntdsguid
)
326 message(GUESS
, "domainlevel :" + str(names
.domainlevel
))
329 def handle_special_case(att
, delta
, new
, old
, useReplMetadata
, basedn
, aldb
):
330 """Define more complicate update rules for some attributes
332 :param att: The attribute to be updated
333 :param delta: A messageElement object that correspond to the difference
334 between the updated object and the reference one
335 :param new: The reference object
336 :param old: The Updated object
337 :param useReplMetadata: A boolean that indicate if the update process
338 use replPropertyMetaData to decide what has to be updated.
339 :param basedn: The base DN of the provision
340 :param aldb: An ldb object used to build DN
341 :return: True to indicate that the attribute should be kept, False for
344 flag
= delta
.get(att
).flags()
345 # We do most of the special case handle if we do not have the
346 # highest usn as otherwise the replPropertyMetaData will guide us more
348 if not useReplMetadata
:
349 if (att
== "sPNMappings" and flag
== FLAG_MOD_REPLACE
and
350 ldb
.Dn(aldb
, "CN=Directory Service,CN=Windows NT,"
351 "CN=Services,CN=Configuration,%s" % basedn
)
354 if (att
== "userAccountControl" and flag
== FLAG_MOD_REPLACE
and
355 ldb
.Dn(aldb
, "CN=Administrator,CN=Users,%s" % basedn
)
357 message(SIMPLE
, "We suggest that you change the userAccountControl"
358 " for user Administrator from value %d to %d" %
359 (int(str(old
[0][att
])), int(str(new
[0][att
]))))
361 if (att
== "minPwdAge" and flag
== FLAG_MOD_REPLACE
):
362 if (long(str(old
[0][att
])) == 0):
363 delta
[att
] = MessageElement(new
[0][att
], FLAG_MOD_REPLACE
, att
)
366 if (att
== "member" and flag
== FLAG_MOD_REPLACE
):
370 for elem
in old
[0][att
]:
371 hash[str(elem
).lower()]=1
372 newval
.append(str(elem
))
374 for elem
in new
[0][att
]:
375 if not hash.has_key(str(elem
).lower()):
377 newval
.append(str(elem
))
379 delta
[att
] = MessageElement(newval
, FLAG_MOD_REPLACE
, att
)
384 if (att
in ("gPLink", "gPCFileSysPath") and
385 flag
== FLAG_MOD_REPLACE
and
386 str(new
[0].dn
).lower() == str(old
[0].dn
).lower()):
390 if att
== "forceLogoff":
391 ref
=0x8000000000000000
392 oldval
=int(old
[0][att
][0])
393 newval
=int(new
[0][att
][0])
394 ref
== old
and ref
== abs(new
)
397 if att
in ("adminDisplayName", "adminDescription"):
400 if (str(old
[0].dn
) == "CN=Samba4-Local-Domain, %s" % (names
.schemadn
)
401 and att
== "defaultObjectCategory" and flag
== FLAG_MOD_REPLACE
):
404 if (str(old
[0].dn
) == "CN=Title, %s" % (str(names
.schemadn
)) and
405 att
== "rangeUpper" and flag
== FLAG_MOD_REPLACE
):
408 if (str(old
[0].dn
) == "%s" % (str(names
.rootdn
))
409 and att
== "subRefs" and flag
== FLAG_MOD_REPLACE
):
411 #Allow to change revision of ForestUpdates objects
412 if (att
== "revision" or att
== "objectVersion"):
413 if str(delta
.dn
).lower().find("domainupdates") and str(delta
.dn
).lower().find("forestupdates") > 0:
415 if str(delta
.dn
).endswith("CN=DisplaySpecifiers, %s" % names
.configdn
):
418 # This is a bit of special animal as we might have added
419 # already SPN entries to the list that has to be modified
420 # So we go in detail to try to find out what has to be added ...
421 if (att
== "servicePrincipalName" and flag
== FLAG_MOD_REPLACE
):
425 for elem
in old
[0][att
]:
427 newval
.append(str(elem
))
429 for elem
in new
[0][att
]:
430 if not hash.has_key(str(elem
)):
432 newval
.append(str(elem
))
434 delta
[att
] = MessageElement(newval
, FLAG_MOD_REPLACE
, att
)
441 def dump_denied_change(dn
, att
, flagtxt
, current
, reference
):
442 """Print detailed information about why a change is denied
444 :param dn: DN of the object which attribute is denied
445 :param att: Attribute that was supposed to be upgraded
446 :param flagtxt: Type of the update that should be performed
447 (add, change, remove, ...)
448 :param current: Value(s) of the current attribute
449 :param reference: Value(s) of the reference attribute"""
451 message(CHANGE
, "dn= " + str(dn
)+" " + att
+" with flag " + flagtxt
452 + " must not be changed/removed. Discarding the change")
453 if att
== "objectSid" :
454 message(CHANGE
, "old : %s" % ndr_unpack(security
.dom_sid
, current
[0]))
455 message(CHANGE
, "new : %s" % ndr_unpack(security
.dom_sid
, reference
[0]))
456 elif att
== "rIDPreviousAllocationPool" or att
== "rIDAllocationPool":
457 message(CHANGE
, "old : %s" % int64range2str(current
[0]))
458 message(CHANGE
, "new : %s" % int64range2str(reference
[0]))
461 for e
in range(0, len(current
)):
462 message(CHANGE
, "old %d : %s" % (i
, str(current
[e
])))
464 if reference
is not None:
466 for e
in range(0, len(reference
)):
467 message(CHANGE
, "new %d : %s" % (i
, str(reference
[e
])))
470 def handle_special_add(samdb
, dn
, names
):
471 """Handle special operation (like remove) on some object needed during
474 This is mostly due to wrong creation of the object in previous provision.
475 :param samdb: An Ldb object representing the SAM database
476 :param dn: DN of the object to inspect
477 :param names: list of key provision parameters
481 objDn
= Dn(samdb
, "CN=IIS_IUSRS, CN=Builtin, %s" % names
.rootdn
)
483 #This entry was misplaced lets remove it if it exists
484 dntoremove
= "CN=IIS_IUSRS, CN=Users, %s" % names
.rootdn
487 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names
.rootdn
)
489 #This entry was misplaced lets remove it if it exists
490 dntoremove
= "CN=Certificate Service DCOM Access,"\
491 "CN=Users, %s" % names
.rootdn
493 objDn
= Dn(samdb
, "CN=Cryptographic Operators, CN=Builtin, %s" % names
.rootdn
)
495 #This entry was misplaced lets remove it if it exists
496 dntoremove
= "CN=Cryptographic Operators, CN=Users, %s" % names
.rootdn
498 objDn
= Dn(samdb
, "CN=Event Log Readers, CN=Builtin, %s" % names
.rootdn
)
500 #This entry was misplaced lets remove it if it exists
501 dntoremove
= "CN=Event Log Readers, CN=Users, %s" % names
.rootdn
503 objDn
= Dn(samdb
,"CN=System,CN=WellKnown Security Principals,"
504 "CN=Configuration,%s" % names
.rootdn
)
506 oldDn
= Dn(samdb
,"CN=Well-Known-Security-Id-System,"
507 "CN=WellKnown Security Principals,"
508 "CN=Configuration,%s" % names
.rootdn
)
510 res
= samdb
.search(expression
="(dn=%s)" % oldDn
,
511 base
=str(names
.rootdn
),
512 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
513 controls
=["search_options:1:2"])
515 res2
= samdb
.search(expression
="(dn=%s)" % dn
,
516 base
=str(names
.rootdn
),
517 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
518 controls
=["search_options:1:2"])
520 if len(res
) > 0 and len(res2
) == 0:
521 message(CHANGE
, "Existing object %s must be replaced by %s. "
522 "Renaming old object" % (str(oldDn
), str(dn
)))
523 samdb
.rename(oldDn
, objDn
, ["relax:0", "provision:0"])
527 if dntoremove
is not None:
528 res
= samdb
.search(expression
="(cn=RID Set)",
529 base
=str(names
.rootdn
),
530 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
531 controls
=["search_options:1:2"])
535 res
= samdb
.search(expression
="(dn=%s)" % dntoremove
,
536 base
=str(names
.rootdn
),
537 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
538 controls
=["search_options:1:2"])
540 message(CHANGE
, "Existing object %s must be replaced by %s. "
541 "Removing old object" % (dntoremove
, str(dn
)))
542 samdb
.delete(res
[0]["dn"])
548 def check_dn_nottobecreated(hash, index
, listdn
):
549 """Check if one of the DN present in the list has a creation order
550 greater than the current.
552 Hash is indexed by dn to be created, with each key
553 is associated the creation order.
555 First dn to be created has the creation order 0, second has 1, ...
556 Index contain the current creation order
558 :param hash: Hash holding the different DN of the object to be
560 :param index: Current creation order
561 :param listdn: List of DNs on which the current DN depends on
562 :return: None if the current object do not depend on other
563 object or if all object have been created before."""
567 key
= str(dn
).lower()
568 if hash.has_key(key
) and hash[key
] > index
:
574 def add_missing_object(ref_samdb
, samdb
, dn
, names
, basedn
, hash, index
):
575 """Add a new object if the dependencies are satisfied
577 The function add the object if the object on which it depends are already
580 :param ref_samdb: Ldb object representing the SAM db of the reference
582 :param samdb: Ldb object representing the SAM db of the upgraded
584 :param dn: DN of the object to be added
585 :param names: List of key provision parameters
586 :param basedn: DN of the partition to be updated
587 :param hash: Hash holding the different DN of the object to be
589 :param index: Current creation order
590 :return: True if the object was created False otherwise"""
592 ret
= handle_special_add(samdb
, dn
, names
)
601 reference
= ref_samdb
.search(expression
="dn=%s" % (str(dn
)), base
=basedn
,
602 scope
=SCOPE_SUBTREE
, controls
=["search_options:1:2"])
604 delta
= samdb
.msg_diff(empty
, reference
[0])
608 if str(reference
[0].get("cn")) == "RID Set":
609 for klass
in reference
[0].get("objectClass"):
610 if str(klass
).lower() == "ridset":
613 if delta
.get("objectSid"):
614 sid
= str(ndr_unpack(security
.dom_sid
, str(reference
[0]["objectSid"])))
615 m
= re
.match(r
".*-(\d+)$", sid
)
616 if m
and int(m
.group(1))>999:
617 delta
.remove("objectSid")
618 for att
in attrNotCopied
:
620 for att
in backlinked
:
622 depend_on_yettobecreated
= None
623 for att
in dn_syntax_att
:
624 depend_on_yet_tobecreated
= check_dn_nottobecreated(hash, index
,
626 if depend_on_yet_tobecreated
is not None:
627 message(CHANGE
, "Object %s depends on %s in attribute %s. "
628 "Delaying the creation" % (dn
,
629 depend_on_yet_tobecreated
, att
))
634 message(CHANGE
,"Object %s will be added" % dn
)
635 samdb
.add(delta
, ["relax:0", "provision:0"])
637 message(CHANGE
,"Object %s was skipped" % dn
)
641 def gen_dn_index_hash(listMissing
):
642 """Generate a hash associating the DN to its creation order
644 :param listMissing: List of DN
645 :return: Hash with DN as keys and creation order as values"""
647 for i
in range(0, len(listMissing
)):
648 hash[str(listMissing
[i
]).lower()] = i
651 def add_deletedobj_containers(ref_samdb
, samdb
, names
):
652 """Add the object containter: CN=Deleted Objects
654 This function create the container for each partition that need one and
655 then reference the object into the root of the partition
657 :param ref_samdb: Ldb object representing the SAM db of the reference
659 :param samdb: Ldb object representing the SAM db of the upgraded provision
660 :param names: List of key provision parameters"""
663 wkoPrefix
= "B:32:18E2EA80684F11D2B9AA00C04F79F805"
664 partitions
= [str(names
.rootdn
), str(names
.configdn
)]
665 for part
in partitions
:
666 ref_delObjCnt
= ref_samdb
.search(expression
="(cn=Deleted Objects)",
667 base
=part
, scope
=SCOPE_SUBTREE
,
669 controls
=["show_deleted:0",
671 delObjCnt
= samdb
.search(expression
="(cn=Deleted Objects)",
672 base
=part
, scope
=SCOPE_SUBTREE
,
674 controls
=["show_deleted:0",
676 if len(ref_delObjCnt
) > len(delObjCnt
):
677 reference
= ref_samdb
.search(expression
="cn=Deleted Objects",
678 base
=part
, scope
=SCOPE_SUBTREE
,
679 controls
=["show_deleted:0",
682 delta
= samdb
.msg_diff(empty
, reference
[0])
684 delta
.dn
= Dn(samdb
, str(reference
[0]["dn"]))
685 for att
in attrNotCopied
:
688 modcontrols
= ["relax:0", "provision:0"]
689 samdb
.add(delta
, modcontrols
)
692 res
= samdb
.search(expression
="(objectClass=*)", base
=part
,
694 attrs
=["dn", "wellKnownObjects"])
696 targetWKO
= "%s:%s" % (wkoPrefix
, str(reference
[0]["dn"]))
700 wko
= res
[0]["wellKnownObjects"]
702 # The wellKnownObject that we want to add.
704 if str(o
) == targetWKO
:
706 listwko
.append(str(o
))
709 listwko
.append(targetWKO
)
712 delta
.dn
= Dn(samdb
, str(res
[0]["dn"]))
713 delta
["wellKnownObjects"] = MessageElement(listwko
,
718 def add_missing_entries(ref_samdb
, samdb
, names
, basedn
, list):
719 """Add the missing object whose DN is the list
721 The function add the object if the objects on which it depends are
724 :param ref_samdb: Ldb object representing the SAM db of the reference
726 :param samdb: Ldb object representing the SAM db of the upgraded
728 :param dn: DN of the object to be added
729 :param names: List of key provision parameters
730 :param basedn: DN of the partition to be updated
731 :param list: List of DN to be added in the upgraded provision"""
736 while(len(listDefered
) != len(listMissing
) and len(listDefered
) > 0):
738 listMissing
= listDefered
740 hashMissing
= gen_dn_index_hash(listMissing
)
741 for dn
in listMissing
:
742 ret
= add_missing_object(ref_samdb
, samdb
, dn
, names
, basedn
,
746 # DN can't be created because it depends on some
747 # other DN in the list
748 listDefered
.append(dn
)
750 if len(listDefered
) != 0:
751 raise ProvisioningError("Unable to insert missing elements: "
752 "circular references")
754 def handle_links(samdb
, att
, basedn
, dn
, value
, ref_value
, delta
):
755 """This function handle updates on links
757 :param samdb: An LDB object pointing to the updated provision
758 :param att: Attribute to update
759 :param basedn: The root DN of the provision
760 :param dn: The DN of the inspected object
761 :param value: The value of the attribute
762 :param ref_value: The value of this attribute in the reference provision
763 :param delta: The MessageElement object that will be applied for
764 transforming the current provision"""
766 res
= samdb
.search(expression
="dn=%s" % dn
, base
=basedn
,
767 controls
=["search_options:1:2", "reveal:1"],
775 newlinklist
.extend(value
)
779 # for w2k domain level the reveal won't reveal anything ...
780 # it means that we can readd links that were removed on purpose ...
781 # Also this function in fact just accept add not removal
783 for e
in res
[0][att
]:
784 if not hash.has_key(e
):
785 # We put in the blacklist all the element that are in the "revealed"
786 # result and not in the "standard" result
787 # This element are links that were removed before and so that
788 # we don't wan't to readd
792 if not blacklist
.has_key(e
) and not hash.has_key(e
):
793 newlinklist
.append(str(e
))
796 delta
[att
] = MessageElement(newlinklist
, FLAG_MOD_REPLACE
, att
)
801 msg_elt_flag_strs
= {
802 ldb
.FLAG_MOD_ADD
: "MOD_ADD",
803 ldb
.FLAG_MOD_REPLACE
: "MOD_REPLACE",
804 ldb
.FLAG_MOD_DELETE
: "MOD_DELETE" }
806 def checkKeepAttributeOldMtd(delta
, att
, reference
, current
,
808 """ Check if we should keep the attribute modification or not.
809 This function didn't use replicationMetadata to take a decision.
811 :param delta: A message diff object
812 :param att: An attribute
813 :param reference: A message object for the current entry comming from
814 the reference provision.
815 :param current: A message object for the current entry commin from
816 the current provision.
817 :param basedn: The DN of the partition
818 :param samdb: A ldb connection to the sam database of the current provision.
820 :return: The modified message diff.
822 # Old school way of handling things for pre alpha12 upgrade
828 for att
in list(delta
):
829 msgElt
= delta
.get(att
)
831 if att
== "nTSecurityDescriptor":
839 if not hashOverwrittenAtt
.has_key(att
):
840 if msgElt
.flags() != FLAG_MOD_ADD
:
841 if not handle_special_case(att
, delta
, reference
, current
,
842 False, basedn
, samdb
):
843 if opts
.debugchange
or opts
.debugall
:
845 dump_denied_change(dn
, att
,
846 msg_elt_flag_strs
[msgElt
.flags()],
847 current
[0][att
], reference
[0][att
])
849 dump_denied_change(dn
, att
,
850 msg_elt_flag_strs
[msgElt
.flags()],
851 current
[0][att
], None)
855 if hashOverwrittenAtt
.get(att
)&2**msgElt
.flags() :
857 elif hashOverwrittenAtt
.get(att
)==never
:
863 def checkKeepAttributeWithMetadata(delta
, att
, message
, reference
, current
,
864 hash_attr_usn
, basedn
, usns
, samdb
):
865 """ Check if we should keep the attribute modification or not
867 :param delta: A message diff object
868 :param att: An attribute
869 :param message: A function to print messages
870 :param reference: A message object for the current entry comming from
871 the reference provision.
872 :param current: A message object for the current entry commin from
873 the current provision.
874 :param hash_attr_usn: A dictionnary with attribute name as keys,
875 USN and invocation id as values.
876 :param basedn: The DN of the partition
877 :param usns: A dictionnary with invocation ID as keys and USN ranges
879 :param samdb: A ldb object pointing to the sam DB
881 :return: The modified message diff.
888 for att
in list(delta
):
889 if att
in ["dn", "objectSid"]:
893 # We have updated by provision usn information so let's exploit
894 # replMetadataProperties
895 if att
in forwardlinked
:
896 curval
= current
[0].get(att
, ())
897 refval
= reference
[0].get(att
, ())
898 handle_links(samdb
, att
, basedn
, current
[0]["dn"],
899 curval
, refval
, delta
)
902 if isFirst
and len(delta
.items())>1:
904 txt
= "%s\n" % (str(dn
))
906 if handle_special_case(att
, delta
, reference
, current
, True, None, None):
907 # This attribute is "complicated" to handle and handling
908 # was done in handle_special_case
912 if hash_attr_usn
.get(att
):
913 [attrUSN
, attInvId
] = hash_attr_usn
.get(att
)
916 # If it's a replicated attribute and we don't have any USN
917 # information about it. It means that we never saw it before
919 # If it is a replicated attribute but we are not master on it
920 # (ie. not initially added in the provision we masterize).
922 if isReplicated(att
):
925 message(CHANGE
, "Non replicated attribute %s changed" % att
)
928 if att
== "nTSecurityDescriptor":
929 cursd
= ndr_unpack(security
.descriptor
,
930 str(current
[0]["nTSecurityDescriptor"]))
931 cursddl
= cursd
.as_sddl(names
.domainsid
)
932 refsd
= ndr_unpack(security
.descriptor
,
933 str(reference
[0]["nTSecurityDescriptor"]))
934 refsddl
= refsd
.as_sddl(names
.domainsid
)
936 diff
= get_diff_sddls(refsddl
, cursddl
)
938 # FIXME find a way to have it only with huge huge verbose mode
939 # message(CHANGE, "%ssd are identical" % txt)
945 message(CHANGESD
, "%ssd are not identical:\n%s" % (txt
, diff
))
948 message(CHANGESD
, "But the SD has been changed by someonelse "\
949 "so it's impossible to know if the difference"\
950 " cames from the modification or from a previous bug")
951 dnNotToRecalculate
.append(str(dn
))
953 dnToRecalculate
.append(str(dn
))
957 # This attribute was last modified by another DC forget
959 message(CHANGE
, "%sAttribute: %s has been "
960 "created/modified/deleted by another DC. "
961 "Doing nothing" % (txt
, att
))
965 elif not usn_in_range(int(attrUSN
), usns
.get(attInvId
)):
966 message(CHANGE
, "%sAttribute: %s was not "
967 "created/modified/deleted during a "
968 "provision or upgradeprovision. Current "
969 "usn: %d. Doing nothing" % (txt
, att
,
975 if att
== "defaultSecurityDescriptor":
978 message(CHANGE
, "%sAttribute: %s will be modified"
979 "/deleted it was last modified "
980 "during a provision. Current usn: "
981 "%d" % (txt
, att
, attrUSN
))
984 message(CHANGE
, "%sAttribute: %s will be added because "
985 "it did not exist before" % (txt
, att
))
991 def update_present(ref_samdb
, samdb
, basedn
, listPresent
, usns
):
992 """ This function updates the object that are already present in the
995 :param ref_samdb: An LDB object pointing to the reference provision
996 :param samdb: An LDB object pointing to the updated provision
997 :param basedn: A string with the value of the base DN for the provision
999 :param listPresent: A list of object that is present in the provision
1000 :param usns: A list of USN range modified by previous provision and
1001 upgradeprovision grouped by invocation ID
1004 # This hash is meant to speedup lookup of attribute name from an oid,
1005 # it's for the replPropertyMetaData handling
1007 res
= samdb
.search(expression
="objectClass=attributeSchema", base
=basedn
,
1008 controls
=["search_options:1:2"], attrs
=["attributeID",
1012 strDisplay
= str(e
.get("lDAPDisplayName"))
1013 hash_oid_name
[str(e
.get("attributeID"))] = strDisplay
1015 msg
= "Unable to insert missing elements: circular references"
1016 raise ProvisioningError(msg
)
1019 controls
= ["search_options:1:2", "sd_flags:1:0"]
1020 if usns
is not None:
1021 message(CHANGE
, "Using replPropertyMetadata for change selection")
1022 for dn
in listPresent
:
1023 reference
= ref_samdb
.search(expression
="dn=%s" % (str(dn
)), base
=basedn
,
1024 scope
=SCOPE_SUBTREE
,
1026 current
= samdb
.search(expression
="dn=%s" % (str(dn
)), base
=basedn
,
1027 scope
=SCOPE_SUBTREE
, controls
=controls
)
1030 (str(current
[0].dn
) != str(reference
[0].dn
)) and
1031 (str(current
[0].dn
).upper() == str(reference
[0].dn
).upper())
1033 message(CHANGE
, "Names are the same except for the case. "
1034 "Renaming %s to %s" % (str(current
[0].dn
),
1035 str(reference
[0].dn
)))
1036 identic_rename(samdb
, reference
[0].dn
)
1037 current
= samdb
.search(expression
="dn=%s" % (str(dn
)), base
=basedn
,
1038 scope
=SCOPE_SUBTREE
,
1041 delta
= samdb
.msg_diff(current
[0], reference
[0])
1043 for att
in backlinked
:
1046 for att
in attrNotCopied
:
1049 delta
.remove("name")
1051 if len(delta
.items()) == 1:
1054 if len(delta
.items()) > 1 and usns
is not None:
1055 # Fetch the replPropertyMetaData
1056 res
= samdb
.search(expression
="dn=%s" % (str(dn
)), base
=basedn
,
1057 scope
=SCOPE_SUBTREE
, controls
=controls
,
1058 attrs
=["replPropertyMetaData"])
1059 ctr
= ndr_unpack(drsblobs
.replPropertyMetaDataBlob
,
1060 str(res
[0]["replPropertyMetaData"])).ctr
1064 # We put in this hash only modification
1065 # made on the current host
1066 att
= hash_oid_name
[samdb
.get_oid_from_attid(o
.attid
)]
1067 if str(o
.originating_invocation_id
) in usns
.keys():
1068 hash_attr_usn
[att
] = [o
.originating_usn
, str(o
.originating_invocation_id
)]
1070 hash_attr_usn
[att
] = [-1, None]
1072 if usns
is not None:
1073 delta
= checkKeepAttributeWithMetadata(delta
, att
, message
, reference
,
1074 current
, hash_attr_usn
,
1075 basedn
, usns
, samdb
)
1077 delta
= checkKeepAttributeOldMtd(delta
, att
, reference
, current
, basedn
, samdb
)
1080 if len(delta
.items()) >1:
1081 # Skip dn as the value is not really changed ...
1082 attributes
=", ".join(delta
.keys()[1:])
1084 relaxedatt
= ['iscriticalsystemobject', 'grouptype']
1085 # Let's try to reduce as much as possible the use of relax control
1086 for attr
in delta
.keys():
1087 if attr
.lower() in relaxedatt
:
1088 modcontrols
= ["relax:0", "provision:0"]
1089 message(CHANGE
, "%s is different from the reference one, changed"
1090 " attributes: %s\n" % (dn
, attributes
))
1092 samdb
.modify(delta
, modcontrols
)
1095 def reload_full_schema(samdb
, names
):
1096 """Load the updated schema with all the new and existing classes
1099 :param samdb: An LDB object connected to the sam.ldb of the update
1101 :param names: List of key provision parameters
1104 current
= samdb
.search(expression
="objectClass=*", base
=str(names
.schemadn
),
1105 scope
=SCOPE_SUBTREE
)
1110 schema_ldif
+= samdb
.write_ldif(ent
, ldb
.CHANGETYPE_NONE
)
1112 prefixmap_data
= open(setup_path("prefixMap.txt"), 'r').read()
1113 prefixmap_data
= b64encode(prefixmap_data
)
1115 # We don't actually add this ldif, just parse it
1116 prefixmap_ldif
= "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap_data
1118 dsdb
._dsdb
_set
_schema
_from
_ldif
(samdb
, prefixmap_ldif
, schema_ldif
)
1121 def update_partition(ref_samdb
, samdb
, basedn
, names
, schema
, provisionUSNs
, prereloadfunc
):
1122 """Check differences between the reference provision and the upgraded one.
1124 It looks for all objects which base DN is name.
1126 This function will also add the missing object and update existing object
1127 to add or remove attributes that were missing.
1129 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1131 :param samdb: An LDB object connected to the sam.ldb of the update
1133 :param basedn: String value of the DN of the partition
1134 :param names: List of key provision parameters
1135 :param schema: A Schema object
1136 :param provisionUSNs: A dictionnary with range of USN modified during provision
1137 or upgradeprovision. Ranges are grouped by invocationID.
1138 :param prereloadfunc: A function that must be executed just before the reload
1149 # Connect to the reference provision and get all the attribute in the
1150 # partition referred by name
1151 reference
= ref_samdb
.search(expression
="objectClass=*", base
=basedn
,
1152 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1153 controls
=["search_options:1:2"])
1155 current
= samdb
.search(expression
="objectClass=*", base
=basedn
,
1156 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1157 controls
=["search_options:1:2"])
1158 # Create a hash for speeding the search of new object
1159 for i
in range(0, len(reference
)):
1160 hash_new
[str(reference
[i
]["dn"]).lower()] = reference
[i
]["dn"]
1162 # Create a hash for speeding the search of existing object in the
1164 for i
in range(0, len(current
)):
1165 hash[str(current
[i
]["dn"]).lower()] = current
[i
]["dn"]
1168 for k
in hash_new
.keys():
1169 if not hash.has_key(k
):
1170 if not str(hash_new
[k
]) == "CN=Deleted Objects, %s" % names
.rootdn
:
1171 listMissing
.append(hash_new
[k
])
1173 listPresent
.append(hash_new
[k
])
1175 # Sort the missing object in order to have object of the lowest level
1176 # first (which can be containers for higher level objects)
1177 listMissing
.sort(dn_sort
)
1178 listPresent
.sort(dn_sort
)
1180 # The following lines is to load the up to
1181 # date schema into our current LDB
1182 # a complete schema is needed as the insertion of attributes
1183 # and class is done against it
1184 # and the schema is self validated
1185 samdb
.set_schema(schema
)
1187 message(SIMPLE
, "There are %d missing objects" % (len(listMissing
)))
1188 add_deletedobj_containers(ref_samdb
, samdb
, names
)
1190 add_missing_entries(ref_samdb
, samdb
, names
, basedn
, listMissing
)
1193 message(SIMPLE
, "Reloading a merged schema, which might trigger "
1194 "reindexing so please be patient")
1195 reload_full_schema(samdb
, names
)
1196 message(SIMPLE
, "Schema reloaded!")
1198 changed
= update_present(ref_samdb
, samdb
, basedn
, listPresent
,
1200 message(SIMPLE
, "There are %d changed objects" % (changed
))
1203 except StandardError, err
:
1204 message(ERROR
, "Exception during upgrade of samdb:")
1205 (typ
, val
, tb
) = sys
.exc_info()
1206 traceback
.print_exception(typ
, val
, tb
)
1210 def check_updated_sd(ref_sam
, cur_sam
, names
):
1211 """Check if the security descriptor in the upgraded provision are the same
1214 :param ref_sam: A LDB object connected to the sam.ldb file used as
1215 the reference provision
1216 :param cur_sam: A LDB object connected to the sam.ldb file used as
1218 :param names: List of key provision parameters"""
1219 reference
= ref_sam
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1220 scope
=SCOPE_SUBTREE
,
1221 attrs
=["dn", "nTSecurityDescriptor"],
1222 controls
=["search_options:1:2"])
1223 current
= cur_sam
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1224 scope
=SCOPE_SUBTREE
,
1225 attrs
=["dn", "nTSecurityDescriptor"],
1226 controls
=["search_options:1:2"])
1228 for i
in range(0, len(reference
)):
1229 refsd
= ndr_unpack(security
.descriptor
,
1230 str(reference
[i
]["nTSecurityDescriptor"]))
1231 hash[str(reference
[i
]["dn"]).lower()] = refsd
.as_sddl(names
.domainsid
)
1234 for i
in range(0, len(current
)):
1235 key
= str(current
[i
]["dn"]).lower()
1236 if hash.has_key(key
):
1237 cursd
= ndr_unpack(security
.descriptor
,
1238 str(current
[i
]["nTSecurityDescriptor"]))
1239 sddl
= cursd
.as_sddl(names
.domainsid
)
1240 if sddl
!= hash[key
]:
1241 txt
= get_diff_sddls(hash[key
], sddl
, False)
1243 message(CHANGESD
, "On object %s ACL is different"
1244 " \n%s" % (current
[i
]["dn"], txt
))
1248 def fix_partition_sd(samdb
, names
):
1249 """This function fix the SD for partition containers (basedn, configdn, ...)
1250 This is needed because some provision use to have broken SD on containers
1252 :param samdb: An LDB object pointing to the sam of the current provision
1253 :param names: A list of key provision parameters
1255 alwaysRecalculate
= False
1256 if len(dnToRecalculate
) == 0 and len(dnNotToRecalculate
) == 0:
1257 alwaysRecalculate
= True
1260 # NC's DN can't be both in dnToRecalculate and dnNotToRecalculate
1261 # First update the SD for the rootdn
1262 if alwaysRecalculate
or str(names
.rootdn
) in dnToRecalculate
:
1264 delta
.dn
= Dn(samdb
, str(names
.rootdn
))
1265 descr
= get_domain_descriptor(names
.domainsid
)
1266 delta
["nTSecurityDescriptor"] = MessageElement(descr
, FLAG_MOD_REPLACE
,
1267 "nTSecurityDescriptor")
1270 # Then the config dn
1271 if alwaysRecalculate
or str(names
.configdn
) in dnToRecalculate
:
1273 delta
.dn
= Dn(samdb
, str(names
.configdn
))
1274 descr
= get_config_descriptor(names
.domainsid
)
1275 delta
["nTSecurityDescriptor"] = MessageElement(descr
, FLAG_MOD_REPLACE
,
1276 "nTSecurityDescriptor" )
1279 # Then the schema dn
1280 if alwaysRecalculate
or str(names
.schemadn
) in dnToRecalculate
:
1282 delta
.dn
= Dn(samdb
, str(names
.schemadn
))
1283 descr
= get_schema_descriptor(names
.domainsid
)
1284 delta
["nTSecurityDescriptor"] = MessageElement(descr
, FLAG_MOD_REPLACE
,
1285 "nTSecurityDescriptor" )
1288 def rebuild_sd(samdb
, names
):
1289 """Rebuild security descriptor of the current provision from scratch
1291 During the different pre release of samba4 security descriptors (SD)
1292 were notarly broken (up to alpha11 included)
1293 This function allow to get them back in order, this function make the
1294 assumption that nobody has modified manualy an SD
1295 and so SD can be safely recalculated from scratch to get them right.
1297 :param names: List of key provision parameters"""
1299 fix_partition_sd(samdb
, names
)
1301 # List of namming contexts
1302 listNC
= [str(names
.rootdn
), str(names
.configdn
), str(names
.schemadn
)]
1304 if len(dnToRecalculate
) == 0:
1305 res
= samdb
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1306 scope
=SCOPE_SUBTREE
, attrs
=["dn", "whenCreated"],
1307 controls
=["search_options:1:2"])
1309 hash[str(obj
["dn"])] = obj
["whenCreated"]
1311 for dn
in dnToRecalculate
:
1312 if hash.has_key(dn
):
1314 # fetch each dn to recalculate and their child within the same partition
1315 res
= samdb
.search(expression
="objectClass=*", base
=dn
,
1316 scope
=SCOPE_SUBTREE
, attrs
=["dn", "whenCreated"])
1318 hash[str(obj
["dn"])] = obj
["whenCreated"]
1320 listKeys
= list(set(hash.keys()))
1321 listKeys
.sort(dn_sort
)
1323 if len(dnToRecalculate
) != 0:
1324 message(CHANGESD
, "%d DNs have been marked as needed to be recalculated"\
1325 ", recalculating %d due to inheritance"
1326 % (len(dnToRecalculate
), len(listKeys
)))
1328 for key
in listKeys
:
1329 if (key
in listNC
or
1330 key
in dnNotToRecalculate
):
1333 delta
.dn
= Dn(samdb
, key
)
1335 delta
["whenCreated"] = MessageElement(hash[key
], FLAG_MOD_REPLACE
,
1337 samdb
.modify(delta
, ["recalculate_sd:0","relax:0"])
1339 samdb
.transaction_cancel()
1340 res
= samdb
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1341 scope
=SCOPE_SUBTREE
,
1342 attrs
=["dn", "nTSecurityDescriptor"],
1343 controls
=["search_options:1:2"])
1344 badsd
= ndr_unpack(security
.descriptor
,
1345 str(res
[0]["nTSecurityDescriptor"]))
1346 message(ERROR
, "On %s bad stuff %s" % (str(delta
.dn
),badsd
.as_sddl(names
.domainsid
)))
1349 def removeProvisionUSN(samdb
):
1350 attrs
= [samba
.provision
.LAST_PROVISION_USN_ATTRIBUTE
, "dn"]
1351 entry
= samdb
.search(expression
="dn=@PROVISION", base
= "",
1352 scope
=SCOPE_SUBTREE
,
1355 empty
.dn
= entry
[0].dn
1356 delta
= samdb
.msg_diff(entry
[0], empty
)
1358 delta
.dn
= entry
[0].dn
1361 def remove_stored_generated_attrs(paths
, creds
, session
, lp
):
1362 """Remove previously stored constructed attributes
1364 :param paths: List of paths for different provision objects
1365 from the upgraded provision
1366 :param creds: A credential object
1367 :param session: A session object
1368 :param lp: A line parser object
1369 :return: An associative array whose key are the different constructed
1370 attributes and the value the dn where this attributes were found.
1374 def simple_update_basesamdb(newpaths
, paths
, names
):
1375 """Update the provision container db: sam.ldb
1376 This function is aimed at very old provision (before alpha9)
1378 :param newpaths: List of paths for different provision objects
1379 from the reference provision
1380 :param paths: List of paths for different provision objects
1381 from the upgraded provision
1382 :param names: List of key provision parameters"""
1384 message(SIMPLE
, "Copy samdb")
1385 shutil
.copy(newpaths
.samdb
, paths
.samdb
)
1387 message(SIMPLE
, "Update partitions filename if needed")
1388 schemaldb
= os
.path
.join(paths
.private_dir
, "schema.ldb")
1389 configldb
= os
.path
.join(paths
.private_dir
, "configuration.ldb")
1390 usersldb
= os
.path
.join(paths
.private_dir
, "users.ldb")
1391 samldbdir
= os
.path
.join(paths
.private_dir
, "sam.ldb.d")
1393 if not os
.path
.isdir(samldbdir
):
1395 os
.chmod(samldbdir
, 0700)
1396 if os
.path
.isfile(schemaldb
):
1397 shutil
.copy(schemaldb
, os
.path
.join(samldbdir
,
1398 "%s.ldb"%str
(names
.schemadn
).upper()))
1399 os
.remove(schemaldb
)
1400 if os
.path
.isfile(usersldb
):
1401 shutil
.copy(usersldb
, os
.path
.join(samldbdir
,
1402 "%s.ldb"%str
(names
.rootdn
).upper()))
1404 if os
.path
.isfile(configldb
):
1405 shutil
.copy(configldb
, os
.path
.join(samldbdir
,
1406 "%s.ldb"%str
(names
.configdn
).upper()))
1407 os
.remove(configldb
)
1410 def update_privilege(ref_private_path
, cur_private_path
):
1411 """Update the privilege database
1413 :param ref_private_path: Path to the private directory of the reference
1415 :param cur_private_path: Path to the private directory of the current
1416 (and to be updated) provision."""
1417 message(SIMPLE
, "Copy privilege")
1418 shutil
.copy(os
.path
.join(ref_private_path
, "privilege.ldb"),
1419 os
.path
.join(cur_private_path
, "privilege.ldb"))
1422 def update_samdb(ref_samdb
, samdb
, names
, provisionUSNs
, schema
, prereloadfunc
):
1423 """Upgrade the SAM DB contents for all the provision partitions
1425 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1427 :param samdb: An LDB object connected to the sam.ldb of the update
1429 :param names: List of key provision parameters
1430 :param provisionUSNs: A dictionnary with range of USN modified during provision
1431 or upgradeprovision. Ranges are grouped by invocationID.
1432 :param schema: A Schema object that represent the schema of the provision
1433 :param prereloadfunc: A function that must be executed just before the reload
1437 message(SIMPLE
, "Starting update of samdb")
1438 ret
= update_partition(ref_samdb
, samdb
, str(names
.rootdn
), names
,
1439 schema
, provisionUSNs
, prereloadfunc
)
1441 message(SIMPLE
, "Update of samdb finished")
1444 message(SIMPLE
, "Update failed")
1448 def copyxattrs(dir, refdir
):
1449 """ Copy owner, groups, extended ACL and NT acls from
1450 a reference dir to a destination dir
1452 Both dir are supposed to hold the same files
1453 :param dir: Destination dir
1454 :param refdir: Reference directory"""
1457 for root
, dirs
, files
in os
.walk(dir, topdown
=True):
1459 subdir
=root
[len(dir):]
1460 ref
= os
.path
.join("%s%s" % (refdir
, subdir
), name
)
1461 statsinfo
= os
.stat(ref
)
1462 tgt
= os
.path
.join(root
, name
)
1465 os
.chown(tgt
, statsinfo
.st_uid
, statsinfo
.st_gid
)
1466 # Get the xattr attributes if any
1468 attribute
= samba
.xattr_native
.wrap_getxattr(ref
,
1469 xattr
.XATTR_NTACL_NAME
)
1470 samba
.xattr_native
.wrap_setxattr(tgt
,
1471 xattr
.XATTR_NTACL_NAME
,
1475 attribute
= samba
.xattr_native
.wrap_getxattr(ref
,
1476 "system.posix_acl_access")
1477 samba
.xattr_native
.wrap_setxattr(tgt
,
1478 "system.posix_acl_access",
1483 subdir
=root
[len(dir):]
1484 ref
= os
.path
.join("%s%s" % (refdir
, subdir
), name
)
1485 statsinfo
= os
.stat(ref
)
1486 tgt
= os
.path
.join(root
, name
)
1488 os
.chown(os
.path
.join(root
, name
), statsinfo
.st_uid
,
1491 attribute
= samba
.xattr_native
.wrap_getxattr(ref
,
1492 xattr
.XATTR_NTACL_NAME
)
1493 samba
.xattr_native
.wrap_setxattr(tgt
,
1494 xattr
.XATTR_NTACL_NAME
,
1498 attribute
= samba
.xattr_native
.wrap_getxattr(ref
,
1499 "system.posix_acl_access")
1500 samba
.xattr_native
.wrap_setxattr(tgt
,
1501 "system.posix_acl_access",
1508 def backup_provision(paths
, dir):
1509 """This function backup the provision files so that a rollback
1512 :param paths: Paths to different objects
1513 :param dir: Directory where to store the backup
1516 shutil
.copytree(paths
.sysvol
, os
.path
.join(dir, "sysvol"))
1517 copyxattrs(os
.path
.join(dir, "sysvol"), paths
.sysvol
)
1518 shutil
.copy2(paths
.samdb
, dir)
1519 shutil
.copy2(paths
.secrets
, dir)
1520 shutil
.copy2(paths
.idmapdb
, dir)
1521 shutil
.copy2(paths
.privilege
, dir)
1522 if os
.path
.isfile(os
.path
.join(paths
.private_dir
,"eadb.tdb")):
1523 shutil
.copy2(os
.path
.join(paths
.private_dir
,"eadb.tdb"), dir)
1524 shutil
.copy2(paths
.smbconf
, dir)
1525 shutil
.copy2(os
.path
.join(paths
.private_dir
,"secrets.keytab"), dir)
1527 samldbdir
= os
.path
.join(paths
.private_dir
, "sam.ldb.d")
1528 if not os
.path
.isdir(samldbdir
):
1529 samldbdir
= paths
.private_dir
1530 schemaldb
= os
.path
.join(paths
.private_dir
, "schema.ldb")
1531 configldb
= os
.path
.join(paths
.private_dir
, "configuration.ldb")
1532 usersldb
= os
.path
.join(paths
.private_dir
, "users.ldb")
1533 shutil
.copy2(schemaldb
, dir)
1534 shutil
.copy2(usersldb
, dir)
1535 shutil
.copy2(configldb
, dir)
1537 shutil
.copytree(samldbdir
, os
.path
.join(dir, "sam.ldb.d"))
1542 def sync_calculated_attributes(samdb
, names
):
1543 """Synchronize attributes used for constructed ones, with the
1544 old constructed that were stored in the database.
1546 This apply for instance to msds-keyversionnumber that was
1547 stored and that is now constructed from replpropertymetadata.
1549 :param samdb: An LDB object attached to the currently upgraded samdb
1550 :param names: Various key parameter about current provision.
1552 listAttrs
= ["msDs-KeyVersionNumber"]
1553 hash = search_constructed_attrs_stored(samdb
, names
.rootdn
, listAttrs
)
1554 if hash.has_key("msDs-KeyVersionNumber"):
1555 increment_calculated_keyversion_number(samdb
, names
.rootdn
,
1556 hash["msDs-KeyVersionNumber"])
1558 # Synopsis for updateprovision
1559 # 1) get path related to provision to be update (called current)
1560 # 2) open current provision ldbs
1561 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1563 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1564 # by either upgradeprovision or provision
1565 # 5) creation of a new provision the latest version of provision script
1566 # (called reference)
1567 # 6) get reference provision paths
1568 # 7) open reference provision ldbs
1569 # 8) setup helpers data that will help the update process
1570 # 9) update the privilege ldb by copying the one of referecence provision to
1571 # the current provision
1572 # 10)get the oemInfo field, this field contains information about the different
1573 # provision that have been done
1574 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1575 # integer) or none of this the following things are done
1576 # A) When alpha9 or alphaxx is present
1577 # The base sam.ldb file is updated by looking at the difference between
1578 # referrence one and the current one. Everything is copied with the
1579 # exception of lastProvisionUSN attributes.
1580 # B) Other case (it reflect that that provision was done before alpha9)
1581 # The base sam.ldb of the reference provision is copied over
1582 # the current one, if necessary ldb related to partitions are moved
1584 # The highest used USN is fetched so that changed by upgradeprovision
1585 # usn can be tracked
1586 # 12)A Schema object is created, it will be used to provide a complete
1587 # schema to current provision during update (as the schema of the
1588 # current provision might not be complete and so won't allow some
1589 # object to be created)
1590 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1591 # 14)The secrets db is updated by pull all the difference from the reference
1592 # provision into the current provision
1593 # 15)As the previous step has most probably modified the password stored in
1594 # in secret for the current DC, a new password is generated,
1595 # the kvno is bumped and the entry in samdb is also updated
1596 # 16)For current provision older than alpha9, we must fix the SD a little bit
1597 # administrator to update them because SD used to be generated with the
1598 # system account before alpha9.
1599 # 17)The highest usn modified so far is searched in the database it will be
1600 # the upper limit for usn modified during provision.
1601 # This is done before potential SD recalculation because we do not want
1602 # SD modified during recalculation to be marked as modified during provision
1603 # (and so possibly remplaced at next upgradeprovision)
1604 # 18)Rebuilt SD if the flag indicate to do so
1605 # 19)Check difference between SD of reference provision and those of the
1606 # current provision. The check is done by getting the sddl representation
1607 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1608 # Each part is verified separetly, for dacl and sacl ACL is splited into
1609 # ACEs and each ACE is verified separately (so that a permutation in ACE
1610 # didn't raise as an error).
1611 # 20)The oemInfo field is updated to add information about the fact that the
1612 # provision has been updated by the upgradeprovision version xxx
1613 # (the version is the one obtained when starting samba with the --version
1615 # 21)Check if the current provision has all the settings needed for dynamic
1616 # DNS update to work (that is to say the provision is newer than
1617 # january 2010). If not dns configuration file from reference provision
1618 # are copied in a sub folder and the administrator is invited to
1619 # do what is needed.
1620 # 22)If the lastProvisionUSN attribute was present it is updated to add
1621 # the range of usns modified by the current upgradeprovision
1624 # About updating the sam DB
1625 # The update takes place in update_partition function
1626 # This function read both current and reference provision and list all
1627 # the available DN of objects
1628 # If the string representation of a DN in reference provision is
1629 # equal to the string representation of a DN in current provision
1630 # (without taking care of case) then the object is flaged as being
1631 # present. If the object is not present in current provision the object
1632 # is being flaged as missing in current provision. Object present in current
1633 # provision but not in reference provision are ignored.
1634 # Once the list of objects present and missing is done, the deleted object
1635 # containers are created in the differents partitions (if missing)
1637 # Then the function add_missing_entries is called
1638 # This function will go through the list of missing entries by calling
1639 # add_missing_object for the given object. If this function returns 0
1640 # it means that the object needs some other object in order to be created
1641 # The object is reappended at the end of the list to be created later
1642 # (and preferably after all the needed object have been created)
1643 # The function keeps on looping on the list of object to be created until
1644 # it's empty or that the number of defered creation is equal to the number
1645 # of object that still needs to be created.
1647 # The function add_missing_object will first check if the object can be created.
1648 # That is to say that it didn't depends other not yet created objects
1649 # If requisit can't be fullfilled it exists with 0
1650 # Then it will try to create the missing entry by creating doing
1651 # an ldb_message_diff between the object in the reference provision and
1653 # This resulting object is filtered to remove all the back link attribute
1654 # (ie. memberOf) as they will be created by the other linked object (ie.
1655 # the one with the member attribute)
1656 # All attributes specified in the attrNotCopied array are
1657 # also removed it's most of the time generated attributes
1659 # After missing entries have been added the update_partition function will
1660 # take care of object that exist but that need some update.
1661 # In order to do so the function update_present is called with the list
1662 # of object that are present in both provision and that might need an update.
1664 # This function handle first case mismatch so that the DN in the current
1665 # provision have the same case as in reference provision
1667 # It will then construct an associative array consiting of attributes as
1668 # key and invocationid as value( if the originating invocation id is
1669 # different from the invocation id of the current DC the value is -1 instead).
1671 # If the range of provision modified attributes is present, the function will
1672 # use the replMetadataProperty update method which is the following:
1673 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1674 # creationTime, msDs-KeyVersionNumber, oEMInformation
1675 # Check for each attribute if its usn is within one of the modified by
1676 # provision range and if its originating id is the invocation id of the
1677 # current DC, then validate the update from reference to current.
1678 # If not or if there is no replMetatdataProperty for this attribute then we
1680 # Otherwise (case the range of provision modified attribute is not present) it
1681 # use the following process:
1682 # All attributes that need to be added are accepted at the exeption of those
1683 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1684 # correct flags specified.
1685 # For attributes that need to be modified or removed, a check is performed
1686 # in OverwrittenAtt, if the attribute is present and the modification flag
1687 # (remove, delete) is one of those listed for this attribute then modification
1688 # is accepted. For complicated handling of attribute update, the control is passed
1689 # to handle_special_case
1693 if __name__
== '__main__':
1694 global defSDmodified
1695 defSDmodified
= False
1696 # From here start the big steps of the program
1697 # 1) First get files paths
1698 paths
= get_paths(param
, smbconf
=smbconf
)
1699 # Get ldbs with the system session, it is needed for searching
1700 # provision parameters
1701 session
= system_session()
1703 # This variable will hold the last provision USN once if it exists.
1706 ldbs
= get_ldbs(paths
, creds
, session
, lp
)
1707 backupdir
= tempfile
.mkdtemp(dir=paths
.private_dir
,
1708 prefix
="backupprovision")
1709 backup_provision(paths
, backupdir
)
1711 ldbs
.startTransactions()
1713 # 3) Guess all the needed names (variables in fact) from the current
1715 names
= find_provision_key_parameters(ldbs
.sam
, ldbs
.secrets
, ldbs
.idmap
,
1718 lastProvisionUSNs
= get_last_provision_usn(ldbs
.sam
)
1719 if lastProvisionUSNs
is not None:
1721 for k
in lastProvisionUSNs
.keys():
1722 for r
in lastProvisionUSNs
[k
]:
1726 "Find last provision USN, %d invocation(s) for a total of %d ranges" % \
1727 (len(lastProvisionUSNs
.keys()), v
/2 ))
1729 if lastProvisionUSNs
.get("default") != None:
1730 message(CHANGE
, "Old style for usn ranges used")
1731 lastProvisionUSNs
[str(names
.invocation
)] = lastProvisionUSNs
["default"]
1732 del lastProvisionUSNs
["default"]
1734 message(SIMPLE
, "Your provision lacks provision range information")
1735 if confirm("Do you want to run findprovisionusnranges to try to find them ?", False):
1736 ldbs
.groupedRollback()
1737 os
.system("%s %s %s %s %s" % (os
.path
.join(os
.path
.dirname(sys
.argv
[0]),
1738 "findprovisionusnranges"),
1743 message(SIMPLE
, "Once you applied/adapted the change(s) please restart the upgradeprovision script")
1746 # Objects will be created with the admin session
1747 # (not anymore system session)
1748 adm_session
= admin_session(lp
, str(names
.domainsid
))
1749 # So we reget handle on objects
1750 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1751 if not opts
.fixntacl
:
1752 if not sanitychecks(ldbs
.sam
, names
):
1753 message(SIMPLE
, "Sanity checks for the upgrade have failed. "
1754 "Check the messages and correct the errors "
1755 "before rerunning upgradeprovision")
1756 ldbs
.groupedRollback()
1759 # Let's see provision parameters
1760 print_provision_key_parameters(names
)
1762 # 5) With all this information let's create a fresh new provision used as
1764 message(SIMPLE
, "Creating a reference provision")
1765 provisiondir
= tempfile
.mkdtemp(dir=paths
.private_dir
,
1766 prefix
="referenceprovision")
1767 newprovision(names
, creds
, session
, smbconf
, provisiondir
,
1772 # We need to get a list of object which SD is directly computed from
1773 # defaultSecurityDescriptor.
1774 # This will allow us to know which object we can rebuild the SD in case
1775 # of change of the parent's SD or of the defaultSD.
1776 # Get file paths of this new provision
1777 newpaths
= get_paths(param
, targetdir
=provisiondir
)
1778 new_ldbs
= get_ldbs(newpaths
, creds
, session
, lp
)
1779 new_ldbs
.startTransactions()
1781 # 8) Populate some associative array to ease the update process
1782 # List of attribute which are link and backlink
1783 populate_links(new_ldbs
.sam
, names
.schemadn
)
1784 # List of attribute with ASN DN synthax)
1785 populate_dnsyntax(new_ldbs
.sam
, names
.schemadn
)
1787 update_privilege(newpaths
.private_dir
, paths
.private_dir
)
1789 oem
= getOEMInfo(ldbs
.sam
, str(names
.rootdn
))
1790 # Do some modification on sam.ldb
1791 ldbs
.groupedCommit()
1792 new_ldbs
.groupedCommit()
1795 if re
.match(".*alpha((9)|(\d\d+)).*", str(oem
)):
1797 # Starting from alpha9 we can consider that the structure is quite ok
1798 # and that we should do only dela
1799 deltaattr
= delta_update_basesamdb(newpaths
.samdb
,
1807 simple_update_basesamdb(newpaths
, paths
, names
)
1808 ldbs
= get_ldbs(paths
, creds
, session
, lp
)
1809 removeProvisionUSN(ldbs
.sam
)
1811 ldbs
.startTransactions()
1812 minUSN
= int(str(get_max_usn(ldbs
.sam
, str(names
.rootdn
)))) + 1
1813 new_ldbs
.startTransactions()
1816 schema
= Schema(names
.domainsid
, schemadn
=str(names
.schemadn
))
1817 # We create a closure that will be invoked just before schema reload
1818 def schemareloadclosure():
1819 basesam
= Ldb(paths
.samdb
, session_info
=session
, credentials
=creds
, lp
=lp
,
1820 options
=["modules:"])
1822 if deltaattr
is not None and len(deltaattr
) > 1:
1825 deltaattr
.remove("dn")
1826 for att
in deltaattr
:
1827 if att
.lower() == "dn":
1829 if (deltaattr
.get(att
) is not None
1830 and deltaattr
.get(att
).flags() != FLAG_MOD_ADD
):
1832 elif deltaattr
.get(att
) is None:
1835 message(CHANGE
, "Applying delta to @ATTRIBUTES")
1836 deltaattr
.dn
= ldb
.Dn(basesam
, "@ATTRIBUTES")
1837 basesam
.modify(deltaattr
)
1839 message(CHANGE
, "Not applying delta to @ATTRIBUTES because "
1840 "there is not only add")
1843 if not update_samdb(new_ldbs
.sam
, ldbs
.sam
, names
, lastProvisionUSNs
,
1844 schema
, schemareloadclosure
):
1845 message(SIMPLE
, "Rolling back all changes. Check the cause"
1847 message(SIMPLE
, "Your system is as it was before the upgrade")
1848 ldbs
.groupedRollback()
1849 new_ldbs
.groupedRollback()
1850 shutil
.rmtree(provisiondir
)
1853 # Try to reapply the change also when we do not change the sam
1854 # as the delta_upgrade
1855 schemareloadclosure()
1856 sync_calculated_attributes(ldbs
.sam
, names
)
1857 res
= ldbs
.sam
.search(expression
="(samaccountname=dns)",
1858 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1859 controls
=["search_options:1:2"])
1861 message(SIMPLE
, "You still have the old DNS object for managing "
1862 "dynamic DNS, but you didn't supply --full so "
1863 "a correct update can't be done")
1864 ldbs
.groupedRollback()
1865 new_ldbs
.groupedRollback()
1866 shutil
.rmtree(provisiondir
)
1869 update_secrets(new_ldbs
.secrets
, ldbs
.secrets
, message
)
1871 res
= ldbs
.sam
.search(expression
="(samaccountname=dns)",
1872 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1873 controls
=["search_options:1:2"])
1876 ldbs
.sam
.delete(res
[0]["dn"])
1877 res2
= ldbs
.secrets
.search(expression
="(samaccountname=dns)",
1878 scope
=SCOPE_SUBTREE
, attrs
=["dn"])
1879 update_dns_account_password(ldbs
.sam
, ldbs
.secrets
, names
)
1880 message(SIMPLE
, "IMPORTANT!!! "
1881 "If you were using Dynamic DNS before you need "
1882 "to update your configuration, so that the "
1883 "tkey-gssapi-credential has the following value: "
1884 "DNS/%s.%s" % (names
.netbiosname
.lower(),
1885 names
.realm
.lower()))
1887 message(SIMPLE
, "Update machine account")
1888 update_machine_account_password(ldbs
.sam
, ldbs
.secrets
, names
)
1890 dnToRecalculate
.sort(dn_sort
)
1891 # 16) SD should be created with admin but as some previous acl were so wrong
1892 # that admin can't modify them we have first to recreate them with the good
1893 # form but with system account and then give the ownership to admin ...
1894 if str(oem
) != "" and not re
.match(r
'.*alpha(9|\d\d+)', str(oem
)):
1895 message(SIMPLE
, "Fixing very old provision SD")
1896 rebuild_sd(ldbs
.sam
, names
)
1898 # We calculate the max USN before recalculating the SD because we might
1899 # touch object that have been modified after a provision and we do not
1900 # want that the next upgradeprovision thinks that it has a green light
1904 maxUSN
= get_max_usn(ldbs
.sam
, str(names
.rootdn
))
1906 # 18) We rebuild SD if a we have a list of DN to recalculate or if the
1907 # defSDmodified is set.
1908 if defSDmodified
or len(dnToRecalculate
) >0:
1909 message(SIMPLE
, "Some defaultSecurityDescriptors and/or"
1910 "securityDescriptor have changed, recalculating SD ")
1911 ldbs
.sam
.set_session_info(adm_session
)
1912 rebuild_sd(ldbs
.sam
, names
)
1915 # Now we are quite confident in the recalculate process of the SD, we make
1916 # it optional. And we don't do it if there is DN that we must touch
1917 # as we are assured that on this DNs we will have differences !
1918 # Also the check must be done in a clever way as for the moment we just
1920 if len(dnNotToRecalculate
) == 0 and (opts
.debugchangesd
or opts
.debugall
):
1921 message(CHANGESD
, "Checking recalculated SDs")
1922 check_updated_sd(new_ldbs
.sam
, ldbs
.sam
, names
)
1925 updateOEMInfo(ldbs
.sam
, str(names
.rootdn
))
1927 check_for_DNS(newpaths
.private_dir
, paths
.private_dir
)
1929 if lastProvisionUSNs
is not None:
1930 update_provision_usn(ldbs
.sam
, minUSN
, maxUSN
, names
.invocation
)
1931 if opts
.full
and (names
.policyid
is None or names
.policyid_dc
is None):
1932 update_policyids(names
, ldbs
.sam
)
1933 if opts
.full
or opts
.resetfileacl
or opts
.fixntacl
:
1935 update_gpo(paths
, ldbs
.sam
, names
, lp
, message
, 1)
1936 except ProvisioningError
, e
:
1937 message(ERROR
, "The policy for domain controller is missing. "
1938 "You should restart upgradeprovision with --full")
1940 message(ERROR
, "Setting ACL not supported on your filesystem")
1943 update_gpo(paths
, ldbs
.sam
, names
, lp
, message
, 0)
1944 except ProvisioningError
, e
:
1945 message(ERROR
, "The policy for domain controller is missing. "
1946 "You should restart upgradeprovision with --full")
1947 if not opts
.fixntacl
:
1948 ldbs
.groupedCommit()
1949 new_ldbs
.groupedCommit()
1950 message(SIMPLE
, "Upgrade finished!")
1951 # remove reference provision now that everything is done !
1952 # So we have reindexed first if need when the merged schema was reloaded
1953 # (as new attributes could have quick in)
1954 # But the second part of the update (when we update existing objects
1955 # can also have an influence on indexing as some attribute might have their
1956 # searchflag modificated
1957 message(SIMPLE
, "Reopenning samdb to trigger reindexing if needed "
1958 "after modification")
1959 samdb
= Ldb(paths
.samdb
, session_info
=session
, credentials
=creds
, lp
=lp
)
1960 message(SIMPLE
, "Reindexing finished")
1962 shutil
.rmtree(provisiondir
)
1964 ldbs
.groupedRollback()
1965 message(SIMPLE
, "ACLs fixed !")
1966 except StandardError, err
:
1967 message(ERROR
, "A problem occurred while trying to upgrade your "
1968 "provision. A full backup is located at %s" % backupdir
)
1969 if opts
.debugall
or opts
.debugchange
:
1970 (typ
, val
, tb
) = sys
.exc_info()
1971 traceback
.print_exception(typ
, val
, tb
)