f0baabcfae0f0270063960f0a5a8f4b98f3575d8
[obnox/samba/samba-obnox.git] / source4 / scripting / bin / upgradeprovision
1 #!/usr/bin/env python
2 # vim: expandtab
3 #
4 # Copyright (C) Matthieu Patou <mat@matws.net> 2009 - 2010
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 import traceback
33 # Allow to run from s4 source directory (without installing samba)
34 sys.path.insert(0, "bin/python")
35
36 import ldb
37 import samba
38 import samba.getopt as options
39
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)
46 from samba import param, dsdb, Ldb
47 from samba.provision import (get_domain_descriptor,
48                             get_config_descriptor,
49                             ProvisioningError, get_last_provision_usn,
50                             get_max_usn, update_provision_usn, setup_path)
51 from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
52 from samba.dcerpc import security, drsblobs, xattr
53 from samba.ndr import ndr_unpack
54 from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
55                                  find_provision_key_parameters, get_ldbs,
56                                  usn_in_range, identic_rename, get_diff_sddls,
57                                  update_secrets, CHANGE, ERROR, SIMPLE,
58                                  CHANGEALL, GUESS, CHANGESD, PROVISION,
59                                  updateOEMInfo, getOEMInfo, update_gpo,
60                                  delta_update_basesamdb, update_policyids,
61                                  update_machine_account_password,
62                                  search_constructed_attrs_stored,
63                                  int64range2str, update_dns_account_password,
64                                  increment_calculated_keyversion_number)
65
66 replace=2**FLAG_MOD_REPLACE
67 add=2**FLAG_MOD_ADD
68 delete=2**FLAG_MOD_DELETE
69 never=0
70
71
72 # Will be modified during provision to tell if default sd has been modified
73 # somehow ...
74
75 #Errors are always logged
76
77 __docformat__ = "restructuredText"
78
79 # Attributes that are never copied from the reference provision (even if they
80 # do not exist in the destination object).
81 # This is most probably because they are populated automatcally when object is
82 # created
83 # This also apply to imported object from reference provision
84 hashAttrNotCopied = {   "dn": 1, "whenCreated": 1, "whenChanged": 1,
85                         "objectGUID": 1, "uSNCreated": 1,
86                         "replPropertyMetaData": 1, "uSNChanged": 1,
87                         "parentGUID": 1, "objectCategory": 1,
88                         "distinguishedName": 1, "nTMixedDomain": 1,
89                         "showInAdvancedViewOnly": 1, "instanceType": 1,
90                         "msDS-Behavior-Version":1, "nextRid":1, "cn": 1,
91                         "lmPwdHistory":1, "pwdLastSet": 1,
92                         "ntPwdHistory":1, "unicodePwd":1,"dBCSPwd":1,
93                         "supplementalCredentials":1, "gPCUserExtensionNames":1,
94                         "gPCMachineExtensionNames":1,"maxPwdAge":1, "secret":1,
95                         "possibleInferiors":1, "privilege":1,
96                         "sAMAccountType":1 }
97
98 # Usually for an object that already exists we do not overwrite attributes as
99 # they might have been changed for good reasons. Anyway for a few of them it's
100 # mandatory to replace them otherwise the provision will be broken somehow.
101 # But for attribute that are just missing we do not have to specify them as the default
102 # behavior is to add missing attribute
103 hashOverwrittenAtt = {  "prefixMap": replace, "systemMayContain": replace,
104                         "systemOnly":replace, "searchFlags":replace,
105                         "mayContain":replace, "systemFlags":replace+add,
106                         "description":replace, "operatingSystemVersion":replace,
107                         "adminPropertyPages":replace, "groupType":replace,
108                         "wellKnownObjects":replace, "privilege":never,
109                         "defaultSecurityDescriptor": replace,
110                         "rIDAvailablePool": never,
111                         "rIDNextRID": add, "rIDUsedPool": never,
112                         "defaultSecurityDescriptor": replace + add,
113                         "isMemberOfPartialAttributeSet": delete,
114                         "attributeDisplayNames": replace + add,
115                         "versionNumber": add}
116
117 backlinked = []
118 forwardlinked = set()
119 dn_syntax_att = []
120 def define_what_to_log(opts):
121     what = 0
122     if opts.debugchange:
123         what = what | CHANGE
124     if opts.debugchangesd:
125         what = what | CHANGESD
126     if opts.debugguess:
127         what = what | GUESS
128     if opts.debugprovision:
129         what = what | PROVISION
130     if opts.debugall:
131         what = what | CHANGEALL
132     return what
133
134
135 parser = optparse.OptionParser("provision [options]")
136 sambaopts = options.SambaOptions(parser)
137 parser.add_option_group(sambaopts)
138 parser.add_option_group(options.VersionOptions(parser))
139 credopts = options.CredentialsOptions(parser)
140 parser.add_option_group(credopts)
141 parser.add_option("--setupdir", type="string", metavar="DIR",
142                   help="directory with setup files")
143 parser.add_option("--debugprovision", help="Debug provision", action="store_true")
144 parser.add_option("--debugguess", action="store_true",
145                   help="Print information on what is different but won't be changed")
146 parser.add_option("--debugchange", action="store_true",
147                   help="Print information on what is different but won't be changed")
148 parser.add_option("--debugchangesd", action="store_true",
149                   help="Print security descriptor differences")
150 parser.add_option("--debugall", action="store_true",
151                   help="Print all available information (very verbose)")
152 parser.add_option("--resetfileacl", action="store_true",
153                   help="Force a reset on filesystem acls in sysvol / netlogon share")
154 parser.add_option("--full", action="store_true",
155                   help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
156
157 opts = parser.parse_args()[0]
158
159 handler = logging.StreamHandler(sys.stdout)
160 upgrade_logger = logging.getLogger("upgradeprovision")
161 upgrade_logger.setLevel(logging.INFO)
162
163 upgrade_logger.addHandler(handler)
164
165 provision_logger = logging.getLogger("provision")
166 provision_logger.addHandler(handler)
167
168 whatToLog = define_what_to_log(opts)
169
170 def message(what, text):
171     """Print a message if this message type has been selected to be printed
172
173     :param what: Category of the message
174     :param text: Message to print """
175     if (whatToLog & what) or what <= 0:
176         upgrade_logger.info("%s", text)
177
178 if len(sys.argv) == 1:
179     opts.interactive = True
180 lp = sambaopts.get_loadparm()
181 smbconf = lp.configfile
182
183 creds = credopts.get_credentials(lp)
184 creds.set_kerberos_state(DONT_USE_KERBEROS)
185
186
187
188 def check_for_DNS(refprivate, private):
189     """Check if the provision has already the requirement for dynamic dns
190
191     :param refprivate: The path to the private directory of the reference
192                        provision
193     :param private: The path to the private directory of the upgraded
194                     provision"""
195
196     spnfile = "%s/spn_update_list" % private
197     dnsfile = "%s/dns_update_list" % private
198     namedfile = lp.get("dnsupdate:path")
199
200     if not namedfile:
201        namedfile = "%s/named.conf.update" % private
202
203     if not os.path.exists(spnfile):
204         shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
205
206     if not os.path.exists(dnsfile):
207         shutil.copy("%s/dns_update_list" % refprivate, "%s" % dnsfile)
208
209     destdir = "%s/new_dns" % private
210     dnsdir = "%s/dns" % private
211
212     if not os.path.exists(namedfile):
213         if not os.path.exists(destdir):
214             os.mkdir(destdir)
215         if not os.path.exists(dnsdir):
216             os.mkdir(dnsdir)
217         shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
218         shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
219         message(SIMPLE, "It seems that your provision did not integrate "
220                 "new rules for dynamic dns update of domain related entries")
221         message(SIMPLE, "A copy of the new bind configuration files and "
222                 "template has been put in %s, you should read them and "
223                 "configure dynamic dns updates" % destdir)
224
225
226 def populate_links(samdb, schemadn):
227     """Populate an array with all the back linked attributes
228
229     This attributes that are modified automaticaly when
230     front attibutes are changed
231
232     :param samdb: A LDB object for sam.ldb file
233     :param schemadn: DN of the schema for the partition"""
234     linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
235     backlinked.extend(linkedAttHash.values())
236     for t in linkedAttHash.keys():
237         forwardlinked.add(t)
238
239
240 def populate_dnsyntax(samdb, schemadn):
241     """Populate an array with all the attributes that have DN synthax
242        (oid 2.5.5.1)
243
244     :param samdb: A LDB object for sam.ldb file
245     :param schemadn: DN of the schema for the partition"""
246     res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
247                         str(schemadn)), scope=SCOPE_SUBTREE,
248                         attrs=["lDAPDisplayName"])
249     for elem in res:
250         dn_syntax_att.append(elem["lDAPDisplayName"])
251
252
253 def sanitychecks(samdb, names):
254     """Make some checks before trying to update
255
256     :param samdb: An LDB object opened on sam.ldb
257     :param names: list of key provision parameters
258     :return: Status of check (1 for Ok, 0 for not Ok) """
259     res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
260                          scope=SCOPE_SUBTREE, attrs=["dn"],
261                          controls=["search_options:1:2"])
262     if len(res) == 0:
263         print "No DC found. Your provision is most probably broken!"
264         return False
265     elif len(res) != 1:
266         print "Found %d domain controllers. For the moment " \
267               "upgradeprovision is not able to handle an upgrade on a " \
268               "domain with more than one DC. Please demote the other " \
269               "DC(s) before upgrading" % len(res)
270         return False
271     else:
272         return True
273
274
275 def print_provision_key_parameters(names):
276     """Do a a pretty print of provision parameters
277
278     :param names: list of key provision parameters """
279     message(GUESS, "rootdn      :" + str(names.rootdn))
280     message(GUESS, "configdn    :" + str(names.configdn))
281     message(GUESS, "schemadn    :" + str(names.schemadn))
282     message(GUESS, "serverdn    :" + str(names.serverdn))
283     message(GUESS, "netbiosname :" + names.netbiosname)
284     message(GUESS, "defaultsite :" + names.sitename)
285     message(GUESS, "dnsdomain   :" + names.dnsdomain)
286     message(GUESS, "hostname    :" + names.hostname)
287     message(GUESS, "domain      :" + names.domain)
288     message(GUESS, "realm       :" + names.realm)
289     message(GUESS, "invocationid:" + names.invocation)
290     message(GUESS, "policyguid  :" + names.policyid)
291     message(GUESS, "policyguiddc:" + str(names.policyid_dc))
292     message(GUESS, "domainsid   :" + str(names.domainsid))
293     message(GUESS, "domainguid  :" + names.domainguid)
294     message(GUESS, "ntdsguid    :" + names.ntdsguid)
295     message(GUESS, "domainlevel :" + str(names.domainlevel))
296
297
298 def handle_special_case(att, delta, new, old, usn, basedn, aldb):
299     """Define more complicate update rules for some attributes
300
301     :param att: The attribute to be updated
302     :param delta: A messageElement object that correspond to the difference
303                   between the updated object and the reference one
304     :param new: The reference object
305     :param old: The Updated object
306     :param usn: The highest usn modified by a previous (upgrade)provision
307     :param basedn: The base DN of the provision
308     :param aldb: An ldb object used to build DN
309     :return: True to indicate that the attribute should be kept, False for
310              discarding it"""
311
312     flag = delta.get(att).flags()
313     # We do most of the special case handle if we do not have the
314     # highest usn as otherwise the replPropertyMetaData will guide us more
315     # correctly
316     if usn is None:
317         if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and
318             ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT,"
319                         "CN=Services,CN=Configuration,%s" % basedn)
320                         == old[0].dn):
321             return True
322         if (att == "userAccountControl" and flag == FLAG_MOD_REPLACE and
323             ldb.Dn(aldb, "CN=Administrator,CN=Users,%s" % basedn)
324                         == old[0].dn):
325             message(SIMPLE, "We suggest that you change the userAccountControl"
326                             " for user Administrator from value %d to %d" %
327                             (int(str(old[0][att])), int(str(new[0][att]))))
328             return False
329         if (att == "minPwdAge" and flag == FLAG_MOD_REPLACE):
330             if (long(str(old[0][att])) == 0):
331                 delta[att] = MessageElement(new[0][att], FLAG_MOD_REPLACE, att)
332             return True
333
334         if (att == "member" and flag == FLAG_MOD_REPLACE):
335             hash = {}
336             newval = []
337             changeDelta=0
338             for elem in old[0][att]:
339                 hash[str(elem).lower()]=1
340                 newval.append(str(elem))
341
342             for elem in new[0][att]:
343                 if not hash.has_key(str(elem).lower()):
344                     changeDelta=1
345                     newval.append(str(elem))
346             if changeDelta == 1:
347                 delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
348             else:
349                 delta.remove(att)
350             return True
351
352         if (att in ("gPLink", "gPCFileSysPath") and
353             flag == FLAG_MOD_REPLACE and
354             str(new[0].dn).lower() == str(old[0].dn).lower()):
355             delta.remove(att)
356             return True
357
358         if att == "forceLogoff":
359             ref=0x8000000000000000
360             oldval=int(old[0][att][0])
361             newval=int(new[0][att][0])
362             ref == old and ref == abs(new)
363             return True
364
365         if att in ("adminDisplayName", "adminDescription"):
366             return True
367
368         if (str(old[0].dn) == "CN=Samba4-Local-Domain, %s" % (names.schemadn)
369             and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
370             return True
371
372         if (str(old[0].dn) == "CN=Title, %s" % (str(names.schemadn)) and
373                 att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
374             return True
375
376         if (str(old[0].dn) == "%s" % (str(names.rootdn))
377                 and att == "subRefs" and flag == FLAG_MOD_REPLACE):
378             return True
379         #Allow to change revision of ForestUpdates objects
380         if (att == "revision" or att == "objectVersion"):
381             if str(delta.dn).lower().find("domainupdates") and str(delta.dn).lower().find("forestupdates") > 0:
382                 return True
383         if str(delta.dn).endswith("CN=DisplaySpecifiers, %s" % names.configdn):
384             return True
385
386     # This is a bit of special animal as we might have added
387     # already SPN entries to the list that has to be modified
388     # So we go in detail to try to find out what has to be added ...
389     if (att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):
390         hash = {}
391         newval = []
392         changeDelta=0
393         for elem in old[0][att]:
394             hash[str(elem)]=1
395             newval.append(str(elem))
396
397         for elem in new[0][att]:
398             if not hash.has_key(str(elem)):
399                 changeDelta=1
400                 newval.append(str(elem))
401         if changeDelta == 1:
402             delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
403         else:
404             delta.remove(att)
405         return True
406
407     return False
408
409 def dump_denied_change(dn, att, flagtxt, current, reference):
410     """Print detailed information about why a change is denied
411
412     :param dn: DN of the object which attribute is denied
413     :param att: Attribute that was supposed to be upgraded
414     :param flagtxt: Type of the update that should be performed
415                     (add, change, remove, ...)
416     :param current: Value(s) of the current attribute
417     :param reference: Value(s) of the reference attribute"""
418
419     message(CHANGE, "dn= " + str(dn)+" " + att+" with flag " + flagtxt
420                 + " must not be changed/removed. Discarding the change")
421     if att == "objectSid" :
422         message(CHANGE, "old : %s" % ndr_unpack(security.dom_sid, current[0]))
423         message(CHANGE, "new : %s" % ndr_unpack(security.dom_sid, reference[0]))
424     elif att == "rIDPreviousAllocationPool" or att == "rIDAllocationPool":
425         message(CHANGE, "old : %s" % int64range2str(current[0]))
426         message(CHANGE, "new : %s" % int64range2str(reference[0]))
427     else:
428         i = 0
429         for e in range(0, len(current)):
430             message(CHANGE, "old %d : %s" % (i, str(current[e])))
431             i+=1
432         if reference is not None:
433             i = 0
434             for e in range(0, len(reference)):
435                 message(CHANGE, "new %d : %s" % (i, str(reference[e])))
436                 i+=1
437
438 def handle_special_add(samdb, dn, names):
439     """Handle special operation (like remove) on some object needed during
440     upgrade
441
442     This is mostly due to wrong creation of the object in previous provision.
443     :param samdb: An Ldb object representing the SAM database
444     :param dn: DN of the object to inspect
445     :param names: list of key provision parameters
446     """
447
448     dntoremove = None
449     objDn = Dn(samdb, "CN=IIS_IUSRS, CN=Builtin, %s" % names.rootdn)
450     if dn == objDn :
451         #This entry was misplaced lets remove it if it exists
452         dntoremove = "CN=IIS_IUSRS, CN=Users, %s" % names.rootdn
453
454     objDn = Dn(samdb,
455                 "CN=Certificate Service DCOM Access, CN=Builtin, %s" % names.rootdn)
456     if dn == objDn:
457         #This entry was misplaced lets remove it if it exists
458         dntoremove = "CN=Certificate Service DCOM Access,"\
459                      "CN=Users, %s" % names.rootdn
460
461     objDn = Dn(samdb, "CN=Cryptographic Operators, CN=Builtin, %s" % names.rootdn)
462     if dn == objDn:
463         #This entry was misplaced lets remove it if it exists
464         dntoremove = "CN=Cryptographic Operators, CN=Users, %s" % names.rootdn
465
466     objDn = Dn(samdb, "CN=Event Log Readers, CN=Builtin, %s" % names.rootdn)
467     if dn == objDn:
468         #This entry was misplaced lets remove it if it exists
469         dntoremove = "CN=Event Log Readers, CN=Users, %s" % names.rootdn
470
471     objDn = Dn(samdb,"CN=System,CN=WellKnown Security Principals,"
472                      "CN=Configuration,%s" % names.rootdn)
473     if dn == objDn:
474         oldDn = Dn(samdb,"CN=Well-Known-Security-Id-System,"
475                          "CN=WellKnown Security Principals,"
476                          "CN=Configuration,%s" % names.rootdn)
477
478         res = samdb.search(expression="(dn=%s)" % oldDn,
479                             base=str(names.rootdn),
480                             scope=SCOPE_SUBTREE, attrs=["dn"],
481                             controls=["search_options:1:2"])
482
483         res2 = samdb.search(expression="(dn=%s)" % dn,
484                             base=str(names.rootdn),
485                             scope=SCOPE_SUBTREE, attrs=["dn"],
486                             controls=["search_options:1:2"])
487
488         if len(res) > 0 and len(res2) == 0:
489             message(CHANGE, "Existing object %s must be replaced by %s. "
490                             "Renaming old object" % (str(oldDn), str(dn)))
491             samdb.rename(oldDn, objDn, ["relax:0", "provision:0"])
492
493         return 0
494
495     if dntoremove is not None:
496         res = samdb.search(expression="(cn=RID Set)",
497                             base=str(names.rootdn),
498                             scope=SCOPE_SUBTREE, attrs=["dn"],
499                             controls=["search_options:1:2"])
500
501         if len(res) == 0:
502             return 2
503         res = samdb.search(expression="(dn=%s)" % dntoremove,
504                             base=str(names.rootdn),
505                             scope=SCOPE_SUBTREE, attrs=["dn"],
506                             controls=["search_options:1:2"])
507         if len(res) > 0:
508             message(CHANGE, "Existing object %s must be replaced by %s. "
509                             "Removing old object" % (dntoremove, str(dn)))
510             samdb.delete(res[0]["dn"])
511             return 0
512
513     return 1
514
515
516 def check_dn_nottobecreated(hash, index, listdn):
517     """Check if one of the DN present in the list has a creation order
518        greater than the current.
519
520     Hash is indexed by dn to be created, with each key
521     is associated the creation order.
522
523     First dn to be created has the creation order 0, second has 1, ...
524     Index contain the current creation order
525
526     :param hash: Hash holding the different DN of the object to be
527                   created as key
528     :param index: Current creation order
529     :param listdn: List of DNs on which the current DN depends on
530     :return: None if the current object do not depend on other
531               object or if all object have been created before."""
532     if listdn is None:
533         return None
534     for dn in listdn:
535         key = str(dn).lower()
536         if hash.has_key(key) and hash[key] > index:
537             return str(dn)
538     return None
539
540
541
542 def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
543     """Add a new object if the dependencies are satisfied
544
545     The function add the object if the object on which it depends are already
546     created
547
548     :param ref_samdb: Ldb object representing the SAM db of the reference
549                        provision
550     :param samdb: Ldb object representing the SAM db of the upgraded
551                    provision
552     :param dn: DN of the object to be added
553     :param names: List of key provision parameters
554     :param basedn: DN of the partition to be updated
555     :param hash: Hash holding the different DN of the object to be
556                   created as key
557     :param index: Current creation order
558     :return: True if the object was created False otherwise"""
559
560     ret = handle_special_add(samdb, dn, names)
561
562     if ret == 2:
563         return False
564
565     if ret == 0:
566         return True
567
568
569     reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
570                     scope=SCOPE_SUBTREE, controls=["search_options:1:2"])
571     empty = Message()
572     delta = samdb.msg_diff(empty, reference[0])
573     delta.dn
574     skip = False
575     try:
576         if str(reference[0].get("cn"))  == "RID Set":
577             for klass in reference[0].get("objectClass"):
578                 if str(klass).lower() == "ridset":
579                     skip = True
580     finally:
581         if delta.get("objectSid"):
582             sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
583             m = re.match(r".*-(\d+)$", sid)
584             if m and int(m.group(1))>999:
585                 delta.remove("objectSid")
586         for att in hashAttrNotCopied.keys():
587             delta.remove(att)
588         for att in backlinked:
589             delta.remove(att)
590         depend_on_yettobecreated = None
591         for att in dn_syntax_att:
592             depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
593                                                                 delta.get(str(att)))
594             if depend_on_yet_tobecreated is not None:
595                 message(CHANGE, "Object %s depends on %s in attribute %s. "
596                                 "Delaying the creation" % (dn,
597                                           depend_on_yet_tobecreated, att))
598                 return False
599
600         delta.dn = dn
601         if not skip:
602             message(CHANGE,"Object %s will be added" % dn)
603             samdb.add(delta, ["relax:0", "provision:0"])
604         else:
605             message(CHANGE,"Object %s was skipped" % dn)
606
607         return True
608
609 def gen_dn_index_hash(listMissing):
610     """Generate a hash associating the DN to its creation order
611
612     :param listMissing: List of DN
613     :return: Hash with DN as keys and creation order as values"""
614     hash = {}
615     for i in range(0, len(listMissing)):
616         hash[str(listMissing[i]).lower()] = i
617     return hash
618
619 def add_deletedobj_containers(ref_samdb, samdb, names):
620     """Add the object containter: CN=Deleted Objects
621
622     This function create the container for each partition that need one and
623     then reference the object into the root of the partition
624
625     :param ref_samdb: Ldb object representing the SAM db of the reference
626                        provision
627     :param samdb: Ldb object representing the SAM db of the upgraded provision
628     :param names: List of key provision parameters"""
629
630
631     wkoPrefix = "B:32:18E2EA80684F11D2B9AA00C04F79F805"
632     partitions = [str(names.rootdn), str(names.configdn)]
633     for part in partitions:
634         ref_delObjCnt = ref_samdb.search(expression="(cn=Deleted Objects)",
635                                             base=part, scope=SCOPE_SUBTREE,
636                                             attrs=["dn"],
637                                             controls=["show_deleted:0",
638                                                       "show_recycled:0"])
639         delObjCnt = samdb.search(expression="(cn=Deleted Objects)",
640                                     base=part, scope=SCOPE_SUBTREE,
641                                     attrs=["dn"],
642                                     controls=["show_deleted:0",
643                                               "show_recycled:0"])
644         if len(ref_delObjCnt) > len(delObjCnt):
645             reference = ref_samdb.search(expression="cn=Deleted Objects",
646                                             base=part, scope=SCOPE_SUBTREE,
647                                             controls=["show_deleted:0",
648                                                       "show_recycled:0"])
649             empty = Message()
650             delta = samdb.msg_diff(empty, reference[0])
651
652             delta.dn = Dn(samdb, str(reference[0]["dn"]))
653             for att in hashAttrNotCopied.keys():
654                 delta.remove(att)
655
656             modcontrols = ["relax:0", "provision:0"]
657             samdb.add(delta, modcontrols)
658
659             listwko = []
660             res = samdb.search(expression="(objectClass=*)", base=part,
661                                scope=SCOPE_BASE,
662                                attrs=["dn", "wellKnownObjects"])
663
664             targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
665             found = False
666
667             if len(res[0]) > 0:
668                 wko = res[0]["wellKnownObjects"]
669
670                 # The wellKnownObject that we want to add.
671                 for o in wko:
672                     if str(o) == targetWKO:
673                         found = True
674                     listwko.append(str(o))
675
676             if not found:
677                 listwko.append(targetWKO)
678
679                 delta = Message()
680                 delta.dn = Dn(samdb, str(res[0]["dn"]))
681                 delta["wellKnownObjects"] = MessageElement(listwko,
682                                                 FLAG_MOD_REPLACE,
683                                                 "wellKnownObjects" )
684                 samdb.modify(delta)
685
686 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
687     """Add the missing object whose DN is the list
688
689     The function add the object if the objects on which it depends are
690     already created.
691
692     :param ref_samdb: Ldb object representing the SAM db of the reference
693                       provision
694     :param samdb: Ldb object representing the SAM db of the upgraded
695                   provision
696     :param dn: DN of the object to be added
697     :param names: List of key provision parameters
698     :param basedn: DN of the partition to be updated
699     :param list: List of DN to be added in the upgraded provision"""
700
701     listMissing = []
702     listDefered = list
703
704     while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
705         index = 0
706         listMissing = listDefered
707         listDefered = []
708         hashMissing = gen_dn_index_hash(listMissing)
709         for dn in listMissing:
710             ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
711                                         hashMissing, index)
712             index = index + 1
713             if ret == 0:
714                 # DN can't be created because it depends on some
715                 # other DN in the list
716                 listDefered.append(dn)
717
718     if len(listDefered) != 0:
719         raise ProvisioningError("Unable to insert missing elements: "
720                                 "circular references")
721
722 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
723     """This function handle updates on links
724
725     :param samdb: An LDB object pointing to the updated provision
726     :param att: Attribute to update
727     :param basedn: The root DN of the provision
728     :param dn: The DN of the inspected object
729     :param value: The value of the attribute
730     :param ref_value: The value of this attribute in the reference provision
731     :param delta: The MessageElement object that will be applied for
732                    transforming the current provision"""
733
734     res = samdb.search(expression="dn=%s" % dn, base=basedn,
735                         controls=["search_options:1:2", "reveal:1"],
736                         attrs=[att])
737
738     blacklist = {}
739     hash = {}
740     newlinklist = []
741     changed = False
742
743     newlinklist.extend(value)
744
745     for e in value:
746         hash[e] = 1
747     # for w2k domain level the reveal won't reveal anything ...
748     # it means that we can readd links that were removed on purpose ...
749     # Also this function in fact just accept add not removal
750
751     for e in res[0][att]:
752         if not hash.has_key(e):
753             # We put in the blacklist all the element that are in the "revealed"
754             # result and not in the "standard" result
755             # This element are links that were removed before and so that
756             # we don't wan't to readd
757             blacklist[e] = 1
758
759     for e in ref_value:
760         if not blacklist.has_key(e) and not hash.has_key(e):
761             newlinklist.append(str(e))
762             changed = True
763     if changed:
764         delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
765     else:
766         delta.remove(att)
767
768
769 msg_elt_flag_strs = {
770     ldb.FLAG_MOD_ADD: "MOD_ADD",
771     ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
772     ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
773
774
775 def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
776     """ This function updates the object that are already present in the
777         provision
778
779     :param ref_samdb: An LDB object pointing to the reference provision
780     :param samdb: An LDB object pointing to the updated provision
781     :param basedn: A string with the value of the base DN for the provision
782                    (ie. DC=foo, DC=bar)
783     :param listPresent: A list of object that is present in the provision
784     :param usns: A list of USN range modified by previous provision and
785                  upgradeprovision
786     :param invocationid: The value of the invocationid for the current DC"""
787
788     global defSDmodified
789     # This hash is meant to speedup lookup of attribute name from an oid,
790     # it's for the replPropertyMetaData handling
791     hash_oid_name = {}
792     res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
793                         controls=["search_options:1:2"], attrs=["attributeID",
794                         "lDAPDisplayName"])
795     if len(res) > 0:
796         for e in res:
797             strDisplay = str(e.get("lDAPDisplayName"))
798             hash_oid_name[str(e.get("attributeID"))] = strDisplay
799     else:
800         msg = "Unable to insert missing elements: circular references"
801         raise ProvisioningError(msg)
802
803     changed = 0
804     controls = ["search_options:1:2", "sd_flags:1:2"]
805     for dn in listPresent:
806         reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
807                                         scope=SCOPE_SUBTREE,
808                                         controls=controls)
809         current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
810                                 scope=SCOPE_SUBTREE, controls=controls)
811
812         if (
813              (str(current[0].dn) != str(reference[0].dn)) and
814              (str(current[0].dn).upper() == str(reference[0].dn).upper())
815            ):
816             message(CHANGE, "Names are the same except for the case. "
817                             "Renaming %s to %s" % (str(current[0].dn),
818                                                    str(reference[0].dn)))
819             identic_rename(samdb, reference[0].dn)
820             current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
821                                     scope=SCOPE_SUBTREE,
822                                     controls=controls)
823
824         delta = samdb.msg_diff(current[0], reference[0])
825
826         for att in hashAttrNotCopied.keys():
827             delta.remove(att)
828
829         for att in backlinked:
830             delta.remove(att)
831
832         delta.remove("name")
833
834         if len(delta.items()) > 1 and usns is not None:
835             # Fetch the replPropertyMetaData
836             res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
837                                 scope=SCOPE_SUBTREE, controls=controls,
838                                 attrs=["replPropertyMetaData"])
839             ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
840                                 str(res[0]["replPropertyMetaData"])).ctr
841
842             hash_attr_usn = {}
843             for o in ctr.array:
844                 # We put in this hash only modification
845                 # made on the current host
846                 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
847                 if str(o.originating_invocation_id) == str(invocationid):
848                 # Note we could just use 1 here
849                     hash_attr_usn[att] = o.originating_usn
850                 else:
851                     hash_attr_usn[att] = -1
852
853         isFirst = 0
854         txt = ""
855
856         for att in list(delta):
857             if usns is not None:
858                 # We have updated by provision usn information so let's exploit
859                 # replMetadataProperties
860                 if att in forwardlinked:
861                     curval = current[0].get(att, ())
862                     refval = reference[0].get(att, ())
863                     handle_links(samdb, att, basedn, current[0]["dn"],
864                                     curval, refval, delta)
865                     continue
866
867                 if isFirst == 0 and len(delta.items())>1:
868                     isFirst = 1
869                     txt = "%s\n" % (str(dn))
870                 if att == "dn":
871                     # There is always a dn attribute after a msg_diff
872                     continue
873                 if att == "rIDAvailablePool":
874                     delta.remove(att)
875                     continue
876                 if att == "objectSid":
877                     delta.remove(att)
878                     continue
879                 if att == "creationTime":
880                     delta.remove(att)
881                     continue
882                 if att == "oEMInformation":
883                     delta.remove(att)
884                     continue
885                 if att == "msDs-KeyVersionNumber":
886                 # This is the kvno of the computer/user it's a very bad
887                 # idea to change it
888                     delta.remove(att)
889                     continue
890                 if handle_special_case(att, delta, reference, current, usns, basedn, samdb):
891                     # This attribute is "complicated" to handle and handling
892                     # was done in handle_special_case
893                     continue
894                 attrUSN = hash_attr_usn.get(att)
895                 if att == "forceLogoff" and attrUSN is None:
896                     continue
897                 if  attrUSN is None:
898                     delta.remove(att)
899                     continue
900                 if att == "nTSecurityDescriptor":
901                     cursd = ndr_unpack(security.descriptor,
902                         str(current[0]["nTSecurityDescriptor"]))
903                     cursddl = cursd.as_sddl(names.domainsid)
904                     refsd = ndr_unpack(security.descriptor,
905                         str(reference[0]["nTSecurityDescriptor"]))
906                     refsddl = cursd.as_sddl(names.domainsid)
907
908                     if get_diff_sddls(refsddl, cursddl) == "":
909                        message(CHANGE, "sd are identical")
910                     else:
911                        message(CHANGE, "sd are not identical")
912                 if attrUSN == -1:
913                     # This attribute was last modified by another DC forget
914                     # about it
915                     message(CHANGE, "%sAttribute: %s has been "
916                             "created/modified/deleted by another DC. "
917                             "Doing nothing" % (txt, att))
918                     txt = ""
919                     delta.remove(att)
920                     continue
921                 elif not usn_in_range(int(attrUSN), usns):
922                     message(CHANGE, "%sAttribute: %s was not "
923                                     "created/modified/deleted during a "
924                                     "provision or upgradeprovision. Current "
925                                     "usn: %d. Doing nothing" % (txt, att,
926                                                                 attrUSN))
927                     txt = ""
928                     delta.remove(att)
929                     continue
930                 else:
931                     if att == "defaultSecurityDescriptor":
932                         defSDmodified = True
933                     if attrUSN:
934                         message(CHANGE, "%sAttribute: %s will be modified"
935                                         "/deleted it was last modified "
936                                         "during a provision. Current usn: "
937                                         "%d" % (txt, att, attrUSN))
938                         txt = ""
939                     else:
940                         message(CHANGE, "%sAttribute: %s will be added because "
941                                         "it did not exist before" % (txt, att))
942                         txt = ""
943                     continue
944
945             else:
946             # Old school way of handling things for pre alpha12 upgrade
947                 defSDmodified = True
948                 msgElt = delta.get(att)
949
950                 if att == "nTSecurityDescriptor":
951                     delta.remove(att)
952                     continue
953
954                 if att == "dn":
955                     continue
956
957                 if not hashOverwrittenAtt.has_key(att):
958                     if msgElt.flags() != FLAG_MOD_ADD:
959                         if not handle_special_case(att, delta, reference, current,
960                                                     usns, basedn, samdb):
961                             if opts.debugchange or opts.debugall:
962                                 try:
963                                     dump_denied_change(dn, att,
964                                         msg_elt_flag_strs[msgElt.flags()],
965                                         current[0][att], reference[0][att])
966                                 except KeyError:
967                                     dump_denied_change(dn, att,
968                                         msg_elt_flag_strs[msgElt.flags()],
969                                         current[0][att], None)
970                             delta.remove(att)
971                         continue
972                 else:
973                     if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
974                         continue
975                     elif  hashOverwrittenAtt.get(att)==never:
976                         delta.remove(att)
977                         continue
978
979         delta.dn = dn
980         if len(delta.items()) >1:
981             attributes=", ".join(delta.keys())
982             modcontrols = []
983             relaxedatt = ['iscriticalsystemobject', 'grouptype']
984             # Let's try to reduce as much as possible the use of relax control
985             #for checkedatt in relaxedatt:
986             for attr in delta.keys():
987                 if attr.lower() in relaxedatt:
988                     modcontrols = ["relax:0", "provision:0"]
989             message(CHANGE, "%s is different from the reference one, changed"
990                             " attributes: %s\n" % (dn, attributes))
991             changed += 1
992             samdb.modify(delta, modcontrols)
993     return changed
994
995 def reload_full_schema(samdb, names):
996     """Load the updated schema with all the new and existing classes
997        and attributes.
998
999     :param samdb: An LDB object connected to the sam.ldb of the update
1000                   provision
1001     :param names: List of key provision parameters
1002     """
1003
1004     current = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1005                                 scope=SCOPE_SUBTREE)
1006     schema_ldif = ""
1007     prefixmap_data = ""
1008
1009     for ent in current:
1010         schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
1011
1012     prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
1013     prefixmap_data = b64encode(prefixmap_data)
1014
1015     # We don't actually add this ldif, just parse it
1016     prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap_data
1017
1018     dsdb._dsdb_set_schema_from_ldif(samdb, prefixmap_ldif, schema_ldif)
1019
1020
1021 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, prereloadfunc):
1022     """Check differences between the reference provision and the upgraded one.
1023
1024     It looks for all objects which base DN is name.
1025
1026     This function will also add the missing object and update existing object
1027     to add or remove attributes that were missing.
1028
1029     :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1030                        reference provision
1031     :param samdb: An LDB object connected to the sam.ldb of the update
1032                   provision
1033     :param basedn: String value of the DN of the partition
1034     :param names: List of key provision parameters
1035     :param schema: A Schema object
1036     :param provisionUSNs:  The USNs modified by provision/upgradeprovision
1037                            last time
1038     :param prereloadfunc: A function that must be executed just before the reload
1039                   of the schema
1040     """
1041
1042     hash_new = {}
1043     hash = {}
1044     listMissing = []
1045     listPresent = []
1046     reference = []
1047     current = []
1048
1049     # Connect to the reference provision and get all the attribute in the
1050     # partition referred by name
1051     reference = ref_samdb.search(expression="objectClass=*", base=basedn,
1052                                     scope=SCOPE_SUBTREE, attrs=["dn"],
1053                                     controls=["search_options:1:2"])
1054
1055     current = samdb.search(expression="objectClass=*", base=basedn,
1056                                 scope=SCOPE_SUBTREE, attrs=["dn"],
1057                                 controls=["search_options:1:2"])
1058     # Create a hash for speeding the search of new object
1059     for i in range(0, len(reference)):
1060         hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
1061
1062     # Create a hash for speeding the search of existing object in the
1063     # current provision
1064     for i in range(0, len(current)):
1065         hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
1066
1067
1068     for k in hash_new.keys():
1069         if not hash.has_key(k):
1070             if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
1071                 listMissing.append(hash_new[k])
1072         else:
1073             listPresent.append(hash_new[k])
1074
1075     # Sort the missing object in order to have object of the lowest level
1076     # first (which can be containers for higher level objects)
1077     listMissing.sort(dn_sort)
1078     listPresent.sort(dn_sort)
1079
1080     # The following lines is to load the up to
1081     # date schema into our current LDB
1082     # a complete schema is needed as the insertion of attributes
1083     # and class is done against it
1084     # and the schema is self validated
1085     samdb.set_schema(schema)
1086     try:
1087         message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
1088         add_deletedobj_containers(ref_samdb, samdb, names)
1089
1090         add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
1091
1092         prereloadfunc()
1093         message(SIMPLE, "Reloading a merged schema, which might trigger "
1094                         "reindexing so please be patient")
1095         reload_full_schema(samdb, names)
1096         message(SIMPLE, "Schema reloaded!")
1097
1098         changed = update_present(ref_samdb, samdb, basedn, listPresent,
1099                                     provisionUSNs, names.invocation)
1100         message(SIMPLE, "There are %d changed objects" % (changed))
1101         return 1
1102
1103     except StandardError, err:
1104         message(ERROR, "Exception during upgrade of samdb:")
1105         (typ, val, tb) = sys.exc_info()
1106         traceback.print_exception(typ, val, tb)
1107         return 0
1108
1109
1110 def check_updated_sd(ref_sam, cur_sam, names):
1111     """Check if the security descriptor in the upgraded provision are the same
1112        as the reference
1113
1114     :param ref_sam: A LDB object connected to the sam.ldb file used as
1115                     the reference provision
1116     :param cur_sam: A LDB object connected to the sam.ldb file used as
1117                     upgraded provision
1118     :param names: List of key provision parameters"""
1119     reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1120                                 scope=SCOPE_SUBTREE,
1121                                 attrs=["dn", "nTSecurityDescriptor"],
1122                                 controls=["search_options:1:2"])
1123     current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1124                                 scope=SCOPE_SUBTREE,
1125                                 attrs=["dn", "nTSecurityDescriptor"],
1126                                 controls=["search_options:1:2"])
1127     hash = {}
1128     for i in range(0, len(reference)):
1129         refsd = ndr_unpack(security.descriptor,
1130                     str(reference[i]["nTSecurityDescriptor"]))
1131         hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
1132
1133
1134     for i in range(0, len(current)):
1135         key = str(current[i]["dn"]).lower()
1136         if hash.has_key(key):
1137             cursd = ndr_unpack(security.descriptor,
1138                         str(current[i]["nTSecurityDescriptor"]))
1139             sddl = cursd.as_sddl(names.domainsid)
1140             if sddl != hash[key]:
1141                 txt = get_diff_sddls(hash[key], sddl)
1142                 if txt != "":
1143                     message(CHANGESD, "On object %s ACL is different"
1144                                       " \n%s" % (current[i]["dn"], txt))
1145
1146
1147
1148 def fix_partition_sd(samdb, names):
1149     """This function fix the SD for partition containers (basedn, configdn, ...)
1150     This is needed because some provision use to have broken SD on containers
1151
1152     :param samdb: An LDB object pointing to the sam of the current provision
1153     :param names: A list of key provision parameters
1154     """
1155     # First update the SD for the rootdn
1156     res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1157                          scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1158                          controls=["search_options:1:2"])
1159     delta = Message()
1160     delta.dn = Dn(samdb, str(res[0]["dn"]))
1161     descr = get_domain_descriptor(names.domainsid)
1162     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1163                                                     "nTSecurityDescriptor")
1164     samdb.modify(delta)
1165     # Then the config dn
1166     res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1167                         scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1168                         controls=["search_options:1:2"])
1169     delta = Message()
1170     delta.dn = Dn(samdb, str(res[0]["dn"]))
1171     descr = get_config_descriptor(names.domainsid)
1172     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1173                                                     "nTSecurityDescriptor" )
1174     samdb.modify(delta)
1175     # Then the schema dn
1176     res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1177                         scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1178                         controls=["search_options:1:2"])
1179
1180     delta = Message()
1181     delta.dn = Dn(samdb, str(res[0]["dn"]))
1182     descr = get_schema_descriptor(names.domainsid)
1183     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1184                                                     "nTSecurityDescriptor" )
1185     samdb.modify(delta)
1186
1187 def rebuild_sd(samdb, names):
1188     """Rebuild security descriptor of the current provision from scratch
1189
1190     During the different pre release of samba4 security descriptors (SD)
1191     were notarly broken (up to alpha11 included)
1192     This function allow to get them back in order, this function make the
1193     assumption that nobody has modified manualy an SD
1194     and so SD can be safely recalculated from scratch to get them right.
1195
1196     :param names: List of key provision parameters"""
1197
1198
1199     hash = {}
1200     res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1201                         scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1202                         controls=["search_options:1:2"])
1203     for obj in res:
1204         if not (str(obj["dn"]) == str(names.rootdn) or
1205             str(obj["dn"]) == str(names.configdn) or
1206             str(obj["dn"]) == str(names.schemadn)):
1207             hash[str(obj["dn"])] = obj["whenCreated"]
1208
1209     listkeys = hash.keys()
1210     listkeys.sort(dn_sort)
1211
1212     for key in listkeys:
1213         try:
1214             delta = Message()
1215             delta.dn = Dn(samdb, key)
1216             delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1217                                                     "whenCreated" )
1218             samdb.modify(delta, ["recalculate_sd:0"])
1219         except:
1220             # XXX: We should always catch an explicit exception.
1221             # What could go wrong here?
1222             samdb.transaction_cancel()
1223             res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1224                                 scope=SCOPE_SUBTREE,
1225                                 attrs=["dn", "nTSecurityDescriptor"],
1226                                 controls=["search_options:1:2"])
1227             badsd = ndr_unpack(security.descriptor,
1228                         str(res[0]["nTSecurityDescriptor"]))
1229             print "bad stuff %s" % badsd.as_sddl(names.domainsid)
1230             return
1231
1232 def removeProvisionUSN(samdb):
1233         attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1234         entry = samdb.search(expression="dn=@PROVISION", base = "",
1235                                 scope=SCOPE_SUBTREE,
1236                                 controls=["search_options:1:2"],
1237                                 attrs=attrs)
1238         empty = Message()
1239         empty.dn = entry[0].dn
1240         delta = samdb.msg_diff(entry[0], empty)
1241         delta.remove("dn")
1242         delta.dn = entry[0].dn
1243         samdb.modify(delta)
1244
1245 def remove_stored_generated_attrs(paths, creds, session, lp):
1246     """Remove previously stored constructed attributes
1247
1248     :param paths: List of paths for different provision objects
1249                         from the upgraded provision
1250     :param creds: A credential object
1251     :param session: A session object
1252     :param lp: A line parser object
1253     :return: An associative array whose key are the different constructed
1254              attributes and the value the dn where this attributes were found.
1255      """
1256
1257
1258 def simple_update_basesamdb(newpaths, paths, names):
1259     """Update the provision container db: sam.ldb
1260     This function is aimed at very old provision (before alpha9)
1261
1262     :param newpaths: List of paths for different provision objects
1263                         from the reference provision
1264     :param paths: List of paths for different provision objects
1265                         from the upgraded provision
1266     :param names: List of key provision parameters"""
1267
1268     message(SIMPLE, "Copy samdb")
1269     shutil.copy(newpaths.samdb, paths.samdb)
1270
1271     message(SIMPLE, "Update partitions filename if needed")
1272     schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1273     configldb = os.path.join(paths.private_dir, "configuration.ldb")
1274     usersldb = os.path.join(paths.private_dir, "users.ldb")
1275     samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1276
1277     if not os.path.isdir(samldbdir):
1278         os.mkdir(samldbdir)
1279         os.chmod(samldbdir, 0700)
1280     if os.path.isfile(schemaldb):
1281         shutil.copy(schemaldb, os.path.join(samldbdir,
1282                                             "%s.ldb"%str(names.schemadn).upper()))
1283         os.remove(schemaldb)
1284     if os.path.isfile(usersldb):
1285         shutil.copy(usersldb, os.path.join(samldbdir,
1286                                             "%s.ldb"%str(names.rootdn).upper()))
1287         os.remove(usersldb)
1288     if os.path.isfile(configldb):
1289         shutil.copy(configldb, os.path.join(samldbdir,
1290                                             "%s.ldb"%str(names.configdn).upper()))
1291         os.remove(configldb)
1292
1293
1294 def update_privilege(ref_private_path, cur_private_path):
1295     """Update the privilege database
1296
1297     :param ref_private_path: Path to the private directory of the reference
1298                              provision.
1299     :param cur_private_path: Path to the private directory of the current
1300                              (and to be updated) provision."""
1301     message(SIMPLE, "Copy privilege")
1302     shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1303                 os.path.join(cur_private_path, "privilege.ldb"))
1304
1305
1306 def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc):
1307     """Upgrade the SAM DB contents for all the provision partitions
1308
1309     :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1310                        provision
1311     :param samdb: An LDB object connected to the sam.ldb of the update
1312                   provision
1313     :param names: List of key provision parameters
1314     :param highestUSN:  The highest USN modified by provision/upgradeprovision
1315                         last time
1316     :param schema: A Schema object that represent the schema of the provision
1317     :param prereloadfunc: A function that must be executed just before the reload
1318                   of the schema
1319     """
1320
1321     message(SIMPLE, "Starting update of samdb")
1322     ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1323                             schema, highestUSN, prereloadfunc)
1324     if ret:
1325         message(SIMPLE, "Update of samdb finished")
1326         return 1
1327     else:
1328         message(SIMPLE, "Update failed")
1329         return 0
1330
1331
1332 def copyxattrs(dir, refdir):
1333     """ Copy owner, groups, extended ACL and NT acls from
1334     a reference dir to a destination dir
1335
1336     Both dir are supposed to hold the same files
1337     :param dir: Destination dir
1338     :param refdir: Reference directory"""
1339
1340     noxattr = 0
1341     for root, dirs, files in os.walk(dir, topdown=True):
1342         for name in files:
1343             subdir=root[len(dir):]
1344             ref = os.path.join("%s%s" % (refdir, subdir), name)
1345             statsinfo = os.stat(ref)
1346             tgt = os.path.join(root, name)
1347             try:
1348
1349                 os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
1350                 # Get the xattr attributes if any
1351                 try:
1352                     attribute = samba.xattr_native.wrap_getxattr(ref,
1353                                                  xattr.XATTR_NTACL_NAME)
1354                     samba.xattr_native.wrap_setxattr(tgt,
1355                                                  xattr.XATTR_NTACL_NAME,
1356                                                  attribute)
1357                 except:
1358                     noxattr = 1
1359                 attribute = samba.xattr_native.wrap_getxattr(ref,
1360                                                  "system.posix_acl_access")
1361                 samba.xattr_native.wrap_setxattr(tgt,
1362                                                  "system.posix_acl_access",
1363                                                   attribute)
1364             except:
1365                 continue
1366         for name in dirs:
1367             subdir=root[len(dir):]
1368             ref = os.path.join("%s%s" % (refdir, subdir), name)
1369             statsinfo = os.stat(ref)
1370             tgt = os.path.join(root, name)
1371             try:
1372                 os.chown(os.path.join(root, name), statsinfo.st_uid,
1373                           statsinfo.st_gid)
1374                 try:
1375                     attribute = samba.xattr_native.wrap_getxattr(ref,
1376                                                  xattr.XATTR_NTACL_NAME)
1377                     samba.xattr_native.wrap_setxattr(tgt,
1378                                                  xattr.XATTR_NTACL_NAME,
1379                                                  attribute)
1380                 except:
1381                     noxattr = 1
1382                 attribute = samba.xattr_native.wrap_getxattr(ref,
1383                                                  "system.posix_acl_access")
1384                 samba.xattr_native.wrap_setxattr(tgt,
1385                                                  "system.posix_acl_access",
1386                                                   attribute)
1387
1388             except:
1389                 continue
1390
1391
1392 def backup_provision(paths, dir):
1393     """This function backup the provision files so that a rollback
1394     is possible
1395
1396     :param paths: Paths to different objects
1397     :param dir: Directory where to store the backup
1398     """
1399
1400     shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
1401     copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
1402     shutil.copy2(paths.samdb, dir)
1403     shutil.copy2(paths.secrets, dir)
1404     shutil.copy2(paths.idmapdb, dir)
1405     shutil.copy2(paths.privilege, dir)
1406     if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1407         shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1408     shutil.copy2(paths.smbconf, dir)
1409     shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1410
1411     samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1412     if not os.path.isdir(samldbdir):
1413         samldbdir = paths.private_dir
1414         schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1415         configldb = os.path.join(paths.private_dir, "configuration.ldb")
1416         usersldb = os.path.join(paths.private_dir, "users.ldb")
1417         shutil.copy2(schemaldb, dir)
1418         shutil.copy2(usersldb, dir)
1419         shutil.copy2(configldb, dir)
1420     else:
1421         shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1422
1423
1424
1425
1426 def sync_calculated_attributes(samdb, names):
1427    """Synchronize attributes used for constructed ones, with the
1428       old constructed that were stored in the database.
1429
1430       This apply for instance to msds-keyversionnumber that was
1431       stored and that is now constructed from replpropertymetadata.
1432
1433       :param samdb: An LDB object attached to the currently upgraded samdb
1434       :param names: Various key parameter about current provision.
1435    """
1436    listAttrs = ["msDs-KeyVersionNumber"]
1437    hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
1438    if hash.has_key("msDs-KeyVersionNumber"):
1439        increment_calculated_keyversion_number(samdb, names.rootdn,
1440                                             hash["msDs-KeyVersionNumber"])
1441
1442 # Synopsis for updateprovision
1443 # 1) get path related to provision to be update (called current)
1444 # 2) open current provision ldbs
1445 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1446 #    of the DC ....)
1447 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1448 #    by either upgradeprovision or provision
1449 # 5) creation of a new provision the latest version of provision script
1450 #    (called reference)
1451 # 6) get reference provision paths
1452 # 7) open reference provision ldbs
1453 # 8) setup helpers data that will help the update process
1454 # 9) update the privilege ldb by copying the one of referecence provision to
1455 #    the current provision
1456 # 10)get the oemInfo field, this field contains information about the different
1457 #    provision that have been done
1458 # 11)Depending  on whether oemInfo has the string "alpha9" or alphaxx (x as an
1459 #    integer) or none of this the following things are done
1460 #    A) When alpha9 or alphaxx is present
1461 #       The base sam.ldb file is updated by looking at the difference between
1462 #       referrence one and the current one. Everything is copied with the
1463 #       exception of lastProvisionUSN attributes.
1464 #    B) Other case (it reflect that that provision was done before alpha9)
1465 #       The base sam.ldb of the reference provision is copied over
1466 #       the current one, if necessary ldb related to partitions are moved
1467 #       and renamed
1468 # The highest used USN is fetched so that changed by upgradeprovision
1469 # usn can be tracked
1470 # 12)A Schema object is created, it will be used to provide a complete
1471 #    schema to current provision during update (as the schema of the
1472 #    current provision might not be complete and so won't allow some
1473 #    object to be created)
1474 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1475 # 14)The secrets db is updated by pull all the difference from the reference
1476 #    provision into the current provision
1477 # 15)As the previous step has most probably modified the password stored in
1478 #    in secret for the current DC, a new password is generated,
1479 #    the kvno is bumped and the entry in samdb is also updated
1480 # 16)For current provision older than alpha9, we must fix the SD a little bit
1481 #    administrator to update them because SD used to be generated with the
1482 #    system account before alpha9.
1483 # 17)The highest usn modified so far is searched in the database it will be
1484 #    the upper limit for usn modified during provision.
1485 #    This is done before potential SD recalculation because we do not want
1486 #    SD modified during recalculation to be marked as modified during provision
1487 #    (and so possibly remplaced at next upgradeprovision)
1488 # 18)Rebuilt SD if the flag indicate to do so
1489 # 19)Check difference between SD of reference provision and those of the
1490 #    current provision. The check is done by getting the sddl representation
1491 #    of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1492 #    Each part is verified separetly, for dacl and sacl ACL is splited into
1493 #    ACEs and each ACE is verified separately (so that a permutation in ACE
1494 #    didn't raise as an error).
1495 # 20)The oemInfo field is updated to add information about the fact that the
1496 #    provision has been updated by the upgradeprovision version xxx
1497 #    (the version is the one obtained when starting samba with the --version
1498 #    parameter)
1499 # 21)Check if the current provision has all the settings needed for dynamic
1500 #    DNS update to work (that is to say the provision is newer than
1501 #    january 2010). If not dns configuration file from reference provision
1502 #    are copied in a sub folder and the administrator is invited to
1503 #    do what is needed.
1504 # 22)If the lastProvisionUSN attribute was present it is updated to add
1505 #    the range of usns modified by the current upgradeprovision
1506
1507
1508 # About updating the sam DB
1509 # The update takes place in update_partition function
1510 # This function read both current and reference provision and list all
1511 # the available DN of objects
1512 # If the string representation of a DN in reference provision is
1513 # equal to the string representation of a DN in current provision
1514 # (without taking care of case) then the object is flaged as being
1515 # present. If the object is not present in current provision the object
1516 # is being flaged as missing in current provision. Object present in current
1517 # provision but not in reference provision are ignored.
1518 # Once the list of objects present and missing is done, the deleted object
1519 # containers are created in the differents partitions (if missing)
1520 #
1521 # Then the function add_missing_entries is called
1522 # This function will go through the list of missing entries by calling
1523 # add_missing_object for the given object. If this function returns 0
1524 # it means that the object needs some other object in order to be created
1525 # The object is reappended at the end of the list to be created later
1526 # (and preferably after all the needed object have been created)
1527 # The function keeps on looping on the list of object to be created until
1528 # it's empty or that the number of defered creation is equal to the number
1529 # of object that still needs to be created.
1530
1531 # The function add_missing_object will first check if the object can be created.
1532 # That is to say that it didn't depends other not yet created objects
1533 # If requisit can't be fullfilled it exists with 0
1534 # Then it will try to create the missing entry by creating doing
1535 # an ldb_message_diff between the object in the reference provision and
1536 # an empty object.
1537 # This resulting object is filtered to remove all the back link attribute
1538 # (ie. memberOf) as they will be created by the other linked object (ie.
1539 # the one with the member attribute)
1540 # All attributes specified in the hashAttrNotCopied associative array are
1541 # also removed it's most of the time generated attributes
1542
1543 # After missing entries have been added the update_partition function will
1544 # take care of object that exist but that need some update.
1545 # In order to do so the function update_present is called with the list
1546 # of object that are present in both provision and that might need an update.
1547
1548 # This function handle first case mismatch so that the DN in the current
1549 # provision have the same case as in reference provision
1550
1551 # It will then construct an associative array consiting of attributes as
1552 # key and invocationid as value( if the originating invocation id is
1553 # different from the invocation id of the current DC the value is -1 instead).
1554
1555 # If the range of provision modified attributes is present, the function will
1556 # use the replMetadataProperty update method which is the following:
1557 #  Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1558 #   creationTime, msDs-KeyVersionNumber, oEMInformation
1559 #  Check for each attribute if its usn is within one of the modified by
1560 #   provision range and if its originating id is the invocation id of the
1561 #   current DC, then validate the update from reference to current.
1562 #   If not or if there is no replMetatdataProperty for this attribute then we
1563 #   do not update it.
1564 # Otherwise (case the range of provision modified attribute is not present) it
1565 # use the following process:
1566 #  All attributes that need to be added are accepted at the exeption of those
1567 #   listed in hashOverwrittenAtt, in this case the attribute needs to have the
1568 #   correct flags specified.
1569 #  For attributes that need to be modified or removed, a check is performed
1570 #  in OverwrittenAtt, if the attribute is present and the modification flag
1571 #  (remove, delete) is one of those listed for this attribute then modification
1572 #  is accepted. For complicated handling of attribute update, the control is passed
1573 #  to handle_special_case
1574
1575
1576
1577 if __name__ == '__main__':
1578     global defSDmodified
1579     defSDmodified = False
1580     # From here start the big steps of the program
1581     # 1) First get files paths
1582     paths = get_paths(param, smbconf=smbconf)
1583     # Get ldbs with the system session, it is needed for searching
1584     # provision parameters
1585     session = system_session()
1586
1587     # This variable will hold the last provision USN once if it exists.
1588     minUSN = 0
1589     # 2)
1590     ldbs = get_ldbs(paths, creds, session, lp)
1591     backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1592                                     prefix="backupprovision")
1593     backup_provision(paths, backupdir)
1594     try:
1595         ldbs.startTransactions()
1596
1597         # 3) Guess all the needed names (variables in fact) from the current
1598         # provision.
1599         names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1600                                                 paths, smbconf, lp)
1601         # 4)
1602         lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1603         if lastProvisionUSNs is not None:
1604             message(CHANGE,
1605                 "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
1606
1607         # Objects will be created with the admin session
1608         # (not anymore system session)
1609         adm_session = admin_session(lp, str(names.domainsid))
1610         # So we reget handle on objects
1611         # ldbs = get_ldbs(paths, creds, adm_session, lp)
1612
1613         if not sanitychecks(ldbs.sam, names):
1614             message(SIMPLE, "Sanity checks for the upgrade have failed. "
1615                             "Check the messages and correct the errors "
1616                             "before rerunning upgradeprovision")
1617             sys.exit(1)
1618
1619         # Let's see provision parameters
1620         print_provision_key_parameters(names)
1621
1622         # 5) With all this information let's create a fresh new provision used as
1623         # reference
1624         message(SIMPLE, "Creating a reference provision")
1625         provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1626                                         prefix="referenceprovision")
1627         newprovision(names, creds, session, smbconf, provisiondir,
1628                         provision_logger)
1629
1630         # TODO
1631         # 6) and 7)
1632         # We need to get a list of object which SD is directly computed from
1633         # defaultSecurityDescriptor.
1634         # This will allow us to know which object we can rebuild the SD in case
1635         # of change of the parent's SD or of the defaultSD.
1636         # Get file paths of this new provision
1637         newpaths = get_paths(param, targetdir=provisiondir)
1638         new_ldbs = get_ldbs(newpaths, creds, session, lp)
1639         new_ldbs.startTransactions()
1640
1641         # 8) Populate some associative array to ease the update process
1642         # List of attribute which are link and backlink
1643         populate_links(new_ldbs.sam, names.schemadn)
1644         # List of attribute with ASN DN synthax)
1645         populate_dnsyntax(new_ldbs.sam, names.schemadn)
1646         # 9)
1647         update_privilege(newpaths.private_dir, paths.private_dir)
1648         # 10)
1649         oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1650         # Do some modification on sam.ldb
1651         ldbs.groupedCommit()
1652         new_ldbs.groupedCommit()
1653         deltaattr = None
1654 # 11)
1655         if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1656             # 11) A
1657             # Starting from alpha9 we can consider that the structure is quite ok
1658             # and that we should do only dela
1659             deltaattr = delta_update_basesamdb(newpaths.samdb,
1660                                                 paths.samdb,
1661                                                 creds,
1662                                                 session,
1663                                                 lp,
1664                                                 message)
1665         else:
1666             # 11) B
1667             simple_update_basesamdb(newpaths, paths, names)
1668             ldbs = get_ldbs(paths, creds, session, lp)
1669             removeProvisionUSN(ldbs.sam)
1670
1671         ldbs.startTransactions()
1672         minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1673         new_ldbs.startTransactions()
1674
1675         # 12)
1676         schema = Schema(names.domainsid, schemadn=str(names.schemadn))
1677         # We create a closure that will be invoked just before schema reload
1678         def schemareloadclosure():
1679             basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
1680                             options=["modules:"])
1681             doit = False
1682             if deltaattr is not None and len(deltaattr) > 1:
1683                 doit = True
1684             if doit:
1685                 deltaattr.remove("dn")
1686                 for att in deltaattr:
1687                     if att.lower() == "dn":
1688                         continue
1689                     if (deltaattr.get(att) is not None
1690                         and deltaattr.get(att).flags() != FLAG_MOD_ADD):
1691                         doit = False
1692                     elif deltaattr.get(att) is None:
1693                         doit = False
1694             if doit:
1695                 message(CHANGE, "Applying delta to @ATTRIBUTES")
1696                 deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
1697                 basesam.modify(deltaattr)
1698             else:
1699                 message(CHANGE, "Not applying delta to @ATTRIBUTES because "
1700                                 "there is not only add")
1701         # 13)
1702         if opts.full:
1703             if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1704                                 schema, schemareloadclosure):
1705                 message(SIMPLE, "Rolling back all changes. Check the cause"
1706                                 " of the problem")
1707                 message(SIMPLE, "Your system is as it was before the upgrade")
1708                 ldbs.groupedRollback()
1709                 new_ldbs.groupedRollback()
1710                 shutil.rmtree(provisiondir)
1711                 sys.exit(1)
1712         else:
1713             # Try to reapply the change also when we do not change the sam
1714             # as the delta_upgrade
1715             schemareloadclosure()
1716             sync_calculated_attributes(ldbs.sam, names)
1717             res = ldbs.sam.search(expression="(samaccountname=dns)",
1718                                 scope=SCOPE_SUBTREE, attrs=["dn"],
1719                                 controls=["search_options:1:2"])
1720             if len(res) > 0:
1721                 message(SIMPLE, "You still have the old DNS object for managing "
1722                                 "dynamic DNS, but you didn't supply --full so "
1723                                 "a correct update can't be done")
1724                 ldbs.groupedRollback()
1725                 new_ldbs.groupedRollback()
1726                 shutil.rmtree(provisiondir)
1727                 sys.exit(1)
1728         # 14)
1729         update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1730         # 14bis)
1731         res = ldbs.sam.search(expression="(samaccountname=dns)",
1732                                   scope=SCOPE_SUBTREE, attrs=["dn"],
1733                                   controls=["search_options:1:2"])
1734
1735         if (len(res) == 1):
1736             ldbs.sam.delete(res[0]["dn"])
1737             res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
1738                                   scope=SCOPE_SUBTREE, attrs=["dn"])
1739             update_dns_account_password(ldbs.sam, ldbs.secrets, names)
1740             message(SIMPLE, "IMPORTANT!!! "
1741                             "If you were using Dynamic DNS before you need "
1742                             "to update your configuration, so that the "
1743                             "tkey-gssapi-credential has the following value: "
1744                             "DNS/%s.%s" % (names.netbiosname.lower(),
1745                                            names.realm.lower()))
1746         # 15)
1747         message(SIMPLE, "Update machine account")
1748         update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1749
1750         # 16) SD should be created with admin but as some previous acl were so wrong
1751         # that admin can't modify them we have first to recreate them with the good
1752         # form but with system account and then give the ownership to admin ...
1753         if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1754             message(SIMPLE, "Fixing old povision SD")
1755             fix_partition_sd(ldbs.sam, names)
1756             rebuild_sd(ldbs.sam, names)
1757
1758         # We calculate the max USN before recalculating the SD because we might
1759         # touch object that have been modified after a provision and we do not
1760         # want that the next upgradeprovision thinks that it has a green light
1761         # to modify them
1762
1763         # 17)
1764         maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1765
1766         # 18) We rebuild SD only if defaultSecurityDescriptor is modified
1767         # But in fact we should do it also if one object has its SD modified as
1768         # child might need rebuild
1769         if defSDmodified:
1770             message(SIMPLE, "Updating SD")
1771             ldbs.sam.set_session_info(adm_session)
1772             # Alpha10 was a bit broken still
1773             if re.match(r'.*alpha(\d|10)', str(oem)):
1774                 fix_partition_sd(ldbs.sam, names)
1775             rebuild_sd(ldbs.sam, names)
1776
1777         # 19)
1778         # Now we are quite confident in the recalculate process of the SD, we make
1779         # it optional.
1780         # Also the check must be done in a clever way as for the moment we just
1781         # compare SDDL
1782         if opts.debugchangesd:
1783             check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1784
1785         # 20)
1786         updateOEMInfo(ldbs.sam, str(names.rootdn))
1787         # 21)
1788         check_for_DNS(newpaths.private_dir, paths.private_dir)
1789         # 22)
1790         if lastProvisionUSNs is not None:
1791             update_provision_usn(ldbs.sam, minUSN, maxUSN)
1792         if opts.full and (names.policyid is None or names.policyid_dc is None):
1793             update_policyids(names, ldbs.sam)
1794         if opts.full or opts.resetfileacl:
1795             try:
1796                 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1797             except ProvisioningError, e:
1798                 message(ERROR, "The policy for domain controller is missing. "
1799                                "You should restart upgradeprovision with --full")
1800             except IOError, e:
1801                 message(ERROR, "Setting ACL not supported on your filesystem")
1802         else:
1803             try:
1804                 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1805             except ProvisioningError, e:
1806                 message(ERROR, "The policy for domain controller is missing. "
1807                                "You should restart upgradeprovision with --full")
1808         ldbs.groupedCommit()
1809         new_ldbs.groupedCommit()
1810         message(SIMPLE, "Upgrade finished!")
1811         # remove reference provision now that everything is done !
1812         # So we have reindexed first if need when the merged schema was reloaded
1813         # (as new attributes could have quick in)
1814         # But the second part of the update (when we update existing objects
1815         # can also have an influence on indexing as some attribute might have their
1816         # searchflag modificated
1817         message(SIMPLE, "Reopenning samdb to trigger reindexing if needed "
1818                         "after modification")
1819         samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
1820         message(SIMPLE, "Reindexing finished")
1821
1822         shutil.rmtree(provisiondir)
1823     except StandardError, err:
1824         message(ERROR, "A problem occurred while trying to upgrade your "
1825                        "provision. A full backup is located at %s" % backupdir)
1826         if opts.debugall or opts.debugchange:
1827             (typ, val, tb) = sys.exc_info()
1828             traceback.print_exception(typ, val, tb)
1829         sys.exit(1)