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