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