-#!/usr/bin/env python
+#!/usr/bin/env python3
# vim: expandtab
#
# Copyright (C) Matthieu Patou <mat@matws.net> 2009 - 2010
import ldb
import samba
import samba.getopt as options
+from samba.samdb import get_default_backend_store
from base64 import b64encode
from samba.credentials import DONT_USE_KERBEROS
from samba.auth import system_session, admin_session
from samba import tdb_util
+from samba import mdb_util
from ldb import (SCOPE_SUBTREE, SCOPE_BASE,
FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE,
MessageElement, Message, Dn, LdbError)
from samba import param, dsdb, Ldb
from samba.common import confirm
+from samba.descriptor import get_wellknown_sds, get_empty_descriptor, get_diff_sds
from samba.provision import (find_provision_key_parameters,
- get_empty_descriptor,
- get_config_descriptor,
- get_config_partitions_descriptor,
- get_config_sites_descriptor,
- get_config_ntds_quotas_descriptor,
- get_config_delete_protected1_descriptor,
- get_config_delete_protected1wd_descriptor,
- get_config_delete_protected2_descriptor,
- get_domain_descriptor,
- get_domain_infrastructure_descriptor,
- get_domain_builtin_descriptor,
- get_domain_computers_descriptor,
- get_domain_users_descriptor,
- get_domain_controllers_descriptor,
- get_domain_delete_protected1_descriptor,
- get_domain_delete_protected2_descriptor,
- get_dns_partition_descriptor,
- get_dns_forest_microsoft_dns_descriptor,
- get_dns_domain_microsoft_dns_descriptor,
ProvisioningError, get_last_provision_usn,
get_max_usn, update_provision_usn, setup_path)
from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
from samba.ndr import ndr_unpack
from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
get_ldbs, findprovisionrange,
- usn_in_range, identic_rename, get_diff_sds,
+ usn_in_range, identic_rename,
update_secrets, CHANGE, ERROR, SIMPLE,
CHANGEALL, GUESS, CHANGESD, PROVISION,
updateOEMInfo, getOEMInfo, update_gpo,
increment_calculated_keyversion_number,
print_provision_ranges)
from samba.xattr import copytree_with_xattrs
+from samba.compat import cmp_to_key_fn
# make sure the script dies immediately when hitting control-C,
# rather than raising KeyboardInterrupt. As we do all database
# created
# This also apply to imported object from reference provision
replAttrNotCopied = [ "dn", "whenCreated", "whenChanged", "objectGUID",
- "parentGUID", "objectCategory", "distinguishedName",
+ "parentGUID", "distinguishedName",
"instanceType", "cn",
"lmPwdHistory", "pwdLastSet", "ntPwdHistory",
"unicodePwd", "dBCSPwd", "supplementalCredentials",
"description":replace, "operatingSystemVersion":replace,
"adminPropertyPages":replace, "groupType":replace,
"wellKnownObjects":replace, "privilege":never,
- "defaultSecurityDescriptor": replace,
"rIDAvailablePool": never,
"versionNumber" : add,
"rIDNextRID": add, "rIDUsedPool": never,
"attributeDisplayNames": replace + add,
"versionNumber": add}
-dnNotToRecalculate = []
+dnNotToRecalculateFound = False
dnToRecalculate = []
backlinked = []
forwardlinked = set()
-def check_for_DNS(refprivate, private, dns_backend):
+def check_for_DNS(refprivate, private, refbinddns_dir, binddns_dir, dns_backend):
"""Check if the provision has already the requirement for dynamic dns
:param refprivate: The path to the private directory of the reference
if not os.path.exists(dnsfile):
shutil.copy("%s/dns_update_list" % refprivate, "%s" % dnsfile)
+ if not os.path.exists(binddns_dir):
+ os.mkdir(binddns_dir)
+
if dns_backend not in ['BIND9_DLZ', 'BIND9_FLATFILE']:
return
namedfile = lp.get("dnsupdate:path")
if not namedfile:
- namedfile = "%s/named.conf.update" % private
+ namedfile = "%s/named.conf.update" % binddns_dir
if not os.path.exists(namedfile):
- destdir = "%s/new_dns" % private
- dnsdir = "%s/dns" % private
+ destdir = "%s/new_dns" % binddns_dir
+ dnsdir = "%s/dns" % binddns_dir
if not os.path.exists(destdir):
os.mkdir(destdir)
if not os.path.exists(dnsdir):
os.mkdir(dnsdir)
- shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
- shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
+ shutil.copy("%s/named.conf" % refbinddns_dir, "%s/named.conf" % destdir)
+ shutil.copy("%s/named.txt" % refbinddns_dir, "%s/named.txt" % destdir)
message(SIMPLE, "It seems that your provision did not integrate "
"new rules for dynamic dns update of domain related entries")
message(SIMPLE, "A copy of the new bind configuration files and "
scope=SCOPE_SUBTREE, attrs=["dn"],
controls=["search_options:1:2"])
if len(res) == 0:
- print "No DC found. Your provision is most probably broken!"
+ print("No DC found. Your provision is most probably broken!")
return False
elif len(res) != 1:
- print "Found %d domain controllers. For the moment " \
+ print("Found %d domain controllers. For the moment " \
"upgradeprovision is not able to handle an upgrade on a " \
"domain with more than one DC. Please demote the other " \
- "DC(s) before upgrading" % len(res)
+ "DC(s) before upgrading") % len(res)
return False
else:
return True
(int(str(old[0][att])), int(str(new[0][att]))))
return False
if (att == "minPwdAge" and flag == FLAG_MOD_REPLACE):
- if (long(str(old[0][att])) == 0):
+ if (int(str(old[0][att])) == 0):
delta[att] = MessageElement(new[0][att], FLAG_MOD_REPLACE, att)
return True
newval.append(str(elem))
for elem in new[0][att]:
- if not hash.has_key(str(elem).lower()):
+ if not str(elem).lower() in hash:
changeDelta=1
newval.append(str(elem))
if changeDelta == 1:
newval.append(str(elem))
for elem in new[0][att]:
- if not hash.has_key(str(elem)):
+ if not str(elem) in hash:
changeDelta = 1
newval.append(str(elem))
if changeDelta == 1:
return None
for dn in listdn:
key = str(dn).lower()
- if hash.has_key(key) and hash[key] > index:
+ if key in hash and hash[key] > index:
return str(dn)
return None
skip = True
finally:
if delta.get("objectSid"):
- sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
+ sid = str(ndr_unpack(security.dom_sid, reference[0]["objectSid"][0]))
m = re.match(r".*-(\d+)$", sid)
if m and int(m.group(1))>999:
delta.remove("objectSid")
delta.remove(att)
for att in backlinked:
delta.remove(att)
- depend_on_yettobecreated = None
for att in dn_syntax_att:
depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
delta.get(str(att)))
# Also this function in fact just accept add not removal
for e in res[0][att]:
- if not hash.has_key(e):
+ if not e in hash:
# We put in the blacklist all the element that are in the "revealed"
# result and not in the "standard" result
# This element are links that were removed before and so that
blacklist[e] = 1
for e in ref_value:
- if not blacklist.has_key(e) and not hash.has_key(e):
+ if not e in blacklist and not e in hash:
newlinklist.append(str(e))
changed = True
if changed:
if att == "nTSecurityDescriptor":
cursd = ndr_unpack(security.descriptor,
- str(current[0]["nTSecurityDescriptor"]))
+ current[0]["nTSecurityDescriptor"][0])
refsd = ndr_unpack(security.descriptor,
- str(reference[0]["nTSecurityDescriptor"]))
+ reference[0]["nTSecurityDescriptor"][0])
diff = get_diff_sds(refsd, cursd, names.domainsid)
if diff == "":
message(CHANGESD, "But the SD has been changed by someonelse "
"so it's impossible to know if the difference"
" cames from the modification or from a previous bug")
- dnNotToRecalculate.append(str(dn))
+ global dnNotToRecalculateFound
+ dnNotToRecalculateFound = True
else:
- dnToRecalculate.append(str(dn))
+ dnToRecalculate.append(dn)
continue
if attrUSN == -1:
scope=SCOPE_SUBTREE, controls=controls,
attrs=["replPropertyMetaData"])
ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
- str(res[0]["replPropertyMetaData"])).ctr
+ res[0]["replPropertyMetaData"][0]).ctr
hash_attr_usn = {}
for o in ctr.array:
for ent in current:
schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
- prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
- prefixmap_data = b64encode(prefixmap_data)
+ prefixmap_data = open(setup_path("prefixMap.txt"), 'rb').read()
+ prefixmap_data = b64encode(prefixmap_data).decode('utf8')
# We don't actually add this ldif, just parse it
prefixmap_ldif = "dn: %s\nprefixMap:: %s\n\n" % (schemadn, prefixmap_data)
for k in hash_new.keys():
- if not hash.has_key(k):
+ if not k in hash:
if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
listMissing.append(hash_new[k])
else:
# Sort the missing object in order to have object of the lowest level
# first (which can be containers for higher level objects)
- listMissing.sort(dn_sort)
- listPresent.sort(dn_sort)
+ listMissing.sort(key=cmp_to_key_fn(dn_sort))
+ listPresent.sort(key=cmp_to_key_fn(dn_sort))
# The following lines is to load the up to
# date schema into our current LDB
message(SIMPLE, "There are %d changed objects" % (changed))
return 1
- except StandardError, err:
+ except Exception as err:
message(ERROR, "Exception during upgrade of samdb:")
(typ, val, tb) = sys.exc_info()
traceback.print_exception(typ, val, tb)
controls=["search_options:1:2"])
hash = {}
for i in range(0, len(reference)):
- refsd_blob = str(reference[i]["nTSecurityDescriptor"])
+ refsd_blob = reference[i]["nTSecurityDescriptor"][0]
hash[str(reference[i]["dn"]).lower()] = refsd_blob
for i in range(0, len(current)):
key = str(current[i]["dn"]).lower()
- if hash.has_key(key):
- cursd_blob = str(current[i]["nTSecurityDescriptor"])
+ if key in hash:
+ cursd_blob = current[i]["nTSecurityDescriptor"][0]
cursd = ndr_unpack(security.descriptor,
cursd_blob)
if cursd_blob != hash[key]:
:param samdb: An LDB object pointing to the sam of the current provision
:param names: A list of key provision parameters
"""
- alwaysRecalculate = False
- if len(dnToRecalculate) == 0 and len(dnNotToRecalculate) == 0:
- alwaysRecalculate = True
list_wellknown_dns = []
- # Then subcontainers
- subcontainers = [
- ("%s" % str(names.domaindn), get_domain_descriptor),
- ("CN=LostAndFound,%s" % str(names.domaindn), get_domain_delete_protected2_descriptor),
- ("CN=System,%s" % str(names.domaindn), get_domain_delete_protected1_descriptor),
- ("CN=Infrastructure,%s" % str(names.domaindn), get_domain_infrastructure_descriptor),
- ("CN=Builtin,%s" % str(names.domaindn), get_domain_builtin_descriptor),
- ("CN=Computers,%s" % str(names.domaindn), get_domain_computers_descriptor),
- ("CN=Users,%s" % str(names.domaindn), get_domain_users_descriptor),
- ("OU=Domain Controllers,%s" % str(names.domaindn), get_domain_controllers_descriptor),
- ("CN=MicrosoftDNS,CN=System,%s" % str(names.domaindn), get_dns_domain_microsoft_dns_descriptor),
-
- ("%s" % str(names.configdn), get_config_descriptor),
- ("CN=NTDS Quotas,%s" % str(names.configdn), get_config_ntds_quotas_descriptor),
- ("CN=LostAndFoundConfig,%s" % str(names.configdn), get_config_delete_protected1wd_descriptor),
- ("CN=Services,%s" % str(names.configdn), get_config_delete_protected1_descriptor),
- ("CN=Physical Locations,%s" % str(names.configdn), get_config_delete_protected1wd_descriptor),
- ("CN=WellKnown Security Principals,%s" % str(names.configdn), get_config_delete_protected1wd_descriptor),
- ("CN=ForestUpdates,%s" % str(names.configdn), get_config_delete_protected1wd_descriptor),
- ("CN=DisplaySpecifiers,%s" % str(names.configdn), get_config_delete_protected2_descriptor),
- ("CN=Extended-Rights,%s" % str(names.configdn), get_config_delete_protected2_descriptor),
- ("CN=Partitions,%s" % str(names.configdn), get_config_partitions_descriptor),
- ("CN=Sites,%s" % str(names.configdn), get_config_sites_descriptor),
-
- ("%s" % str(names.schemadn), get_schema_descriptor),
- ]
-
- if names.dnsforestdn is not None:
- c = ("%s" % str(names.dnsforestdn), get_dns_partition_descriptor)
- subcontainers.append(c)
- c = ("CN=Infrastructure,%s" % str(names.dnsforestdn),
- get_domain_delete_protected1_descriptor)
- subcontainers.append(c)
- c = ("CN=LostAndFound,%s" % str(names.dnsforestdn),
- get_domain_delete_protected2_descriptor)
- subcontainers.append(c)
- c = ("CN=MicrosoftDNS,%s" % str(names.dnsforestdn),
- get_dns_forest_microsoft_dns_descriptor)
- subcontainers.append(c)
-
- if names.dnsdomaindn is not None:
- c = ("%s" % str(names.dnsdomaindn), get_dns_partition_descriptor)
- subcontainers.append(c)
- c = ("CN=Infrastructure,%s" % str(names.dnsdomaindn),
- get_domain_delete_protected1_descriptor)
- subcontainers.append(c)
- c = ("CN=LostAndFound,%s" % str(names.dnsdomaindn),
- get_domain_delete_protected2_descriptor)
- subcontainers.append(c)
- c = ("CN=MicrosoftDNS,%s" % str(names.dnsdomaindn),
- get_dns_domain_microsoft_dns_descriptor)
- subcontainers.append(c)
+ subcontainers = get_wellknown_sds(samdb)
for [dn, descriptor_fn] in subcontainers:
list_wellknown_dns.append(dn)
- if alwaysRecalculate or dn in dnToRecalculate:
+ if dn in dnToRecalculate:
delta = Message()
- delta.dn = Dn(samdb, str(dn))
+ delta.dn = dn
descr = descriptor_fn(names.domainsid, name_map=names.name_map)
delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
"nTSecurityDescriptor" )
def rebuild_sd(samdb, names):
"""Rebuild security descriptor of the current provision from scratch
- During the different pre release of samba4 security descriptors (SD)
- were notarly broken (up to alpha11 included)
- This function allow to get them back in order, this function make the
- assumption that nobody has modified manualy an SD
- and so SD can be safely recalculated from scratch to get them right.
+ During the different pre release of samba4 security descriptors
+ (SD) were notarly broken (up to alpha11 included)
- :param names: List of key provision parameters"""
+ This function allows one to get them back in order, this function works
+ only after the database comparison that --full mode uses and which
+ populates the dnToRecalculate and dnNotToRecalculate lists.
- listWellknown = fix_wellknown_sd(samdb, names)
+ The idea is that the SD can be safely recalculated from scratch to get it right.
- hash = {}
- if len(dnToRecalculate) == 0:
- res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
- scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
- controls=["search_options:1:2"])
- for obj in res:
- hash[str(obj["dn"])] = obj["whenCreated"]
- else:
- for dn in dnToRecalculate:
- if hash.has_key(dn):
- continue
- # fetch each dn to recalculate and their child within the same partition
- res = samdb.search(expression="objectClass=*", base=dn,
- scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"])
- for obj in res:
- hash[str(obj["dn"])] = obj["whenCreated"]
+ :param names: List of key provision parameters"""
- listKeys = list(set(hash.keys()))
- listKeys.sort(dn_sort)
+ listWellknown = fix_wellknown_sd(samdb, names)
if len(dnToRecalculate) != 0:
message(CHANGESD, "%d DNs have been marked as needed to be recalculated"
- ", recalculating %d due to inheritance"
- % (len(dnToRecalculate), len(listKeys)))
+ % (len(dnToRecalculate)))
- for key in listKeys:
- if key in listWellknown:
- continue
- if key in dnNotToRecalculate:
+ for dn in dnToRecalculate:
+ # well known SDs have already been reset
+ if dn in listWellknown:
continue
delta = Message()
- delta.dn = Dn(samdb, key)
+ delta.dn = dn
sd_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL | SECINFO_SACL
try:
descr = get_empty_descriptor(names.domainsid)
delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
"nTSecurityDescriptor")
samdb.modify(delta, ["sd_flags:1:%d" % sd_flags,"relax:0","local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK])
- except LdbError, e:
+ except LdbError as e:
samdb.transaction_cancel()
res = samdb.search(expression="objectClass=*", base=str(delta.dn),
scope=SCOPE_BASE,
attrs=["nTSecurityDescriptor"],
controls=["sd_flags:1:%d" % sd_flags])
badsd = ndr_unpack(security.descriptor,
- str(res[0]["nTSecurityDescriptor"]))
+ res[0]["nTSecurityDescriptor"][0])
message(ERROR, "On %s bad stuff %s" % (str(delta.dn),badsd.as_sddl(names.domainsid)))
return
if not os.path.isdir(samldbdir):
os.mkdir(samldbdir)
- os.chmod(samldbdir, 0700)
+ os.chmod(samldbdir, 0o700)
if os.path.isfile(schemaldb):
tdb_util.tdb_copy(schemaldb, os.path.join(samldbdir,
"%s.ldb"%str(names.schemadn).upper()))
return 0
-def backup_provision(paths, dir, only_db):
+def backup_provision(samdb, paths, dir, only_db):
"""This function backup the provision files so that a rollback
is possible
:param dir: Directory where to store the backup
:param only_db: Skip sysvol for users with big sysvol
"""
+
+ # Currently we default to tdb for the backend store type
+ #
+ backend_store = "tdb"
+ res = samdb.search(base="@PARTITION",
+ scope=ldb.SCOPE_BASE,
+ attrs=["backendStore"])
+ if "backendStore" in res[0]:
+ backend_store = str(res[0]["backendStore"][0])
+
+
if paths.sysvol and not only_db:
copytree_with_xattrs(paths.sysvol, os.path.join(dir, "sysvol"))
+
tdb_util.tdb_copy(paths.samdb, os.path.join(dir, os.path.basename(paths.samdb)))
tdb_util.tdb_copy(paths.secrets, os.path.join(dir, os.path.basename(paths.secrets)))
tdb_util.tdb_copy(paths.idmapdb, os.path.join(dir, os.path.basename(paths.idmapdb)))
tdb_util.tdb_copy(usersldb, os.path.join(dir, "configuration.ldb"))
tdb_util.tdb_copy(configldb, os.path.join(dir, "users.ldb"))
else:
- os.mkdir(os.path.join(dir, "sam.ldb.d"), 0700)
+ os.mkdir(os.path.join(dir, "sam.ldb.d"), 0o700)
- for ldb in os.listdir(samldbdir):
- tdb_util.tdb_copy(os.path.join(samldbdir, ldb),
- os.path.join(dir, "sam.ldb.d", ldb))
+ for ldb_name in os.listdir(samldbdir):
+ if not ldb_name.endswith("-lock"):
+ if backend_store == "mdb" and ldb_name != "metadata.tdb":
+ mdb_util.mdb_copy(os.path.join(samldbdir, ldb_name),
+ os.path.join(dir, "sam.ldb.d", ldb_name))
+ else:
+ tdb_util.tdb_copy(os.path.join(samldbdir, ldb_name),
+ os.path.join(dir, "sam.ldb.d", ldb_name))
def sync_calculated_attributes(samdb, names):
"""
listAttrs = ["msDs-KeyVersionNumber"]
hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
- if hash.has_key("msDs-KeyVersionNumber"):
+ if "msDs-KeyVersionNumber" in hash:
increment_calculated_keyversion_number(samdb, names.rootdn,
hash["msDs-KeyVersionNumber"])
# The object is reappended at the end of the list to be created later
# (and preferably after all the needed object have been created)
# The function keeps on looping on the list of object to be created until
-# it's empty or that the number of defered creation is equal to the number
+# it's empty or that the number of deferred creation is equal to the number
# of object that still needs to be created.
# The function add_missing_object will first check if the object can be created.
ldbs = get_ldbs(paths, creds, session, lp)
backupdir = tempfile.mkdtemp(dir=paths.private_dir,
prefix="backupprovision")
- backup_provision(paths, backupdir, opts.db_backup_only)
+ backup_provision(ldbs.sam, paths, backupdir, opts.db_backup_only)
try:
ldbs.startTransactions()
message(SIMPLE, "Creating a reference provision")
provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
prefix="referenceprovision")
- result = newprovision(names, creds, session, smbconf, provisiondir,
- provision_logger)
+ result = newprovision(names, session, smbconf, provisiondir,
+ provision_logger, base_schema="2008_R2")
result.report_logger(provision_logger)
# TODO
message(SIMPLE, "Update machine account")
update_machine_account_password(ldbs.sam, ldbs.secrets, names)
- dnToRecalculate.sort(dn_sort)
# 16) SD should be created with admin but as some previous acl were so wrong
# that admin can't modify them we have first to recreate them with the good
# form but with system account and then give the ownership to admin ...
# 18) We rebuild SD if a we have a list of DN to recalculate or if the
# defSDmodified is set.
- if defSDmodified or len(dnToRecalculate) >0:
+ if opts.full and (defSDmodified or len(dnToRecalculate) >0):
message(SIMPLE, "Some (default) security descriptors (SDs) have "
"changed, recalculating them")
ldbs.sam.set_session_info(adm_session)
# as we are assured that on this DNs we will have differences !
# Also the check must be done in a clever way as for the moment we just
# compare SDDL
- if len(dnNotToRecalculate) == 0 and (opts.debugchangesd or opts.debugall):
+ if dnNotToRecalculateFound == False and (opts.debugchangesd or opts.debugall):
message(CHANGESD, "Checking recalculated SDs")
check_updated_sd(new_ldbs.sam, ldbs.sam, names)
# 20)
updateOEMInfo(ldbs.sam, str(names.rootdn))
# 21)
- check_for_DNS(newpaths.private_dir, paths.private_dir, names.dns_backend)
+ check_for_DNS(newpaths.private_dir, paths.private_dir,
+ newpaths.binddns_dir, paths.binddns_dir,
+ names.dns_backend)
# 22)
update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
if opts.full and (names.policyid is None or names.policyid_dc is None):
if opts.full:
try:
update_gpo(paths, ldbs.sam, names, lp, message)
- except ProvisioningError, e:
+ except ProvisioningError as e:
message(ERROR, "The policy for domain controller is missing. "
"You should restart upgradeprovision with --full")
message(SIMPLE, "Reindexing finished")
shutil.rmtree(provisiondir)
- except StandardError, err:
+ except Exception as err:
message(ERROR, "A problem occurred while trying to upgrade your "
"provision. A full backup is located at %s" % backupdir)
if opts.debugall or opts.debugchange: