samba_upgradeprovision: don't reset 'whenCreated' when resetting 'nTSecurityDescriptor'
[Samba/gebeck_regimport.git] / source4 / scripting / bin / samba_upgradeprovision
blob9daabe318facd80df6183cd34c55bbd946b1f2ce
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 descr = get_empty_descriptor(names.domainsid)
1361 delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
1362 "nTSecurityDescriptor")
1363 samdb.modify(delta, ["sd_flags:1:%d" % sd_flags,"relax:0"])
1364 except LdbError, e:
1365 samdb.transaction_cancel()
1366 res = samdb.search(expression="objectClass=*", base=str(delta.dn),
1367 scope=SCOPE_BASE,
1368 attrs=["nTSecurityDescriptor"],
1369 controls=["sd_flags:1:%d" % sd_flags])
1370 badsd = ndr_unpack(security.descriptor,
1371 str(res[0]["nTSecurityDescriptor"]))
1372 message(ERROR, "On %s bad stuff %s" % (str(delta.dn),badsd.as_sddl(names.domainsid)))
1373 return
1375 def hasATProvision(samdb):
1376 entry = samdb.search(expression="(distinguishedName=@PROVISION)", base = "",
1377 scope=SCOPE_BASE,
1378 attrs=["dn"])
1380 if entry is not None and len(entry) == 1:
1381 return True
1382 else:
1383 return False
1385 def removeProvisionUSN(samdb):
1386 attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
1387 entry = samdb.search(expression="(distinguishedName=@PROVISION)", base = "",
1388 scope=SCOPE_BASE,
1389 attrs=attrs)
1390 empty = Message()
1391 empty.dn = entry[0].dn
1392 delta = samdb.msg_diff(entry[0], empty)
1393 delta.remove("dn")
1394 delta.dn = entry[0].dn
1395 samdb.modify(delta)
1397 def remove_stored_generated_attrs(paths, creds, session, lp):
1398 """Remove previously stored constructed attributes
1400 :param paths: List of paths for different provision objects
1401 from the upgraded provision
1402 :param creds: A credential object
1403 :param session: A session object
1404 :param lp: A line parser object
1405 :return: An associative array whose key are the different constructed
1406 attributes and the value the dn where this attributes were found.
1410 def simple_update_basesamdb(newpaths, paths, names):
1411 """Update the provision container db: sam.ldb
1412 This function is aimed at very old provision (before alpha9)
1414 :param newpaths: List of paths for different provision objects
1415 from the reference provision
1416 :param paths: List of paths for different provision objects
1417 from the upgraded provision
1418 :param names: List of key provision parameters"""
1420 message(SIMPLE, "Copy samdb")
1421 shutil.copy(newpaths.samdb, paths.samdb)
1423 message(SIMPLE, "Update partitions filename if needed")
1424 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1425 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1426 usersldb = os.path.join(paths.private_dir, "users.ldb")
1427 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1429 if not os.path.isdir(samldbdir):
1430 os.mkdir(samldbdir)
1431 os.chmod(samldbdir, 0700)
1432 if os.path.isfile(schemaldb):
1433 shutil.copy(schemaldb, os.path.join(samldbdir,
1434 "%s.ldb"%str(names.schemadn).upper()))
1435 os.remove(schemaldb)
1436 if os.path.isfile(usersldb):
1437 shutil.copy(usersldb, os.path.join(samldbdir,
1438 "%s.ldb"%str(names.rootdn).upper()))
1439 os.remove(usersldb)
1440 if os.path.isfile(configldb):
1441 shutil.copy(configldb, os.path.join(samldbdir,
1442 "%s.ldb"%str(names.configdn).upper()))
1443 os.remove(configldb)
1446 def update_privilege(ref_private_path, cur_private_path):
1447 """Update the privilege database
1449 :param ref_private_path: Path to the private directory of the reference
1450 provision.
1451 :param cur_private_path: Path to the private directory of the current
1452 (and to be updated) provision."""
1453 message(SIMPLE, "Copy privilege")
1454 shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
1455 os.path.join(cur_private_path, "privilege.ldb"))
1458 def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):
1459 """Upgrade the SAM DB contents for all the provision partitions
1461 :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
1462 provision
1463 :param samdb: An LDB object connected to the sam.ldb of the update
1464 provision
1465 :param names: List of key provision parameters
1466 :param provisionUSNs: A dictionnary with range of USN modified during provision
1467 or upgradeprovision. Ranges are grouped by invocationID.
1468 :param schema: A Schema object that represent the schema of the provision
1469 :param prereloadfunc: A function that must be executed just before the reload
1470 of the schema
1473 message(SIMPLE, "Starting update of samdb")
1474 ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
1475 schema, provisionUSNs, prereloadfunc)
1476 if ret:
1477 message(SIMPLE, "Update of samdb finished")
1478 return 1
1479 else:
1480 message(SIMPLE, "Update failed")
1481 return 0
1484 def backup_provision(paths, dir, only_db):
1485 """This function backup the provision files so that a rollback
1486 is possible
1488 :param paths: Paths to different objects
1489 :param dir: Directory where to store the backup
1490 :param only_db: Skip sysvol for users with big sysvol
1492 if paths.sysvol and not only_db:
1493 copytree_with_xattrs(paths.sysvol, os.path.join(dir, "sysvol"))
1494 shutil.copy2(paths.samdb, dir)
1495 shutil.copy2(paths.secrets, dir)
1496 shutil.copy2(paths.idmapdb, dir)
1497 shutil.copy2(paths.privilege, dir)
1498 if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
1499 shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
1500 shutil.copy2(paths.smbconf, dir)
1501 shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
1503 samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
1504 if not os.path.isdir(samldbdir):
1505 samldbdir = paths.private_dir
1506 schemaldb = os.path.join(paths.private_dir, "schema.ldb")
1507 configldb = os.path.join(paths.private_dir, "configuration.ldb")
1508 usersldb = os.path.join(paths.private_dir, "users.ldb")
1509 shutil.copy2(schemaldb, dir)
1510 shutil.copy2(usersldb, dir)
1511 shutil.copy2(configldb, dir)
1512 else:
1513 shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
1516 def sync_calculated_attributes(samdb, names):
1517 """Synchronize attributes used for constructed ones, with the
1518 old constructed that were stored in the database.
1520 This apply for instance to msds-keyversionnumber that was
1521 stored and that is now constructed from replpropertymetadata.
1523 :param samdb: An LDB object attached to the currently upgraded samdb
1524 :param names: Various key parameter about current provision.
1526 listAttrs = ["msDs-KeyVersionNumber"]
1527 hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
1528 if hash.has_key("msDs-KeyVersionNumber"):
1529 increment_calculated_keyversion_number(samdb, names.rootdn,
1530 hash["msDs-KeyVersionNumber"])
1532 # Synopsis for updateprovision
1533 # 1) get path related to provision to be update (called current)
1534 # 2) open current provision ldbs
1535 # 3) fetch the key provision parameter (domain sid, domain guid, invocationid
1536 # of the DC ....)
1537 # 4) research of lastProvisionUSN in order to get ranges of USN modified
1538 # by either upgradeprovision or provision
1539 # 5) creation of a new provision the latest version of provision script
1540 # (called reference)
1541 # 6) get reference provision paths
1542 # 7) open reference provision ldbs
1543 # 8) setup helpers data that will help the update process
1544 # 9) update the privilege ldb by copying the one of referecence provision to
1545 # the current provision
1546 # 10)get the oemInfo field, this field contains information about the different
1547 # provision that have been done
1548 # 11)Depending on whether oemInfo has the string "alpha9" or alphaxx (x as an
1549 # integer) or none of this the following things are done
1550 # A) When alpha9 or alphaxx is present
1551 # The base sam.ldb file is updated by looking at the difference between
1552 # referrence one and the current one. Everything is copied with the
1553 # exception of lastProvisionUSN attributes.
1554 # B) Other case (it reflect that that provision was done before alpha9)
1555 # The base sam.ldb of the reference provision is copied over
1556 # the current one, if necessary ldb related to partitions are moved
1557 # and renamed
1558 # The highest used USN is fetched so that changed by upgradeprovision
1559 # usn can be tracked
1560 # 12)A Schema object is created, it will be used to provide a complete
1561 # schema to current provision during update (as the schema of the
1562 # current provision might not be complete and so won't allow some
1563 # object to be created)
1564 # 13)Proceed to full update of sam DB (see the separate paragraph about i)
1565 # 14)The secrets db is updated by pull all the difference from the reference
1566 # provision into the current provision
1567 # 15)As the previous step has most probably modified the password stored in
1568 # in secret for the current DC, a new password is generated,
1569 # the kvno is bumped and the entry in samdb is also updated
1570 # 16)For current provision older than alpha9, we must fix the SD a little bit
1571 # administrator to update them because SD used to be generated with the
1572 # system account before alpha9.
1573 # 17)The highest usn modified so far is searched in the database it will be
1574 # the upper limit for usn modified during provision.
1575 # This is done before potential SD recalculation because we do not want
1576 # SD modified during recalculation to be marked as modified during provision
1577 # (and so possibly remplaced at next upgradeprovision)
1578 # 18)Rebuilt SD if the flag indicate to do so
1579 # 19)Check difference between SD of reference provision and those of the
1580 # current provision. The check is done by getting the sddl representation
1581 # of the SD. Each sddl in chuncked into parts (user,group,dacl,sacl)
1582 # Each part is verified separetly, for dacl and sacl ACL is splited into
1583 # ACEs and each ACE is verified separately (so that a permutation in ACE
1584 # didn't raise as an error).
1585 # 20)The oemInfo field is updated to add information about the fact that the
1586 # provision has been updated by the upgradeprovision version xxx
1587 # (the version is the one obtained when starting samba with the --version
1588 # parameter)
1589 # 21)Check if the current provision has all the settings needed for dynamic
1590 # DNS update to work (that is to say the provision is newer than
1591 # january 2010). If not dns configuration file from reference provision
1592 # are copied in a sub folder and the administrator is invited to
1593 # do what is needed.
1594 # 22)If the lastProvisionUSN attribute was present it is updated to add
1595 # the range of usns modified by the current upgradeprovision
1598 # About updating the sam DB
1599 # The update takes place in update_partition function
1600 # This function read both current and reference provision and list all
1601 # the available DN of objects
1602 # If the string representation of a DN in reference provision is
1603 # equal to the string representation of a DN in current provision
1604 # (without taking care of case) then the object is flaged as being
1605 # present. If the object is not present in current provision the object
1606 # is being flaged as missing in current provision. Object present in current
1607 # provision but not in reference provision are ignored.
1608 # Once the list of objects present and missing is done, the deleted object
1609 # containers are created in the differents partitions (if missing)
1611 # Then the function add_missing_entries is called
1612 # This function will go through the list of missing entries by calling
1613 # add_missing_object for the given object. If this function returns 0
1614 # it means that the object needs some other object in order to be created
1615 # The object is reappended at the end of the list to be created later
1616 # (and preferably after all the needed object have been created)
1617 # The function keeps on looping on the list of object to be created until
1618 # it's empty or that the number of defered creation is equal to the number
1619 # of object that still needs to be created.
1621 # The function add_missing_object will first check if the object can be created.
1622 # That is to say that it didn't depends other not yet created objects
1623 # If requisit can't be fullfilled it exists with 0
1624 # Then it will try to create the missing entry by creating doing
1625 # an ldb_message_diff between the object in the reference provision and
1626 # an empty object.
1627 # This resulting object is filtered to remove all the back link attribute
1628 # (ie. memberOf) as they will be created by the other linked object (ie.
1629 # the one with the member attribute)
1630 # All attributes specified in the attrNotCopied array are
1631 # also removed it's most of the time generated attributes
1633 # After missing entries have been added the update_partition function will
1634 # take care of object that exist but that need some update.
1635 # In order to do so the function update_present is called with the list
1636 # of object that are present in both provision and that might need an update.
1638 # This function handle first case mismatch so that the DN in the current
1639 # provision have the same case as in reference provision
1641 # It will then construct an associative array consiting of attributes as
1642 # key and invocationid as value( if the originating invocation id is
1643 # different from the invocation id of the current DC the value is -1 instead).
1645 # If the range of provision modified attributes is present, the function will
1646 # use the replMetadataProperty update method which is the following:
1647 # Removing attributes that should not be updated: rIDAvailablePool, objectSid,
1648 # creationTime, msDs-KeyVersionNumber, oEMInformation
1649 # Check for each attribute if its usn is within one of the modified by
1650 # provision range and if its originating id is the invocation id of the
1651 # current DC, then validate the update from reference to current.
1652 # If not or if there is no replMetatdataProperty for this attribute then we
1653 # do not update it.
1654 # Otherwise (case the range of provision modified attribute is not present) it
1655 # use the following process:
1656 # All attributes that need to be added are accepted at the exeption of those
1657 # listed in hashOverwrittenAtt, in this case the attribute needs to have the
1658 # correct flags specified.
1659 # For attributes that need to be modified or removed, a check is performed
1660 # in OverwrittenAtt, if the attribute is present and the modification flag
1661 # (remove, delete) is one of those listed for this attribute then modification
1662 # is accepted. For complicated handling of attribute update, the control is passed
1663 # to handle_special_case
1667 if __name__ == '__main__':
1668 global defSDmodified
1669 defSDmodified = False
1671 if opts.nontaclfix and opts.fixntacl:
1672 message(SIMPLE, "nontaclfix and fixntacl are mutally exclusive")
1673 # From here start the big steps of the program
1674 # 1) First get files paths
1675 paths = get_paths(param, smbconf=smbconf)
1676 # Get ldbs with the system session, it is needed for searching
1677 # provision parameters
1678 session = system_session()
1680 # This variable will hold the last provision USN once if it exists.
1681 minUSN = 0
1682 # 2)
1683 ldbs = get_ldbs(paths, creds, session, lp)
1684 backupdir = tempfile.mkdtemp(dir=paths.private_dir,
1685 prefix="backupprovision")
1686 backup_provision(paths, backupdir, opts.db_backup_only)
1687 try:
1688 ldbs.startTransactions()
1690 # 3) Guess all the needed names (variables in fact) from the current
1691 # provision.
1692 names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
1693 paths, smbconf, lp)
1694 # 4)
1695 lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
1696 if lastProvisionUSNs is not None:
1697 v = 0
1698 for k in lastProvisionUSNs.keys():
1699 for r in lastProvisionUSNs[k]:
1700 v = v + 1
1702 message(CHANGE,
1703 "Find last provision USN, %d invocation(s) for a total of %d ranges" %
1704 (len(lastProvisionUSNs.keys()), v /2 ))
1706 if lastProvisionUSNs.get("default") is not None:
1707 message(CHANGE, "Old style for usn ranges used")
1708 lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"]
1709 del lastProvisionUSNs["default"]
1710 else:
1711 message(SIMPLE, "Your provision lacks provision range information")
1712 if confirm("Do you want to run findprovisionusnranges to try to find them ?", False):
1713 ldbs.groupedRollback()
1714 minobj = 5
1715 (hash_id, nb_obj) = findprovisionrange(ldbs.sam, ldb.Dn(ldbs.sam, str(names.rootdn)))
1716 message(SIMPLE, "Here is a list of changes that modified more than %d objects in 1 minute." % minobj)
1717 message(SIMPLE, "Usually changes made by provision and upgradeprovision are those who affect a couple"
1718 " of hundred of objects or more")
1719 message(SIMPLE, "Total number of objects: %d" % nb_obj)
1720 message(SIMPLE, "")
1722 print_provision_ranges(hash_id, minobj, None, str(paths.samdb), str(names.invocation))
1724 message(SIMPLE, "Once you applied/adapted the change(s) please restart the upgradeprovision script")
1725 sys.exit(0)
1727 # Objects will be created with the admin session
1728 # (not anymore system session)
1729 adm_session = admin_session(lp, str(names.domainsid))
1730 # So we reget handle on objects
1731 # ldbs = get_ldbs(paths, creds, adm_session, lp)
1732 if not opts.fixntacl:
1733 if not sanitychecks(ldbs.sam, names):
1734 message(SIMPLE, "Sanity checks for the upgrade have failed. "
1735 "Check the messages and correct the errors "
1736 "before rerunning upgradeprovision")
1737 ldbs.groupedRollback()
1738 sys.exit(1)
1740 # Let's see provision parameters
1741 print_provision_key_parameters(names)
1743 # 5) With all this information let's create a fresh new provision used as
1744 # reference
1745 message(SIMPLE, "Creating a reference provision")
1746 provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
1747 prefix="referenceprovision")
1748 result = newprovision(names, creds, session, smbconf, provisiondir,
1749 provision_logger)
1750 result.report_logger(provision_logger)
1752 # TODO
1753 # 6) and 7)
1754 # We need to get a list of object which SD is directly computed from
1755 # defaultSecurityDescriptor.
1756 # This will allow us to know which object we can rebuild the SD in case
1757 # of change of the parent's SD or of the defaultSD.
1758 # Get file paths of this new provision
1759 newpaths = get_paths(param, targetdir=provisiondir)
1760 new_ldbs = get_ldbs(newpaths, creds, session, lp)
1761 new_ldbs.startTransactions()
1763 populateNotReplicated(new_ldbs.sam, names.schemadn)
1764 # 8) Populate some associative array to ease the update process
1765 # List of attribute which are link and backlink
1766 populate_links(new_ldbs.sam, names.schemadn)
1767 # List of attribute with ASN DN synthax)
1768 populate_dnsyntax(new_ldbs.sam, names.schemadn)
1769 # 9)
1770 update_privilege(newpaths.private_dir, paths.private_dir)
1771 # 10)
1772 oem = getOEMInfo(ldbs.sam, str(names.rootdn))
1773 # Do some modification on sam.ldb
1774 ldbs.groupedCommit()
1775 new_ldbs.groupedCommit()
1776 deltaattr = None
1777 # 11)
1778 message(GUESS, oem)
1779 if oem is None or hasATProvision(ldbs.sam) or re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
1780 # 11) A
1781 # Starting from alpha9 we can consider that the structure is quite ok
1782 # and that we should do only dela
1783 deltaattr = delta_update_basesamdb(newpaths.samdb,
1784 paths.samdb,
1785 creds,
1786 session,
1788 message)
1789 else:
1790 # 11) B
1791 simple_update_basesamdb(newpaths, paths, names)
1792 ldbs = get_ldbs(paths, creds, session, lp)
1793 removeProvisionUSN(ldbs.sam)
1795 ldbs.startTransactions()
1796 minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
1797 new_ldbs.startTransactions()
1799 # 12)
1800 schema = Schema(names.domainsid, schemadn=str(names.schemadn))
1801 # We create a closure that will be invoked just before schema reload
1802 def schemareloadclosure():
1803 basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
1804 options=["modules:"])
1805 doit = False
1806 if deltaattr is not None and len(deltaattr) > 1:
1807 doit = True
1808 if doit:
1809 deltaattr.remove("dn")
1810 for att in deltaattr:
1811 if att.lower() == "dn":
1812 continue
1813 if (deltaattr.get(att) is not None
1814 and deltaattr.get(att).flags() != FLAG_MOD_ADD):
1815 doit = False
1816 elif deltaattr.get(att) is None:
1817 doit = False
1818 if doit:
1819 message(CHANGE, "Applying delta to @ATTRIBUTES")
1820 deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
1821 basesam.modify(deltaattr)
1822 else:
1823 message(CHANGE, "Not applying delta to @ATTRIBUTES because "
1824 "there is not only add")
1825 # 13)
1826 if opts.full:
1827 if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
1828 schema, schemareloadclosure):
1829 message(SIMPLE, "Rolling back all changes. Check the cause"
1830 " of the problem")
1831 message(SIMPLE, "Your system is as it was before the upgrade")
1832 ldbs.groupedRollback()
1833 new_ldbs.groupedRollback()
1834 shutil.rmtree(provisiondir)
1835 sys.exit(1)
1836 else:
1837 # Try to reapply the change also when we do not change the sam
1838 # as the delta_upgrade
1839 schemareloadclosure()
1840 sync_calculated_attributes(ldbs.sam, names)
1841 res = ldbs.sam.search(expression="(samaccountname=dns)",
1842 scope=SCOPE_SUBTREE, attrs=["dn"],
1843 controls=["search_options:1:2"])
1844 if len(res) > 0:
1845 message(SIMPLE, "You still have the old DNS object for managing "
1846 "dynamic DNS, but you didn't supply --full so "
1847 "a correct update can't be done")
1848 ldbs.groupedRollback()
1849 new_ldbs.groupedRollback()
1850 shutil.rmtree(provisiondir)
1851 sys.exit(1)
1852 # 14)
1853 update_secrets(new_ldbs.secrets, ldbs.secrets, message)
1854 # 14bis)
1855 res = ldbs.sam.search(expression="(samaccountname=dns)",
1856 scope=SCOPE_SUBTREE, attrs=["dn"],
1857 controls=["search_options:1:2"])
1859 if (len(res) == 1):
1860 ldbs.sam.delete(res[0]["dn"])
1861 res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
1862 scope=SCOPE_SUBTREE, attrs=["dn"])
1863 update_dns_account_password(ldbs.sam, ldbs.secrets, names)
1864 message(SIMPLE, "IMPORTANT!!! "
1865 "If you were using Dynamic DNS before you need "
1866 "to update your configuration, so that the "
1867 "tkey-gssapi-credential has the following value: "
1868 "DNS/%s.%s" % (names.netbiosname.lower(),
1869 names.realm.lower()))
1870 # 15)
1871 message(SIMPLE, "Update machine account")
1872 update_machine_account_password(ldbs.sam, ldbs.secrets, names)
1874 dnToRecalculate.sort(dn_sort)
1875 # 16) SD should be created with admin but as some previous acl were so wrong
1876 # that admin can't modify them we have first to recreate them with the good
1877 # form but with system account and then give the ownership to admin ...
1878 if str(oem) != "" and not re.match(r'.*alpha(9|\d\d+)', str(oem)):
1879 message(SIMPLE, "Fixing very old provision SD")
1880 rebuild_sd(ldbs.sam, names)
1882 # We calculate the max USN before recalculating the SD because we might
1883 # touch object that have been modified after a provision and we do not
1884 # want that the next upgradeprovision thinks that it has a green light
1885 # to modify them
1887 # 17)
1888 maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
1890 # 18) We rebuild SD if a we have a list of DN to recalculate or if the
1891 # defSDmodified is set.
1892 if defSDmodified or len(dnToRecalculate) >0:
1893 message(SIMPLE, "Some (default) security descriptors (SDs) have "
1894 "changed, recalculating them")
1895 ldbs.sam.set_session_info(adm_session)
1896 rebuild_sd(ldbs.sam, names)
1898 # 19)
1899 # Now we are quite confident in the recalculate process of the SD, we make
1900 # it optional. And we don't do it if there is DN that we must touch
1901 # as we are assured that on this DNs we will have differences !
1902 # Also the check must be done in a clever way as for the moment we just
1903 # compare SDDL
1904 if len(dnNotToRecalculate) == 0 and (opts.debugchangesd or opts.debugall):
1905 message(CHANGESD, "Checking recalculated SDs")
1906 check_updated_sd(new_ldbs.sam, ldbs.sam, names)
1908 # 20)
1909 updateOEMInfo(ldbs.sam, str(names.rootdn))
1910 # 21)
1911 check_for_DNS(newpaths.private_dir, paths.private_dir)
1912 # 22)
1913 if lastProvisionUSNs is not None:
1914 update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
1915 if opts.full and (names.policyid is None or names.policyid_dc is None):
1916 update_policyids(names, ldbs.sam)
1917 if opts.nontaclfix:
1918 if opts.full or opts.resetfileacl or opts.fixntacl:
1919 try:
1920 update_gpo(paths, ldbs.sam, names, lp, message, 1)
1921 except ProvisioningError, e:
1922 message(ERROR, "The policy for domain controller is missing. "
1923 "You should restart upgradeprovision with --full")
1924 except IOError, e:
1925 message(ERROR, "Setting ACL not supported on your filesystem")
1926 else:
1927 try:
1928 update_gpo(paths, ldbs.sam, names, lp, message, 0)
1929 except ProvisioningError, e:
1930 message(ERROR, "The policy for domain controller is missing. "
1931 "You should restart upgradeprovision with --full")
1932 if not opts.fixntacl:
1933 ldbs.groupedCommit()
1934 new_ldbs.groupedCommit()
1935 message(SIMPLE, "Upgrade finished!")
1936 # remove reference provision now that everything is done !
1937 # So we have reindexed first if need when the merged schema was reloaded
1938 # (as new attributes could have quick in)
1939 # But the second part of the update (when we update existing objects
1940 # can also have an influence on indexing as some attribute might have their
1941 # searchflag modificated
1942 message(SIMPLE, "Reopening samdb to trigger reindexing if needed "
1943 "after modification")
1944 samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
1945 message(SIMPLE, "Reindexing finished")
1947 shutil.rmtree(provisiondir)
1948 else:
1949 ldbs.groupedRollback()
1950 message(SIMPLE, "ACLs fixed !")
1951 except StandardError, err:
1952 message(ERROR, "A problem occurred while trying to upgrade your "
1953 "provision. A full backup is located at %s" % backupdir)
1954 if opts.debugall or opts.debugchange:
1955 (typ, val, tb) = sys.exc_info()
1956 traceback.print_exception(typ, val, tb)
1957 sys.exit(1)