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