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