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