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