summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthieu Patou <mat@matws.net>2010-07-03 16:26:24 +0400
committerAndrew Bartlett <abartlet@samba.org>2010-07-15 22:08:21 +1000
commit2afc2f20b65b28140274828249160f1483090b5e (patch)
tree5d49d151add8974e647af91ca79ba2ac07b644fc
parent6c51b3a43298e43332893f52d6951bf475bae6af (diff)
downloadsamba-2afc2f20b65b28140274828249160f1483090b5e.tar.gz
samba-2afc2f20b65b28140274828249160f1483090b5e.tar.bz2
samba-2afc2f20b65b28140274828249160f1483090b5e.zip
s4 upgradeprovision: add function to backup the provision before updating
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
-rwxr-xr-xsource4/scripting/bin/upgradeprovision420
1 files changed, 268 insertions, 152 deletions
diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision
index 400d629602..c22f3ce43b 100755
--- a/source4/scripting/bin/upgradeprovision
+++ b/source4/scripting/bin/upgradeprovision
@@ -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()
-
- # 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)
-
- 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)
-
- # 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()
-
- # 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
+ 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))
+
+ # 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)
+
+ # 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)
+
+ # 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()
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)
-
- # 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)
-
- # 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)):
+ # 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)
+
+ # 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)
-
- # 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)
+ rebuild_sd(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
+
+ # 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)