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