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