s4-upgradeprovision: remove useless code
[ddiss/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 att == "nTSecurityDescriptor":
922             cursd = ndr_unpack(security.descriptor,
923                 str(current[0]["nTSecurityDescriptor"]))
924             cursddl = cursd.as_sddl(names.domainsid)
925             refsd = ndr_unpack(security.descriptor,
926                 str(reference[0]["nTSecurityDescriptor"]))
927             refsddl = refsd.as_sddl(names.domainsid)
928
929             if get_diff_sddls(refsddl, cursddl) == "":
930                 message(CHANGE, "sd are identical")
931             else:
932                 message(CHANGE, "sd are not identical")
933         if attrUSN == -1:
934             # This attribute was last modified by another DC forget
935             # about it
936             message(CHANGE, "%sAttribute: %s has been "
937                     "created/modified/deleted by another DC. "
938                     "Doing nothing" % (txt, att))
939             txt = ""
940             delta.remove(att)
941             continue
942         elif not usn_in_range(int(attrUSN), usns.get(attInvId)):
943             message(CHANGE, "%sAttribute: %s was not "
944                             "created/modified/deleted during a "
945                             "provision or upgradeprovision. Current "
946                             "usn: %d. Doing nothing" % (txt, att,
947                                                         attrUSN))
948             txt = ""
949             delta.remove(att)
950             continue
951         else:
952             if att == "defaultSecurityDescriptor":
953                 defSDmodified = True
954             if attrUSN:
955                 message(CHANGE, "%sAttribute: %s will be modified"
956                                 "/deleted it was last modified "
957                                 "during a provision. Current usn: "
958                                 "%d" % (txt, att, attrUSN))
959                 txt = ""
960             else:
961                 message(CHANGE, "%sAttribute: %s will be added because "
962                                 "it did not exist before" % (txt, att))
963                 txt = ""
964             continue
965
966     return delta
967
968 def update_present(ref_samdb, samdb, basedn, listPresent, usns):
969     """ This function updates the object that are already present in the
970         provision
971
972     :param ref_samdb: An LDB object pointing to the reference provision
973     :param samdb: An LDB object pointing to the updated provision
974     :param basedn: A string with the value of the base DN for the provision
975                    (ie. DC=foo, DC=bar)
976     :param listPresent: A list of object that is present in the provision
977     :param usns: A list of USN range modified by previous provision and
978                  upgradeprovision grouped by invocation ID
979     """
980
981     # This hash is meant to speedup lookup of attribute name from an oid,
982     # it's for the replPropertyMetaData handling
983     hash_oid_name = {}
984     res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
985                         controls=["search_options:1:2"], attrs=["attributeID",
986                         "lDAPDisplayName"])
987     if len(res) > 0:
988         for e in res:
989             strDisplay = str(e.get("lDAPDisplayName"))
990             hash_oid_name[str(e.get("attributeID"))] = strDisplay
991     else:
992         msg = "Unable to insert missing elements: circular references"
993         raise ProvisioningError(msg)
994
995     changed = 0
996     controls = ["search_options:1:2", "sd_flags:1:2"]
997     if usns is not None:
998             message(CHANGE, "Using replPropertyMetadata for change selection")
999     for dn in listPresent:
1000         reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1001                                         scope=SCOPE_SUBTREE,
1002                                         controls=controls)
1003         current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1004                                 scope=SCOPE_SUBTREE, controls=controls)
1005
1006         if (
1007              (str(current[0].dn) != str(reference[0].dn)) and
1008              (str(current[0].dn).upper() == str(reference[0].dn).upper())
1009            ):
1010             message(CHANGE, "Names are the same except for the case. "
1011                             "Renaming %s to %s" % (str(current[0].dn),
1012                                                    str(reference[0].dn)))
1013             identic_rename(samdb, reference[0].dn)
1014             current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1015                                     scope=SCOPE_SUBTREE,
1016                                     controls=controls)
1017
1018         delta = samdb.msg_diff(current[0], reference[0])
1019
1020         for att in backlinked:
1021             delta.remove(att)
1022
1023         delta.remove("name")
1024
1025         if len(delta.items()) > 1 and usns is not None:
1026             # Fetch the replPropertyMetaData
1027             res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
1028                                 scope=SCOPE_SUBTREE, controls=controls,
1029                                 attrs=["replPropertyMetaData"])
1030             ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
1031                                 str(res[0]["replPropertyMetaData"])).ctr
1032
1033             hash_attr_usn = {}
1034             for o in ctr.array:
1035                 # We put in this hash only modification
1036                 # made on the current host
1037                 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
1038                 if str(o.originating_invocation_id) in usns.keys():
1039                     hash_attr_usn[att] = [o.originating_usn, str(o.originating_invocation_id)]
1040                 else:
1041                     hash_attr_usn[att] = [-1, None]
1042
1043         if usns is not None:
1044             delta = checkKeepAttributeWithMetadata(delta, att, message, reference,
1045                                                     current, hash_attr_usn,
1046                                                     basedn, usns, samdb)
1047         else:
1048             for att in hashAttrNotCopied.keys():
1049                 delta.remove(att)
1050             delta =  checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb)
1051
1052         delta.dn = dn
1053         if len(delta.items()) >1:
1054             # Skip dn as the value is not really changed ...
1055             attributes=", ".join(delta.keys()[1:])
1056             modcontrols = []
1057             relaxedatt = ['iscriticalsystemobject', 'grouptype']
1058             # Let's try to reduce as much as possible the use of relax control
1059             #for checkedatt in relaxedatt:
1060             for attr in delta.keys():
1061                 if attr.lower() in relaxedatt:
1062                     modcontrols = ["relax:0", "provision:0"]
1063             message(CHANGE, "%s is different from the reference one, changed"
1064                             " attributes: %s\n" % (dn, attributes))
1065             changed += 1
1066             samdb.modify(delta, modcontrols)
1067     return changed
1068
1069 def reload_full_schema(samdb, names):
1070     """Load the updated schema with all the new and existing classes
1071        and attributes.
1072
1073     :param samdb: An LDB object connected to the sam.ldb of the update
1074                   provision
1075     :param names: List of key provision parameters
1076     """
1077
1078     current = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1079                                 scope=SCOPE_SUBTREE)
1080     schema_ldif = ""
1081     prefixmap_data = ""
1082
1083     for ent in current:
1084         schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
1085
1086     prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
1087     prefixmap_data = b64encode(prefixmap_data)
1088
1089     # We don't actually add this ldif, just parse it
1090     prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap_data
1091
1092     dsdb._dsdb_set_schema_from_ldif(samdb, prefixmap_ldif, schema_ldif)
1093
1094
1095 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, prereloadfunc):
1096     """Check differences between the reference provision and the upgraded one.
1097
1098     It looks for all objects which base DN is name.
1099
1100     This function will also add the missing object and update existing object
1101     to add or remove attributes that were missing.
1102
1103     :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1104                        reference provision
1105     :param samdb: An LDB object connected to the sam.ldb of the update
1106                   provision
1107     :param basedn: String value of the DN of the partition
1108     :param names: List of key provision parameters
1109     :param schema: A Schema object
1110     :param provisionUSNs:  A dictionnary with range of USN modified during provision
1111                             or upgradeprovision. Ranges are grouped by invocationID.
1112     :param prereloadfunc: A function that must be executed just before the reload
1113                   of the schema
1114     """
1115
1116     hash_new = {}
1117     hash = {}
1118     listMissing = []
1119     listPresent = []
1120     reference = []
1121     current = []
1122
1123     # Connect to the reference provision and get all the attribute in the
1124     # partition referred by name
1125     reference = ref_samdb.search(expression="objectClass=*", base=basedn,
1126                                     scope=SCOPE_SUBTREE, attrs=["dn"],
1127                                     controls=["search_options:1:2"])
1128
1129     current = samdb.search(expression="objectClass=*", base=basedn,
1130                                 scope=SCOPE_SUBTREE, attrs=["dn"],
1131                                 controls=["search_options:1:2"])
1132     # Create a hash for speeding the search of new object
1133     for i in range(0, len(reference)):
1134         hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
1135
1136     # Create a hash for speeding the search of existing object in the
1137     # current provision
1138     for i in range(0, len(current)):
1139         hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
1140
1141
1142     for k in hash_new.keys():
1143         if not hash.has_key(k):
1144             if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
1145                 listMissing.append(hash_new[k])
1146         else:
1147             listPresent.append(hash_new[k])
1148
1149     # Sort the missing object in order to have object of the lowest level
1150     # first (which can be containers for higher level objects)
1151     listMissing.sort(dn_sort)
1152     listPresent.sort(dn_sort)
1153
1154     # The following lines is to load the up to
1155     # date schema into our current LDB
1156     # a complete schema is needed as the insertion of attributes
1157     # and class is done against it
1158     # and the schema is self validated
1159     samdb.set_schema(schema)
1160     try:
1161         message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
1162         add_deletedobj_containers(ref_samdb, samdb, names)
1163
1164         add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
1165
1166         prereloadfunc()
1167         message(SIMPLE, "Reloading a merged schema, which might trigger "
1168                         "reindexing so please be patient")
1169         reload_full_schema(samdb, names)
1170         message(SIMPLE, "Schema reloaded!")
1171
1172         changed = update_present(ref_samdb, samdb, basedn, listPresent,
1173                                     provisionUSNs)
1174         message(SIMPLE, "There are %d changed objects" % (changed))
1175         return 1
1176
1177     except StandardError, err:
1178         message(ERROR, "Exception during upgrade of samdb:")
1179         (typ, val, tb) = sys.exc_info()
1180         traceback.print_exception(typ, val, tb)
1181         return 0
1182
1183
1184 def check_updated_sd(ref_sam, cur_sam, names):
1185     """Check if the security descriptor in the upgraded provision are the same
1186        as the reference
1187
1188     :param ref_sam: A LDB object connected to the sam.ldb file used as
1189                     the reference provision
1190     :param cur_sam: A LDB object connected to the sam.ldb file used as
1191                     upgraded provision
1192     :param names: List of key provision parameters"""
1193     reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1194                                 scope=SCOPE_SUBTREE,
1195                                 attrs=["dn", "nTSecurityDescriptor"],
1196                                 controls=["search_options:1:2"])
1197     current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1198                                 scope=SCOPE_SUBTREE,
1199                                 attrs=["dn", "nTSecurityDescriptor"],
1200                                 controls=["search_options:1:2"])
1201     hash = {}
1202     for i in range(0, len(reference)):
1203         refsd = ndr_unpack(security.descriptor,
1204                     str(reference[i]["nTSecurityDescriptor"]))
1205         hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
1206
1207
1208     for i in range(0, len(current)):
1209         key = str(current[i]["dn"]).lower()
1210         if hash.has_key(key):
1211             cursd = ndr_unpack(security.descriptor,
1212                         str(current[i]["nTSecurityDescriptor"]))
1213             sddl = cursd.as_sddl(names.domainsid)
1214             if sddl != hash[key]:
1215                 txt = get_diff_sddls(hash[key], sddl)
1216                 if txt != "":
1217                     message(CHANGESD, "On object %s ACL is different"
1218                                       " \n%s" % (current[i]["dn"], txt))
1219
1220
1221
1222 def fix_partition_sd(samdb, names):
1223     """This function fix the SD for partition containers (basedn, configdn, ...)
1224     This is needed because some provision use to have broken SD on containers
1225
1226     :param samdb: An LDB object pointing to the sam of the current provision
1227     :param names: A list of key provision parameters
1228     """
1229     # First update the SD for the rootdn
1230     res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1231                          scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1232                          controls=["search_options:1:2"])
1233     delta = Message()
1234     delta.dn = Dn(samdb, str(res[0]["dn"]))
1235     descr = get_domain_descriptor(names.domainsid)
1236     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1237                                                     "nTSecurityDescriptor")
1238     samdb.modify(delta)
1239     # Then the config dn
1240     res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1241                         scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1242                         controls=["search_options:1:2"])
1243     delta = Message()
1244     delta.dn = Dn(samdb, str(res[0]["dn"]))
1245     descr = get_config_descriptor(names.domainsid)
1246     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1247                                                     "nTSecurityDescriptor" )
1248     samdb.modify(delta)
1249     # Then the schema dn
1250     res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1251                         scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1252                         controls=["search_options:1:2"])
1253
1254     delta = Message()
1255     delta.dn = Dn(samdb, str(res[0]["dn"]))
1256     descr = get_schema_descriptor(names.domainsid)
1257     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1258                                                     "nTSecurityDescriptor" )
1259     samdb.modify(delta)
1260
1261 def rebuild_sd(samdb, names):
1262     """Rebuild security descriptor of the current provision from scratch
1263
1264     During the different pre release of samba4 security descriptors (SD)
1265     were notarly broken (up to alpha11 included)
1266     This function allow to get them back in order, this function make the
1267     assumption that nobody has modified manualy an SD
1268     and so SD can be safely recalculated from scratch to get them right.
1269
1270     :param names: List of key provision parameters"""
1271
1272
1273     hash = {}
1274     res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1275                         scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1276                         controls=["search_options:1:2"])
1277     for obj in res:
1278         if not (str(obj["dn"]) == str(names.rootdn) or
1279             str(obj["dn"]) == str(names.configdn) or
1280             str(obj["dn"]) == str(names.schemadn)):
1281             hash[str(obj["dn"])] = obj["whenCreated"]
1282
1283     listkeys = hash.keys()
1284     listkeys.sort(dn_sort)
1285
1286     for key in listkeys:
1287         try:
1288             delta = Message()
1289             delta.dn = Dn(samdb, key)
1290             delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1291                                                     "whenCreated" )
1292             samdb.modify(delta, ["recalculate_sd:0"])
1293         except:
1294             # XXX: We should always catch an explicit exception.
1295             # What could go wrong here?
1296             samdb.transaction_cancel()
1297             res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1298                                 scope=SCOPE_SUBTREE,
1299                                 attrs=["dn", "nTSecurityDescriptor"],
1300                                 controls=["search_options:1:2"])
1301             badsd = ndr_unpack(security.descriptor,
1302                         str(res[0]["nTSecurityDescriptor"]))
1303             print "bad stuff %s" % badsd.as_sddl(names.domainsid)
1304             return
1305
1306 def removeProvisionUSN(samdb):
1307         attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1308         entry = samdb.search(expression="dn=@PROVISION", base = "",
1309                                 scope=SCOPE_SUBTREE,
1310                                 attrs=attrs)
1311         empty = Message()
1312         empty.dn = entry[0].dn
1313         delta = samdb.msg_diff(entry[0], empty)
1314         delta.remove("dn")
1315         delta.dn = entry[0].dn
1316         samdb.modify(delta)
1317
1318 def remove_stored_generated_attrs(paths, creds, session, lp):
1319     """Remove previously stored constructed attributes
1320
1321     :param paths: List of paths for different provision objects
1322                         from the upgraded provision
1323     :param creds: A credential object
1324     :param session: A session object
1325     :param lp: A line parser object
1326     :return: An associative array whose key are the different constructed
1327              attributes and the value the dn where this attributes were found.
1328      """
1329
1330
1331 def simple_update_basesamdb(newpaths, paths, names):
1332     """Update the provision container db: sam.ldb
1333     This function is aimed at very old provision (before alpha9)
1334
1335     :param newpaths: List of paths for different provision objects
1336                         from the reference provision
1337     :param paths: List of paths for different provision objects
1338                         from the upgraded provision
1339     :param names: List of key provision parameters"""
1340
1341     message(SIMPLE, "Copy samdb")
1342     shutil.copy(newpaths.samdb, paths.samdb)
1343
1344     message(SIMPLE, "Update partitions filename if needed")
1345     schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1346     configldb = os.path.join(paths.private_dir, "configuration.ldb")
1347     usersldb = os.path.join(paths.private_dir, "users.ldb")
1348     samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1349
1350     if not os.path.isdir(samldbdir):
1351         os.mkdir(samldbdir)
1352         os.chmod(samldbdir, 0700)
1353     if os.path.isfile(schemaldb):
1354         shutil.copy(schemaldb, os.path.join(samldbdir,
1355                                             "%s.ldb"%str(names.schemadn).upper()))
1356         os.remove(schemaldb)
1357     if os.path.isfile(usersldb):
1358         shutil.copy(usersldb, os.path.join(samldbdir,
1359                                             "%s.ldb"%str(names.rootdn).upper()))
1360         os.remove(usersldb)
1361     if os.path.isfile(configldb):
1362         shutil.copy(configldb, os.path.join(samldbdir,
1363                                             "%s.ldb"%str(names.configdn).upper()))
1364         os.remove(configldb)
1365
1366
1367 def update_privilege(ref_private_path, cur_private_path):
1368     """Update the privilege database
1369
1370     :param ref_private_path: Path to the private directory of the reference
1371                              provision.
1372     :param cur_private_path: Path to the private directory of the current
1373                              (and to be updated) provision."""
1374     message(SIMPLE, "Copy privilege")
1375     shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1376                 os.path.join(cur_private_path, "privilege.ldb"))
1377
1378
1379 def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):
1380     """Upgrade the SAM DB contents for all the provision partitions
1381
1382     :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1383                        provision
1384     :param samdb: An LDB object connected to the sam.ldb of the update
1385                   provision
1386     :param names: List of key provision parameters
1387     :param provisionUSNs:  A dictionnary with range of USN modified during provision
1388                             or upgradeprovision. Ranges are grouped by invocationID.
1389     :param schema: A Schema object that represent the schema of the provision
1390     :param prereloadfunc: A function that must be executed just before the reload
1391                   of the schema
1392     """
1393
1394     message(SIMPLE, "Starting update of samdb")
1395     ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1396                             schema, provisionUSNs, prereloadfunc)
1397     if ret:
1398         message(SIMPLE, "Update of samdb finished")
1399         return 1
1400     else:
1401         message(SIMPLE, "Update failed")
1402         return 0
1403
1404
1405 def copyxattrs(dir, refdir):
1406     """ Copy owner, groups, extended ACL and NT acls from
1407     a reference dir to a destination dir
1408
1409     Both dir are supposed to hold the same files
1410     :param dir: Destination dir
1411     :param refdir: Reference directory"""
1412
1413     noxattr = 0
1414     for root, dirs, files in os.walk(dir, topdown=True):
1415         for name in files:
1416             subdir=root[len(dir):]
1417             ref = os.path.join("%s%s" % (refdir, subdir), name)
1418             statsinfo = os.stat(ref)
1419             tgt = os.path.join(root, name)
1420             try:
1421
1422                 os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
1423                 # Get the xattr attributes if any
1424                 try:
1425                     attribute = samba.xattr_native.wrap_getxattr(ref,
1426                                                  xattr.XATTR_NTACL_NAME)
1427                     samba.xattr_native.wrap_setxattr(tgt,
1428                                                  xattr.XATTR_NTACL_NAME,
1429                                                  attribute)
1430                 except:
1431                     noxattr = 1
1432                 attribute = samba.xattr_native.wrap_getxattr(ref,
1433                                                  "system.posix_acl_access")
1434                 samba.xattr_native.wrap_setxattr(tgt,
1435                                                  "system.posix_acl_access",
1436                                                   attribute)
1437             except:
1438                 continue
1439         for name in dirs:
1440             subdir=root[len(dir):]
1441             ref = os.path.join("%s%s" % (refdir, subdir), name)
1442             statsinfo = os.stat(ref)
1443             tgt = os.path.join(root, name)
1444             try:
1445                 os.chown(os.path.join(root, name), statsinfo.st_uid,
1446                           statsinfo.st_gid)
1447                 try:
1448                     attribute = samba.xattr_native.wrap_getxattr(ref,
1449                                                  xattr.XATTR_NTACL_NAME)
1450                     samba.xattr_native.wrap_setxattr(tgt,
1451                                                  xattr.XATTR_NTACL_NAME,
1452                                                  attribute)
1453                 except:
1454                     noxattr = 1
1455                 attribute = samba.xattr_native.wrap_getxattr(ref,
1456                                                  "system.posix_acl_access")
1457                 samba.xattr_native.wrap_setxattr(tgt,
1458                                                  "system.posix_acl_access",
1459                                                   attribute)
1460
1461             except:
1462                 continue
1463
1464
1465 def backup_provision(paths, dir):
1466     """This function backup the provision files so that a rollback
1467     is possible
1468
1469     :param paths: Paths to different objects
1470     :param dir: Directory where to store the backup
1471     """
1472
1473     shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
1474     copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
1475     shutil.copy2(paths.samdb, dir)
1476     shutil.copy2(paths.secrets, dir)
1477     shutil.copy2(paths.idmapdb, dir)
1478     shutil.copy2(paths.privilege, dir)
1479     if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1480         shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1481     shutil.copy2(paths.smbconf, dir)
1482     shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1483
1484     samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1485     if not os.path.isdir(samldbdir):
1486         samldbdir = paths.private_dir
1487         schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1488         configldb = os.path.join(paths.private_dir, "configuration.ldb")
1489         usersldb = os.path.join(paths.private_dir, "users.ldb")
1490         shutil.copy2(schemaldb, dir)
1491         shutil.copy2(usersldb, dir)
1492         shutil.copy2(configldb, dir)
1493     else:
1494         shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1495
1496
1497
1498
1499 def sync_calculated_attributes(samdb, names):
1500    """Synchronize attributes used for constructed ones, with the
1501       old constructed that were stored in the database.
1502
1503       This apply for instance to msds-keyversionnumber that was
1504       stored and that is now constructed from replpropertymetadata.
1505
1506       :param samdb: An LDB object attached to the currently upgraded samdb
1507       :param names: Various key parameter about current provision.
1508    """
1509    listAttrs = ["msDs-KeyVersionNumber"]
1510    hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
1511    if hash.has_key("msDs-KeyVersionNumber"):
1512        increment_calculated_keyversion_number(samdb, names.rootdn,
1513                                             hash["msDs-KeyVersionNumber"])
1514
1515 # Synopsis for updateprovision
1516 # 1) get path related to provision to be update (called current)
1517 # 2) open current provision ldbs
1518 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1519 #    of the DC ....)
1520 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1521 #    by either upgradeprovision or provision
1522 # 5) creation of a new provision the latest version of provision script
1523 #    (called reference)
1524 # 6) get reference provision paths
1525 # 7) open reference provision ldbs
1526 # 8) setup helpers data that will help the update process
1527 # 9) update the privilege ldb by copying the one of referecence provision to
1528 #    the current provision
1529 # 10)get the oemInfo field, this field contains information about the different
1530 #    provision that have been done
1531 # 11)Depending  on whether oemInfo has the string "alpha9" or alphaxx (x as an
1532 #    integer) or none of this the following things are done
1533 #    A) When alpha9 or alphaxx is present
1534 #       The base sam.ldb file is updated by looking at the difference between
1535 #       referrence one and the current one. Everything is copied with the
1536 #       exception of lastProvisionUSN attributes.
1537 #    B) Other case (it reflect that that provision was done before alpha9)
1538 #       The base sam.ldb of the reference provision is copied over
1539 #       the current one, if necessary ldb related to partitions are moved
1540 #       and renamed
1541 # The highest used USN is fetched so that changed by upgradeprovision
1542 # usn can be tracked
1543 # 12)A Schema object is created, it will be used to provide a complete
1544 #    schema to current provision during update (as the schema of the
1545 #    current provision might not be complete and so won't allow some
1546 #    object to be created)
1547 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1548 # 14)The secrets db is updated by pull all the difference from the reference
1549 #    provision into the current provision
1550 # 15)As the previous step has most probably modified the password stored in
1551 #    in secret for the current DC, a new password is generated,
1552 #    the kvno is bumped and the entry in samdb is also updated
1553 # 16)For current provision older than alpha9, we must fix the SD a little bit
1554 #    administrator to update them because SD used to be generated with the
1555 #    system account before alpha9.
1556 # 17)The highest usn modified so far is searched in the database it will be
1557 #    the upper limit for usn modified during provision.
1558 #    This is done before potential SD recalculation because we do not want
1559 #    SD modified during recalculation to be marked as modified during provision
1560 #    (and so possibly remplaced at next upgradeprovision)
1561 # 18)Rebuilt SD if the flag indicate to do so
1562 # 19)Check difference between SD of reference provision and those of the
1563 #    current provision. The check is done by getting the sddl representation
1564 #    of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1565 #    Each part is verified separetly, for dacl and sacl ACL is splited into
1566 #    ACEs and each ACE is verified separately (so that a permutation in ACE
1567 #    didn't raise as an error).
1568 # 20)The oemInfo field is updated to add information about the fact that the
1569 #    provision has been updated by the upgradeprovision version xxx
1570 #    (the version is the one obtained when starting samba with the --version
1571 #    parameter)
1572 # 21)Check if the current provision has all the settings needed for dynamic
1573 #    DNS update to work (that is to say the provision is newer than
1574 #    january 2010). If not dns configuration file from reference provision
1575 #    are copied in a sub folder and the administrator is invited to
1576 #    do what is needed.
1577 # 22)If the lastProvisionUSN attribute was present it is updated to add
1578 #    the range of usns modified by the current upgradeprovision
1579
1580
1581 # About updating the sam DB
1582 # The update takes place in update_partition function
1583 # This function read both current and reference provision and list all
1584 # the available DN of objects
1585 # If the string representation of a DN in reference provision is
1586 # equal to the string representation of a DN in current provision
1587 # (without taking care of case) then the object is flaged as being
1588 # present. If the object is not present in current provision the object
1589 # is being flaged as missing in current provision. Object present in current
1590 # provision but not in reference provision are ignored.
1591 # Once the list of objects present and missing is done, the deleted object
1592 # containers are created in the differents partitions (if missing)
1593 #
1594 # Then the function add_missing_entries is called
1595 # This function will go through the list of missing entries by calling
1596 # add_missing_object for the given object. If this function returns 0
1597 # it means that the object needs some other object in order to be created
1598 # The object is reappended at the end of the list to be created later
1599 # (and preferably after all the needed object have been created)
1600 # The function keeps on looping on the list of object to be created until
1601 # it's empty or that the number of defered creation is equal to the number
1602 # of object that still needs to be created.
1603
1604 # The function add_missing_object will first check if the object can be created.
1605 # That is to say that it didn't depends other not yet created objects
1606 # If requisit can't be fullfilled it exists with 0
1607 # Then it will try to create the missing entry by creating doing
1608 # an ldb_message_diff between the object in the reference provision and
1609 # an empty object.
1610 # This resulting object is filtered to remove all the back link attribute
1611 # (ie. memberOf) as they will be created by the other linked object (ie.
1612 # the one with the member attribute)
1613 # All attributes specified in the hashAttrNotCopied associative array are
1614 # also removed it's most of the time generated attributes
1615
1616 # After missing entries have been added the update_partition function will
1617 # take care of object that exist but that need some update.
1618 # In order to do so the function update_present is called with the list
1619 # of object that are present in both provision and that might need an update.
1620
1621 # This function handle first case mismatch so that the DN in the current
1622 # provision have the same case as in reference provision
1623
1624 # It will then construct an associative array consiting of attributes as
1625 # key and invocationid as value( if the originating invocation id is
1626 # different from the invocation id of the current DC the value is -1 instead).
1627
1628 # If the range of provision modified attributes is present, the function will
1629 # use the replMetadataProperty update method which is the following:
1630 #  Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1631 #   creationTime, msDs-KeyVersionNumber, oEMInformation
1632 #  Check for each attribute if its usn is within one of the modified by
1633 #   provision range and if its originating id is the invocation id of the
1634 #   current DC, then validate the update from reference to current.
1635 #   If not or if there is no replMetatdataProperty for this attribute then we
1636 #   do not update it.
1637 # Otherwise (case the range of provision modified attribute is not present) it
1638 # use the following process:
1639 #  All attributes that need to be added are accepted at the exeption of those
1640 #   listed in hashOverwrittenAtt, in this case the attribute needs to have the
1641 #   correct flags specified.
1642 #  For attributes that need to be modified or removed, a check is performed
1643 #  in OverwrittenAtt, if the attribute is present and the modification flag
1644 #  (remove, delete) is one of those listed for this attribute then modification
1645 #  is accepted. For complicated handling of attribute update, the control is passed
1646 #  to handle_special_case
1647
1648
1649
1650 if __name__ == '__main__':
1651     global defSDmodified
1652     defSDmodified = False
1653     # From here start the big steps of the program
1654     # 1) First get files paths
1655     paths = get_paths(param, smbconf=smbconf)
1656     # Get ldbs with the system session, it is needed for searching
1657     # provision parameters
1658     session = system_session()
1659
1660     # This variable will hold the last provision USN once if it exists.
1661     minUSN = 0
1662     # 2)
1663     ldbs = get_ldbs(paths, creds, session, lp)
1664     backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1665                                     prefix="backupprovision")
1666     backup_provision(paths, backupdir)
1667     try:
1668         ldbs.startTransactions()
1669
1670         # 3) Guess all the needed names (variables in fact) from the current
1671         # provision.
1672         names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1673                                                 paths, smbconf, lp)
1674         # 4)
1675         lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1676         if lastProvisionUSNs is not None:
1677             v = 0
1678             for k in lastProvisionUSNs.keys():
1679                 for r in lastProvisionUSNs[k]:
1680                     v = v + 1
1681
1682             message(CHANGE,
1683                 "Find last provision USN, %d invocation(s) for a total of %d ranges" % \
1684                             (len(lastProvisionUSNs.keys()), v /2 ))
1685
1686             if lastProvisionUSNs.get("default") != None:
1687                 message(CHANGE, "Old style for usn ranges used")
1688                 lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"]
1689                 del lastProvisionUSNs["default"]
1690         # Objects will be created with the admin session
1691         # (not anymore system session)
1692         adm_session = admin_session(lp, str(names.domainsid))
1693         # So we reget handle on objects
1694         # ldbs = get_ldbs(paths, creds, adm_session, lp)
1695         if not opts.fixntacl:
1696             if not sanitychecks(ldbs.sam, names):
1697                 message(SIMPLE, "Sanity checks for the upgrade have failed. "
1698                         "Check the messages and correct the errors "
1699                         "before rerunning upgradeprovision")
1700                 ldbs.groupedRollback()
1701                 sys.exit(1)
1702
1703             # Let's see provision parameters
1704             print_provision_key_parameters(names)
1705
1706             # 5) With all this information let's create a fresh new provision used as
1707             # reference
1708             message(SIMPLE, "Creating a reference provision")
1709             provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1710                             prefix="referenceprovision")
1711             newprovision(names, creds, session, smbconf, provisiondir,
1712                     provision_logger)
1713
1714             # TODO
1715             # 6) and 7)
1716             # We need to get a list of object which SD is directly computed from
1717             # defaultSecurityDescriptor.
1718             # This will allow us to know which object we can rebuild the SD in case
1719             # of change of the parent's SD or of the defaultSD.
1720             # Get file paths of this new provision
1721             newpaths = get_paths(param, targetdir=provisiondir)
1722             new_ldbs = get_ldbs(newpaths, creds, session, lp)
1723             new_ldbs.startTransactions()
1724
1725             # 8) Populate some associative array to ease the update process
1726             # List of attribute which are link and backlink
1727             populate_links(new_ldbs.sam, names.schemadn)
1728             # List of attribute with ASN DN synthax)
1729             populate_dnsyntax(new_ldbs.sam, names.schemadn)
1730             # 9)
1731             update_privilege(newpaths.private_dir, paths.private_dir)
1732             # 10)
1733             oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1734             # Do some modification on sam.ldb
1735             ldbs.groupedCommit()
1736             new_ldbs.groupedCommit()
1737             deltaattr = None
1738         # 11)
1739             if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1740                 # 11) A
1741                 # Starting from alpha9 we can consider that the structure is quite ok
1742                 # and that we should do only dela
1743                 deltaattr = delta_update_basesamdb(newpaths.samdb,
1744                                 paths.samdb,
1745                                 creds,
1746                                 session,
1747                                 lp,
1748                                 message)
1749             else:
1750                 # 11) B
1751                 simple_update_basesamdb(newpaths, paths, names)
1752                 ldbs = get_ldbs(paths, creds, session, lp)
1753                 removeProvisionUSN(ldbs.sam)
1754
1755             ldbs.startTransactions()
1756             minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1757             new_ldbs.startTransactions()
1758
1759             # 12)
1760             schema = Schema(names.domainsid, schemadn=str(names.schemadn))
1761             # We create a closure that will be invoked just before schema reload
1762             def schemareloadclosure():
1763                 basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
1764                         options=["modules:"])
1765                 doit = False
1766                 if deltaattr is not None and len(deltaattr) > 1:
1767                     doit = True
1768                 if doit:
1769                     deltaattr.remove("dn")
1770                 for att in deltaattr:
1771                     if att.lower() == "dn":
1772                         continue
1773                     if (deltaattr.get(att) is not None
1774                         and deltaattr.get(att).flags() != FLAG_MOD_ADD):
1775                         doit = False
1776                     elif deltaattr.get(att) is None:
1777                         doit = False
1778                 if doit:
1779                     message(CHANGE, "Applying delta to @ATTRIBUTES")
1780                     deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
1781                     basesam.modify(deltaattr)
1782                 else:
1783                     message(CHANGE, "Not applying delta to @ATTRIBUTES because "
1784                         "there is not only add")
1785             # 13)
1786             if opts.full:
1787                 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1788                         schema, schemareloadclosure):
1789                     message(SIMPLE, "Rolling back all changes. Check the cause"
1790                             " of the problem")
1791                     message(SIMPLE, "Your system is as it was before the upgrade")
1792                     ldbs.groupedRollback()
1793                     new_ldbs.groupedRollback()
1794                     shutil.rmtree(provisiondir)
1795                     sys.exit(1)
1796             else:
1797                 # Try to reapply the change also when we do not change the sam
1798                 # as the delta_upgrade
1799                 schemareloadclosure()
1800                 sync_calculated_attributes(ldbs.sam, names)
1801                 res = ldbs.sam.search(expression="(samaccountname=dns)",
1802                         scope=SCOPE_SUBTREE, attrs=["dn"],
1803                         controls=["search_options:1:2"])
1804                 if len(res) > 0:
1805                     message(SIMPLE, "You still have the old DNS object for managing "
1806                             "dynamic DNS, but you didn't supply --full so "
1807                             "a correct update can't be done")
1808                     ldbs.groupedRollback()
1809                     new_ldbs.groupedRollback()
1810                     shutil.rmtree(provisiondir)
1811                     sys.exit(1)
1812             # 14)
1813             update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1814             # 14bis)
1815             res = ldbs.sam.search(expression="(samaccountname=dns)",
1816                         scope=SCOPE_SUBTREE, attrs=["dn"],
1817                         controls=["search_options:1:2"])
1818
1819             if (len(res) == 1):
1820                 ldbs.sam.delete(res[0]["dn"])
1821                 res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
1822                         scope=SCOPE_SUBTREE, attrs=["dn"])
1823                 update_dns_account_password(ldbs.sam, ldbs.secrets, names)
1824                 message(SIMPLE, "IMPORTANT!!! "
1825                         "If you were using Dynamic DNS before you need "
1826                         "to update your configuration, so that the "
1827                         "tkey-gssapi-credential has the following value: "
1828                         "DNS/%s.%s" % (names.netbiosname.lower(),
1829                             names.realm.lower()))
1830             # 15)
1831             message(SIMPLE, "Update machine account")
1832             update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1833
1834             # 16) SD should be created with admin but as some previous acl were so wrong
1835             # that admin can't modify them we have first to recreate them with the good
1836             # form but with system account and then give the ownership to admin ...
1837             if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1838                 message(SIMPLE, "Fixing old povision SD")
1839                 fix_partition_sd(ldbs.sam, names)
1840                 rebuild_sd(ldbs.sam, names)
1841
1842             # We calculate the max USN before recalculating the SD because we might
1843             # touch object that have been modified after a provision and we do not
1844             # want that the next upgradeprovision thinks that it has a green light
1845             # to modify them
1846
1847             # 17)
1848             maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1849
1850             # 18) We rebuild SD only if defaultSecurityDescriptor is modified
1851             # But in fact we should do it also if one object has its SD modified as
1852             # child might need rebuild
1853             if defSDmodified:
1854                 message(SIMPLE, "Updating SD")
1855                 ldbs.sam.set_session_info(adm_session)
1856                 # Alpha10 was a bit broken still
1857                 if re.match(r'.*alpha(\d|10)', str(oem)):
1858                     fix_partition_sd(ldbs.sam, names)
1859                     rebuild_sd(ldbs.sam, names)
1860
1861             # 19)
1862             # Now we are quite confident in the recalculate process of the SD, we make
1863             # it optional.
1864             # Also the check must be done in a clever way as for the moment we just
1865             # compare SDDL
1866             if opts.debugchangesd:
1867                 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1868
1869             # 20)
1870             updateOEMInfo(ldbs.sam, str(names.rootdn))
1871             # 21)
1872             check_for_DNS(newpaths.private_dir, paths.private_dir)
1873             # 22)
1874             if lastProvisionUSNs is not None:
1875                 update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
1876             if opts.full and (names.policyid is None or names.policyid_dc is None):
1877                 update_policyids(names, ldbs.sam)
1878         if opts.full or opts.resetfileacl or opts.fixntacl:
1879             try:
1880                 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1881             except ProvisioningError, e:
1882                 message(ERROR, "The policy for domain controller is missing. "
1883                             "You should restart upgradeprovision with --full")
1884             except IOError, e:
1885                 message(ERROR, "Setting ACL not supported on your filesystem")
1886         else:
1887             try:
1888                 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1889             except ProvisioningError, e:
1890                 message(ERROR, "The policy for domain controller is missing. "
1891                             "You should restart upgradeprovision with --full")
1892         if not opts.fixntacl:
1893             ldbs.groupedCommit()
1894             new_ldbs.groupedCommit()
1895             message(SIMPLE, "Upgrade finished!")
1896             # remove reference provision now that everything is done !
1897             # So we have reindexed first if need when the merged schema was reloaded
1898             # (as new attributes could have quick in)
1899             # But the second part of the update (when we update existing objects
1900             # can also have an influence on indexing as some attribute might have their
1901             # searchflag modificated
1902             message(SIMPLE, "Reopenning samdb to trigger reindexing if needed "
1903                     "after modification")
1904             samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
1905             message(SIMPLE, "Reindexing finished")
1906
1907             shutil.rmtree(provisiondir)
1908         else:
1909             ldbs.groupedRollback()
1910             message(SIMPLE, "ACLs fixed !")
1911     except StandardError, err:
1912         message(ERROR, "A problem occurred while trying to upgrade your "
1913                    "provision. A full backup is located at %s" % backupdir)
1914         if opts.debugall or opts.debugchange:
1915             (typ, val, tb) = sys.exc_info()
1916             traceback.print_exception(typ, val, tb)
1917         sys.exit(1)