s4 upgrade provision: Refactor code to do all the modification within 1 transaction
[ddiss/samba.git] / source4 / scripting / bin / upgradeprovision
1 #!/usr/bin/env python
2 # vim: expandtab
3 #
4 # Copyright (C) Matthieu Patou <mat@matws.net> 2009
5 #
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
9 #
10 #
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.
15 #
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.
20 #
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/>.
23
24
25 import logging
26 import optparse
27 import os
28 import shutil
29 import sys
30 import tempfile
31 import re
32 # Allow to run from s4 source directory (without installing samba)
33 sys.path.insert(0, "bin/python")
34
35 import samba
36 import samba.getopt as options
37 from samba.credentials import DONT_USE_KERBEROS
38 from samba.auth import system_session, admin_session
39 from samba import Ldb, version
40 from ldb import SCOPE_ONELEVEL, SCOPE_SUBTREE, SCOPE_BASE,\
41                 FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE,\
42                 MessageElement, Message, Dn
43 from samba import param
44 from samba.misc import messageEltFlagToString
45 from samba.provision import (find_setup_dir, get_domain_descriptor,
46     get_config_descriptor, secretsdb_self_join, set_gpo_acl, 
47     getpolicypath, create_gpo_struct, ProvisioningError)
48 from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
49 from samba.dcerpc import security
50 from samba.ndr import ndr_unpack
51 from samba.dcerpc.misc import SEC_CHAN_BDC
52 from samba.upgradehelpers import dn_sort, get_paths, newprovision,\
53                                  find_provision_key_parameters, get_ldbs
54
55 replace=2**FLAG_MOD_REPLACE
56 add=2**FLAG_MOD_ADD
57 delete=2**FLAG_MOD_DELETE
58 never=0
59
60 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
61
62 #Errors are always logged
63 ERROR =     -1
64 SIMPLE =     0x00
65 CHANGE =     0x01
66 CHANGESD =     0x02
67 GUESS =     0x04
68 PROVISION =    0x08
69 CHANGEALL =    0xff
70
71 __docformat__ = "restructuredText"
72
73 # Attributes that are never copied from the reference provision (even if they
74 # do not exist in the destination object).
75 # This is most probably because they are populated automatcally when object is
76 # created
77 # This also apply to imported object from reference provision
78 hashAttrNotCopied = {     "dn": 1, "whenCreated": 1, "whenChanged": 1, "objectGUID": 1, "replPropertyMetaData": 1, "uSNChanged": 1,
79                         "uSNCreated": 1, "parentGUID": 1, "objectCategory": 1, "distinguishedName": 1,
80                         "showInAdvancedViewOnly": 1, "instanceType": 1, "cn": 1, "msDS-Behavior-Version":1, "nextRid":1,
81                         "nTMixedDomain": 1, "versionNumber":1, "lmPwdHistory":1, "pwdLastSet": 1, "ntPwdHistory":1, "unicodePwd":1,
82                         "dBCSPwd":1, "supplementalCredentials":1, "gPCUserExtensionNames":1, "gPCMachineExtensionNames":1,
83                         "maxPwdAge":1, "secret":1, "possibleInferiors":1, "privilege":1, "sAMAccountType":1 }
84
85 # Usually for an object that already exists we do not overwrite attributes as
86 # they might have been changed for good reasons. Anyway for a few of them it's
87 # mandatory to replace them otherwise the provision will be broken somehow.
88 hashOverwrittenAtt = {    "prefixMap": replace, "systemMayContain": replace, "systemOnly":replace, "searchFlags":replace,
89                         "mayContain":replace,  "systemFlags":replace, "description":replace,
90                         "operatingSystemVersion":replace, "adminPropertyPages":replace,
91                         "defaultSecurityDescriptor": replace, "wellKnownObjects":replace, "privilege":never, "groupType":replace,
92                         "rIDAvailablePool": never, "defaultSecurityDescriptor": replace + add}
93
94
95 backlinked = []
96 dn_syntax_att = []
97 def define_what_to_log(opts):
98     what = 0
99     if opts.debugchange:
100         what = what | CHANGE
101     if opts.debugchangesd:
102         what = what | CHANGESD
103     if opts.debugguess:
104         what = what | GUESS
105     if opts.debugprovision:
106         what = what | PROVISION
107     if opts.debugall:
108         what = what | CHANGEALL
109     return what
110
111
112 parser = optparse.OptionParser("provision [options]")
113 sambaopts = options.SambaOptions(parser)
114 parser.add_option_group(sambaopts)
115 parser.add_option_group(options.VersionOptions(parser))
116 credopts = options.CredentialsOptions(parser)
117 parser.add_option_group(credopts)
118 parser.add_option("--setupdir", type="string", metavar="DIR",
119                     help="directory with setup files")
120 parser.add_option("--debugprovision", help="Debug provision", action="store_true")
121 parser.add_option("--debugguess", help="Print information on what is different but won't be changed", action="store_true")
122 parser.add_option("--debugchange", help="Print information on what is different but won't be changed", action="store_true")
123 parser.add_option("--debugchangesd", help="Print information security descriptors differences", action="store_true")
124 parser.add_option("--debugall", help="Print all available information (very verbose)", action="store_true")
125 parser.add_option("--full", help="Perform full upgrade of the samdb (schema, configuration, new objects, ...", action="store_true")
126
127 opts = parser.parse_args()[0]
128
129 handler = logging.StreamHandler(sys.stdout)
130 upgrade_logger = logging.getLogger("upgradeprovision")
131 upgrade_logger.addHandler(handler)
132
133 provision_logger = logging.getLogger("provision")
134 provision_logger.addHandler(handler)
135
136 whatToLog = define_what_to_log(opts)
137
138 def message(what, text):
139     """Print a message if this message type has been selected to be printed
140
141     :param what: Category of the message
142     :param text: Message to print """
143     if (whatToLog & what) or what <= 0:
144         upgrade_logger.info("%s", text)
145
146 if len(sys.argv) == 1:
147     opts.interactive = True
148 lp = sambaopts.get_loadparm()
149 smbconf = lp.configfile
150
151 creds = credopts.get_credentials(lp)
152 creds.set_kerberos_state(DONT_USE_KERBEROS)
153 setup_dir = opts.setupdir
154 if setup_dir is None:
155     setup_dir = find_setup_dir()
156
157
158 def identic_rename(ldbobj,dn):
159     """Perform a back and forth rename to trigger renaming on attribute that can't be directly modified.
160
161     :param lbdobj: An Ldb Object
162     :param dn: DN of the object to manipulate """
163     (before,sep,after)=str(dn).partition('=')
164     ldbobj.rename(dn,Dn(ldbobj,"%s=foo%s"%(before,after)))
165     ldbobj.rename(Dn(ldbobj,"%s=foo%s"%(before,after)),dn)
166
167
168 def populate_backlink(samdb, schemadn):
169     """Populate an array with all the back linked attributes
170
171     This attributes that are modified automaticaly when
172     front attibutes are changed
173
174     :param samdb: A LDB object for sam.ldb file
175     :param schemadn: DN of the schema for the partition"""
176     linkedAttHash = get_linked_attributes(Dn(samdb,str(schemadn)), samdb)
177     backlinked.extend(linkedAttHash.values())
178
179 def populate_dnsyntax(samdb, schemadn):
180     """Populate an array with all the attributes that have DN synthax
181        (oid 2.5.5.1)
182
183     :param samdb: A LDB object for sam.ldb file
184     :param schemadn: DN of the schema for the partition"""
185     res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
186                         str(schemadn)), scope=SCOPE_SUBTREE,
187                         attrs=["lDAPDisplayName"])
188     for elem in res:
189         dn_syntax_att.append(elem["lDAPDisplayName"])
190
191
192 def sanitychecks(samdb, names):
193     """Make some checks before trying to update
194
195     :param samdb: An LDB object opened on sam.ldb
196     :param names: list of key provision parameters
197     :return: Status of check (1 for Ok, 0 for not Ok) """
198     res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
199                          scope=SCOPE_SUBTREE, attrs=["dn"],
200                          controls=["search_options:1:2"])
201     if len(res) == 0:
202         print "No DC found, your provision is most probably hardly broken !"
203         return False
204     elif len(res) != 1:
205         print "Found %d domain controllers, for the moment upgradeprovision" \
206               "is not able to handle upgrade on domain with more than one DC, please demote" \
207               " the other(s) DC(s) before upgrading" % len(res)
208         return False
209     else:
210         return True
211
212
213 def print_provision_key_parameters(names):
214     """Do a a pretty print of provision parameters
215
216     :param names: list of key provision parameters """
217     message(GUESS, "rootdn      :"+str(names.rootdn))
218     message(GUESS, "configdn    :"+str(names.configdn))
219     message(GUESS, "schemadn    :"+str(names.schemadn))
220     message(GUESS, "serverdn    :"+str(names.serverdn))
221     message(GUESS, "netbiosname :"+names.netbiosname)
222     message(GUESS, "defaultsite :"+names.sitename)
223     message(GUESS, "dnsdomain   :"+names.dnsdomain)
224     message(GUESS, "hostname    :"+names.hostname)
225     message(GUESS, "domain      :"+names.domain)
226     message(GUESS, "realm       :"+names.realm)
227     message(GUESS, "invocationid:"+names.invocation)
228     message(GUESS, "policyguid  :"+names.policyid)
229     message(GUESS, "policyguiddc:"+str(names.policyid_dc))
230     message(GUESS, "domainsid   :"+str(names.domainsid))
231     message(GUESS, "domainguid  :"+names.domainguid)
232     message(GUESS, "ntdsguid    :"+names.ntdsguid)
233     message(GUESS, "domainlevel :"+str(names.domainlevel))
234
235
236 def handle_special_case(att, delta, new, old):
237     """Define more complicate update rules for some attributes
238
239     :param att: The attribute to be updated
240     :param delta: A messageElement object that correspond to the difference between the updated object and the reference one
241     :param new: The reference object
242     :param old: The Updated object
243     :return: Tru to indicate that the attribute should be kept, False for discarding it
244     """
245     flag = delta.get(att).flags()
246     if (att == "gPLink" or att == "gPCFileSysPath") and \
247         flag == FLAG_MOD_REPLACE and str(new[0].dn).lower() == str(old[0].dn).lower():
248         delta.remove(att)
249         return True
250     if att == "forceLogoff":
251         ref=0x8000000000000000
252         oldval=int(old[0][att][0])
253         newval=int(new[0][att][0])
254         ref == old and ref == abs(new)
255         return True
256     if (att == "adminDisplayName" or att == "adminDescription"):
257         return True
258
259     if (str(old[0].dn) == "CN=Samba4-Local-Domain,%s" % (str(names.schemadn))\
260         and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
261         return True
262
263     if (str(old[0].dn) == "CN=Title,%s"%(str(names.schemadn)) and att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
264         return True
265
266     if ((att == "member" or att == "servicePrincipalName") and flag == FLAG_MOD_REPLACE):
267         hash = {}
268         newval = []
269         changeDelta=0
270         for elem in old[0][att]:
271             hash[str(elem)]=1
272             newval.append(str(elem))
273
274         for elem in new[0][att]:
275             if not hash.has_key(str(elem)):
276                 changeDelta=1
277                 newval.append(str(elem))
278         if changeDelta == 1:
279             delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
280         else:
281             delta.remove(att)
282         return True
283
284     if (str(old[0].dn) == "%s"%(str(names.rootdn)) and att == "subRefs" and flag == FLAG_MOD_REPLACE):
285         return True
286     if str(delta.dn).endswith("CN=DisplaySpecifiers,%s"%names.configdn):
287         return True
288     return False
289
290 def update_secrets(newsecrets_ldb, secrets_ldb):
291     """Update secrets.ldb
292
293     :param newsecrets_ldb: An LDB object that is connected to the secrets.ldb
294                             of the reference provision
295     :param secrets_ldb: An LDB object that is connected to the secrets.ldb
296                             of the updated provision"""
297
298     message(SIMPLE, "update secrets.ldb")
299     reference = newsecrets_ldb.search(expression="dn=@MODULES", base="",
300                                         scope=SCOPE_SUBTREE)
301     current = secrets_ldb.search(expression="dn=@MODULES", base="",
302                                         scope=SCOPE_SUBTREE)
303     delta = secrets_ldb.msg_diff(current[0], reference[0])
304     delta.dn = current[0].dn
305     secrets_ldb.modify(delta)
306
307     reference = newsecrets_ldb.search(expression="objectClass=top", base="",
308                                         scope=SCOPE_SUBTREE, attrs=["dn"])
309     current = secrets_ldb.search(expression="objectClass=top", base="",
310                                         scope=SCOPE_SUBTREE, attrs=["dn"])
311     hash_new = {}
312     hash = {}
313     listMissing = []
314     listPresent = []
315
316     empty = Message()
317     for i in range(0, len(reference)):
318         hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
319
320     # Create a hash for speeding the search of existing object in the
321     # current provision
322     for i in range(0, len(current)):
323         hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
324
325     for k in hash_new.keys():
326         if not hash.has_key(k):
327             listMissing.append(hash_new[k])
328         else:
329             listPresent.append(hash_new[k])
330
331     for entry in listMissing:
332         reference = newsecrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
333         current = secrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
334         delta = secrets_ldb.msg_diff(empty,reference[0])
335         for att in hashAttrNotCopied.keys():
336             delta.remove(att)
337         message(CHANGE, "Entry %s is missing from secrets.ldb"%reference[0].dn)
338         for att in delta:
339             message(CHANGE, " Adding attribute %s"%att)
340         delta.dn = reference[0].dn
341         secrets_ldb.add(delta)
342
343     for entry in listPresent:
344         reference = newsecrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
345         current = secrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
346         delta = secrets_ldb.msg_diff(current[0],reference[0])
347         for att in hashAttrNotCopied.keys():
348             delta.remove(att)
349         for att in delta:
350             if att == "name":
351                 message(CHANGE, "Found attribute name on  %s, must rename the DN "%(current[0].dn))
352                 identic_rename(secrets_ldb,reference[0].dn)
353             else:
354                 delta.remove(att)
355
356     for entry in listPresent:
357         reference = newsecrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
358         current = secrets_ldb.search(expression="dn=%s"%entry,base="", scope=SCOPE_SUBTREE)
359         delta = secrets_ldb.msg_diff(current[0],reference[0])
360         for att in hashAttrNotCopied.keys():
361             delta.remove(att)
362         for att in delta:
363             if att != "dn":
364                 message(CHANGE, " Adding/Changing attribute %s to %s"%(att,current[0].dn))
365
366         delta.dn = current[0].dn
367         secrets_ldb.modify(delta)
368
369
370 def dump_denied_change(dn,att,flagtxt,current,reference):
371     """Print detailed information about why a changed is denied
372
373     :param dn: DN of the object which attribute is denied
374     :param att: Attribute that was supposed to be upgraded
375     :param flagtxt: Type of the update that should be performed (add, change, remove, ...)
376     :param current: Value(s) of the current attribute
377     :param reference: Value(s) of the reference attribute"""
378
379     message(CHANGE, "dn= "+str(dn)+" "+att+" with flag "+flagtxt+" is not allowed to be changed/removed, I discard this change ...")
380     if att != "objectSid" :
381         i = 0
382         for e in range(0,len(current)):
383             message(CHANGE, "old %d : %s"%(i,str(current[e])))
384             i+=1
385         if reference != None:
386             i = 0
387             for e in range(0,len(reference)):
388                 message(CHANGE, "new %d : %s"%(i,str(reference[e])))
389                 i+=1
390     else:
391         message(CHANGE, "old : %s"%str(ndr_unpack( security.dom_sid,current[0])))
392         message(CHANGE, "new : %s"%str(ndr_unpack( security.dom_sid,reference[0])))
393
394
395 def handle_special_add(samdb,dn,names):
396     """Handle special operation (like remove) on some object needed during upgrade
397
398     This is mostly due to wrong creation of the object in previous provision.
399     :param samdb: An Ldb object representing the SAM database
400     :param dn: DN of the object to inspect
401     :param names: list of key provision parameters"""
402     dntoremove = None
403     if str(dn).lower() == ("CN=IIS_IUSRS,CN=Builtin,%s" % names.rootdn).lower():
404         #This entry was misplaced lets remove it if it exists
405         dntoremove = "CN=IIS_IUSRS,CN=Users,%s" % names.rootdn
406
407     if str(dn).lower() == ("CN=Certificate Service DCOM Access,CN=Builtin,%s"%names.rootdn).lower():
408         #This entry was misplaced lets remove it if it exists
409         dntoremove = "CN=Certificate Service DCOM Access,CN=Users,%s"%names.rootdn
410
411     if str(dn).lower() == ("CN=Cryptographic Operators,CN=Builtin,%s"%names.rootdn).lower():
412         #This entry was misplaced lets remove it if it exists
413         dntoremove = "CN=Cryptographic Operators,CN=Users,%s"%names.rootdn
414
415     if str(dn).lower() == ("CN=Event Log Readers,CN=Builtin,%s"%names.rootdn).lower():
416         #This entry was misplaced lets remove it if it exists
417         dntoremove = "CN=Event Log Readers,CN=Users,%s"%names.rootdn
418
419     if dntoremove != None:
420         res = samdb.search(expression="(dn=%s)" % dntoremove, base=str(names.rootdn), scope=SCOPE_SUBTREE, attrs=["dn"], controls=["search_options:1:2"])
421         if len(res) > 0:
422             message(CHANGE,"Existing object %s must be replaced by %s, removing old object"%(dntoremove,str(dn)))
423             samdb.delete(res[0]["dn"])
424
425
426 def check_dn_nottobecreated(hash, index, listdn):
427     """Check if one of the DN present in the list has a creation order greater than the current.
428
429     Hash is indexed by dn to be created, with each key is associated the creation order
430     First dn to be created has the creation order 0, second has 1, ...
431     Index contain the current creation order
432
433     :param hash: Hash holding the different DN of the object to be created as key
434     :param index: Current creation order
435     :param listdn: List of DNs on which the current DN depends on
436     :return: None if the current object do not depend on other object or if all object have been
437     created before."""
438     if listdn == None:
439         return None
440     for dn in listdn:
441         key = str(dn).lower()
442         if hash.has_key(key) and hash[key] > index:
443             return str(dn)
444     return None
445
446
447 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
448     """Add a new object if the dependencies are satisfied
449
450     The function add the object if the object on which it depends are already created
451     :param ref_samdb: Ldb object representing the SAM db of the reference provision
452     :param samdb: Ldb object representing the SAM db of the upgraded provision
453     :param dn: DN of the object to be added
454     :param names: List of key provision parameters
455     :param basedn: DN of the partition to be updated
456     :param hash: Hash holding the different DN of the object to be created as key
457     :param index: Current creation order
458     :return: True if the object was created False otherwise"""
459     handle_special_add(samdb,dn,names)
460     reference = ref_samdb.search(expression="dn=%s" % (str(dn)),base=basedn,
461                     scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
462     empty = Message()
463     delta = samdb.msg_diff(empty,reference[0])
464     delta.dn
465     for att in hashAttrNotCopied.keys():
466         delta.remove(att)
467     for att in backlinked:
468         delta.remove(att)
469     depend_on_yettobecreated = None
470     for att in dn_syntax_att:
471         depend_on_yet_tobecreated = check_dn_nottobecreated(hash,index,delta.get(str(att)))
472         if depend_on_yet_tobecreated != None:
473             message(CHANGE, "Object %s depends on %s in attribute %s, delaying the creation"
474                             %(str(dn),depend_on_yet_tobecreated,str(att)))
475             return False
476     delta.dn = dn
477     message(CHANGE,"Object %s will be added"%dn)
478     samdb.add(delta, ["relax:0"])
479     return True
480
481 def gen_dn_index_hash(listMissing):
482     """Generate a hash associating the DN to its creation order
483
484     :param listMissing: List of DN
485     :return: Hash with DN as keys and creation order as values"""
486     hash = {}
487     for i in range(0, len(listMissing)):
488         hash[str(listMissing[i]).lower()] = i
489     return hash
490
491 def add_deletedobj_containers(ref_samdb, samdb, names):
492     """Add the object containter: CN=Deleted Objects
493
494     This function create the container for each partition that need one and then reference the object into
495     the root of the partition
496     :param ref_samdb: Ldb object representing the SAM db of the reference provision
497     :param samdb: Ldb object representing the SAM db of the upgraded provision
498     :param names: List of key provision parameters"""
499
500     partitions = [str(names.rootdn),str(names.configdn)]
501     for part in partitions:
502         ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",base=part, scope=SCOPE_SUBTREE,attrs=["dn"],controls=["show_deleted:0"])
503         delObjCnt = samdb.search(expression="(cn=Deleted Objects)",base=part, scope=SCOPE_SUBTREE,attrs=["dn"],controls=["show_deleted:0"])
504         if len(ref_delObjCnt) > len(delObjCnt):
505             reference = ref_samdb.search(expression="cn=Deleted Objects",base=part,
506                                          scope=SCOPE_SUBTREE,controls=["show_deleted:0"])
507             empty = Message()
508             delta = samdb.msg_diff(empty,reference[0])
509
510             delta.dn = Dn(samdb,str(reference[0]["dn"]))
511             for att in hashAttrNotCopied.keys():
512                 delta.remove(att)
513             samdb.add(delta)
514
515             listwko = []
516             res = samdb.search(expression="(objectClass=*)",base=part,
517                                scope=SCOPE_BASE, attrs=["dn","wellKnownObjects"])
518
519             targetWKO = "B:32:18E2EA80684F11D2B9AA00C04F79F805:%s" % str(reference[0]["dn"])
520             found = 0
521
522             if len(res[0]) > 0:
523                 wko = res[0]["wellKnownObjects"]
524
525                 # The wellKnownObject that we want to add.
526
527                 for o in wko:
528                     if str(o) == targetWKO:
529                         found = 1
530                     listwko.append(str(o))
531             if not found:
532                 listwko.append(targetWKO)
533
534                 delta = Message()
535                 delta.dn = Dn(samdb,str(res[0]["dn"]))
536                 delta["wellKnownObjects"] = MessageElement(listwko, FLAG_MOD_REPLACE, "wellKnownObjects" )
537                 samdb.modify(delta)
538
539 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
540     """Add the missing object whose DN is the list
541
542     The function add the object if the object on which it depends are already created
543     :param ref_samdb: Ldb object representing the SAM db of the reference provision
544     :param samdb: Ldb object representing the SAM db of the upgraded provision
545     :param dn: DN of the object to be added
546     :param names: List of key provision parameters
547     :param basedn: DN of the partition to be updated
548     :param list: List of DN to be added in the upgraded provision"""
549     listMissing = []
550     listDefered = list
551
552     while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
553         index = 0
554         listMissing = listDefered
555         listDefered = []
556         hashMissing = gen_dn_index_hash(listMissing)
557         for dn in listMissing:
558             ret = add_missing_object(ref_samdb,samdb,dn,names,basedn,hashMissing,index)
559             index = index + 1
560             if ret == 0:
561                 #DN can't be created because it depends on some other DN in the list
562                 listDefered.append(dn)
563     if len(listDefered) != 0:
564         raise ProvisioningError("Unable to insert missing elements: circular references")
565
566
567 def update_partition(ref_samdb, samdb, basedn, names, use_ref_schema, highestUSN):
568     """Check differences between the reference provision and the upgraded one.
569
570     It looks for all objects which base DN is name. If ischema is "false" then
571     the scan is done in cross partition mode.
572     If "use_ref_schema" is true, then special handling is done for dealing with schema
573
574     This function will also add the missing object and update existing object to add
575     or remove attributes that were missing.
576     :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference provision
577     :param samdb: An LDB object connected to the sam.ldb of the update provision
578     :param basedn: String value of the DN of the partition
579     :param names: List of key provision parameters
580     :param use_ref_schema: A flag to indicate if we should use the shema of the reference provision
581     :param highestUSN:  The highest USN modified by provision/upgradeprovision last time"""
582
583     hash_new = {}
584     hash = {}
585     listMissing = []
586     listPresent = []
587     reference = []
588     current = []
589
590     # Connect to the reference provision and get all the attribute in the
591     # partition referred by name
592     reference = ref_samdb.search(expression="objectClass=*",base=basedn, scope=SCOPE_SUBTREE,attrs=["dn"],controls=["search_options:1:2"])
593     current = samdb.search(expression="objectClass=*",base=basedn, scope=SCOPE_SUBTREE,attrs=["dn"],controls=["search_options:1:2"])
594
595     # Create a hash for speeding the search of new object
596     for i in range(0,len(reference)):
597         hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
598
599     # Create a hash for speeding the search of existing object in the
600     # current provision
601     for i in range(0,len(current)):
602         hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
603
604
605     for k in hash_new.keys():
606         if not hash.has_key(k):
607             if not str(hash_new[k]) == "CN=Deleted Objects,%s" % names.rootdn:
608                 listMissing.append(hash_new[k])
609         else:
610             listPresent.append(hash_new[k])
611
612     # Sort the missing object in order to have object of the lowest level
613     # first (which can be containers for higher level objects)
614     listMissing.sort(dn_sort)
615     listPresent.sort(dn_sort)
616
617     if use_ref_schema == 1:
618         # The following lines (up to the for loop) is to load the up to
619         # date schema into our current LDB
620         # a complete schema is needed as the insertion of attributes
621         # and class is done against it
622         # and the schema is self validated
623         # The double ldb open and schema validation is taken from the
624         # initial provision script
625         # it's not certain that it is really needed ....
626         schema = Schema(setup_path, names.domainsid, schemadn=basedn, serverdn=str(names.serverdn))
627         # Load the schema from the one we computed earlier
628         samdb.set_schema_from_ldb(schema.ldb)
629
630     try:
631         message(SIMPLE,"There are %d missing objects" % (len(listMissing)))
632         add_deletedobj_containers(ref_samdb, samdb, names)
633
634         add_missing_entries(ref_samdb,samdb,names,basedn,listMissing)
635         changed = 0
636
637         for dn in listPresent:
638             reference = ref_samdb.search(expression="dn=%s" % (str(dn)),base=basedn, scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
639             current = samdb.search(expression="dn=%s" % (str(dn)),base=basedn, scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
640             if ((str(current[0].dn) != str(reference[0].dn)) and (str(current[0].dn).upper() == str(reference[0].dn).upper())):
641                 message(CHANGE,"Name are the same but case change, let's rename %s to %s" % (str(current[0].dn),str(reference[0].dn)))
642                 identic_rename(samdb,reference[0].dn)
643                 current = samdb.search(expression="dn=%s" % (str(dn)),base=basedn, scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
644
645             delta = samdb.msg_diff(current[0],reference[0])
646             for att in hashAttrNotCopied.keys():
647                 delta.remove(att)
648             for att in backlinked:
649                 delta.remove(att)
650             delta.remove("parentGUID")
651             nb = 0
652
653             for att in delta:
654                 msgElt = delta.get(att)
655                 if att == "dn":
656                     continue
657                 if att == "name":
658                     delta.remove(att)
659                     continue
660                 if (not hashOverwrittenAtt.has_key(att) or not (hashOverwrittenAtt.get(att)&2^msgElt.flags())):
661                     if  hashOverwrittenAtt.has_key(att) and hashOverwrittenAtt.get(att)==never:
662                         delta.remove(att)
663                         continue
664                     if not handle_special_case(att,delta,reference,current) and msgElt.flags()!=FLAG_MOD_ADD:
665                         if opts.debugchange or opts.debugall:
666                             try:
667                                 dump_denied_change(dn,att,messageEltFlagToString(msgElt.flags()),current[0][att],reference[0][att])
668                             except KeyError:
669                                 dump_denied_change(dn,att,messageEltFlagToString(msgElt.flags()),current[0][att],None)
670                         delta.remove(att)
671
672             delta.dn = dn
673             if len(delta.items()) >1:
674                 attributes=",".join(delta.keys())
675                 message(CHANGE,"%s is different from the reference one, changed attributes: %s" % (dn,attributes))
676                 changed = changed + 1
677                 samdb.modify(delta)
678
679         message(SIMPLE,"There are %d changed objects" % (changed))
680         return 1
681
682     except Exception, err:
683         message(ERROR,"Exception during upgrade of samdb: %s" % str(err))
684         return 0
685
686
687 def check_updated_sd(ref_sam,cur_sam, names):
688     """Check if the security descriptor in the upgraded provision are the same as the reference
689
690     :param ref_sam: A LDB object connected to the sam.ldb file used as the reference provision
691     :param cur_sam: A LDB object connected to the sam.ldb file used as upgraded provision
692     :param names: List of key provision parameters"""
693     reference = ref_sam.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_SUBTREE,attrs=["dn","nTSecurityDescriptor"],controls=["search_options:1:2"])
694     current = cur_sam.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_SUBTREE,attrs=["dn","nTSecurityDescriptor"],controls=["search_options:1:2"])
695     hash_new = {}
696     for i in range(0,len(reference)):
697         hash_new[str(reference[i]["dn"]).lower()] = ndr_unpack(security.descriptor,str(reference[i]["nTSecurityDescriptor"])).as_sddl(names.domainsid)
698
699     for i in range(0,len(current)):
700         key = str(current[i]["dn"]).lower()
701         if hash_new.has_key(key):
702             sddl = ndr_unpack(security.descriptor,str(current[i]["nTSecurityDescriptor"])).as_sddl(names.domainsid)
703             if sddl != hash_new[key]:
704                 print "%s \nnew sddl /  sddl in ref" % key
705                 print "%s\n%s\n" % (sddl,hash_new[key])
706
707
708 def rebuild_sd(samdb, names):
709     """Rebuild security descriptor of the current provision from scratch
710
711     During the different pre release of samba4 security descriptors (SD) were notarly broken (up to alpha11 included)
712     This function allow to get them back in order, this function make the assumption that nobody has modified manualy an SD
713     and so SD can be safely recalculated from scratch to get them right.
714
715     :param names: List of key provision parameters"""
716
717     # First update the SD for the rootdn
718     res = samdb.search(expression="objectClass=*", base=str(names.rootdn), scope=SCOPE_BASE,\
719                          attrs=["dn", "whenCreated"], controls=["search_options:1:2"])
720     delta = Message()
721     delta.dn = Dn(samdb,str(res[0]["dn"]))
722     descr = get_domain_descriptor(names.domainsid)
723     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, "nTSecurityDescriptor")
724     samdb.modify(delta,["recalculate_sd:0"])
725     # Then the config dn
726     res = samdb.search(expression="objectClass=*",base=str(names.configdn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
727     delta = Message()
728     delta.dn = Dn(samdb,str(res[0]["dn"]))
729     descr = get_config_descriptor(names.domainsid)
730     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, "nTSecurityDescriptor" )
731     samdb.modify(delta,["recalculate_sd:0"])
732     # Then the schema dn
733     res = samdb.search(expression="objectClass=*",base=str(names.schemadn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
734     delta = Message()
735     delta.dn = Dn(samdb,str(res[0]["dn"]))
736     descr = get_schema_descriptor(names.domainsid)
737     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, "nTSecurityDescriptor" )
738     samdb.modify(delta,["recalculate_sd:0"])
739
740     # Then the rest
741     hash = {}
742     res = samdb.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_SUBTREE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
743     for obj in res:
744         if not (str(obj["dn"]) == str(names.rootdn) or
745             str(obj["dn"]) == str(names.configdn) or \
746             str(obj["dn"]) == str(names.schemadn)):
747             hash[str(obj["dn"])] = obj["whenCreated"]
748
749     listkeys = hash.keys()
750     listkeys.sort(dn_sort)
751
752     for key in listkeys:
753         try:
754             delta = Message()
755             delta.dn = Dn(samdb,key)
756             delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE, "whenCreated" )
757             samdb.modify(delta,["recalculate_sd:0"])
758         except:
759             # XXX: We should always catch an explicit exception.
760             # What could go wrong here?
761             samdb.transaction_cancel()
762             res = samdb.search(expression="objectClass=*", base=str(names.rootdn), scope=SCOPE_SUBTREE,\
763                                  attrs=["dn","nTSecurityDescriptor"], controls=["search_options:1:2"])
764             print "bad stuff" +ndr_unpack(security.descriptor,str(res[0]["nTSecurityDescriptor"])).as_sddl(names.domainsid)
765             return
766
767
768 def getLastProvisionUSN(paths, creds, session, lp):
769     """Get the lastest USN modified by a provision or an upgradeprovision
770
771     :param paths: An object holding the different importants paths for upgraded provision object
772     :param creds: Credential used for openning LDB files
773     :param session: Session to use for openning LDB files
774     :param lp: A loadparam object
775     :return an integer corresponding to the highest USN modified by (upgrade)provision, 0 is this value is unknown"""
776
777     sam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp, options=["modules:"] )
778     entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % LAST_PROVISION_USN_ATTRIBUTE, scope=SCOPE_SUBTREE,attrs=[LAST_PROVISION_USN_ATTRIBUTE])
779     if len(entry):
780         message(CHANGE,"Find a last provision USN: %d" % entry[0][LAST_PROVISION_USN_ATTRIBUTE])
781         return entry[0][LAST_PROVISION_USN_ATTRIBUTE]
782     else:
783         return 0
784
785
786
787
788 def delta_update_basesamdb(refpaths, paths, creds, session, lp):
789     """Update the provision container db: sam.ldb
790     This function is aimed for alpha9 and newer;
791
792     :param refpaths: An object holding the different importants paths for reference provision object
793     :param paths: An object holding the different importants paths for upgraded provision object
794     :param creds: Credential used for openning LDB files
795     :param session: Session to use for openning LDB files
796     :param lp: A loadparam object"""
797
798     message(SIMPLE,"Update base samdb by searching difference with reference one")
799     refsam = Ldb(refpaths.samdb, session_info=session, credentials=creds, lp=lp, options=["modules:"] )
800     sam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp, options=["modules:"] )
801
802     empty = Message()
803
804     reference = refsam.search(expression="")
805
806     for refentry in reference:
807         entry = sam.search(expression="dn=%s" % refentry["dn"],scope=SCOPE_SUBTREE)
808         if not len(entry[0]):
809             message(CHANGE,"Adding %s to sam db" % str(delta.dn))
810             delta = sam.msg_diff(empty,refentry)
811             delta.dn = refentry.dn
812             sam.add(delta)
813         else:
814             delta = sam.msg_diff(entry[0],refentry)
815             if refentry.dn == "@PARTITION" and delta.get(LAST_PROVISION_USN_ATTRIBUTE):
816                 delta.remove(LAST_PROVISION_USN_ATTRIBUTE)
817             if len(delta.items()) > 1:
818                 delta.dn = refentry.dn
819                 sam.modify(delta)
820
821
822 def simple_update_basesamdb(newpaths, paths, names):
823     """Update the provision container db: sam.ldb
824     This function is aimed at very old provision (before alpha9)
825
826     :param newpaths: List of paths for different provision objects from the reference provision
827     :param paths: List of paths for different provision objects from the upgraded provision
828     :param names: List of key provision parameters"""
829
830     message(SIMPLE, "Copy samdb")
831     shutil.copy(newpaths.samdb,paths.samdb)
832
833     message(SIMPLE, "Update partitions filename if needed")
834     schemaldb = os.path.join(paths.private_dir, "schema.ldb")
835     configldb = os.path.join(paths.private_dir, "configuration.ldb")
836     usersldb = os.path.join(paths.private_dir, "users.ldb")
837     samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
838
839     if not os.path.isdir(samldbdir):
840         os.mkdir(samldbdir)
841         os.chmod(samldbdir,0700)
842     if os.path.isfile(schemaldb):
843         shutil.copy(schemaldb, os.path.join(samldbdir, "%s.ldb" % str(names.schemadn).upper()))
844         os.remove(schemaldb)
845     if os.path.isfile(usersldb):
846         shutil.copy(usersldb, os.path.join(samldbdir, "%s.ldb" % str(names.rootdn).upper()))
847         os.remove(usersldb)
848     if os.path.isfile(configldb):
849         shutil.copy(configldb, os.path.join(samldbdir, "%s.ldb" % str(names.configdn).upper()))
850         os.remove(configldb)
851
852
853 def update_privilege(ref_private_path, cur_private_path):
854     """Update the privilege database
855
856     :param ref_private_path: Path to the private directory of the reference provision.
857     :param cur_private_path: Path to the private directory of the current (and to be updated) provision."""
858     message(SIMPLE, "Copy privilege")
859     shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
860                 os.path.join(cur_private_path, "privilege.ldb"))
861
862
863 def update_samdb(ref_samdb, samdb, names, highestUSN):
864     """Upgrade the SAM DB contents for all the provision partitions
865
866     :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference provision
867     :param samdb: An LDB object connected to the sam.ldb of the update provision
868     :param names: List of key provision parameters
869     :param highestUSN:  The highest USN modified by provision/upgradeprovision last time"""
870
871     message(SIMPLE, "Starting update of samdb")
872     ret = update_partition(ref_samdb, samdb, str(names.rootdn), names, 1, highestUSN)
873     if ret:
874         message(SIMPLE,"Update of samdb finished")
875         return 1
876     else:
877         message(SIMPLE,"Update failed")
878         return 0
879
880
881 def update_machine_account_password(samdb, secrets_ldb, names):
882     """Update (change) the password of the current DC both in the SAM db and in secret one
883
884     :param samdb: An LDB object related to the sam.ldb file of a given provision
885     :param secrets_ldb: An LDB object related to the secrets.ldb file of a given provision
886     :param names: List of key provision parameters"""
887
888     message(SIMPLE,"Update machine account")
889     secrets_msg = secrets_ldb.search(expression=("samAccountName=%s$" % names.netbiosname), attrs=["secureChannelType"])
890     if int(secrets_msg[0]["secureChannelType"][0]) == SEC_CHAN_BDC:
891         res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname), attrs=[])
892         assert(len(res) == 1)
893
894         msg = Message(res[0].dn)
895         machinepass = samba.generate_random_password(128, 255)
896         msg["userPassword"] = MessageElement(machinepass, FLAG_MOD_REPLACE, "userPassword")
897         samdb.modify(msg)
898
899         res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname),
900                      attrs=["msDs-keyVersionNumber"])
901         assert(len(res) == 1)
902         kvno = int(str(res[0]["msDs-keyVersionNumber"]))
903
904         secretsdb_self_join(secrets_ldb, domain=names.domain,
905                     realm=names.realm or sambaopts._lp.get('realm'),
906                     domainsid=names.domainsid,
907                     dnsdomain=names.dnsdomain,
908                     netbiosname=names.netbiosname,
909                     machinepass=machinepass,
910                     key_version_number=kvno,
911                     secure_channel_type=int(secrets_msg[0]["secureChannelType"][0]))
912     else:
913         raise ProvisioningError("Unable to find a Secure Channel of type SEC_CHAN_BDC")
914
915
916 def update_gpo(paths,creds,session,names):
917     """Create missing GPO file object if needed
918
919     Set ACL correctly also.
920     """
921     dir = getpolicypath(paths.sysvol,names.dnsdomain,names.policyid)
922     if not os.path.isdir(dir):
923         create_gpo_struct(dir)
924
925     dir = getpolicypath(paths.sysvol,names.dnsdomain,names.policyid_dc)
926     if not os.path.isdir(dir):
927         create_gpo_struct(dir)
928     samdb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp)
929     set_gpo_acl(paths.sysvol, names.dnsdomain, names.domainsid,
930         names.domaindn, samdb, lp)
931
932
933 def getOEMInfo(samdb, rootdn):
934     """Return OEM Information on the top level
935     Samba4 use to store version info in this field
936
937     :param samdb: An LDB object connect to sam.ldb
938     :param rootdn: Root DN of the domain
939     :return: The content of the field oEMInformation (if any)"""
940     res = samdb.search(expression="(objectClass=*)", base=str(rootdn),
941                             scope=SCOPE_BASE, attrs=["dn", "oEMInformation"])
942     if len(res) > 0:
943         info = res[0]["oEMInformation"]
944         return info
945     else:
946         return ""
947
948
949
950 def updateProvisionUSN(samdb, names):
951     """Update the field provisionUSN in sam.ldb
952     This field is used to track the highest USN of a modified or created object.
953     This value is used afterward by next provision to figure out if the field have been
954     modified since last provision.
955
956     :param samdb: An LDB object connect to sam.ldb
957     :param names: Key provision parameters"""
958     message(SIMPLE,"Updating the highest USN modified by upgrade: This is a stub function")
959
960
961
962 def updateOEMInfo(samdb, names):
963     res = samdb.search(expression="(objectClass=*)", base=str(names.rootdn),
964                             scope=SCOPE_BASE, attrs=["dn", "oEMInformation"])
965     if len(res) > 0:
966         info = res[0]["oEMInformation"]
967         info = "%s, upgrade to %s" % (info, version)
968         delta = Message()
969         delta.dn = Dn(samdb, str(res[0]["dn"]))
970         delta["oEMInformation"] = MessageElement(info, FLAG_MOD_REPLACE,
971             "oEMInformation" )
972         samdb.modify(delta)
973
974
975 def setup_path(file):
976     return os.path.join(setup_dir, file)
977
978
979 if __name__ == '__main__':
980     # From here start the big steps of the program
981     # First get files paths
982     paths = get_paths(param, smbconf=smbconf)
983     paths.setup = setup_dir
984     # Get ldbs with the system session, it is needed for searching provision parameters
985     session = system_session()
986
987     # This variable will hold the last provision USN once if it exists.
988     lastProvisionUSN = getLastProvisionUSN(paths, creds, session, lp)
989
990     ldbs = get_ldbs(paths, creds, session, lp)
991     ldbs.startTransactions()
992
993     # Guess all the needed names (variables in fact) from the current
994     # provision.
995     names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, paths, smbconf, lp)
996
997     # Objects will be created with the admin session (not anymore system session)
998     adm_session = admin_session(lp, str(names.domainsid))
999     # So we reget handle on objects
1000     # ldbs = get_ldbs(paths, creds, adm_session, lp)
1001
1002
1003     if not sanitychecks(ldbs.sam, names):
1004         message(SIMPLE,"Sanity checks for the upgrade fails, checks messages and correct them before rerunning upgradeprovision")
1005         sys.exit(1)
1006
1007     # Let's see provision parameters
1008     print_provision_key_parameters(names)
1009
1010     # With all this information let's create a fresh new provision used as reference
1011     message(SIMPLE, "Creating a reference provision")
1012     provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1013                                     prefix="referenceprovision")
1014     newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
1015                     provision_logger)
1016
1017     # TODO
1018     # We need to get a list of object which SD is directly computed from
1019     # defaultSecurityDescriptor.
1020     # This will allow us to know which object we can rebuild the SD in case
1021     # of change of the parent's SD or of the defaultSD.
1022     # Get file paths of this new provision
1023     newpaths = get_paths(param, targetdir=provisiondir)
1024     new_ldbs = get_ldbs(newpaths, creds, session, lp)
1025     new_ldbs.startTransactions()
1026
1027     # Populate some associative array to ease the update process
1028     populate_backlink(new_ldbs.sam, names.schemadn) # List of attribute which are backlink
1029     populate_dnsyntax(new_ldbs.sam, names.schemadn) # List of attribute with ASN DN synthax)
1030
1031     update_privilege(newpaths.private_dir,paths.private_dir)
1032     oem = getOEMInfo(ldbs.sam, names.rootdn)
1033     # Do some modification on sam.ldb
1034     ldbs.groupedCommit()
1035     if re.match(".*alpha((9)|(\d\d+)).*",str(oem)):
1036         # Starting from alpha9 we can consider that the structure is quite ok and that we should do only dela
1037         new_ldbs.groupedCommit()
1038         delta_update_basesamdb(newpaths, paths, creds, session, lp)
1039         ldbs.startTransactions()
1040         new_ldbs.startTransactions()
1041     else:
1042         simple_update_basesamdb(newpaths, paths, names)
1043         ldbs = get_ldbs(paths, creds, session, lp)
1044         ldbs.startTransactions()
1045
1046     if opts.full:
1047         if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSN):
1048             message(SIMPLE,"Rollbacking every changes. Check the reason of the problem")
1049             message(SIMPLE,"In any case your system as it was before the upgrade")
1050             ldbs.groupedRollback()
1051             new_ldbs.groupedRollback()
1052             shutil.rmtree(provisiondir)
1053             sys.exit(1)
1054
1055     update_secrets(new_ldbs.secrets,ldbs.secrets)
1056     update_machine_account_password(ldbs.sam,ldbs.secrets, names)
1057
1058     # SD should be created with admin but as some previous acl were so wrong that admin can't modify them we have first
1059     # to recreate them with the good form but with system account and then give the ownership to admin ...
1060     message(SIMPLE, "Updating SD")
1061     if not re.match(r'alpha(9|\d\d+)',str(oem)):
1062         rebuild_sd(ldbs.sam,names)
1063
1064     # We rebuild SD only when we do not have a lastProvisionUSN because otherwise SD have been already updated if needed
1065     if lastProvisionUSN == 0:
1066         ldbs.sam.set_session_info(adm_session)
1067         rebuild_sd(ldbs.sam, names)
1068         check_updated_sd(new_ldbs.sam,ldbs.sam, names)
1069
1070     updateOEMInfo(ldbs.sam,names)
1071     updateProvisionUSN(ldbs.sam,names)
1072     ldbs.groupedCommit()
1073     new_ldbs.groupedCommit()
1074     message(SIMPLE, "Upgrade finished !")
1075     # remove reference provision now that everything is done !
1076     shutil.rmtree(provisiondir)