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