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