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