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