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
, get_empty_descriptor
,
50 ProvisioningError
, get_last_provision_usn
,
51 get_max_usn
, update_provision_usn
, setup_path
)
52 from samba
.schema
import get_linked_attributes
, Schema
, get_schema_descriptor
53 from samba
.dcerpc
import security
, drsblobs
54 from samba
.dcerpc
.security
import (
55 SECINFO_OWNER
, SECINFO_GROUP
, SECINFO_DACL
, SECINFO_SACL
)
56 from samba
.ndr
import ndr_unpack
57 from samba
.upgradehelpers
import (dn_sort
, get_paths
, newprovision
,
58 get_ldbs
, findprovisionrange
,
59 usn_in_range
, identic_rename
, get_diff_sddls
,
60 update_secrets
, CHANGE
, ERROR
, SIMPLE
,
61 CHANGEALL
, GUESS
, CHANGESD
, PROVISION
,
62 updateOEMInfo
, getOEMInfo
, update_gpo
,
63 delta_update_basesamdb
, update_policyids
,
64 update_machine_account_password
,
65 search_constructed_attrs_stored
,
66 int64range2str
, update_dns_account_password
,
67 increment_calculated_keyversion_number
,
68 print_provision_ranges
)
69 from samba
.xattr
import copytree_with_xattrs
71 # make sure the script dies immediately when hitting control-C,
72 # rather than raising KeyboardInterrupt. As we do all database
73 # operations using transactions, this is safe.
75 signal
.signal(signal
.SIGINT
, signal
.SIG_DFL
)
77 replace
=2**FLAG_MOD_REPLACE
79 delete
=2**FLAG_MOD_DELETE
83 # Will be modified during provision to tell if default sd has been modified
86 #Errors are always logged
88 __docformat__
= "restructuredText"
90 # Attributes that are never copied from the reference provision (even if they
91 # do not exist in the destination object).
92 # This is most probably because they are populated automatcally when object is
94 # This also apply to imported object from reference provision
95 replAttrNotCopied
= [ "dn", "whenCreated", "whenChanged", "objectGUID",
96 "parentGUID", "objectCategory", "distinguishedName",
98 "lmPwdHistory", "pwdLastSet", "ntPwdHistory",
99 "unicodePwd", "dBCSPwd", "supplementalCredentials",
100 "gPCUserExtensionNames", "gPCMachineExtensionNames",
101 "maxPwdAge", "secret", "possibleInferiors", "privilege",
102 "sAMAccountType", "oEMInformation", "creationTime" ]
104 nonreplAttrNotCopied
= ["uSNCreated", "replPropertyMetaData", "uSNChanged",
105 "nextRid" ,"rIDNextRID", "rIDPreviousAllocationPool"]
107 nonDSDBAttrNotCopied
= ["msDS-KeyVersionNumber", "priorSecret", "priorWhenChanged"]
110 attrNotCopied
= replAttrNotCopied
111 attrNotCopied
.extend(nonreplAttrNotCopied
)
112 attrNotCopied
.extend(nonDSDBAttrNotCopied
)
113 # Usually for an object that already exists we do not overwrite attributes as
114 # they might have been changed for good reasons. Anyway for a few of them it's
115 # mandatory to replace them otherwise the provision will be broken somehow.
116 # But for attribute that are just missing we do not have to specify them as the default
117 # behavior is to add missing attribute
118 hashOverwrittenAtt
= { "prefixMap": replace
, "systemMayContain": replace
,
119 "systemOnly":replace
, "searchFlags":replace
,
120 "mayContain":replace
, "systemFlags":replace
+add
,
121 "description":replace
, "operatingSystemVersion":replace
,
122 "adminPropertyPages":replace
, "groupType":replace
,
123 "wellKnownObjects":replace
, "privilege":never
,
124 "defaultSecurityDescriptor": replace
,
125 "rIDAvailablePool": never
,
126 "versionNumber" : add
,
127 "rIDNextRID": add
, "rIDUsedPool": never
,
128 "defaultSecurityDescriptor": replace
+ add
,
129 "isMemberOfPartialAttributeSet": delete
,
130 "attributeDisplayNames": replace
+ add
,
131 "versionNumber": add
}
133 dnNotToRecalculate
= []
136 forwardlinked
= set()
139 def define_what_to_log(opts
):
143 if opts
.debugchangesd
:
144 what
= what | CHANGESD
147 if opts
.debugprovision
:
148 what
= what | PROVISION
150 what
= what | CHANGEALL
154 parser
= optparse
.OptionParser("provision [options]")
155 sambaopts
= options
.SambaOptions(parser
)
156 parser
.add_option_group(sambaopts
)
157 parser
.add_option_group(options
.VersionOptions(parser
))
158 credopts
= options
.CredentialsOptions(parser
)
159 parser
.add_option_group(credopts
)
160 parser
.add_option("--setupdir", type="string", metavar
="DIR",
161 help="directory with setup files")
162 parser
.add_option("--debugprovision", help="Debug provision", action
="store_true")
163 parser
.add_option("--debugguess", action
="store_true",
164 help="Print information on which values are guessed")
165 parser
.add_option("--debugchange", action
="store_true",
166 help="Print information on what is different but won't be changed")
167 parser
.add_option("--debugchangesd", action
="store_true",
168 help="Print security descriptor differences")
169 parser
.add_option("--debugall", action
="store_true",
170 help="Print all available information (very verbose)")
171 parser
.add_option("--resetfileacl", action
="store_true",
172 help="Force a reset on filesystem acls in sysvol / netlogon share")
173 parser
.add_option("--nontaclfix", action
="store_true",
174 help="In full upgrade mode do not try to upgrade sysvol / netlogon acls")
175 parser
.add_option("--fixntacl", action
="store_true",
176 help="Only fix NT ACLs in sysvol / netlogon share")
177 parser
.add_option("--db_backup_only", action
="store_true",
178 help="Do the backup of the database in the provision, skip the sysvol / netlogon shares")
179 parser
.add_option("--full", action
="store_true",
180 help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
182 opts
= parser
.parse_args()[0]
184 handler
= logging
.StreamHandler(sys
.stdout
)
185 upgrade_logger
= logging
.getLogger("upgradeprovision")
186 upgrade_logger
.setLevel(logging
.INFO
)
188 upgrade_logger
.addHandler(handler
)
190 provision_logger
= logging
.getLogger("provision")
191 provision_logger
.addHandler(handler
)
193 whatToLog
= define_what_to_log(opts
)
195 def message(what
, text
):
196 """Print a message if this message type has been selected to be printed
198 :param what: Category of the message
199 :param text: Message to print """
200 if (whatToLog
& what
) or what
<= 0:
201 upgrade_logger
.info("%s", text
)
203 if len(sys
.argv
) == 1:
204 opts
.interactive
= True
205 lp
= sambaopts
.get_loadparm()
206 smbconf
= lp
.configfile
208 creds
= credopts
.get_credentials(lp
)
209 creds
.set_kerberos_state(DONT_USE_KERBEROS
)
213 def check_for_DNS(refprivate
, private
):
214 """Check if the provision has already the requirement for dynamic dns
216 :param refprivate: The path to the private directory of the reference
218 :param private: The path to the private directory of the upgraded
221 spnfile
= "%s/spn_update_list" % private
222 dnsfile
= "%s/dns_update_list" % private
223 namedfile
= lp
.get("dnsupdate:path")
226 namedfile
= "%s/named.conf.update" % private
228 if not os
.path
.exists(spnfile
):
229 shutil
.copy("%s/spn_update_list" % refprivate
, "%s" % spnfile
)
231 if not os
.path
.exists(dnsfile
):
232 shutil
.copy("%s/dns_update_list" % refprivate
, "%s" % dnsfile
)
234 destdir
= "%s/new_dns" % private
235 dnsdir
= "%s/dns" % private
237 if not os
.path
.exists(namedfile
):
238 if not os
.path
.exists(destdir
):
240 if not os
.path
.exists(dnsdir
):
242 shutil
.copy("%s/named.conf" % refprivate
, "%s/named.conf" % destdir
)
243 shutil
.copy("%s/named.txt" % refprivate
, "%s/named.txt" % destdir
)
244 message(SIMPLE
, "It seems that your provision did not integrate "
245 "new rules for dynamic dns update of domain related entries")
246 message(SIMPLE
, "A copy of the new bind configuration files and "
247 "template has been put in %s, you should read them and "
248 "configure dynamic dns updates" % destdir
)
251 def populate_links(samdb
, schemadn
):
252 """Populate an array with all the back linked attributes
254 This attributes that are modified automaticaly when
255 front attibutes are changed
257 :param samdb: A LDB object for sam.ldb file
258 :param schemadn: DN of the schema for the partition"""
259 linkedAttHash
= get_linked_attributes(Dn(samdb
, str(schemadn
)), samdb
)
260 backlinked
.extend(linkedAttHash
.values())
261 for t
in linkedAttHash
.keys():
264 def isReplicated(att
):
265 """ Indicate if the attribute is replicated or not
267 :param att: Name of the attribute to be tested
268 :return: True is the attribute is replicated, False otherwise
271 return (att
not in not_replicated
)
273 def populateNotReplicated(samdb
, schemadn
):
274 """Populate an array with all the attributes that are not replicated
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
="(&(objectclass=attributeSchema)(systemflags:1.2.840.113556.1.4.803:=1))", base
=Dn(samdb
,
279 str(schemadn
)), scope
=SCOPE_SUBTREE
,
280 attrs
=["lDAPDisplayName"])
282 not_replicated
.append(str(elem
["lDAPDisplayName"]))
285 def populate_dnsyntax(samdb
, schemadn
):
286 """Populate an array with all the attributes that have DN synthax
289 :param samdb: A LDB object for sam.ldb file
290 :param schemadn: DN of the schema for the partition"""
291 res
= samdb
.search(expression
="(attributeSyntax=2.5.5.1)", base
=Dn(samdb
,
292 str(schemadn
)), scope
=SCOPE_SUBTREE
,
293 attrs
=["lDAPDisplayName"])
295 dn_syntax_att
.append(elem
["lDAPDisplayName"])
298 def sanitychecks(samdb
, names
):
299 """Make some checks before trying to update
301 :param samdb: An LDB object opened on sam.ldb
302 :param names: list of key provision parameters
303 :return: Status of check (1 for Ok, 0 for not Ok) """
304 res
= samdb
.search(expression
="objectClass=ntdsdsa", base
=str(names
.configdn
),
305 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
306 controls
=["search_options:1:2"])
308 print "No DC found. Your provision is most probably broken!"
311 print "Found %d domain controllers. For the moment " \
312 "upgradeprovision is not able to handle an upgrade on a " \
313 "domain with more than one DC. Please demote the other " \
314 "DC(s) before upgrading" % len(res
)
320 def print_provision_key_parameters(names
):
321 """Do a a pretty print of provision parameters
323 :param names: list of key provision parameters """
324 message(GUESS
, "rootdn :" + str(names
.rootdn
))
325 message(GUESS
, "configdn :" + str(names
.configdn
))
326 message(GUESS
, "schemadn :" + str(names
.schemadn
))
327 message(GUESS
, "serverdn :" + str(names
.serverdn
))
328 message(GUESS
, "netbiosname :" + names
.netbiosname
)
329 message(GUESS
, "defaultsite :" + names
.sitename
)
330 message(GUESS
, "dnsdomain :" + names
.dnsdomain
)
331 message(GUESS
, "hostname :" + names
.hostname
)
332 message(GUESS
, "domain :" + names
.domain
)
333 message(GUESS
, "realm :" + names
.realm
)
334 message(GUESS
, "invocationid:" + names
.invocation
)
335 message(GUESS
, "policyguid :" + names
.policyid
)
336 message(GUESS
, "policyguiddc:" + str(names
.policyid_dc
))
337 message(GUESS
, "domainsid :" + str(names
.domainsid
))
338 message(GUESS
, "domainguid :" + names
.domainguid
)
339 message(GUESS
, "ntdsguid :" + names
.ntdsguid
)
340 message(GUESS
, "domainlevel :" + str(names
.domainlevel
))
343 def handle_special_case(att
, delta
, new
, old
, useReplMetadata
, basedn
, aldb
):
344 """Define more complicate update rules for some attributes
346 :param att: The attribute to be updated
347 :param delta: A messageElement object that correspond to the difference
348 between the updated object and the reference one
349 :param new: The reference object
350 :param old: The Updated object
351 :param useReplMetadata: A boolean that indicate if the update process
352 use replPropertyMetaData to decide what has to be updated.
353 :param basedn: The base DN of the provision
354 :param aldb: An ldb object used to build DN
355 :return: True to indicate that the attribute should be kept, False for
358 # We do most of the special case handle if we do not have the
359 # highest usn as otherwise the replPropertyMetaData will guide us more
361 if not useReplMetadata
:
362 flag
= delta
.get(att
).flags()
363 if (att
== "sPNMappings" and flag
== FLAG_MOD_REPLACE
and
364 ldb
.Dn(aldb
, "CN=Directory Service,CN=Windows NT,"
365 "CN=Services,CN=Configuration,%s" % basedn
)
368 if (att
== "userAccountControl" and flag
== FLAG_MOD_REPLACE
and
369 ldb
.Dn(aldb
, "CN=Administrator,CN=Users,%s" % basedn
)
371 message(SIMPLE
, "We suggest that you change the userAccountControl"
372 " for user Administrator from value %d to %d" %
373 (int(str(old
[0][att
])), int(str(new
[0][att
]))))
375 if (att
== "minPwdAge" and flag
== FLAG_MOD_REPLACE
):
376 if (long(str(old
[0][att
])) == 0):
377 delta
[att
] = MessageElement(new
[0][att
], FLAG_MOD_REPLACE
, att
)
380 if (att
== "member" and flag
== FLAG_MOD_REPLACE
):
384 for elem
in old
[0][att
]:
385 hash[str(elem
).lower()]=1
386 newval
.append(str(elem
))
388 for elem
in new
[0][att
]:
389 if not hash.has_key(str(elem
).lower()):
391 newval
.append(str(elem
))
393 delta
[att
] = MessageElement(newval
, FLAG_MOD_REPLACE
, att
)
398 if (att
in ("gPLink", "gPCFileSysPath") and
399 flag
== FLAG_MOD_REPLACE
and
400 str(new
[0].dn
).lower() == str(old
[0].dn
).lower()):
404 if att
== "forceLogoff":
405 ref
=0x8000000000000000
406 oldval
=int(old
[0][att
][0])
407 newval
=int(new
[0][att
][0])
408 ref
== old
and ref
== abs(new
)
411 if att
in ("adminDisplayName", "adminDescription"):
414 if (str(old
[0].dn
) == "CN=Samba4-Local-Domain, %s" % (names
.schemadn
)
415 and att
== "defaultObjectCategory" and flag
== FLAG_MOD_REPLACE
):
418 if (str(old
[0].dn
) == "CN=Title, %s" % (str(names
.schemadn
)) and
419 att
== "rangeUpper" and flag
== FLAG_MOD_REPLACE
):
422 if (str(old
[0].dn
) == "%s" % (str(names
.rootdn
))
423 and att
== "subRefs" and flag
== FLAG_MOD_REPLACE
):
425 #Allow to change revision of ForestUpdates objects
426 if (att
== "revision" or att
== "objectVersion"):
427 if str(delta
.dn
).lower().find("domainupdates") and str(delta
.dn
).lower().find("forestupdates") > 0:
429 if str(delta
.dn
).endswith("CN=DisplaySpecifiers, %s" % names
.configdn
):
432 # This is a bit of special animal as we might have added
433 # already SPN entries to the list that has to be modified
434 # So we go in detail to try to find out what has to be added ...
435 if (att
== "servicePrincipalName" and delta
.get(att
).flags() == FLAG_MOD_REPLACE
):
439 for elem
in old
[0][att
]:
441 newval
.append(str(elem
))
443 for elem
in new
[0][att
]:
444 if not hash.has_key(str(elem
)):
446 newval
.append(str(elem
))
448 delta
[att
] = MessageElement(newval
, FLAG_MOD_REPLACE
, att
)
455 def dump_denied_change(dn
, att
, flagtxt
, current
, reference
):
456 """Print detailed information about why a change is denied
458 :param dn: DN of the object which attribute is denied
459 :param att: Attribute that was supposed to be upgraded
460 :param flagtxt: Type of the update that should be performed
461 (add, change, remove, ...)
462 :param current: Value(s) of the current attribute
463 :param reference: Value(s) of the reference attribute"""
465 message(CHANGE
, "dn= " + str(dn
)+" " + att
+" with flag " + flagtxt
466 + " must not be changed/removed. Discarding the change")
467 if att
== "objectSid" :
468 message(CHANGE
, "old : %s" % ndr_unpack(security
.dom_sid
, current
[0]))
469 message(CHANGE
, "new : %s" % ndr_unpack(security
.dom_sid
, reference
[0]))
470 elif att
== "rIDPreviousAllocationPool" or att
== "rIDAllocationPool":
471 message(CHANGE
, "old : %s" % int64range2str(current
[0]))
472 message(CHANGE
, "new : %s" % int64range2str(reference
[0]))
475 for e
in range(0, len(current
)):
476 message(CHANGE
, "old %d : %s" % (i
, str(current
[e
])))
478 if reference
is not None:
480 for e
in range(0, len(reference
)):
481 message(CHANGE
, "new %d : %s" % (i
, str(reference
[e
])))
484 def handle_special_add(samdb
, dn
, names
):
485 """Handle special operation (like remove) on some object needed during
488 This is mostly due to wrong creation of the object in previous provision.
489 :param samdb: An Ldb object representing the SAM database
490 :param dn: DN of the object to inspect
491 :param names: list of key provision parameters
495 objDn
= Dn(samdb
, "CN=IIS_IUSRS, CN=Builtin, %s" % names
.rootdn
)
497 #This entry was misplaced lets remove it if it exists
498 dntoremove
= "CN=IIS_IUSRS, CN=Users, %s" % names
.rootdn
501 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names
.rootdn
)
503 #This entry was misplaced lets remove it if it exists
504 dntoremove
= "CN=Certificate Service DCOM Access,"\
505 "CN=Users, %s" % names
.rootdn
507 objDn
= Dn(samdb
, "CN=Cryptographic Operators, CN=Builtin, %s" % names
.rootdn
)
509 #This entry was misplaced lets remove it if it exists
510 dntoremove
= "CN=Cryptographic Operators, CN=Users, %s" % names
.rootdn
512 objDn
= Dn(samdb
, "CN=Event Log Readers, CN=Builtin, %s" % names
.rootdn
)
514 #This entry was misplaced lets remove it if it exists
515 dntoremove
= "CN=Event Log Readers, CN=Users, %s" % names
.rootdn
517 objDn
= Dn(samdb
,"CN=System,CN=WellKnown Security Principals,"
518 "CN=Configuration,%s" % names
.rootdn
)
520 oldDn
= Dn(samdb
,"CN=Well-Known-Security-Id-System,"
521 "CN=WellKnown Security Principals,"
522 "CN=Configuration,%s" % names
.rootdn
)
524 res
= samdb
.search(expression
="(distinguishedName=%s)" % oldDn
,
525 base
=str(names
.rootdn
),
526 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
527 controls
=["search_options:1:2"])
529 res2
= samdb
.search(expression
="(distinguishedName=%s)" % dn
,
530 base
=str(names
.rootdn
),
531 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
532 controls
=["search_options:1:2"])
534 if len(res
) > 0 and len(res2
) == 0:
535 message(CHANGE
, "Existing object %s must be replaced by %s. "
536 "Renaming old object" % (str(oldDn
), str(dn
)))
537 samdb
.rename(oldDn
, objDn
, ["relax:0", "provision:0"])
541 if dntoremove
is not None:
542 res
= samdb
.search(expression
="(cn=RID Set)",
543 base
=str(names
.rootdn
),
544 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
545 controls
=["search_options:1:2"])
549 res
= samdb
.search(expression
="(distinguishedName=%s)" % dntoremove
,
550 base
=str(names
.rootdn
),
551 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
552 controls
=["search_options:1:2"])
554 message(CHANGE
, "Existing object %s must be replaced by %s. "
555 "Removing old object" % (dntoremove
, str(dn
)))
556 samdb
.delete(res
[0]["dn"])
562 def check_dn_nottobecreated(hash, index
, listdn
):
563 """Check if one of the DN present in the list has a creation order
564 greater than the current.
566 Hash is indexed by dn to be created, with each key
567 is associated the creation order.
569 First dn to be created has the creation order 0, second has 1, ...
570 Index contain the current creation order
572 :param hash: Hash holding the different DN of the object to be
574 :param index: Current creation order
575 :param listdn: List of DNs on which the current DN depends on
576 :return: None if the current object do not depend on other
577 object or if all object have been created before."""
581 key
= str(dn
).lower()
582 if hash.has_key(key
) and hash[key
] > index
:
588 def add_missing_object(ref_samdb
, samdb
, dn
, names
, basedn
, hash, index
):
589 """Add a new object if the dependencies are satisfied
591 The function add the object if the object on which it depends are already
594 :param ref_samdb: Ldb object representing the SAM db of the reference
596 :param samdb: Ldb object representing the SAM db of the upgraded
598 :param dn: DN of the object to be added
599 :param names: List of key provision parameters
600 :param basedn: DN of the partition to be updated
601 :param hash: Hash holding the different DN of the object to be
603 :param index: Current creation order
604 :return: True if the object was created False otherwise"""
606 ret
= handle_special_add(samdb
, dn
, names
)
615 reference
= ref_samdb
.search(expression
="(distinguishedName=%s)" % (str(dn
)),
616 base
=basedn
, scope
=SCOPE_SUBTREE
,
617 controls
=["search_options:1:2"])
619 delta
= samdb
.msg_diff(empty
, reference
[0])
623 if str(reference
[0].get("cn")) == "RID Set":
624 for klass
in reference
[0].get("objectClass"):
625 if str(klass
).lower() == "ridset":
628 if delta
.get("objectSid"):
629 sid
= str(ndr_unpack(security
.dom_sid
, str(reference
[0]["objectSid"])))
630 m
= re
.match(r
".*-(\d+)$", sid
)
631 if m
and int(m
.group(1))>999:
632 delta
.remove("objectSid")
633 for att
in attrNotCopied
:
635 for att
in backlinked
:
637 depend_on_yettobecreated
= None
638 for att
in dn_syntax_att
:
639 depend_on_yet_tobecreated
= check_dn_nottobecreated(hash, index
,
641 if depend_on_yet_tobecreated
is not None:
642 message(CHANGE
, "Object %s depends on %s in attribute %s. "
643 "Delaying the creation" % (dn
,
644 depend_on_yet_tobecreated
, att
))
649 message(CHANGE
,"Object %s will be added" % dn
)
650 samdb
.add(delta
, ["relax:0", "provision:0"])
652 message(CHANGE
,"Object %s was skipped" % dn
)
656 def gen_dn_index_hash(listMissing
):
657 """Generate a hash associating the DN to its creation order
659 :param listMissing: List of DN
660 :return: Hash with DN as keys and creation order as values"""
662 for i
in range(0, len(listMissing
)):
663 hash[str(listMissing
[i
]).lower()] = i
666 def add_deletedobj_containers(ref_samdb
, samdb
, names
):
667 """Add the object containter: CN=Deleted Objects
669 This function create the container for each partition that need one and
670 then reference the object into the root of the partition
672 :param ref_samdb: Ldb object representing the SAM db of the reference
674 :param samdb: Ldb object representing the SAM db of the upgraded provision
675 :param names: List of key provision parameters"""
678 wkoPrefix
= "B:32:18E2EA80684F11D2B9AA00C04F79F805"
679 partitions
= [str(names
.rootdn
), str(names
.configdn
)]
680 for part
in partitions
:
681 ref_delObjCnt
= ref_samdb
.search(expression
="(cn=Deleted Objects)",
682 base
=part
, scope
=SCOPE_SUBTREE
,
684 controls
=["show_deleted:0",
686 delObjCnt
= samdb
.search(expression
="(cn=Deleted Objects)",
687 base
=part
, scope
=SCOPE_SUBTREE
,
689 controls
=["show_deleted:0",
691 if len(ref_delObjCnt
) > len(delObjCnt
):
692 reference
= ref_samdb
.search(expression
="cn=Deleted Objects",
693 base
=part
, scope
=SCOPE_SUBTREE
,
694 controls
=["show_deleted:0",
697 delta
= samdb
.msg_diff(empty
, reference
[0])
699 delta
.dn
= Dn(samdb
, str(reference
[0]["dn"]))
700 for att
in attrNotCopied
:
703 modcontrols
= ["relax:0", "provision:0"]
704 samdb
.add(delta
, modcontrols
)
707 res
= samdb
.search(expression
="(objectClass=*)", base
=part
,
709 attrs
=["dn", "wellKnownObjects"])
711 targetWKO
= "%s:%s" % (wkoPrefix
, str(reference
[0]["dn"]))
715 wko
= res
[0]["wellKnownObjects"]
717 # The wellKnownObject that we want to add.
719 if str(o
) == targetWKO
:
721 listwko
.append(str(o
))
724 listwko
.append(targetWKO
)
727 delta
.dn
= Dn(samdb
, str(res
[0]["dn"]))
728 delta
["wellKnownObjects"] = MessageElement(listwko
,
733 def add_missing_entries(ref_samdb
, samdb
, names
, basedn
, list):
734 """Add the missing object whose DN is the list
736 The function add the object if the objects on which it depends are
739 :param ref_samdb: Ldb object representing the SAM db of the reference
741 :param samdb: Ldb object representing the SAM db of the upgraded
743 :param dn: DN of the object to be added
744 :param names: List of key provision parameters
745 :param basedn: DN of the partition to be updated
746 :param list: List of DN to be added in the upgraded provision"""
751 while(len(listDefered
) != len(listMissing
) and len(listDefered
) > 0):
753 listMissing
= listDefered
755 hashMissing
= gen_dn_index_hash(listMissing
)
756 for dn
in listMissing
:
757 ret
= add_missing_object(ref_samdb
, samdb
, dn
, names
, basedn
,
761 # DN can't be created because it depends on some
762 # other DN in the list
763 listDefered
.append(dn
)
765 if len(listDefered
) != 0:
766 raise ProvisioningError("Unable to insert missing elements: "
767 "circular references")
769 def handle_links(samdb
, att
, basedn
, dn
, value
, ref_value
, delta
):
770 """This function handle updates on links
772 :param samdb: An LDB object pointing to the updated provision
773 :param att: Attribute to update
774 :param basedn: The root DN of the provision
775 :param dn: The DN of the inspected object
776 :param value: The value of the attribute
777 :param ref_value: The value of this attribute in the reference provision
778 :param delta: The MessageElement object that will be applied for
779 transforming the current provision"""
781 res
= samdb
.search(base
=dn
, controls
=["search_options:1:2", "reveal:1"],
790 newlinklist
.append(str(v
))
794 # for w2k domain level the reveal won't reveal anything ...
795 # it means that we can readd links that were removed on purpose ...
796 # Also this function in fact just accept add not removal
798 for e
in res
[0][att
]:
799 if not hash.has_key(e
):
800 # We put in the blacklist all the element that are in the "revealed"
801 # result and not in the "standard" result
802 # This element are links that were removed before and so that
803 # we don't wan't to readd
807 if not blacklist
.has_key(e
) and not hash.has_key(e
):
808 newlinklist
.append(str(e
))
811 delta
[att
] = MessageElement(newlinklist
, FLAG_MOD_REPLACE
, att
)
818 msg_elt_flag_strs
= {
819 ldb
.FLAG_MOD_ADD
: "MOD_ADD",
820 ldb
.FLAG_MOD_REPLACE
: "MOD_REPLACE",
821 ldb
.FLAG_MOD_DELETE
: "MOD_DELETE" }
823 def checkKeepAttributeOldMtd(delta
, att
, reference
, current
,
825 """ Check if we should keep the attribute modification or not.
826 This function didn't use replicationMetadata to take a decision.
828 :param delta: A message diff object
829 :param att: An attribute
830 :param reference: A message object for the current entry comming from
831 the reference provision.
832 :param current: A message object for the current entry commin from
833 the current provision.
834 :param basedn: The DN of the partition
835 :param samdb: A ldb connection to the sam database of the current provision.
837 :return: The modified message diff.
839 # Old school way of handling things for pre alpha12 upgrade
845 for att
in list(delta
):
846 msgElt
= delta
.get(att
)
848 if att
== "nTSecurityDescriptor":
856 if not hashOverwrittenAtt
.has_key(att
):
857 if msgElt
.flags() != FLAG_MOD_ADD
:
858 if not handle_special_case(att
, delta
, reference
, current
,
859 False, basedn
, samdb
):
860 if opts
.debugchange
or opts
.debugall
:
862 dump_denied_change(dn
, att
,
863 msg_elt_flag_strs
[msgElt
.flags()],
864 current
[0][att
], reference
[0][att
])
866 dump_denied_change(dn
, att
,
867 msg_elt_flag_strs
[msgElt
.flags()],
868 current
[0][att
], None)
872 if hashOverwrittenAtt
.get(att
)&2**msgElt
.flags() :
874 elif hashOverwrittenAtt
.get(att
) == never
:
880 def checkKeepAttributeWithMetadata(delta
, att
, message
, reference
, current
,
881 hash_attr_usn
, basedn
, usns
, samdb
):
882 """ Check if we should keep the attribute modification or not
884 :param delta: A message diff object
885 :param att: An attribute
886 :param message: A function to print messages
887 :param reference: A message object for the current entry comming from
888 the reference provision.
889 :param current: A message object for the current entry commin from
890 the current provision.
891 :param hash_attr_usn: A dictionnary with attribute name as keys,
892 USN and invocation id as values.
893 :param basedn: The DN of the partition
894 :param usns: A dictionnary with invocation ID as keys and USN ranges
896 :param samdb: A ldb object pointing to the sam DB
898 :return: The modified message diff.
905 for att
in list(delta
):
906 if att
in ["dn", "objectSid"]:
910 # We have updated by provision usn information so let's exploit
911 # replMetadataProperties
912 if att
in forwardlinked
:
913 curval
= current
[0].get(att
, ())
914 refval
= reference
[0].get(att
, ())
915 delta
= handle_links(samdb
, att
, basedn
, current
[0]["dn"],
916 curval
, refval
, delta
)
920 if isFirst
and len(list(delta
)) > 1:
922 txt
= "%s\n" % (str(dn
))
924 if handle_special_case(att
, delta
, reference
, current
, True, None, None):
925 # This attribute is "complicated" to handle and handling
926 # was done in handle_special_case
930 if hash_attr_usn
.get(att
):
931 [attrUSN
, attInvId
] = hash_attr_usn
.get(att
)
934 # If it's a replicated attribute and we don't have any USN
935 # information about it. It means that we never saw it before
937 # If it is a replicated attribute but we are not master on it
938 # (ie. not initially added in the provision we masterize).
940 if isReplicated(att
):
943 message(CHANGE
, "Non replicated attribute %s changed" % att
)
946 if att
== "nTSecurityDescriptor":
947 cursd
= ndr_unpack(security
.descriptor
,
948 str(current
[0]["nTSecurityDescriptor"]))
949 cursddl
= cursd
.as_sddl(names
.domainsid
)
950 refsd
= ndr_unpack(security
.descriptor
,
951 str(reference
[0]["nTSecurityDescriptor"]))
952 refsddl
= refsd
.as_sddl(names
.domainsid
)
954 diff
= get_diff_sddls(refsddl
, cursddl
)
956 # FIXME find a way to have it only with huge huge verbose mode
957 # message(CHANGE, "%ssd are identical" % txt)
963 message(CHANGESD
, "%ssd are not identical:\n%s" % (txt
, diff
))
966 message(CHANGESD
, "But the SD has been changed by someonelse "
967 "so it's impossible to know if the difference"
968 " cames from the modification or from a previous bug")
969 dnNotToRecalculate
.append(str(dn
))
971 dnToRecalculate
.append(str(dn
))
975 # This attribute was last modified by another DC forget
977 message(CHANGE
, "%sAttribute: %s has been "
978 "created/modified/deleted by another DC. "
979 "Doing nothing" % (txt
, att
))
983 elif not usn_in_range(int(attrUSN
), usns
.get(attInvId
)):
984 message(CHANGE
, "%sAttribute: %s was not "
985 "created/modified/deleted during a "
986 "provision or upgradeprovision. Current "
987 "usn: %d. Doing nothing" % (txt
, att
,
993 if att
== "defaultSecurityDescriptor":
996 message(CHANGE
, "%sAttribute: %s will be modified"
997 "/deleted it was last modified "
998 "during a provision. Current usn: "
999 "%d" % (txt
, att
, attrUSN
))
1002 message(CHANGE
, "%sAttribute: %s will be added because "
1003 "it did not exist before" % (txt
, att
))
1009 def update_present(ref_samdb
, samdb
, basedn
, listPresent
, usns
):
1010 """ This function updates the object that are already present in the
1013 :param ref_samdb: An LDB object pointing to the reference provision
1014 :param samdb: An LDB object pointing to the updated provision
1015 :param basedn: A string with the value of the base DN for the provision
1016 (ie. DC=foo, DC=bar)
1017 :param listPresent: A list of object that is present in the provision
1018 :param usns: A list of USN range modified by previous provision and
1019 upgradeprovision grouped by invocation ID
1022 # This hash is meant to speedup lookup of attribute name from an oid,
1023 # it's for the replPropertyMetaData handling
1025 res
= samdb
.search(expression
="objectClass=attributeSchema", base
=basedn
,
1026 controls
=["search_options:1:2"], attrs
=["attributeID",
1030 strDisplay
= str(e
.get("lDAPDisplayName"))
1031 hash_oid_name
[str(e
.get("attributeID"))] = strDisplay
1033 msg
= "Unable to insert missing elements: circular references"
1034 raise ProvisioningError(msg
)
1037 sd_flags
= SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL | SECINFO_SACL
1038 controls
= ["search_options:1:2", "sd_flags:1:%d" % sd_flags
]
1039 if usns
is not None:
1040 message(CHANGE
, "Using replPropertyMetadata for change selection")
1041 for dn
in listPresent
:
1042 reference
= ref_samdb
.search(expression
="(distinguishedName=%s)" % (str(dn
)), base
=basedn
,
1043 scope
=SCOPE_SUBTREE
,
1045 current
= samdb
.search(expression
="(distinguishedName=%s)" % (str(dn
)), base
=basedn
,
1046 scope
=SCOPE_SUBTREE
, controls
=controls
)
1049 (str(current
[0].dn
) != str(reference
[0].dn
)) and
1050 (str(current
[0].dn
).upper() == str(reference
[0].dn
).upper())
1052 message(CHANGE
, "Names are the same except for the case. "
1053 "Renaming %s to %s" % (str(current
[0].dn
),
1054 str(reference
[0].dn
)))
1055 identic_rename(samdb
, reference
[0].dn
)
1056 current
= samdb
.search(expression
="(distinguishedName=%s)" % (str(dn
)), base
=basedn
,
1057 scope
=SCOPE_SUBTREE
,
1060 delta
= samdb
.msg_diff(current
[0], reference
[0])
1062 for att
in backlinked
:
1065 for att
in attrNotCopied
:
1068 delta
.remove("name")
1070 nb_items
= len(list(delta
))
1075 if nb_items
> 1 and usns
is not None:
1076 # Fetch the replPropertyMetaData
1077 res
= samdb
.search(expression
="(distinguishedName=%s)" % (str(dn
)), base
=basedn
,
1078 scope
=SCOPE_SUBTREE
, controls
=controls
,
1079 attrs
=["replPropertyMetaData"])
1080 ctr
= ndr_unpack(drsblobs
.replPropertyMetaDataBlob
,
1081 str(res
[0]["replPropertyMetaData"])).ctr
1085 # We put in this hash only modification
1086 # made on the current host
1087 att
= hash_oid_name
[samdb
.get_oid_from_attid(o
.attid
)]
1088 if str(o
.originating_invocation_id
) in usns
.keys():
1089 hash_attr_usn
[att
] = [o
.originating_usn
, str(o
.originating_invocation_id
)]
1091 hash_attr_usn
[att
] = [-1, None]
1093 if usns
is not None:
1094 delta
= checkKeepAttributeWithMetadata(delta
, att
, message
, reference
,
1095 current
, hash_attr_usn
,
1096 basedn
, usns
, samdb
)
1098 delta
= checkKeepAttributeOldMtd(delta
, att
, reference
, current
, basedn
, samdb
)
1104 # Skip dn as the value is not really changed ...
1105 attributes
=", ".join(delta
.keys()[1:])
1107 relaxedatt
= ['iscriticalsystemobject', 'grouptype']
1108 # Let's try to reduce as much as possible the use of relax control
1109 for attr
in delta
.keys():
1110 if attr
.lower() in relaxedatt
:
1111 modcontrols
= ["relax:0", "provision:0"]
1112 message(CHANGE
, "%s is different from the reference one, changed"
1113 " attributes: %s\n" % (dn
, attributes
))
1115 samdb
.modify(delta
, modcontrols
)
1118 def reload_full_schema(samdb
, names
):
1119 """Load the updated schema with all the new and existing classes
1122 :param samdb: An LDB object connected to the sam.ldb of the update
1124 :param names: List of key provision parameters
1127 schemadn
= str(names
.schemadn
)
1128 current
= samdb
.search(expression
="objectClass=*", base
=schemadn
,
1129 scope
=SCOPE_SUBTREE
)
1134 schema_ldif
+= samdb
.write_ldif(ent
, ldb
.CHANGETYPE_NONE
)
1136 prefixmap_data
= open(setup_path("prefixMap.txt"), 'r').read()
1137 prefixmap_data
= b64encode(prefixmap_data
)
1139 # We don't actually add this ldif, just parse it
1140 prefixmap_ldif
= "dn: %s\nprefixMap:: %s\n\n" % (schemadn
, prefixmap_data
)
1142 dsdb
._dsdb
_set
_schema
_from
_ldif
(samdb
, prefixmap_ldif
, schema_ldif
, schemadn
)
1145 def update_partition(ref_samdb
, samdb
, basedn
, names
, schema
, provisionUSNs
, prereloadfunc
):
1146 """Check differences between the reference provision and the upgraded one.
1148 It looks for all objects which base DN is name.
1150 This function will also add the missing object and update existing object
1151 to add or remove attributes that were missing.
1153 :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1155 :param samdb: An LDB object connected to the sam.ldb of the update
1157 :param basedn: String value of the DN of the partition
1158 :param names: List of key provision parameters
1159 :param schema: A Schema object
1160 :param provisionUSNs: A dictionnary with range of USN modified during provision
1161 or upgradeprovision. Ranges are grouped by invocationID.
1162 :param prereloadfunc: A function that must be executed just before the reload
1173 # Connect to the reference provision and get all the attribute in the
1174 # partition referred by name
1175 reference
= ref_samdb
.search(expression
="objectClass=*", base
=basedn
,
1176 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1177 controls
=["search_options:1:2"])
1179 current
= samdb
.search(expression
="objectClass=*", base
=basedn
,
1180 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1181 controls
=["search_options:1:2"])
1182 # Create a hash for speeding the search of new object
1183 for i
in range(0, len(reference
)):
1184 hash_new
[str(reference
[i
]["dn"]).lower()] = reference
[i
]["dn"]
1186 # Create a hash for speeding the search of existing object in the
1188 for i
in range(0, len(current
)):
1189 hash[str(current
[i
]["dn"]).lower()] = current
[i
]["dn"]
1192 for k
in hash_new
.keys():
1193 if not hash.has_key(k
):
1194 if not str(hash_new
[k
]) == "CN=Deleted Objects, %s" % names
.rootdn
:
1195 listMissing
.append(hash_new
[k
])
1197 listPresent
.append(hash_new
[k
])
1199 # Sort the missing object in order to have object of the lowest level
1200 # first (which can be containers for higher level objects)
1201 listMissing
.sort(dn_sort
)
1202 listPresent
.sort(dn_sort
)
1204 # The following lines is to load the up to
1205 # date schema into our current LDB
1206 # a complete schema is needed as the insertion of attributes
1207 # and class is done against it
1208 # and the schema is self validated
1209 samdb
.set_schema(schema
)
1211 message(SIMPLE
, "There are %d missing objects" % (len(listMissing
)))
1212 add_deletedobj_containers(ref_samdb
, samdb
, names
)
1214 add_missing_entries(ref_samdb
, samdb
, names
, basedn
, listMissing
)
1217 message(SIMPLE
, "Reloading a merged schema, which might trigger "
1218 "reindexing so please be patient")
1219 reload_full_schema(samdb
, names
)
1220 message(SIMPLE
, "Schema reloaded!")
1222 changed
= update_present(ref_samdb
, samdb
, basedn
, listPresent
,
1224 message(SIMPLE
, "There are %d changed objects" % (changed
))
1227 except StandardError, err
:
1228 message(ERROR
, "Exception during upgrade of samdb:")
1229 (typ
, val
, tb
) = sys
.exc_info()
1230 traceback
.print_exception(typ
, val
, tb
)
1234 def check_updated_sd(ref_sam
, cur_sam
, names
):
1235 """Check if the security descriptor in the upgraded provision are the same
1238 :param ref_sam: A LDB object connected to the sam.ldb file used as
1239 the reference provision
1240 :param cur_sam: A LDB object connected to the sam.ldb file used as
1242 :param names: List of key provision parameters"""
1243 reference
= ref_sam
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1244 scope
=SCOPE_SUBTREE
,
1245 attrs
=["dn", "nTSecurityDescriptor"],
1246 controls
=["search_options:1:2"])
1247 current
= cur_sam
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1248 scope
=SCOPE_SUBTREE
,
1249 attrs
=["dn", "nTSecurityDescriptor"],
1250 controls
=["search_options:1:2"])
1252 for i
in range(0, len(reference
)):
1253 refsd
= ndr_unpack(security
.descriptor
,
1254 str(reference
[i
]["nTSecurityDescriptor"]))
1255 hash[str(reference
[i
]["dn"]).lower()] = refsd
.as_sddl(names
.domainsid
)
1258 for i
in range(0, len(current
)):
1259 key
= str(current
[i
]["dn"]).lower()
1260 if hash.has_key(key
):
1261 cursd
= ndr_unpack(security
.descriptor
,
1262 str(current
[i
]["nTSecurityDescriptor"]))
1263 sddl
= cursd
.as_sddl(names
.domainsid
)
1264 if sddl
!= hash[key
]:
1265 txt
= get_diff_sddls(hash[key
], sddl
, False)
1267 message(CHANGESD
, "On object %s ACL is different"
1268 " \n%s" % (current
[i
]["dn"], txt
))
1272 def fix_partition_sd(samdb
, names
):
1273 """This function fix the SD for partition containers (basedn, configdn, ...)
1274 This is needed because some provision use to have broken SD on containers
1276 :param samdb: An LDB object pointing to the sam of the current provision
1277 :param names: A list of key provision parameters
1279 alwaysRecalculate
= False
1280 if len(dnToRecalculate
) == 0 and len(dnNotToRecalculate
) == 0:
1281 alwaysRecalculate
= True
1284 # NC's DN can't be both in dnToRecalculate and dnNotToRecalculate
1285 # First update the SD for the rootdn
1286 if alwaysRecalculate
or str(names
.rootdn
) in dnToRecalculate
:
1288 delta
.dn
= Dn(samdb
, str(names
.rootdn
))
1289 descr
= get_domain_descriptor(names
.domainsid
)
1290 delta
["nTSecurityDescriptor"] = MessageElement(descr
, FLAG_MOD_REPLACE
,
1291 "nTSecurityDescriptor")
1294 # Then the config dn
1295 if alwaysRecalculate
or str(names
.configdn
) in dnToRecalculate
:
1297 delta
.dn
= Dn(samdb
, str(names
.configdn
))
1298 descr
= get_config_descriptor(names
.domainsid
)
1299 delta
["nTSecurityDescriptor"] = MessageElement(descr
, FLAG_MOD_REPLACE
,
1300 "nTSecurityDescriptor" )
1303 # Then the schema dn
1304 if alwaysRecalculate
or str(names
.schemadn
) in dnToRecalculate
:
1306 delta
.dn
= Dn(samdb
, str(names
.schemadn
))
1307 descr
= get_schema_descriptor(names
.domainsid
)
1308 delta
["nTSecurityDescriptor"] = MessageElement(descr
, FLAG_MOD_REPLACE
,
1309 "nTSecurityDescriptor" )
1312 def rebuild_sd(samdb
, names
):
1313 """Rebuild security descriptor of the current provision from scratch
1315 During the different pre release of samba4 security descriptors (SD)
1316 were notarly broken (up to alpha11 included)
1317 This function allow to get them back in order, this function make the
1318 assumption that nobody has modified manualy an SD
1319 and so SD can be safely recalculated from scratch to get them right.
1321 :param names: List of key provision parameters"""
1323 fix_partition_sd(samdb
, names
)
1325 # List of namming contexts
1326 listNC
= [str(names
.rootdn
), str(names
.configdn
), str(names
.schemadn
)]
1328 if len(dnToRecalculate
) == 0:
1329 res
= samdb
.search(expression
="objectClass=*", base
=str(names
.rootdn
),
1330 scope
=SCOPE_SUBTREE
, attrs
=["dn", "whenCreated"],
1331 controls
=["search_options:1:2"])
1333 hash[str(obj
["dn"])] = obj
["whenCreated"]
1335 for dn
in dnToRecalculate
:
1336 if hash.has_key(dn
):
1338 # fetch each dn to recalculate and their child within the same partition
1339 res
= samdb
.search(expression
="objectClass=*", base
=dn
,
1340 scope
=SCOPE_SUBTREE
, attrs
=["dn", "whenCreated"])
1342 hash[str(obj
["dn"])] = obj
["whenCreated"]
1344 listKeys
= list(set(hash.keys()))
1345 listKeys
.sort(dn_sort
)
1347 if len(dnToRecalculate
) != 0:
1348 message(CHANGESD
, "%d DNs have been marked as needed to be recalculated"
1349 ", recalculating %d due to inheritance"
1350 % (len(dnToRecalculate
), len(listKeys
)))
1352 for key
in listKeys
:
1353 if (key
in listNC
or
1354 key
in dnNotToRecalculate
):
1357 delta
.dn
= Dn(samdb
, key
)
1358 sd_flags
= SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL | SECINFO_SACL
1360 delta
["whenCreated"] = MessageElement(hash[key
], FLAG_MOD_REPLACE
,
1362 descr
= get_empty_descriptor(names
.domainsid
)
1363 delta
["nTSecurityDescriptor"] = MessageElement(descr
, FLAG_MOD_REPLACE
,
1364 "nTSecurityDescriptor")
1365 samdb
.modify(delta
, ["sd_flags:1:%d" % sd_flags
,"relax:0"])
1367 samdb
.transaction_cancel()
1368 res
= samdb
.search(expression
="objectClass=*", base
=str(delta
.dn
),
1370 attrs
=["nTSecurityDescriptor"],
1371 controls
=["sd_flags:1:%d" % sd_flags
])
1372 badsd
= ndr_unpack(security
.descriptor
,
1373 str(res
[0]["nTSecurityDescriptor"]))
1374 message(ERROR
, "On %s bad stuff %s" % (str(delta
.dn
),badsd
.as_sddl(names
.domainsid
)))
1377 def hasATProvision(samdb
):
1378 entry
= samdb
.search(expression
="(distinguishedName=@PROVISION)", base
= "",
1382 if entry
is not None and len(entry
) == 1:
1387 def removeProvisionUSN(samdb
):
1388 attrs
= [samba
.provision
.LAST_PROVISION_USN_ATTRIBUTE
, "dn"]
1389 entry
= samdb
.search(expression
="(distinguishedName=@PROVISION)", base
= "",
1393 empty
.dn
= entry
[0].dn
1394 delta
= samdb
.msg_diff(entry
[0], empty
)
1396 delta
.dn
= entry
[0].dn
1399 def remove_stored_generated_attrs(paths
, creds
, session
, lp
):
1400 """Remove previously stored constructed attributes
1402 :param paths: List of paths for different provision objects
1403 from the upgraded provision
1404 :param creds: A credential object
1405 :param session: A session object
1406 :param lp: A line parser object
1407 :return: An associative array whose key are the different constructed
1408 attributes and the value the dn where this attributes were found.
1412 def simple_update_basesamdb(newpaths
, paths
, names
):
1413 """Update the provision container db: sam.ldb
1414 This function is aimed at very old provision (before alpha9)
1416 :param newpaths: List of paths for different provision objects
1417 from the reference provision
1418 :param paths: List of paths for different provision objects
1419 from the upgraded provision
1420 :param names: List of key provision parameters"""
1422 message(SIMPLE
, "Copy samdb")
1423 shutil
.copy(newpaths
.samdb
, paths
.samdb
)
1425 message(SIMPLE
, "Update partitions filename if needed")
1426 schemaldb
= os
.path
.join(paths
.private_dir
, "schema.ldb")
1427 configldb
= os
.path
.join(paths
.private_dir
, "configuration.ldb")
1428 usersldb
= os
.path
.join(paths
.private_dir
, "users.ldb")
1429 samldbdir
= os
.path
.join(paths
.private_dir
, "sam.ldb.d")
1431 if not os
.path
.isdir(samldbdir
):
1433 os
.chmod(samldbdir
, 0700)
1434 if os
.path
.isfile(schemaldb
):
1435 shutil
.copy(schemaldb
, os
.path
.join(samldbdir
,
1436 "%s.ldb"%str
(names
.schemadn
).upper()))
1437 os
.remove(schemaldb
)
1438 if os
.path
.isfile(usersldb
):
1439 shutil
.copy(usersldb
, os
.path
.join(samldbdir
,
1440 "%s.ldb"%str
(names
.rootdn
).upper()))
1442 if os
.path
.isfile(configldb
):
1443 shutil
.copy(configldb
, os
.path
.join(samldbdir
,
1444 "%s.ldb"%str
(names
.configdn
).upper()))
1445 os
.remove(configldb
)
1448 def update_privilege(ref_private_path
, cur_private_path
):
1449 """Update the privilege database
1451 :param ref_private_path: Path to the private directory of the reference
1453 :param cur_private_path: Path to the private directory of the current
1454 (and to be updated) provision."""
1455 message(SIMPLE
, "Copy privilege")
1456 shutil
.copy(os
.path
.join(ref_private_path
, "privilege.ldb"),
1457 os
.path
.join(cur_private_path
, "privilege.ldb"))
1460 def update_samdb(ref_samdb
, samdb
, names
, provisionUSNs
, schema
, prereloadfunc
):
1461 """Upgrade the SAM DB contents for all the provision partitions
1463 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1465 :param samdb: An LDB object connected to the sam.ldb of the update
1467 :param names: List of key provision parameters
1468 :param provisionUSNs: A dictionnary with range of USN modified during provision
1469 or upgradeprovision. Ranges are grouped by invocationID.
1470 :param schema: A Schema object that represent the schema of the provision
1471 :param prereloadfunc: A function that must be executed just before the reload
1475 message(SIMPLE
, "Starting update of samdb")
1476 ret
= update_partition(ref_samdb
, samdb
, str(names
.rootdn
), names
,
1477 schema
, provisionUSNs
, prereloadfunc
)
1479 message(SIMPLE
, "Update of samdb finished")
1482 message(SIMPLE
, "Update failed")
1486 def backup_provision(paths
, dir, only_db
):
1487 """This function backup the provision files so that a rollback
1490 :param paths: Paths to different objects
1491 :param dir: Directory where to store the backup
1492 :param only_db: Skip sysvol for users with big sysvol
1494 if paths
.sysvol
and not only_db
:
1495 copytree_with_xattrs(paths
.sysvol
, os
.path
.join(dir, "sysvol"))
1496 shutil
.copy2(paths
.samdb
, dir)
1497 shutil
.copy2(paths
.secrets
, dir)
1498 shutil
.copy2(paths
.idmapdb
, dir)
1499 shutil
.copy2(paths
.privilege
, dir)
1500 if os
.path
.isfile(os
.path
.join(paths
.private_dir
,"eadb.tdb")):
1501 shutil
.copy2(os
.path
.join(paths
.private_dir
,"eadb.tdb"), dir)
1502 shutil
.copy2(paths
.smbconf
, dir)
1503 shutil
.copy2(os
.path
.join(paths
.private_dir
,"secrets.keytab"), dir)
1505 samldbdir
= os
.path
.join(paths
.private_dir
, "sam.ldb.d")
1506 if not os
.path
.isdir(samldbdir
):
1507 samldbdir
= paths
.private_dir
1508 schemaldb
= os
.path
.join(paths
.private_dir
, "schema.ldb")
1509 configldb
= os
.path
.join(paths
.private_dir
, "configuration.ldb")
1510 usersldb
= os
.path
.join(paths
.private_dir
, "users.ldb")
1511 shutil
.copy2(schemaldb
, dir)
1512 shutil
.copy2(usersldb
, dir)
1513 shutil
.copy2(configldb
, dir)
1515 shutil
.copytree(samldbdir
, os
.path
.join(dir, "sam.ldb.d"))
1518 def sync_calculated_attributes(samdb
, names
):
1519 """Synchronize attributes used for constructed ones, with the
1520 old constructed that were stored in the database.
1522 This apply for instance to msds-keyversionnumber that was
1523 stored and that is now constructed from replpropertymetadata.
1525 :param samdb: An LDB object attached to the currently upgraded samdb
1526 :param names: Various key parameter about current provision.
1528 listAttrs
= ["msDs-KeyVersionNumber"]
1529 hash = search_constructed_attrs_stored(samdb
, names
.rootdn
, listAttrs
)
1530 if hash.has_key("msDs-KeyVersionNumber"):
1531 increment_calculated_keyversion_number(samdb
, names
.rootdn
,
1532 hash["msDs-KeyVersionNumber"])
1534 # Synopsis for updateprovision
1535 # 1) get path related to provision to be update (called current)
1536 # 2) open current provision ldbs
1537 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1539 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1540 # by either upgradeprovision or provision
1541 # 5) creation of a new provision the latest version of provision script
1542 # (called reference)
1543 # 6) get reference provision paths
1544 # 7) open reference provision ldbs
1545 # 8) setup helpers data that will help the update process
1546 # 9) update the privilege ldb by copying the one of referecence provision to
1547 # the current provision
1548 # 10)get the oemInfo field, this field contains information about the different
1549 # provision that have been done
1550 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1551 # integer) or none of this the following things are done
1552 # A) When alpha9 or alphaxx is present
1553 # The base sam.ldb file is updated by looking at the difference between
1554 # referrence one and the current one. Everything is copied with the
1555 # exception of lastProvisionUSN attributes.
1556 # B) Other case (it reflect that that provision was done before alpha9)
1557 # The base sam.ldb of the reference provision is copied over
1558 # the current one, if necessary ldb related to partitions are moved
1560 # The highest used USN is fetched so that changed by upgradeprovision
1561 # usn can be tracked
1562 # 12)A Schema object is created, it will be used to provide a complete
1563 # schema to current provision during update (as the schema of the
1564 # current provision might not be complete and so won't allow some
1565 # object to be created)
1566 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1567 # 14)The secrets db is updated by pull all the difference from the reference
1568 # provision into the current provision
1569 # 15)As the previous step has most probably modified the password stored in
1570 # in secret for the current DC, a new password is generated,
1571 # the kvno is bumped and the entry in samdb is also updated
1572 # 16)For current provision older than alpha9, we must fix the SD a little bit
1573 # administrator to update them because SD used to be generated with the
1574 # system account before alpha9.
1575 # 17)The highest usn modified so far is searched in the database it will be
1576 # the upper limit for usn modified during provision.
1577 # This is done before potential SD recalculation because we do not want
1578 # SD modified during recalculation to be marked as modified during provision
1579 # (and so possibly remplaced at next upgradeprovision)
1580 # 18)Rebuilt SD if the flag indicate to do so
1581 # 19)Check difference between SD of reference provision and those of the
1582 # current provision. The check is done by getting the sddl representation
1583 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1584 # Each part is verified separetly, for dacl and sacl ACL is splited into
1585 # ACEs and each ACE is verified separately (so that a permutation in ACE
1586 # didn't raise as an error).
1587 # 20)The oemInfo field is updated to add information about the fact that the
1588 # provision has been updated by the upgradeprovision version xxx
1589 # (the version is the one obtained when starting samba with the --version
1591 # 21)Check if the current provision has all the settings needed for dynamic
1592 # DNS update to work (that is to say the provision is newer than
1593 # january 2010). If not dns configuration file from reference provision
1594 # are copied in a sub folder and the administrator is invited to
1595 # do what is needed.
1596 # 22)If the lastProvisionUSN attribute was present it is updated to add
1597 # the range of usns modified by the current upgradeprovision
1600 # About updating the sam DB
1601 # The update takes place in update_partition function
1602 # This function read both current and reference provision and list all
1603 # the available DN of objects
1604 # If the string representation of a DN in reference provision is
1605 # equal to the string representation of a DN in current provision
1606 # (without taking care of case) then the object is flaged as being
1607 # present. If the object is not present in current provision the object
1608 # is being flaged as missing in current provision. Object present in current
1609 # provision but not in reference provision are ignored.
1610 # Once the list of objects present and missing is done, the deleted object
1611 # containers are created in the differents partitions (if missing)
1613 # Then the function add_missing_entries is called
1614 # This function will go through the list of missing entries by calling
1615 # add_missing_object for the given object. If this function returns 0
1616 # it means that the object needs some other object in order to be created
1617 # The object is reappended at the end of the list to be created later
1618 # (and preferably after all the needed object have been created)
1619 # The function keeps on looping on the list of object to be created until
1620 # it's empty or that the number of defered creation is equal to the number
1621 # of object that still needs to be created.
1623 # The function add_missing_object will first check if the object can be created.
1624 # That is to say that it didn't depends other not yet created objects
1625 # If requisit can't be fullfilled it exists with 0
1626 # Then it will try to create the missing entry by creating doing
1627 # an ldb_message_diff between the object in the reference provision and
1629 # This resulting object is filtered to remove all the back link attribute
1630 # (ie. memberOf) as they will be created by the other linked object (ie.
1631 # the one with the member attribute)
1632 # All attributes specified in the attrNotCopied array are
1633 # also removed it's most of the time generated attributes
1635 # After missing entries have been added the update_partition function will
1636 # take care of object that exist but that need some update.
1637 # In order to do so the function update_present is called with the list
1638 # of object that are present in both provision and that might need an update.
1640 # This function handle first case mismatch so that the DN in the current
1641 # provision have the same case as in reference provision
1643 # It will then construct an associative array consiting of attributes as
1644 # key and invocationid as value( if the originating invocation id is
1645 # different from the invocation id of the current DC the value is -1 instead).
1647 # If the range of provision modified attributes is present, the function will
1648 # use the replMetadataProperty update method which is the following:
1649 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1650 # creationTime, msDs-KeyVersionNumber, oEMInformation
1651 # Check for each attribute if its usn is within one of the modified by
1652 # provision range and if its originating id is the invocation id of the
1653 # current DC, then validate the update from reference to current.
1654 # If not or if there is no replMetatdataProperty for this attribute then we
1656 # Otherwise (case the range of provision modified attribute is not present) it
1657 # use the following process:
1658 # All attributes that need to be added are accepted at the exeption of those
1659 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1660 # correct flags specified.
1661 # For attributes that need to be modified or removed, a check is performed
1662 # in OverwrittenAtt, if the attribute is present and the modification flag
1663 # (remove, delete) is one of those listed for this attribute then modification
1664 # is accepted. For complicated handling of attribute update, the control is passed
1665 # to handle_special_case
1669 if __name__
== '__main__':
1670 global defSDmodified
1671 defSDmodified
= False
1673 if opts
.nontaclfix
and opts
.fixntacl
:
1674 message(SIMPLE
, "nontaclfix and fixntacl are mutally exclusive")
1675 # From here start the big steps of the program
1676 # 1) First get files paths
1677 paths
= get_paths(param
, smbconf
=smbconf
)
1678 # Get ldbs with the system session, it is needed for searching
1679 # provision parameters
1680 session
= system_session()
1682 # This variable will hold the last provision USN once if it exists.
1685 ldbs
= get_ldbs(paths
, creds
, session
, lp
)
1686 backupdir
= tempfile
.mkdtemp(dir=paths
.private_dir
,
1687 prefix
="backupprovision")
1688 backup_provision(paths
, backupdir
, opts
.db_backup_only
)
1690 ldbs
.startTransactions()
1692 # 3) Guess all the needed names (variables in fact) from the current
1694 names
= find_provision_key_parameters(ldbs
.sam
, ldbs
.secrets
, ldbs
.idmap
,
1697 lastProvisionUSNs
= get_last_provision_usn(ldbs
.sam
)
1698 if lastProvisionUSNs
is not None:
1700 for k
in lastProvisionUSNs
.keys():
1701 for r
in lastProvisionUSNs
[k
]:
1705 "Find last provision USN, %d invocation(s) for a total of %d ranges" %
1706 (len(lastProvisionUSNs
.keys()), v
/2 ))
1708 if lastProvisionUSNs
.get("default") is not None:
1709 message(CHANGE
, "Old style for usn ranges used")
1710 lastProvisionUSNs
[str(names
.invocation
)] = lastProvisionUSNs
["default"]
1711 del lastProvisionUSNs
["default"]
1713 message(SIMPLE
, "Your provision lacks provision range information")
1714 if confirm("Do you want to run findprovisionusnranges to try to find them ?", False):
1715 ldbs
.groupedRollback()
1717 (hash_id
, nb_obj
) = findprovisionrange(ldbs
.sam
, ldb
.Dn(ldbs
.sam
, str(names
.rootdn
)))
1718 message(SIMPLE
, "Here is a list of changes that modified more than %d objects in 1 minute." % minobj
)
1719 message(SIMPLE
, "Usually changes made by provision and upgradeprovision are those who affect a couple"
1720 " of hundred of objects or more")
1721 message(SIMPLE
, "Total number of objects: %d" % nb_obj
)
1724 print_provision_ranges(hash_id
, minobj
, None, str(paths
.samdb
), str(names
.invocation
))
1726 message(SIMPLE
, "Once you applied/adapted the change(s) please restart the upgradeprovision script")
1729 # Objects will be created with the admin session
1730 # (not anymore system session)
1731 adm_session
= admin_session(lp
, str(names
.domainsid
))
1732 # So we reget handle on objects
1733 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1734 if not opts
.fixntacl
:
1735 if not sanitychecks(ldbs
.sam
, names
):
1736 message(SIMPLE
, "Sanity checks for the upgrade have failed. "
1737 "Check the messages and correct the errors "
1738 "before rerunning upgradeprovision")
1739 ldbs
.groupedRollback()
1742 # Let's see provision parameters
1743 print_provision_key_parameters(names
)
1745 # 5) With all this information let's create a fresh new provision used as
1747 message(SIMPLE
, "Creating a reference provision")
1748 provisiondir
= tempfile
.mkdtemp(dir=paths
.private_dir
,
1749 prefix
="referenceprovision")
1750 result
= newprovision(names
, creds
, session
, smbconf
, provisiondir
,
1752 result
.report_logger(provision_logger
)
1756 # We need to get a list of object which SD is directly computed from
1757 # defaultSecurityDescriptor.
1758 # This will allow us to know which object we can rebuild the SD in case
1759 # of change of the parent's SD or of the defaultSD.
1760 # Get file paths of this new provision
1761 newpaths
= get_paths(param
, targetdir
=provisiondir
)
1762 new_ldbs
= get_ldbs(newpaths
, creds
, session
, lp
)
1763 new_ldbs
.startTransactions()
1765 populateNotReplicated(new_ldbs
.sam
, names
.schemadn
)
1766 # 8) Populate some associative array to ease the update process
1767 # List of attribute which are link and backlink
1768 populate_links(new_ldbs
.sam
, names
.schemadn
)
1769 # List of attribute with ASN DN synthax)
1770 populate_dnsyntax(new_ldbs
.sam
, names
.schemadn
)
1772 update_privilege(newpaths
.private_dir
, paths
.private_dir
)
1774 oem
= getOEMInfo(ldbs
.sam
, str(names
.rootdn
))
1775 # Do some modification on sam.ldb
1776 ldbs
.groupedCommit()
1777 new_ldbs
.groupedCommit()
1781 if oem
is None or hasATProvision(ldbs
.sam
) or re
.match(".*alpha((9)|(\d\d+)).*", str(oem
)):
1783 # Starting from alpha9 we can consider that the structure is quite ok
1784 # and that we should do only dela
1785 deltaattr
= delta_update_basesamdb(newpaths
.samdb
,
1793 simple_update_basesamdb(newpaths
, paths
, names
)
1794 ldbs
= get_ldbs(paths
, creds
, session
, lp
)
1795 removeProvisionUSN(ldbs
.sam
)
1797 ldbs
.startTransactions()
1798 minUSN
= int(str(get_max_usn(ldbs
.sam
, str(names
.rootdn
)))) + 1
1799 new_ldbs
.startTransactions()
1802 schema
= Schema(names
.domainsid
, schemadn
=str(names
.schemadn
))
1803 # We create a closure that will be invoked just before schema reload
1804 def schemareloadclosure():
1805 basesam
= Ldb(paths
.samdb
, session_info
=session
, credentials
=creds
, lp
=lp
,
1806 options
=["modules:"])
1808 if deltaattr
is not None and len(deltaattr
) > 1:
1811 deltaattr
.remove("dn")
1812 for att
in deltaattr
:
1813 if att
.lower() == "dn":
1815 if (deltaattr
.get(att
) is not None
1816 and deltaattr
.get(att
).flags() != FLAG_MOD_ADD
):
1818 elif deltaattr
.get(att
) is None:
1821 message(CHANGE
, "Applying delta to @ATTRIBUTES")
1822 deltaattr
.dn
= ldb
.Dn(basesam
, "@ATTRIBUTES")
1823 basesam
.modify(deltaattr
)
1825 message(CHANGE
, "Not applying delta to @ATTRIBUTES because "
1826 "there is not only add")
1829 if not update_samdb(new_ldbs
.sam
, ldbs
.sam
, names
, lastProvisionUSNs
,
1830 schema
, schemareloadclosure
):
1831 message(SIMPLE
, "Rolling back all changes. Check the cause"
1833 message(SIMPLE
, "Your system is as it was before the upgrade")
1834 ldbs
.groupedRollback()
1835 new_ldbs
.groupedRollback()
1836 shutil
.rmtree(provisiondir
)
1839 # Try to reapply the change also when we do not change the sam
1840 # as the delta_upgrade
1841 schemareloadclosure()
1842 sync_calculated_attributes(ldbs
.sam
, names
)
1843 res
= ldbs
.sam
.search(expression
="(samaccountname=dns)",
1844 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1845 controls
=["search_options:1:2"])
1847 message(SIMPLE
, "You still have the old DNS object for managing "
1848 "dynamic DNS, but you didn't supply --full so "
1849 "a correct update can't be done")
1850 ldbs
.groupedRollback()
1851 new_ldbs
.groupedRollback()
1852 shutil
.rmtree(provisiondir
)
1855 update_secrets(new_ldbs
.secrets
, ldbs
.secrets
, message
)
1857 res
= ldbs
.sam
.search(expression
="(samaccountname=dns)",
1858 scope
=SCOPE_SUBTREE
, attrs
=["dn"],
1859 controls
=["search_options:1:2"])
1862 ldbs
.sam
.delete(res
[0]["dn"])
1863 res2
= ldbs
.secrets
.search(expression
="(samaccountname=dns)",
1864 scope
=SCOPE_SUBTREE
, attrs
=["dn"])
1865 update_dns_account_password(ldbs
.sam
, ldbs
.secrets
, names
)
1866 message(SIMPLE
, "IMPORTANT!!! "
1867 "If you were using Dynamic DNS before you need "
1868 "to update your configuration, so that the "
1869 "tkey-gssapi-credential has the following value: "
1870 "DNS/%s.%s" % (names
.netbiosname
.lower(),
1871 names
.realm
.lower()))
1873 message(SIMPLE
, "Update machine account")
1874 update_machine_account_password(ldbs
.sam
, ldbs
.secrets
, names
)
1876 dnToRecalculate
.sort(dn_sort
)
1877 # 16) SD should be created with admin but as some previous acl were so wrong
1878 # that admin can't modify them we have first to recreate them with the good
1879 # form but with system account and then give the ownership to admin ...
1880 if str(oem
) != "" and not re
.match(r
'.*alpha(9|\d\d+)', str(oem
)):
1881 message(SIMPLE
, "Fixing very old provision SD")
1882 rebuild_sd(ldbs
.sam
, names
)
1884 # We calculate the max USN before recalculating the SD because we might
1885 # touch object that have been modified after a provision and we do not
1886 # want that the next upgradeprovision thinks that it has a green light
1890 maxUSN
= get_max_usn(ldbs
.sam
, str(names
.rootdn
))
1892 # 18) We rebuild SD if a we have a list of DN to recalculate or if the
1893 # defSDmodified is set.
1894 if defSDmodified
or len(dnToRecalculate
) >0:
1895 message(SIMPLE
, "Some (default) security descriptors (SDs) have "
1896 "changed, recalculating them")
1897 ldbs
.sam
.set_session_info(adm_session
)
1898 rebuild_sd(ldbs
.sam
, names
)
1901 # Now we are quite confident in the recalculate process of the SD, we make
1902 # it optional. And we don't do it if there is DN that we must touch
1903 # as we are assured that on this DNs we will have differences !
1904 # Also the check must be done in a clever way as for the moment we just
1906 if len(dnNotToRecalculate
) == 0 and (opts
.debugchangesd
or opts
.debugall
):
1907 message(CHANGESD
, "Checking recalculated SDs")
1908 check_updated_sd(new_ldbs
.sam
, ldbs
.sam
, names
)
1911 updateOEMInfo(ldbs
.sam
, str(names
.rootdn
))
1913 check_for_DNS(newpaths
.private_dir
, paths
.private_dir
)
1915 if lastProvisionUSNs
is not None:
1916 update_provision_usn(ldbs
.sam
, minUSN
, maxUSN
, names
.invocation
)
1917 if opts
.full
and (names
.policyid
is None or names
.policyid_dc
is None):
1918 update_policyids(names
, ldbs
.sam
)
1920 if opts
.full
or opts
.resetfileacl
or opts
.fixntacl
:
1922 update_gpo(paths
, ldbs
.sam
, names
, lp
, message
, 1)
1923 except ProvisioningError
, e
:
1924 message(ERROR
, "The policy for domain controller is missing. "
1925 "You should restart upgradeprovision with --full")
1927 message(ERROR
, "Setting ACL not supported on your filesystem")
1930 update_gpo(paths
, ldbs
.sam
, names
, lp
, message
, 0)
1931 except ProvisioningError
, e
:
1932 message(ERROR
, "The policy for domain controller is missing. "
1933 "You should restart upgradeprovision with --full")
1934 if not opts
.fixntacl
:
1935 ldbs
.groupedCommit()
1936 new_ldbs
.groupedCommit()
1937 message(SIMPLE
, "Upgrade finished!")
1938 # remove reference provision now that everything is done !
1939 # So we have reindexed first if need when the merged schema was reloaded
1940 # (as new attributes could have quick in)
1941 # But the second part of the update (when we update existing objects
1942 # can also have an influence on indexing as some attribute might have their
1943 # searchflag modificated
1944 message(SIMPLE
, "Reopening samdb to trigger reindexing if needed "
1945 "after modification")
1946 samdb
= Ldb(paths
.samdb
, session_info
=session
, credentials
=creds
, lp
=lp
)
1947 message(SIMPLE
, "Reindexing finished")
1949 shutil
.rmtree(provisiondir
)
1951 ldbs
.groupedRollback()
1952 message(SIMPLE
, "ACLs fixed !")
1953 except StandardError, err
:
1954 message(ERROR
, "A problem occurred while trying to upgrade your "
1955 "provision. A full backup is located at %s" % backupdir
)
1956 if opts
.debugall
or opts
.debugchange
:
1957 (typ
, val
, tb
) = sys
.exc_info()
1958 traceback
.print_exception(typ
, val
, tb
)