s4-upgradeprovision: Fix an error, so that cursddl and refsddl are not the same
[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 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 which values are guessed")
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 security descriptor 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("--fixntacl", action="store_true",
155                   help="Only fix NT ACLs in sysvol / netlogon share")
156 parser.add_option("--full", action="store_true",
157                   help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
158
159 opts = parser.parse_args()[0]
160
161 handler = logging.StreamHandler(sys.stdout)
162 upgrade_logger = logging.getLogger("upgradeprovision")
163 upgrade_logger.setLevel(logging.INFO)
164
165 upgrade_logger.addHandler(handler)
166
167 provision_logger = logging.getLogger("provision")
168 provision_logger.addHandler(handler)
169
170 whatToLog = define_what_to_log(opts)
171
172 def message(what, text):
173     """Print a message if this message type has been selected to be printed
174
175     :param what: Category of the message
176     :param text: Message to print """
177     if (whatToLog & what) or what <= 0:
178         upgrade_logger.info("%s", text)
179
180 if len(sys.argv) == 1:
181     opts.interactive = True
182 lp = sambaopts.get_loadparm()
183 smbconf = lp.configfile
184
185 creds = credopts.get_credentials(lp)
186 creds.set_kerberos_state(DONT_USE_KERBEROS)
187
188
189
190 def check_for_DNS(refprivate, private):
191     """Check if the provision has already the requirement for dynamic dns
192
193     :param refprivate: The path to the private directory of the reference
194                        provision
195     :param private: The path to the private directory of the upgraded
196                     provision"""
197
198     spnfile = "%s/spn_update_list" % private
199     dnsfile = "%s/dns_update_list" % private
200     namedfile = lp.get("dnsupdate:path")
201
202     if not namedfile:
203        namedfile = "%s/named.conf.update" % private
204
205     if not os.path.exists(spnfile):
206         shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
207
208     if not os.path.exists(dnsfile):
209         shutil.copy("%s/dns_update_list" % refprivate, "%s" % dnsfile)
210
211     destdir = "%s/new_dns" % private
212     dnsdir = "%s/dns" % private
213
214     if not os.path.exists(namedfile):
215         if not os.path.exists(destdir):
216             os.mkdir(destdir)
217         if not os.path.exists(dnsdir):
218             os.mkdir(dnsdir)
219         shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
220         shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
221         message(SIMPLE, "It seems that your provision did not integrate "
222                 "new rules for dynamic dns update of domain related entries")
223         message(SIMPLE, "A copy of the new bind configuration files and "
224                 "template has been put in %s, you should read them and "
225                 "configure dynamic dns updates" % destdir)
226
227
228 def populate_links(samdb, schemadn):
229     """Populate an array with all the back linked attributes
230
231     This attributes that are modified automaticaly when
232     front attibutes are changed
233
234     :param samdb: A LDB object for sam.ldb file
235     :param schemadn: DN of the schema for the partition"""
236     linkedAttHash = get_linked_attributes(Dn(samdb, str(schemadn)), samdb)
237     backlinked.extend(linkedAttHash.values())
238     for t in linkedAttHash.keys():
239         forwardlinked.add(t)
240
241
242 def populate_dnsyntax(samdb, schemadn):
243     """Populate an array with all the attributes that have DN synthax
244        (oid 2.5.5.1)
245
246     :param samdb: A LDB object for sam.ldb file
247     :param schemadn: DN of the schema for the partition"""
248     res = samdb.search(expression="(attributeSyntax=2.5.5.1)", base=Dn(samdb,
249                         str(schemadn)), scope=SCOPE_SUBTREE,
250                         attrs=["lDAPDisplayName"])
251     for elem in res:
252         dn_syntax_att.append(elem["lDAPDisplayName"])
253
254
255 def sanitychecks(samdb, names):
256     """Make some checks before trying to update
257
258     :param samdb: An LDB object opened on sam.ldb
259     :param names: list of key provision parameters
260     :return: Status of check (1 for Ok, 0 for not Ok) """
261     res = samdb.search(expression="objectClass=ntdsdsa", base=str(names.configdn),
262                          scope=SCOPE_SUBTREE, attrs=["dn"],
263                          controls=["search_options:1:2"])
264     if len(res) == 0:
265         print "No DC found. Your provision is most probably broken!"
266         return False
267     elif len(res) != 1:
268         print "Found %d domain controllers. For the moment " \
269               "upgradeprovision is not able to handle an upgrade on a " \
270               "domain with more than one DC. Please demote the other " \
271               "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 change 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                 + " must not be changed/removed. Discarding the 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", "provision: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", "provision: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
658             modcontrols = ["relax:0", "provision:0"]
659             samdb.add(delta, modcontrols)
660
661             listwko = []
662             res = samdb.search(expression="(objectClass=*)", base=part,
663                                scope=SCOPE_BASE,
664                                attrs=["dn", "wellKnownObjects"])
665
666             targetWKO = "%s:%s" % (wkoPrefix, str(reference[0]["dn"]))
667             found = False
668
669             if len(res[0]) > 0:
670                 wko = res[0]["wellKnownObjects"]
671
672                 # The wellKnownObject that we want to add.
673                 for o in wko:
674                     if str(o) == targetWKO:
675                         found = True
676                     listwko.append(str(o))
677
678             if not found:
679                 listwko.append(targetWKO)
680
681                 delta = Message()
682                 delta.dn = Dn(samdb, str(res[0]["dn"]))
683                 delta["wellKnownObjects"] = MessageElement(listwko,
684                                                 FLAG_MOD_REPLACE,
685                                                 "wellKnownObjects" )
686                 samdb.modify(delta)
687
688 def add_missing_entries(ref_samdb, samdb, names, basedn, list):
689     """Add the missing object whose DN is the list
690
691     The function add the object if the objects on which it depends are
692     already created.
693
694     :param ref_samdb: Ldb object representing the SAM db of the reference
695                       provision
696     :param samdb: Ldb object representing the SAM db of the upgraded
697                   provision
698     :param dn: DN of the object to be added
699     :param names: List of key provision parameters
700     :param basedn: DN of the partition to be updated
701     :param list: List of DN to be added in the upgraded provision"""
702
703     listMissing = []
704     listDefered = list
705
706     while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
707         index = 0
708         listMissing = listDefered
709         listDefered = []
710         hashMissing = gen_dn_index_hash(listMissing)
711         for dn in listMissing:
712             ret = add_missing_object(ref_samdb, samdb, dn, names, basedn,
713                                         hashMissing, index)
714             index = index + 1
715             if ret == 0:
716                 # DN can't be created because it depends on some
717                 # other DN in the list
718                 listDefered.append(dn)
719
720     if len(listDefered) != 0:
721         raise ProvisioningError("Unable to insert missing elements: "
722                                 "circular references")
723
724 def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
725     """This function handle updates on links
726
727     :param samdb: An LDB object pointing to the updated provision
728     :param att: Attribute to update
729     :param basedn: The root DN of the provision
730     :param dn: The DN of the inspected object
731     :param value: The value of the attribute
732     :param ref_value: The value of this attribute in the reference provision
733     :param delta: The MessageElement object that will be applied for
734                    transforming the current provision"""
735
736     res = samdb.search(expression="dn=%s" % dn, base=basedn,
737                         controls=["search_options:1:2", "reveal:1"],
738                         attrs=[att])
739
740     blacklist = {}
741     hash = {}
742     newlinklist = []
743     changed = False
744
745     newlinklist.extend(value)
746
747     for e in value:
748         hash[e] = 1
749     # for w2k domain level the reveal won't reveal anything ...
750     # it means that we can readd links that were removed on purpose ...
751     # Also this function in fact just accept add not removal
752
753     for e in res[0][att]:
754         if not hash.has_key(e):
755             # We put in the blacklist all the element that are in the "revealed"
756             # result and not in the "standard" result
757             # This element are links that were removed before and so that
758             # we don't wan't to readd
759             blacklist[e] = 1
760
761     for e in ref_value:
762         if not blacklist.has_key(e) and not hash.has_key(e):
763             newlinklist.append(str(e))
764             changed = True
765     if changed:
766         delta[att] = MessageElement(newlinklist, FLAG_MOD_REPLACE, att)
767     else:
768         delta.remove(att)
769
770
771 msg_elt_flag_strs = {
772     ldb.FLAG_MOD_ADD: "MOD_ADD",
773     ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
774     ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
775
776
777 def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
778     """ This function updates the object that are already present in the
779         provision
780
781     :param ref_samdb: An LDB object pointing to the reference provision
782     :param samdb: An LDB object pointing to the updated provision
783     :param basedn: A string with the value of the base DN for the provision
784                    (ie. DC=foo, DC=bar)
785     :param listPresent: A list of object that is present in the provision
786     :param usns: A list of USN range modified by previous provision and
787                  upgradeprovision
788     :param invocationid: The value of the invocationid for the current DC"""
789
790     global defSDmodified
791     # This hash is meant to speedup lookup of attribute name from an oid,
792     # it's for the replPropertyMetaData handling
793     hash_oid_name = {}
794     res = samdb.search(expression="objectClass=attributeSchema", base=basedn,
795                         controls=["search_options:1:2"], attrs=["attributeID",
796                         "lDAPDisplayName"])
797     if len(res) > 0:
798         for e in res:
799             strDisplay = str(e.get("lDAPDisplayName"))
800             hash_oid_name[str(e.get("attributeID"))] = strDisplay
801     else:
802         msg = "Unable to insert missing elements: circular references"
803         raise ProvisioningError(msg)
804
805     changed = 0
806     controls = ["search_options:1:2", "sd_flags:1:2"]
807     for dn in listPresent:
808         reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
809                                         scope=SCOPE_SUBTREE,
810                                         controls=controls)
811         current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
812                                 scope=SCOPE_SUBTREE, controls=controls)
813
814         if (
815              (str(current[0].dn) != str(reference[0].dn)) and
816              (str(current[0].dn).upper() == str(reference[0].dn).upper())
817            ):
818             message(CHANGE, "Names are the same except for the case. "
819                             "Renaming %s to %s" % (str(current[0].dn),
820                                                    str(reference[0].dn)))
821             identic_rename(samdb, reference[0].dn)
822             current = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
823                                     scope=SCOPE_SUBTREE,
824                                     controls=controls)
825
826         delta = samdb.msg_diff(current[0], reference[0])
827
828         for att in hashAttrNotCopied.keys():
829             delta.remove(att)
830
831         for att in backlinked:
832             delta.remove(att)
833
834         delta.remove("name")
835
836         if len(delta.items()) > 1 and usns is not None:
837             # Fetch the replPropertyMetaData
838             res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
839                                 scope=SCOPE_SUBTREE, controls=controls,
840                                 attrs=["replPropertyMetaData"])
841             ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
842                                 str(res[0]["replPropertyMetaData"])).ctr
843
844             hash_attr_usn = {}
845             for o in ctr.array:
846                 # We put in this hash only modification
847                 # made on the current host
848                 att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
849                 if str(o.originating_invocation_id) == str(invocationid):
850                 # Note we could just use 1 here
851                     hash_attr_usn[att] = o.originating_usn
852                 else:
853                     hash_attr_usn[att] = -1
854
855         isFirst = 0
856         txt = ""
857
858         for att in list(delta):
859             if usns is not None:
860                 # We have updated by provision usn information so let's exploit
861                 # replMetadataProperties
862                 if att in forwardlinked:
863                     curval = current[0].get(att, ())
864                     refval = reference[0].get(att, ())
865                     handle_links(samdb, att, basedn, current[0]["dn"],
866                                     curval, refval, delta)
867                     continue
868
869                 if isFirst == 0 and len(delta.items())>1:
870                     isFirst = 1
871                     txt = "%s\n" % (str(dn))
872                 if att == "dn":
873                     # There is always a dn attribute after a msg_diff
874                     continue
875                 if att == "rIDAvailablePool":
876                     delta.remove(att)
877                     continue
878                 if att == "objectSid":
879                     delta.remove(att)
880                     continue
881                 if att == "creationTime":
882                     delta.remove(att)
883                     continue
884                 if att == "oEMInformation":
885                     delta.remove(att)
886                     continue
887                 if att == "msDs-KeyVersionNumber":
888                 # This is the kvno of the computer/user it's a very bad
889                 # idea to change it
890                     delta.remove(att)
891                     continue
892                 if handle_special_case(att, delta, reference, current, usns, basedn, samdb):
893                     # This attribute is "complicated" to handle and handling
894                     # was done in handle_special_case
895                     continue
896                 attrUSN = hash_attr_usn.get(att)
897                 if att == "forceLogoff" and attrUSN is None:
898                     continue
899                 if  attrUSN is None:
900                     delta.remove(att)
901                     continue
902                 if att == "nTSecurityDescriptor":
903                     cursd = ndr_unpack(security.descriptor,
904                         str(current[0]["nTSecurityDescriptor"]))
905                     cursddl = cursd.as_sddl(names.domainsid)
906                     refsd = ndr_unpack(security.descriptor,
907                         str(reference[0]["nTSecurityDescriptor"]))
908                     refsddl = refsd.as_sddl(names.domainsid)
909
910                     if get_diff_sddls(refsddl, cursddl) == "":
911                        message(CHANGE, "sd are identical")
912                     else:
913                        message(CHANGE, "sd are not identical")
914                 if attrUSN == -1:
915                     # This attribute was last modified by another DC forget
916                     # about it
917                     message(CHANGE, "%sAttribute: %s has been "
918                             "created/modified/deleted by another DC. "
919                             "Doing nothing" % (txt, att))
920                     txt = ""
921                     delta.remove(att)
922                     continue
923                 elif not usn_in_range(int(attrUSN), usns):
924                     message(CHANGE, "%sAttribute: %s was not "
925                                     "created/modified/deleted during a "
926                                     "provision or upgradeprovision. Current "
927                                     "usn: %d. Doing nothing" % (txt, att,
928                                                                 attrUSN))
929                     txt = ""
930                     delta.remove(att)
931                     continue
932                 else:
933                     if att == "defaultSecurityDescriptor":
934                         defSDmodified = True
935                     if attrUSN:
936                         message(CHANGE, "%sAttribute: %s will be modified"
937                                         "/deleted it was last modified "
938                                         "during a provision. Current usn: "
939                                         "%d" % (txt, att, attrUSN))
940                         txt = ""
941                     else:
942                         message(CHANGE, "%sAttribute: %s will be added because "
943                                         "it did not exist before" % (txt, att))
944                         txt = ""
945                     continue
946
947             else:
948             # Old school way of handling things for pre alpha12 upgrade
949                 defSDmodified = True
950                 msgElt = delta.get(att)
951
952                 if att == "nTSecurityDescriptor":
953                     delta.remove(att)
954                     continue
955
956                 if att == "dn":
957                     continue
958
959                 if not hashOverwrittenAtt.has_key(att):
960                     if msgElt.flags() != FLAG_MOD_ADD:
961                         if not handle_special_case(att, delta, reference, current,
962                                                     usns, basedn, samdb):
963                             if opts.debugchange or opts.debugall:
964                                 try:
965                                     dump_denied_change(dn, att,
966                                         msg_elt_flag_strs[msgElt.flags()],
967                                         current[0][att], reference[0][att])
968                                 except KeyError:
969                                     dump_denied_change(dn, att,
970                                         msg_elt_flag_strs[msgElt.flags()],
971                                         current[0][att], None)
972                             delta.remove(att)
973                         continue
974                 else:
975                     if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
976                         continue
977                     elif  hashOverwrittenAtt.get(att)==never:
978                         delta.remove(att)
979                         continue
980
981         delta.dn = dn
982         if len(delta.items()) >1:
983             attributes=", ".join(delta.keys())
984             modcontrols = []
985             relaxedatt = ['iscriticalsystemobject', 'grouptype']
986             # Let's try to reduce as much as possible the use of relax control
987             #for checkedatt in relaxedatt:
988             for attr in delta.keys():
989                 if attr.lower() in relaxedatt:
990                     modcontrols = ["relax:0", "provision:0"]
991             message(CHANGE, "%s is different from the reference one, changed"
992                             " attributes: %s\n" % (dn, attributes))
993             changed += 1
994             samdb.modify(delta, modcontrols)
995     return changed
996
997 def reload_full_schema(samdb, names):
998     """Load the updated schema with all the new and existing classes
999        and attributes.
1000
1001     :param samdb: An LDB object connected to the sam.ldb of the update
1002                   provision
1003     :param names: List of key provision parameters
1004     """
1005
1006     current = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1007                                 scope=SCOPE_SUBTREE)
1008     schema_ldif = ""
1009     prefixmap_data = ""
1010
1011     for ent in current:
1012         schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
1013
1014     prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
1015     prefixmap_data = b64encode(prefixmap_data)
1016
1017     # We don't actually add this ldif, just parse it
1018     prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap_data
1019
1020     dsdb._dsdb_set_schema_from_ldif(samdb, prefixmap_ldif, schema_ldif)
1021
1022
1023 def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, prereloadfunc):
1024     """Check differences between the reference provision and the upgraded one.
1025
1026     It looks for all objects which base DN is name.
1027
1028     This function will also add the missing object and update existing object
1029     to add or remove attributes that were missing.
1030
1031     :param ref_sambdb: An LDB object conntected to the sam.ldb of the
1032                        reference provision
1033     :param samdb: An LDB object connected to the sam.ldb of the update
1034                   provision
1035     :param basedn: String value of the DN of the partition
1036     :param names: List of key provision parameters
1037     :param schema: A Schema object
1038     :param provisionUSNs:  The USNs modified by provision/upgradeprovision
1039                            last time
1040     :param prereloadfunc: A function that must be executed just before the reload
1041                   of the schema
1042     """
1043
1044     hash_new = {}
1045     hash = {}
1046     listMissing = []
1047     listPresent = []
1048     reference = []
1049     current = []
1050
1051     # Connect to the reference provision and get all the attribute in the
1052     # partition referred by name
1053     reference = ref_samdb.search(expression="objectClass=*", base=basedn,
1054                                     scope=SCOPE_SUBTREE, attrs=["dn"],
1055                                     controls=["search_options:1:2"])
1056
1057     current = samdb.search(expression="objectClass=*", base=basedn,
1058                                 scope=SCOPE_SUBTREE, attrs=["dn"],
1059                                 controls=["search_options:1:2"])
1060     # Create a hash for speeding the search of new object
1061     for i in range(0, len(reference)):
1062         hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
1063
1064     # Create a hash for speeding the search of existing object in the
1065     # current provision
1066     for i in range(0, len(current)):
1067         hash[str(current[i]["dn"]).lower()] = current[i]["dn"]
1068
1069
1070     for k in hash_new.keys():
1071         if not hash.has_key(k):
1072             if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
1073                 listMissing.append(hash_new[k])
1074         else:
1075             listPresent.append(hash_new[k])
1076
1077     # Sort the missing object in order to have object of the lowest level
1078     # first (which can be containers for higher level objects)
1079     listMissing.sort(dn_sort)
1080     listPresent.sort(dn_sort)
1081
1082     # The following lines is to load the up to
1083     # date schema into our current LDB
1084     # a complete schema is needed as the insertion of attributes
1085     # and class is done against it
1086     # and the schema is self validated
1087     samdb.set_schema(schema)
1088     try:
1089         message(SIMPLE, "There are %d missing objects" % (len(listMissing)))
1090         add_deletedobj_containers(ref_samdb, samdb, names)
1091
1092         add_missing_entries(ref_samdb, samdb, names, basedn, listMissing)
1093
1094         prereloadfunc()
1095         message(SIMPLE, "Reloading a merged schema, which might trigger "
1096                         "reindexing so please be patient")
1097         reload_full_schema(samdb, names)
1098         message(SIMPLE, "Schema reloaded!")
1099
1100         changed = update_present(ref_samdb, samdb, basedn, listPresent,
1101                                     provisionUSNs, names.invocation)
1102         message(SIMPLE, "There are %d changed objects" % (changed))
1103         return 1
1104
1105     except StandardError, err:
1106         message(ERROR, "Exception during upgrade of samdb:")
1107         (typ, val, tb) = sys.exc_info()
1108         traceback.print_exception(typ, val, tb)
1109         return 0
1110
1111
1112 def check_updated_sd(ref_sam, cur_sam, names):
1113     """Check if the security descriptor in the upgraded provision are the same
1114        as the reference
1115
1116     :param ref_sam: A LDB object connected to the sam.ldb file used as
1117                     the reference provision
1118     :param cur_sam: A LDB object connected to the sam.ldb file used as
1119                     upgraded provision
1120     :param names: List of key provision parameters"""
1121     reference = ref_sam.search(expression="objectClass=*", base=str(names.rootdn),
1122                                 scope=SCOPE_SUBTREE,
1123                                 attrs=["dn", "nTSecurityDescriptor"],
1124                                 controls=["search_options:1:2"])
1125     current = cur_sam.search(expression="objectClass=*", base=str(names.rootdn),
1126                                 scope=SCOPE_SUBTREE,
1127                                 attrs=["dn", "nTSecurityDescriptor"],
1128                                 controls=["search_options:1:2"])
1129     hash = {}
1130     for i in range(0, len(reference)):
1131         refsd = ndr_unpack(security.descriptor,
1132                     str(reference[i]["nTSecurityDescriptor"]))
1133         hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
1134
1135
1136     for i in range(0, len(current)):
1137         key = str(current[i]["dn"]).lower()
1138         if hash.has_key(key):
1139             cursd = ndr_unpack(security.descriptor,
1140                         str(current[i]["nTSecurityDescriptor"]))
1141             sddl = cursd.as_sddl(names.domainsid)
1142             if sddl != hash[key]:
1143                 txt = get_diff_sddls(hash[key], sddl)
1144                 if txt != "":
1145                     message(CHANGESD, "On object %s ACL is different"
1146                                       " \n%s" % (current[i]["dn"], txt))
1147
1148
1149
1150 def fix_partition_sd(samdb, names):
1151     """This function fix the SD for partition containers (basedn, configdn, ...)
1152     This is needed because some provision use to have broken SD on containers
1153
1154     :param samdb: An LDB object pointing to the sam of the current provision
1155     :param names: A list of key provision parameters
1156     """
1157     # First update the SD for the rootdn
1158     res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1159                          scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1160                          controls=["search_options:1:2"])
1161     delta = Message()
1162     delta.dn = Dn(samdb, str(res[0]["dn"]))
1163     descr = get_domain_descriptor(names.domainsid)
1164     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1165                                                     "nTSecurityDescriptor")
1166     samdb.modify(delta)
1167     # Then the config dn
1168     res = samdb.search(expression="objectClass=*", base=str(names.configdn),
1169                         scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1170                         controls=["search_options:1:2"])
1171     delta = Message()
1172     delta.dn = Dn(samdb, str(res[0]["dn"]))
1173     descr = get_config_descriptor(names.domainsid)
1174     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1175                                                     "nTSecurityDescriptor" )
1176     samdb.modify(delta)
1177     # Then the schema dn
1178     res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
1179                         scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
1180                         controls=["search_options:1:2"])
1181
1182     delta = Message()
1183     delta.dn = Dn(samdb, str(res[0]["dn"]))
1184     descr = get_schema_descriptor(names.domainsid)
1185     delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1186                                                     "nTSecurityDescriptor" )
1187     samdb.modify(delta)
1188
1189 def rebuild_sd(samdb, names):
1190     """Rebuild security descriptor of the current provision from scratch
1191
1192     During the different pre release of samba4 security descriptors (SD)
1193     were notarly broken (up to alpha11 included)
1194     This function allow to get them back in order, this function make the
1195     assumption that nobody has modified manualy an SD
1196     and so SD can be safely recalculated from scratch to get them right.
1197
1198     :param names: List of key provision parameters"""
1199
1200
1201     hash = {}
1202     res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1203                         scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
1204                         controls=["search_options:1:2"])
1205     for obj in res:
1206         if not (str(obj["dn"]) == str(names.rootdn) or
1207             str(obj["dn"]) == str(names.configdn) or
1208             str(obj["dn"]) == str(names.schemadn)):
1209             hash[str(obj["dn"])] = obj["whenCreated"]
1210
1211     listkeys = hash.keys()
1212     listkeys.sort(dn_sort)
1213
1214     for key in listkeys:
1215         try:
1216             delta = Message()
1217             delta.dn = Dn(samdb, key)
1218             delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
1219                                                     "whenCreated" )
1220             samdb.modify(delta, ["recalculate_sd:0"])
1221         except:
1222             # XXX: We should always catch an explicit exception.
1223             # What could go wrong here?
1224             samdb.transaction_cancel()
1225             res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
1226                                 scope=SCOPE_SUBTREE,
1227                                 attrs=["dn", "nTSecurityDescriptor"],
1228                                 controls=["search_options:1:2"])
1229             badsd = ndr_unpack(security.descriptor,
1230                         str(res[0]["nTSecurityDescriptor"]))
1231             print "bad stuff %s" % badsd.as_sddl(names.domainsid)
1232             return
1233
1234 def removeProvisionUSN(samdb):
1235         attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1236         entry = samdb.search(expression="dn=@PROVISION", base = "",
1237                                 scope=SCOPE_SUBTREE,
1238                                 controls=["search_options:1:2"],
1239                                 attrs=attrs)
1240         empty = Message()
1241         empty.dn = entry[0].dn
1242         delta = samdb.msg_diff(entry[0], empty)
1243         delta.remove("dn")
1244         delta.dn = entry[0].dn
1245         samdb.modify(delta)
1246
1247 def remove_stored_generated_attrs(paths, creds, session, lp):
1248     """Remove previously stored constructed attributes
1249
1250     :param paths: List of paths for different provision objects
1251                         from the upgraded provision
1252     :param creds: A credential object
1253     :param session: A session object
1254     :param lp: A line parser object
1255     :return: An associative array whose key are the different constructed
1256              attributes and the value the dn where this attributes were found.
1257      """
1258
1259
1260 def simple_update_basesamdb(newpaths, paths, names):
1261     """Update the provision container db: sam.ldb
1262     This function is aimed at very old provision (before alpha9)
1263
1264     :param newpaths: List of paths for different provision objects
1265                         from the reference provision
1266     :param paths: List of paths for different provision objects
1267                         from the upgraded provision
1268     :param names: List of key provision parameters"""
1269
1270     message(SIMPLE, "Copy samdb")
1271     shutil.copy(newpaths.samdb, paths.samdb)
1272
1273     message(SIMPLE, "Update partitions filename if needed")
1274     schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1275     configldb = os.path.join(paths.private_dir, "configuration.ldb")
1276     usersldb = os.path.join(paths.private_dir, "users.ldb")
1277     samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1278
1279     if not os.path.isdir(samldbdir):
1280         os.mkdir(samldbdir)
1281         os.chmod(samldbdir, 0700)
1282     if os.path.isfile(schemaldb):
1283         shutil.copy(schemaldb, os.path.join(samldbdir,
1284                                             "%s.ldb"%str(names.schemadn).upper()))
1285         os.remove(schemaldb)
1286     if os.path.isfile(usersldb):
1287         shutil.copy(usersldb, os.path.join(samldbdir,
1288                                             "%s.ldb"%str(names.rootdn).upper()))
1289         os.remove(usersldb)
1290     if os.path.isfile(configldb):
1291         shutil.copy(configldb, os.path.join(samldbdir,
1292                                             "%s.ldb"%str(names.configdn).upper()))
1293         os.remove(configldb)
1294
1295
1296 def update_privilege(ref_private_path, cur_private_path):
1297     """Update the privilege database
1298
1299     :param ref_private_path: Path to the private directory of the reference
1300                              provision.
1301     :param cur_private_path: Path to the private directory of the current
1302                              (and to be updated) provision."""
1303     message(SIMPLE, "Copy privilege")
1304     shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1305                 os.path.join(cur_private_path, "privilege.ldb"))
1306
1307
1308 def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc):
1309     """Upgrade the SAM DB contents for all the provision partitions
1310
1311     :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1312                        provision
1313     :param samdb: An LDB object connected to the sam.ldb of the update
1314                   provision
1315     :param names: List of key provision parameters
1316     :param highestUSN:  The highest USN modified by provision/upgradeprovision
1317                         last time
1318     :param schema: A Schema object that represent the schema of the provision
1319     :param prereloadfunc: A function that must be executed just before the reload
1320                   of the schema
1321     """
1322
1323     message(SIMPLE, "Starting update of samdb")
1324     ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1325                             schema, highestUSN, prereloadfunc)
1326     if ret:
1327         message(SIMPLE, "Update of samdb finished")
1328         return 1
1329     else:
1330         message(SIMPLE, "Update failed")
1331         return 0
1332
1333
1334 def copyxattrs(dir, refdir):
1335     """ Copy owner, groups, extended ACL and NT acls from
1336     a reference dir to a destination dir
1337
1338     Both dir are supposed to hold the same files
1339     :param dir: Destination dir
1340     :param refdir: Reference directory"""
1341
1342     noxattr = 0
1343     for root, dirs, files in os.walk(dir, topdown=True):
1344         for name in files:
1345             subdir=root[len(dir):]
1346             ref = os.path.join("%s%s" % (refdir, subdir), name)
1347             statsinfo = os.stat(ref)
1348             tgt = os.path.join(root, name)
1349             try:
1350
1351                 os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
1352                 # Get the xattr attributes if any
1353                 try:
1354                     attribute = samba.xattr_native.wrap_getxattr(ref,
1355                                                  xattr.XATTR_NTACL_NAME)
1356                     samba.xattr_native.wrap_setxattr(tgt,
1357                                                  xattr.XATTR_NTACL_NAME,
1358                                                  attribute)
1359                 except:
1360                     noxattr = 1
1361                 attribute = samba.xattr_native.wrap_getxattr(ref,
1362                                                  "system.posix_acl_access")
1363                 samba.xattr_native.wrap_setxattr(tgt,
1364                                                  "system.posix_acl_access",
1365                                                   attribute)
1366             except:
1367                 continue
1368         for name in dirs:
1369             subdir=root[len(dir):]
1370             ref = os.path.join("%s%s" % (refdir, subdir), name)
1371             statsinfo = os.stat(ref)
1372             tgt = os.path.join(root, name)
1373             try:
1374                 os.chown(os.path.join(root, name), statsinfo.st_uid,
1375                           statsinfo.st_gid)
1376                 try:
1377                     attribute = samba.xattr_native.wrap_getxattr(ref,
1378                                                  xattr.XATTR_NTACL_NAME)
1379                     samba.xattr_native.wrap_setxattr(tgt,
1380                                                  xattr.XATTR_NTACL_NAME,
1381                                                  attribute)
1382                 except:
1383                     noxattr = 1
1384                 attribute = samba.xattr_native.wrap_getxattr(ref,
1385                                                  "system.posix_acl_access")
1386                 samba.xattr_native.wrap_setxattr(tgt,
1387                                                  "system.posix_acl_access",
1388                                                   attribute)
1389
1390             except:
1391                 continue
1392
1393
1394 def backup_provision(paths, dir):
1395     """This function backup the provision files so that a rollback
1396     is possible
1397
1398     :param paths: Paths to different objects
1399     :param dir: Directory where to store the backup
1400     """
1401
1402     shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
1403     copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
1404     shutil.copy2(paths.samdb, dir)
1405     shutil.copy2(paths.secrets, dir)
1406     shutil.copy2(paths.idmapdb, dir)
1407     shutil.copy2(paths.privilege, dir)
1408     if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1409         shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1410     shutil.copy2(paths.smbconf, dir)
1411     shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1412
1413     samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1414     if not os.path.isdir(samldbdir):
1415         samldbdir = paths.private_dir
1416         schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1417         configldb = os.path.join(paths.private_dir, "configuration.ldb")
1418         usersldb = os.path.join(paths.private_dir, "users.ldb")
1419         shutil.copy2(schemaldb, dir)
1420         shutil.copy2(usersldb, dir)
1421         shutil.copy2(configldb, dir)
1422     else:
1423         shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1424
1425
1426
1427
1428 def sync_calculated_attributes(samdb, names):
1429    """Synchronize attributes used for constructed ones, with the
1430       old constructed that were stored in the database.
1431
1432       This apply for instance to msds-keyversionnumber that was
1433       stored and that is now constructed from replpropertymetadata.
1434
1435       :param samdb: An LDB object attached to the currently upgraded samdb
1436       :param names: Various key parameter about current provision.
1437    """
1438    listAttrs = ["msDs-KeyVersionNumber"]
1439    hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
1440    if hash.has_key("msDs-KeyVersionNumber"):
1441        increment_calculated_keyversion_number(samdb, names.rootdn,
1442                                             hash["msDs-KeyVersionNumber"])
1443
1444 # Synopsis for updateprovision
1445 # 1) get path related to provision to be update (called current)
1446 # 2) open current provision ldbs
1447 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1448 #    of the DC ....)
1449 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1450 #    by either upgradeprovision or provision
1451 # 5) creation of a new provision the latest version of provision script
1452 #    (called reference)
1453 # 6) get reference provision paths
1454 # 7) open reference provision ldbs
1455 # 8) setup helpers data that will help the update process
1456 # 9) update the privilege ldb by copying the one of referecence provision to
1457 #    the current provision
1458 # 10)get the oemInfo field, this field contains information about the different
1459 #    provision that have been done
1460 # 11)Depending  on whether oemInfo has the string "alpha9" or alphaxx (x as an
1461 #    integer) or none of this the following things are done
1462 #    A) When alpha9 or alphaxx is present
1463 #       The base sam.ldb file is updated by looking at the difference between
1464 #       referrence one and the current one. Everything is copied with the
1465 #       exception of lastProvisionUSN attributes.
1466 #    B) Other case (it reflect that that provision was done before alpha9)
1467 #       The base sam.ldb of the reference provision is copied over
1468 #       the current one, if necessary ldb related to partitions are moved
1469 #       and renamed
1470 # The highest used USN is fetched so that changed by upgradeprovision
1471 # usn can be tracked
1472 # 12)A Schema object is created, it will be used to provide a complete
1473 #    schema to current provision during update (as the schema of the
1474 #    current provision might not be complete and so won't allow some
1475 #    object to be created)
1476 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1477 # 14)The secrets db is updated by pull all the difference from the reference
1478 #    provision into the current provision
1479 # 15)As the previous step has most probably modified the password stored in
1480 #    in secret for the current DC, a new password is generated,
1481 #    the kvno is bumped and the entry in samdb is also updated
1482 # 16)For current provision older than alpha9, we must fix the SD a little bit
1483 #    administrator to update them because SD used to be generated with the
1484 #    system account before alpha9.
1485 # 17)The highest usn modified so far is searched in the database it will be
1486 #    the upper limit for usn modified during provision.
1487 #    This is done before potential SD recalculation because we do not want
1488 #    SD modified during recalculation to be marked as modified during provision
1489 #    (and so possibly remplaced at next upgradeprovision)
1490 # 18)Rebuilt SD if the flag indicate to do so
1491 # 19)Check difference between SD of reference provision and those of the
1492 #    current provision. The check is done by getting the sddl representation
1493 #    of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1494 #    Each part is verified separetly, for dacl and sacl ACL is splited into
1495 #    ACEs and each ACE is verified separately (so that a permutation in ACE
1496 #    didn't raise as an error).
1497 # 20)The oemInfo field is updated to add information about the fact that the
1498 #    provision has been updated by the upgradeprovision version xxx
1499 #    (the version is the one obtained when starting samba with the --version
1500 #    parameter)
1501 # 21)Check if the current provision has all the settings needed for dynamic
1502 #    DNS update to work (that is to say the provision is newer than
1503 #    january 2010). If not dns configuration file from reference provision
1504 #    are copied in a sub folder and the administrator is invited to
1505 #    do what is needed.
1506 # 22)If the lastProvisionUSN attribute was present it is updated to add
1507 #    the range of usns modified by the current upgradeprovision
1508
1509
1510 # About updating the sam DB
1511 # The update takes place in update_partition function
1512 # This function read both current and reference provision and list all
1513 # the available DN of objects
1514 # If the string representation of a DN in reference provision is
1515 # equal to the string representation of a DN in current provision
1516 # (without taking care of case) then the object is flaged as being
1517 # present. If the object is not present in current provision the object
1518 # is being flaged as missing in current provision. Object present in current
1519 # provision but not in reference provision are ignored.
1520 # Once the list of objects present and missing is done, the deleted object
1521 # containers are created in the differents partitions (if missing)
1522 #
1523 # Then the function add_missing_entries is called
1524 # This function will go through the list of missing entries by calling
1525 # add_missing_object for the given object. If this function returns 0
1526 # it means that the object needs some other object in order to be created
1527 # The object is reappended at the end of the list to be created later
1528 # (and preferably after all the needed object have been created)
1529 # The function keeps on looping on the list of object to be created until
1530 # it's empty or that the number of defered creation is equal to the number
1531 # of object that still needs to be created.
1532
1533 # The function add_missing_object will first check if the object can be created.
1534 # That is to say that it didn't depends other not yet created objects
1535 # If requisit can't be fullfilled it exists with 0
1536 # Then it will try to create the missing entry by creating doing
1537 # an ldb_message_diff between the object in the reference provision and
1538 # an empty object.
1539 # This resulting object is filtered to remove all the back link attribute
1540 # (ie. memberOf) as they will be created by the other linked object (ie.
1541 # the one with the member attribute)
1542 # All attributes specified in the hashAttrNotCopied associative array are
1543 # also removed it's most of the time generated attributes
1544
1545 # After missing entries have been added the update_partition function will
1546 # take care of object that exist but that need some update.
1547 # In order to do so the function update_present is called with the list
1548 # of object that are present in both provision and that might need an update.
1549
1550 # This function handle first case mismatch so that the DN in the current
1551 # provision have the same case as in reference provision
1552
1553 # It will then construct an associative array consiting of attributes as
1554 # key and invocationid as value( if the originating invocation id is
1555 # different from the invocation id of the current DC the value is -1 instead).
1556
1557 # If the range of provision modified attributes is present, the function will
1558 # use the replMetadataProperty update method which is the following:
1559 #  Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1560 #   creationTime, msDs-KeyVersionNumber, oEMInformation
1561 #  Check for each attribute if its usn is within one of the modified by
1562 #   provision range and if its originating id is the invocation id of the
1563 #   current DC, then validate the update from reference to current.
1564 #   If not or if there is no replMetatdataProperty for this attribute then we
1565 #   do not update it.
1566 # Otherwise (case the range of provision modified attribute is not present) it
1567 # use the following process:
1568 #  All attributes that need to be added are accepted at the exeption of those
1569 #   listed in hashOverwrittenAtt, in this case the attribute needs to have the
1570 #   correct flags specified.
1571 #  For attributes that need to be modified or removed, a check is performed
1572 #  in OverwrittenAtt, if the attribute is present and the modification flag
1573 #  (remove, delete) is one of those listed for this attribute then modification
1574 #  is accepted. For complicated handling of attribute update, the control is passed
1575 #  to handle_special_case
1576
1577
1578
1579 if __name__ == '__main__':
1580     global defSDmodified
1581     defSDmodified = False
1582     # From here start the big steps of the program
1583     # 1) First get files paths
1584     paths = get_paths(param, smbconf=smbconf)
1585     # Get ldbs with the system session, it is needed for searching
1586     # provision parameters
1587     session = system_session()
1588
1589     # This variable will hold the last provision USN once if it exists.
1590     minUSN = 0
1591     # 2)
1592     ldbs = get_ldbs(paths, creds, session, lp)
1593     backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1594                                     prefix="backupprovision")
1595     backup_provision(paths, backupdir)
1596     try:
1597         ldbs.startTransactions()
1598
1599         # 3) Guess all the needed names (variables in fact) from the current
1600         # provision.
1601         names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1602                                                 paths, smbconf, lp)
1603         # 4)
1604         lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1605         if lastProvisionUSNs is not None:
1606             message(CHANGE,
1607                 "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
1608
1609         # Objects will be created with the admin session
1610         # (not anymore system session)
1611         adm_session = admin_session(lp, str(names.domainsid))
1612         # So we reget handle on objects
1613         # ldbs = get_ldbs(paths, creds, adm_session, lp)
1614         if not opts.fixntacl:
1615             if not sanitychecks(ldbs.sam, names):
1616                 message(SIMPLE, "Sanity checks for the upgrade have failed. "
1617                         "Check the messages and correct the errors "
1618                         "before rerunning upgradeprovision")
1619                 ldbs.groupedRollback()
1620                 sys.exit(1)
1621
1622             # Let's see provision parameters
1623             print_provision_key_parameters(names)
1624
1625             # 5) With all this information let's create a fresh new provision used as
1626             # reference
1627             message(SIMPLE, "Creating a reference provision")
1628             provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1629                             prefix="referenceprovision")
1630             newprovision(names, creds, session, smbconf, provisiondir,
1631                     provision_logger)
1632
1633             # TODO
1634             # 6) and 7)
1635             # We need to get a list of object which SD is directly computed from
1636             # defaultSecurityDescriptor.
1637             # This will allow us to know which object we can rebuild the SD in case
1638             # of change of the parent's SD or of the defaultSD.
1639             # Get file paths of this new provision
1640             newpaths = get_paths(param, targetdir=provisiondir)
1641             new_ldbs = get_ldbs(newpaths, creds, session, lp)
1642             new_ldbs.startTransactions()
1643
1644             # 8) Populate some associative array to ease the update process
1645             # List of attribute which are link and backlink
1646             populate_links(new_ldbs.sam, names.schemadn)
1647             # List of attribute with ASN DN synthax)
1648             populate_dnsyntax(new_ldbs.sam, names.schemadn)
1649             # 9)
1650             update_privilege(newpaths.private_dir, paths.private_dir)
1651             # 10)
1652             oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1653             # Do some modification on sam.ldb
1654             ldbs.groupedCommit()
1655             new_ldbs.groupedCommit()
1656             deltaattr = None
1657         # 11)
1658             if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1659                 # 11) A
1660                 # Starting from alpha9 we can consider that the structure is quite ok
1661                 # and that we should do only dela
1662                 deltaattr = delta_update_basesamdb(newpaths.samdb,
1663                                 paths.samdb,
1664                                 creds,
1665                                 session,
1666                                 lp,
1667                                 message)
1668             else:
1669                 # 11) B
1670                 simple_update_basesamdb(newpaths, paths, names)
1671                 ldbs = get_ldbs(paths, creds, session, lp)
1672                 removeProvisionUSN(ldbs.sam)
1673
1674             ldbs.startTransactions()
1675             minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1676             new_ldbs.startTransactions()
1677
1678             # 12)
1679             schema = Schema(names.domainsid, schemadn=str(names.schemadn))
1680             # We create a closure that will be invoked just before schema reload
1681             def schemareloadclosure():
1682                 basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
1683                         options=["modules:"])
1684                 doit = False
1685                 if deltaattr is not None and len(deltaattr) > 1:
1686                     doit = True
1687                 if doit:
1688                     deltaattr.remove("dn")
1689                 for att in deltaattr:
1690                     if att.lower() == "dn":
1691                         continue
1692                     if (deltaattr.get(att) is not None
1693                         and deltaattr.get(att).flags() != FLAG_MOD_ADD):
1694                         doit = False
1695                     elif deltaattr.get(att) is None:
1696                         doit = False
1697                 if doit:
1698                     message(CHANGE, "Applying delta to @ATTRIBUTES")
1699                     deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
1700                     basesam.modify(deltaattr)
1701                 else:
1702                     message(CHANGE, "Not applying delta to @ATTRIBUTES because "
1703                         "there is not only add")
1704             # 13)
1705             if opts.full:
1706                 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1707                         schema, schemareloadclosure):
1708                     message(SIMPLE, "Rolling back all changes. Check the cause"
1709                             " of the problem")
1710                     message(SIMPLE, "Your system is as it was before the upgrade")
1711                     ldbs.groupedRollback()
1712                     new_ldbs.groupedRollback()
1713                     shutil.rmtree(provisiondir)
1714                     sys.exit(1)
1715             else:
1716                 # Try to reapply the change also when we do not change the sam
1717                 # as the delta_upgrade
1718                 schemareloadclosure()
1719                 sync_calculated_attributes(ldbs.sam, names)
1720                 res = ldbs.sam.search(expression="(samaccountname=dns)",
1721                         scope=SCOPE_SUBTREE, attrs=["dn"],
1722                         controls=["search_options:1:2"])
1723                 if len(res) > 0:
1724                     message(SIMPLE, "You still have the old DNS object for managing "
1725                             "dynamic DNS, but you didn't supply --full so "
1726                             "a correct update can't be done")
1727                     ldbs.groupedRollback()
1728                     new_ldbs.groupedRollback()
1729                     shutil.rmtree(provisiondir)
1730                     sys.exit(1)
1731             # 14)
1732             update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1733             # 14bis)
1734             res = ldbs.sam.search(expression="(samaccountname=dns)",
1735                         scope=SCOPE_SUBTREE, attrs=["dn"],
1736                         controls=["search_options:1:2"])
1737
1738             if (len(res) == 1):
1739                 ldbs.sam.delete(res[0]["dn"])
1740                 res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
1741                         scope=SCOPE_SUBTREE, attrs=["dn"])
1742                 update_dns_account_password(ldbs.sam, ldbs.secrets, names)
1743                 message(SIMPLE, "IMPORTANT!!! "
1744                         "If you were using Dynamic DNS before you need "
1745                         "to update your configuration, so that the "
1746                         "tkey-gssapi-credential has the following value: "
1747                         "DNS/%s.%s" % (names.netbiosname.lower(),
1748                             names.realm.lower()))
1749             # 15)
1750             message(SIMPLE, "Update machine account")
1751             update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1752
1753             # 16) SD should be created with admin but as some previous acl were so wrong
1754             # that admin can't modify them we have first to recreate them with the good
1755             # form but with system account and then give the ownership to admin ...
1756             if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1757                 message(SIMPLE, "Fixing old povision SD")
1758                 fix_partition_sd(ldbs.sam, names)
1759                 rebuild_sd(ldbs.sam, names)
1760
1761             # We calculate the max USN before recalculating the SD because we might
1762             # touch object that have been modified after a provision and we do not
1763             # want that the next upgradeprovision thinks that it has a green light
1764             # to modify them
1765
1766             # 17)
1767             maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1768
1769             # 18) We rebuild SD only if defaultSecurityDescriptor is modified
1770             # But in fact we should do it also if one object has its SD modified as
1771             # child might need rebuild
1772             if defSDmodified:
1773                 message(SIMPLE, "Updating SD")
1774                 ldbs.sam.set_session_info(adm_session)
1775                 # Alpha10 was a bit broken still
1776                 if re.match(r'.*alpha(\d|10)', str(oem)):
1777                     fix_partition_sd(ldbs.sam, names)
1778                     rebuild_sd(ldbs.sam, names)
1779
1780             # 19)
1781             # Now we are quite confident in the recalculate process of the SD, we make
1782             # it optional.
1783             # Also the check must be done in a clever way as for the moment we just
1784             # compare SDDL
1785             if opts.debugchangesd:
1786                 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1787
1788             # 20)
1789             updateOEMInfo(ldbs.sam, str(names.rootdn))
1790             # 21)
1791             check_for_DNS(newpaths.private_dir, paths.private_dir)
1792             # 22)
1793             if lastProvisionUSNs is not None:
1794                 update_provision_usn(ldbs.sam, minUSN, maxUSN)
1795             if opts.full and (names.policyid is None or names.policyid_dc is None):
1796                 update_policyids(names, ldbs.sam)
1797         if opts.full or opts.resetfileacl or opts.fixntacl:
1798             try:
1799                 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1800             except ProvisioningError, e:
1801                 message(ERROR, "The policy for domain controller is missing. "
1802                             "You should restart upgradeprovision with --full")
1803             except IOError, e:
1804                 message(ERROR, "Setting ACL not supported on your filesystem")
1805         else:
1806             try:
1807                 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1808             except ProvisioningError, e:
1809                 message(ERROR, "The policy for domain controller is missing. "
1810                             "You should restart upgradeprovision with --full")
1811         if not opts.fixntacl:
1812             ldbs.groupedCommit()
1813             new_ldbs.groupedCommit()
1814             message(SIMPLE, "Upgrade finished!")
1815             # remove reference provision now that everything is done !
1816             # So we have reindexed first if need when the merged schema was reloaded
1817             # (as new attributes could have quick in)
1818             # But the second part of the update (when we update existing objects
1819             # can also have an influence on indexing as some attribute might have their
1820             # searchflag modificated
1821             message(SIMPLE, "Reopenning samdb to trigger reindexing if needed "
1822                     "after modification")
1823             samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
1824             message(SIMPLE, "Reindexing finished")
1825
1826             shutil.rmtree(provisiondir)
1827         else:
1828             ldbs.groupedRollback()
1829             message(SIMPLE, "ACLs fixed !")
1830     except StandardError, err:
1831         message(ERROR, "A problem occurred while trying to upgrade your "
1832                    "provision. A full backup is located at %s" % backupdir)
1833         if opts.debugall or opts.debugchange:
1834             (typ, val, tb) = sys.exc_info()
1835             traceback.print_exception(typ, val, tb)
1836         sys.exit(1)