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