1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-11 16:58:40 +03:00

s4 upgradeprovision: add function to backup the provision before updating

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
Matthieu Patou 2010-07-03 16:26:24 +04:00 committed by Andrew Bartlett
parent 6c51b3a432
commit 2afc2f20b6

View File

@ -47,7 +47,7 @@ from samba.provision import (find_setup_dir, get_domain_descriptor,
ProvisioningError, get_last_provision_usn,
get_max_usn, update_provision_usn)
from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
from samba.dcerpc import security, drsblobs
from samba.dcerpc import security, drsblobs, xattr
from samba.ndr import ndr_unpack
from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
find_provision_key_parameters, get_ldbs,
@ -1002,6 +1002,7 @@ def check_updated_sd(ref_sam, cur_sam, names):
str(reference[i]["nTSecurityDescriptor"]))
hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
for i in range(0, len(current)):
key = str(current[i]["dn"]).lower()
if hash.has_key(key):
@ -1113,6 +1114,18 @@ def removeProvisionUSN(samdb):
delta.dn = entry[0].dn
samdb.modify(delta)
def remove_stored_generated_attrs(paths, creds, session, lp):
"""Remove previously stored constructed attributes
:param paths: List of paths for different provision objects
from the upgraded provision
:param creds: A credential object
:param session: A session object
:param lp: A line parser object
:return: An associative array whose key are the different constructed
attributes and the value the dn where this attributes were found.
"""
def simple_update_basesamdb(newpaths, paths, names):
"""Update the provision container db: sam.ldb
@ -1185,6 +1198,96 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema):
return 0
def copyxattrs(dir, refdir):
""" Copy owner, groups, extended ACL and NT acls from
a reference dir to a destination dir
Both dir are supposed to hold the same files
:param dir: Destination dir
:param refdir: Reference directory"""
noxattr = 0
for root, dirs, files in os.walk(dir, topdown=True):
for name in files:
subdir=root[len(dir):]
ref = os.path.join("%s%s" % (refdir, subdir), name)
statsinfo = os.stat(ref)
tgt = os.path.join(root, name)
try:
os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
# Get the xattr attributes if any
try:
attribute = samba.xattr_native.wrap_getxattr(ref,
xattr.XATTR_NTACL_NAME)
samba.xattr_native.wrap_setxattr(tgt,
xattr.XATTR_NTACL_NAME,
attribute)
except:
noxattr = 1
attribute = samba.xattr_native.wrap_getxattr(ref,
"system.posix_acl_access")
samba.xattr_native.wrap_setxattr(tgt,
"system.posix_acl_access",
attribute)
except:
continue
for name in dirs:
subdir=root[len(dir):]
ref = os.path.join("%s%s" % (refdir, subdir), name)
statsinfo = os.stat(ref)
tgt = os.path.join(root, name)
try:
os.chown(os.path.join(root, name), statsinfo.st_uid,
statsinfo.st_gid)
try:
attribute = samba.xattr_native.wrap_getxattr(ref,
xattr.XATTR_NTACL_NAME)
samba.xattr_native.wrap_setxattr(tgt,
xattr.XATTR_NTACL_NAME,
attribute)
except:
noxattr = 1
attribute = samba.xattr_native.wrap_getxattr(ref,
"system.posix_acl_access")
samba.xattr_native.wrap_setxattr(tgt,
"system.posix_acl_access",
attribute)
except:
continue
def backup_provision(paths, dir):
"""This function backup the provision files so that a rollback
is possible
:param paths: Paths to different objects
:param dir: Directory where to store the backup
"""
shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
shutil.copy2(paths.samdb, dir)
shutil.copy2(paths.secrets, dir)
shutil.copy2(paths.idmapdb, dir)
shutil.copy2(paths.privilege, dir)
if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
shutil.copy2(paths.smbconf, dir)
shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
if not os.path.isdir(samldbdir):
samldbdir = paths.private_dir
schemaldb = os.path.join(paths.private_dir, "schema.ldb")
configldb = os.path.join(paths.private_dir, "configuration.ldb")
usersldb = os.path.join(paths.private_dir, "users.ldb")
shutil.copy2(schemaldb, dir)
shutil.copy2(usersldb, dir)
shutil.copy2(configldb, dir)
else:
shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
def setup_path(file):
return os.path.join(setup_dir, file)
@ -1210,12 +1313,13 @@ def setup_path(file):
# A) When alpha9 or alphaxx is present
# The base sam.ldb file is updated by looking at the difference between
# referrence one and the current one. Everything is copied with the
# exception of lastProvisionUSN attributes. The highest used USN
# is fetched so that changed by upgradeprovision usn can be tracked
# exception of lastProvisionUSN attributes.
# B) Other case (it reflect that that provision was done before alpha9)
# The base sam.ldb of the reference provision is copied over
# the current one, if necessary ldb related to partitions are moved
# and renamed
# The highest used USN is fetched so that changed by upgradeprovision
# usn can be tracked
# 12)A Schema object is created, it will be used to provide a complete
# schema to current provision during update (as the schema of the
# current provision might not be complete and so won't allow some
@ -1338,158 +1442,170 @@ if __name__ == '__main__':
minUSN = 0
# 2)
ldbs = get_ldbs(paths, creds, session, lp)
ldbs.startTransactions()
backupdir = tempfile.mkdtemp(dir=paths.private_dir,
prefix="backupprovision")
backup_provision(paths, backupdir)
try:
ldbs.startTransactions()
# 3) Guess all the needed names (variables in fact) from the current
# provision.
names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
paths, smbconf, lp)
# 4)
lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
if lastProvisionUSNs is not None:
message(CHANGE,
"Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
# 3) Guess all the needed names (variables in fact) from the current
# provision.
names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
paths, smbconf, lp)
# 4)
lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
if lastProvisionUSNs is not None:
message(CHANGE,
"Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
# Objects will be created with the admin session
# (not anymore system session)
adm_session = admin_session(lp, str(names.domainsid))
# So we reget handle on objects
# ldbs = get_ldbs(paths, creds, adm_session, lp)
# Objects will be created with the admin session
# (not anymore system session)
adm_session = admin_session(lp, str(names.domainsid))
# So we reget handle on objects
# ldbs = get_ldbs(paths, creds, adm_session, lp)
if not sanitychecks(ldbs.sam, names):
message(SIMPLE, "Sanity checks for the upgrade fails, checks messages"
" and correct them before rerunning upgradeprovision")
sys.exit(1)
if not sanitychecks(ldbs.sam, names):
message(SIMPLE, "Sanity checks for the upgrade fails, checks messages"
" and correct them before rerunning upgradeprovision")
sys.exit(1)
# Let's see provision parameters
print_provision_key_parameters(names)
# Let's see provision parameters
print_provision_key_parameters(names)
# 5) With all this information let's create a fresh new provision used as
# reference
message(SIMPLE, "Creating a reference provision")
provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
prefix="referenceprovision")
newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
provision_logger)
# 5) With all this information let's create a fresh new provision used as
# reference
message(SIMPLE, "Creating a reference provision")
provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
prefix="referenceprovision")
newprovision(names, setup_dir, creds, session, smbconf, provisiondir,
provision_logger)
# TODO
# 6) and 7)
# We need to get a list of object which SD is directly computed from
# defaultSecurityDescriptor.
# This will allow us to know which object we can rebuild the SD in case
# of change of the parent's SD or of the defaultSD.
# Get file paths of this new provision
newpaths = get_paths(param, targetdir=provisiondir)
new_ldbs = get_ldbs(newpaths, creds, session, lp)
new_ldbs.startTransactions()
# TODO
# 6) and 7)
# We need to get a list of object which SD is directly computed from
# defaultSecurityDescriptor.
# This will allow us to know which object we can rebuild the SD in case
# of change of the parent's SD or of the defaultSD.
# Get file paths of this new provision
newpaths = get_paths(param, targetdir=provisiondir)
new_ldbs = get_ldbs(newpaths, creds, session, lp)
new_ldbs.startTransactions()
# 8) Populate some associative array to ease the update process
# List of attribute which are link and backlink
populate_links(new_ldbs.sam, names.schemadn)
# List of attribute with ASN DN synthax)
populate_dnsyntax(new_ldbs.sam, names.schemadn)
# 9)
update_privilege(newpaths.private_dir, paths.private_dir)
# 10)
oem = getOEMInfo(ldbs.sam, str(names.rootdn))
# Do some modification on sam.ldb
ldbs.groupedCommit()
# 11)
if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
# 11) A
# Starting from alpha9 we can consider that the structure is quite ok
# and that we should do only dela
# 8) Populate some associative array to ease the update process
# List of attribute which are link and backlink
populate_links(new_ldbs.sam, names.schemadn)
# List of attribute with ASN DN synthax)
populate_dnsyntax(new_ldbs.sam, names.schemadn)
# 9)
update_privilege(newpaths.private_dir, paths.private_dir)
# 10)
oem = getOEMInfo(ldbs.sam, str(names.rootdn))
# Do some modification on sam.ldb
ldbs.groupedCommit()
new_ldbs.groupedCommit()
delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message)
# 11)
if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
# 11) A
# Starting from alpha9 we can consider that the structure is quite ok
# and that we should do only dela
delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message)
else:
# 11) B
simple_update_basesamdb(newpaths, paths, names)
ldbs = get_ldbs(paths, creds, session, lp)
removeProvisionUSN(ldbs.sam)
ldbs.startTransactions()
minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
new_ldbs.startTransactions()
else:
# 11) B
simple_update_basesamdb(newpaths, paths, names)
ldbs = get_ldbs(paths, creds, session, lp)
removeProvisionUSN(ldbs.sam)
ldbs.startTransactions()
# 12)
schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
serverdn=str(names.serverdn))
# 13)
if opts.full:
if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
schema):
message(SIMPLE, "Rollbacking every changes. Check the reason"
" of the problem")
message(SIMPLE, "In any case your system as it was before"
" the upgrade")
ldbs.groupedRollback()
new_ldbs.groupedRollback()
shutil.rmtree(provisiondir)
sys.exit(1)
# 14)
update_secrets(new_ldbs.secrets, ldbs.secrets, message)
# 15)
message(SIMPLE, "Update machine account")
update_machine_account_password(ldbs.sam, ldbs.secrets, names)
# 12)
schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
serverdn=str(names.serverdn))
# 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 ...
if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
message(SIMPLE, "Fixing old povision SD")
fix_partition_sd(ldbs.sam, names)
rebuild_sd(ldbs.sam, names)
# 13)
if opts.full:
if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
schema):
message(SIMPLE, "Rollbacking every changes. Check the reason"
" of the problem")
message(SIMPLE, "In any case your system as it was before"
" the upgrade")
ldbs.groupedRollback()
new_ldbs.groupedRollback()
shutil.rmtree(provisiondir)
sys.exit(1)
# 14)
update_secrets(new_ldbs.secrets, ldbs.secrets, message)
# 15)
message(SIMPLE, "Update machine account")
update_machine_account_password(ldbs.sam, ldbs.secrets, names)
# We calculate the max USN before recalculating the SD because we might
# touch object that have been modified after a provision and we do not
# want that the next upgradeprovision thinks that it has a green light
# to modify them
# 17)
maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
# 18) We rebuild SD only if defaultSecurityDescriptor is modified
# But in fact we should do it also if one object has its SD modified as
# child might need rebuild
if defSDmodified:
message(SIMPLE, "Updating SD")
ldbs.sam.set_session_info(adm_session)
# Alpha10 was a bit broken still
if re.match(r'.*alpha(\d|10)', str(oem)):
# 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 ...
if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
message(SIMPLE, "Fixing old povision SD")
fix_partition_sd(ldbs.sam, names)
rebuild_sd(ldbs.sam, names)
rebuild_sd(ldbs.sam, names)
# 19)
# Now we are quite confident in the recalculate process of the SD, we make
# it optional.
# Also the check must be done in a clever way as for the moment we just
# compare SDDL
if opts.debugchangesd:
check_updated_sd(new_ldbs.sam, ldbs.sam, names)
# We calculate the max USN before recalculating the SD because we might
# touch object that have been modified after a provision and we do not
# want that the next upgradeprovision thinks that it has a green light
# to modify them
# 20)
updateOEMInfo(ldbs.sam, str(names.rootdn))
# 21)
check_for_DNS(newpaths.private_dir, paths.private_dir)
# 22)
if lastProvisionUSNs is not None:
update_provision_usn(ldbs.sam, minUSN, maxUSN)
if opts.full and (names.policyid is None or names.policyid_dc is None):
update_policyids(names, ldbs.sam)
if opts.full or opts.resetfileacl:
try:
update_gpo(paths, ldbs.sam, names, lp, message, 1)
except ProvisioningError, e:
message(ERROR, "The policy for domain controller is missing,"
" you should restart upgradeprovision with --full")
else:
try:
update_gpo(paths, ldbs.sam, names, lp, message, 0)
except ProvisioningError, e:
message(ERROR, "The policy for domain controller is missing,"
" you should restart upgradeprovision with --full")
ldbs.groupedCommit()
new_ldbs.groupedCommit()
message(SIMPLE, "Upgrade finished !")
# remove reference provision now that everything is done !
shutil.rmtree(provisiondir)
# 17)
maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
# 18) We rebuild SD only if defaultSecurityDescriptor is modified
# But in fact we should do it also if one object has its SD modified as
# child might need rebuild
if defSDmodified:
message(SIMPLE, "Updating SD")
ldbs.sam.set_session_info(adm_session)
# Alpha10 was a bit broken still
if re.match(r'.*alpha(\d|10)', str(oem)):
fix_partition_sd(ldbs.sam, names)
rebuild_sd(ldbs.sam, names)
# 19)
# Now we are quite confident in the recalculate process of the SD, we make
# it optional.
# Also the check must be done in a clever way as for the moment we just
# compare SDDL
if opts.debugchangesd:
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)
# 22)
if lastProvisionUSNs is not None:
update_provision_usn(ldbs.sam, minUSN, maxUSN)
if opts.full and (names.policyid is None or names.policyid_dc is None):
update_policyids(names, ldbs.sam)
if opts.full or opts.resetfileacl:
try:
update_gpo(paths, ldbs.sam, names, lp, message, 1)
except ProvisioningError, e:
message(ERROR, "The policy for domain controller is missing,"
" you should restart upgradeprovision with --full")
else:
try:
update_gpo(paths, ldbs.sam, names, lp, message, 0)
except ProvisioningError, e:
message(ERROR, "The policy for domain controller is missing,"
" you should restart upgradeprovision with --full")
ldbs.groupedCommit()
new_ldbs.groupedCommit()
message(SIMPLE, "Upgrade finished !")
# remove reference provision now that everything is done !
shutil.rmtree(provisiondir)
except StandardError, err:
message(ERROR,"A problem has occured when trying to upgrade your provision,"
" a full backup is located at %s" % backupdir)
if opts.changeall:
(typ, val, tb) = sys.exc_info()
traceback.print_exception(typ, val, tb)