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