From 06780ae82281fb62a08d0c3604d2e679976756c2 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sat, 16 Feb 2013 08:51:51 +1100 Subject: samba_upgradeprovision: Remove options to fix FS ACLs samba-tool ntacl sysvolreset handles this better, and makes this tool much less confusing internally. Andrew Bartlett Reviewed-by: Matthieu Patou Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Tue Feb 19 06:06:41 CET 2013 on sn-devel-104 --- source4/scripting/bin/samba_upgradeprovision | 427 +++++++++++------------ source4/scripting/python/samba/upgradehelpers.py | 49 +-- 2 files changed, 205 insertions(+), 271 deletions(-) diff --git a/source4/scripting/bin/samba_upgradeprovision b/source4/scripting/bin/samba_upgradeprovision index e2c57f2998..570f7839fc 100755 --- a/source4/scripting/bin/samba_upgradeprovision +++ b/source4/scripting/bin/samba_upgradeprovision @@ -186,12 +186,6 @@ parser.add_option("--debugchangesd", action="store_true", help="Print security descriptor differences") parser.add_option("--debugall", action="store_true", help="Print all available information (very verbose)") -parser.add_option("--resetfileacl", action="store_true", - help="Force a reset on filesystem acls in sysvol / netlogon share") -parser.add_option("--nontaclfix", action="store_true", - help="In full upgrade mode do not try to upgrade sysvol / netlogon acls") -parser.add_option("--fixntacl", action="store_true", - help="Only fix NT ACLs in sysvol / netlogon share") parser.add_option("--db_backup_only", action="store_true", help="Do the backup of the database in the provision, skip the sysvol / netlogon shares") parser.add_option("--full", action="store_true", @@ -1726,8 +1720,6 @@ if __name__ == '__main__': global defSDmodified defSDmodified = False - if opts.nontaclfix and opts.fixntacl: - message(SIMPLE, "nontaclfix and fixntacl are mutally exclusive") # From here start the big steps of the program # 1) First get files paths paths = get_paths(param, smbconf=smbconf) @@ -1787,225 +1779,214 @@ if __name__ == '__main__': adm_session = admin_session(lp, str(names.domainsid)) # So we reget handle on objects # ldbs = get_ldbs(paths, creds, adm_session, lp) - if not opts.fixntacl: - if not sanitychecks(ldbs.sam, names): - message(SIMPLE, "Sanity checks for the upgrade have failed. " - "Check the messages and correct the errors " - "before rerunning upgradeprovision") - ldbs.groupedRollback() - 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") - result = newprovision(names, creds, session, smbconf, provisiondir, - provision_logger) - result.report_logger(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() - - populateNotReplicated(new_ldbs.sam, names.schemadn) - # 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() - deltaattr = None - # 11) - message(GUESS, oem) - if oem is None or hasATProvision(ldbs.sam) or 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 - deltaattr = 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() - - # 12) - schema = Schema(names.domainsid, schemadn=str(names.schemadn)) - # We create a closure that will be invoked just before schema reload - def schemareloadclosure(): - basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp, - options=["modules:"]) - doit = False - if deltaattr is not None and len(deltaattr) > 1: - doit = True - if doit: - deltaattr.remove("dn") - for att in deltaattr: - if att.lower() == "dn": - continue - if (deltaattr.get(att) is not None - and deltaattr.get(att).flags() != FLAG_MOD_ADD): - doit = False - elif deltaattr.get(att) is None: - doit = False - if doit: - message(CHANGE, "Applying delta to @ATTRIBUTES") - deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES") - basesam.modify(deltaattr) - else: - message(CHANGE, "Not applying delta to @ATTRIBUTES because " - "there is not only add") - # 13) - if opts.full: - if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs, - schema, schemareloadclosure): - message(SIMPLE, "Rolling back all changes. Check the cause" - " of the problem") - message(SIMPLE, "Your system is as it was before the upgrade") - ldbs.groupedRollback() - new_ldbs.groupedRollback() - shutil.rmtree(provisiondir) - sys.exit(1) - else: - # Try to reapply the change also when we do not change the sam - # as the delta_upgrade - schemareloadclosure() - sync_calculated_attributes(ldbs.sam, names) - res = ldbs.sam.search(expression="(samaccountname=dns)", - scope=SCOPE_SUBTREE, attrs=["dn"], - controls=["search_options:1:2"]) - if len(res) > 0: - message(SIMPLE, "You still have the old DNS object for managing " - "dynamic DNS, but you didn't supply --full so " - "a correct update can't be done") - ldbs.groupedRollback() - new_ldbs.groupedRollback() - shutil.rmtree(provisiondir) - sys.exit(1) - # 14) - update_secrets(new_ldbs.secrets, ldbs.secrets, message) - # 14bis) - res = ldbs.sam.search(expression="(samaccountname=dns)", - scope=SCOPE_SUBTREE, attrs=["dn"], - controls=["search_options:1:2"]) + if not sanitychecks(ldbs.sam, names): + message(SIMPLE, "Sanity checks for the upgrade have failed. " + "Check the messages and correct the errors " + "before rerunning upgradeprovision") + ldbs.groupedRollback() + 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") + result = newprovision(names, creds, session, smbconf, provisiondir, + provision_logger) + result.report_logger(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() + + populateNotReplicated(new_ldbs.sam, names.schemadn) + # 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() + deltaattr = None + # 11) + message(GUESS, oem) + if oem is None or hasATProvision(ldbs.sam) or 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 + deltaattr = 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) - if (len(res) == 1): - ldbs.sam.delete(res[0]["dn"]) - res2 = ldbs.secrets.search(expression="(samaccountname=dns)", - scope=SCOPE_SUBTREE, attrs=["dn"]) - update_dns_account_password(ldbs.sam, ldbs.secrets, names) - message(SIMPLE, "IMPORTANT!!! " - "If you were using Dynamic DNS before you need " - "to update your configuration, so that the " - "tkey-gssapi-credential has the following value: " - "DNS/%s.%s" % (names.netbiosname.lower(), - names.realm.lower())) - # 15) - 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 ... - if str(oem) != "" and not re.match(r'.*alpha(9|\d\d+)', str(oem)): - message(SIMPLE, "Fixing very old provision SD") - 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 if a we have a list of DN to recalculate or if the - # defSDmodified is set. - if defSDmodified or len(dnToRecalculate) >0: - message(SIMPLE, "Some (default) security descriptors (SDs) have " - "changed, recalculating them") - ldbs.sam.set_session_info(adm_session) - rebuild_sd(ldbs.sam, names) - - # 19) - # Now we are quite confident in the recalculate process of the SD, we make - # it optional. And we don't do it if there is DN that we must touch - # 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): - 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) - # 22) - if lastProvisionUSNs is not None: - update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation) - if opts.full and (names.policyid is None or names.policyid_dc is None): - update_policyids(names, ldbs.sam) - if opts.nontaclfix: - if opts.full or opts.resetfileacl or opts.fixntacl: - 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") - except IOError, e: - message(ERROR, "Setting ACL not supported on your filesystem") + ldbs.startTransactions() + minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1 + new_ldbs.startTransactions() + + # 12) + schema = Schema(names.domainsid, schemadn=str(names.schemadn)) + # We create a closure that will be invoked just before schema reload + def schemareloadclosure(): + basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp, + options=["modules:"]) + doit = False + if deltaattr is not None and len(deltaattr) > 1: + doit = True + if doit: + deltaattr.remove("dn") + for att in deltaattr: + if att.lower() == "dn": + continue + if (deltaattr.get(att) is not None + and deltaattr.get(att).flags() != FLAG_MOD_ADD): + doit = False + elif deltaattr.get(att) is None: + doit = False + if doit: + message(CHANGE, "Applying delta to @ATTRIBUTES") + deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES") + basesam.modify(deltaattr) 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") - if not opts.fixntacl: - ldbs.groupedCommit() - new_ldbs.groupedCommit() - message(SIMPLE, "Upgrade finished!") - # remove reference provision now that everything is done ! - # So we have reindexed first if need when the merged schema was reloaded - # (as new attributes could have quick in) - # But the second part of the update (when we update existing objects - # can also have an influence on indexing as some attribute might have their - # searchflag modificated - message(SIMPLE, "Reopening samdb to trigger reindexing if needed " - "after modification") - samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp) - message(SIMPLE, "Reindexing finished") - - shutil.rmtree(provisiondir) + message(CHANGE, "Not applying delta to @ATTRIBUTES because " + "there is not only add") + # 13) + if opts.full: + if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs, + schema, schemareloadclosure): + message(SIMPLE, "Rolling back all changes. Check the cause" + " of the problem") + message(SIMPLE, "Your system is as it was before the upgrade") + ldbs.groupedRollback() + new_ldbs.groupedRollback() + shutil.rmtree(provisiondir) + sys.exit(1) else: - ldbs.groupedRollback() - message(SIMPLE, "ACLs fixed !") + # Try to reapply the change also when we do not change the sam + # as the delta_upgrade + schemareloadclosure() + sync_calculated_attributes(ldbs.sam, names) + res = ldbs.sam.search(expression="(samaccountname=dns)", + scope=SCOPE_SUBTREE, attrs=["dn"], + controls=["search_options:1:2"]) + if len(res) > 0: + message(SIMPLE, "You still have the old DNS object for managing " + "dynamic DNS, but you didn't supply --full so " + "a correct update can't be done") + ldbs.groupedRollback() + new_ldbs.groupedRollback() + shutil.rmtree(provisiondir) + sys.exit(1) + # 14) + update_secrets(new_ldbs.secrets, ldbs.secrets, message) + # 14bis) + res = ldbs.sam.search(expression="(samaccountname=dns)", + scope=SCOPE_SUBTREE, attrs=["dn"], + controls=["search_options:1:2"]) + + if (len(res) == 1): + ldbs.sam.delete(res[0]["dn"]) + res2 = ldbs.secrets.search(expression="(samaccountname=dns)", + scope=SCOPE_SUBTREE, attrs=["dn"]) + update_dns_account_password(ldbs.sam, ldbs.secrets, names) + message(SIMPLE, "IMPORTANT!!! " + "If you were using Dynamic DNS before you need " + "to update your configuration, so that the " + "tkey-gssapi-credential has the following value: " + "DNS/%s.%s" % (names.netbiosname.lower(), + names.realm.lower())) + # 15) + 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 ... + if str(oem) != "" and not re.match(r'.*alpha(9|\d\d+)', str(oem)): + message(SIMPLE, "Fixing very old provision SD") + 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 if a we have a list of DN to recalculate or if the + # defSDmodified is set. + if defSDmodified or len(dnToRecalculate) >0: + message(SIMPLE, "Some (default) security descriptors (SDs) have " + "changed, recalculating them") + ldbs.sam.set_session_info(adm_session) + rebuild_sd(ldbs.sam, names) + + # 19) + # Now we are quite confident in the recalculate process of the SD, we make + # it optional. And we don't do it if there is DN that we must touch + # 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): + 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) + # 22) + if lastProvisionUSNs is not None: + update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation) + if opts.full and (names.policyid is None or names.policyid_dc is None): + update_policyids(names, ldbs.sam) + + if opts.full: + try: + update_gpo(paths, ldbs.sam, names, lp, message) + 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 ! + # So we have reindexed first if need when the merged schema was reloaded + # (as new attributes could have quick in) + # But the second part of the update (when we update existing objects + # can also have an influence on indexing as some attribute might have their + # searchflag modificated + message(SIMPLE, "Reopening samdb to trigger reindexing if needed " + "after modification") + samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp) + message(SIMPLE, "Reindexing finished") + + shutil.rmtree(provisiondir) except StandardError, err: message(ERROR, "A problem occurred while trying to upgrade your " "provision. A full backup is located at %s" % backupdir) diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py index 8427f6ae37..1ec19d4ab6 100644 --- a/source4/scripting/python/samba/upgradehelpers.py +++ b/source4/scripting/python/samba/upgradehelpers.py @@ -560,32 +560,9 @@ def updateOEMInfo(samdb, rootdn): "oEMInformation" ) samdb.modify(delta) -def update_gpo(paths, samdb, names, lp, message, force=0): +def update_gpo(paths, samdb, names, lp, message): """Create missing GPO file object if needed - - Set ACL correctly also. - Check ACLs for sysvol/netlogon dirs also """ - resetacls = False - try: - ntacls.checkset_backend(lp, None, None) - eadbname = lp.get("posix:eadb") - if eadbname is not None and eadbname != "": - try: - attribute = samba.xattr_tdb.wrap_getxattr(eadbname, - paths.sysvol, xattr.XATTR_NTACL_NAME) - except Exception: - attribute = samba.xattr_native.wrap_getxattr(paths.sysvol, - xattr.XATTR_NTACL_NAME) - else: - attribute = samba.xattr_native.wrap_getxattr(paths.sysvol, - xattr.XATTR_NTACL_NAME) - except Exception: - resetacls = True - - if force: - resetacls = True - dir = getpolicypath(paths.sysvol, names.dnsdomain, names.policyid) if not os.path.isdir(dir): create_gpo_struct(dir) @@ -596,30 +573,6 @@ def update_gpo(paths, samdb, names, lp, message, force=0): if not os.path.isdir(dir): create_gpo_struct(dir) - def acl_error(e): - if os.geteuid() == 0: - message(ERROR, "Unable to set ACLs on policies related objects: %s" % e) - else: - message(ERROR, "Unable to set ACLs on policies related objects. " - "ACLs must be set as root if file system ACLs " - "(rather than posix:eadb) are used.") - - # We always reinforce acls on GPO folder because they have to be in sync - # with the one in DS - try: - set_gpos_acl(paths.sysvol, names.dnsdomain, names.domainsid, - names.domaindn, samdb, lp) - except TypeError, e: - acl_error(e) - - if resetacls: - try: - setsysvolacl(samdb, paths.netlogon, paths.sysvol, names.root_gid, - names.domainsid, names.dnsdomain, names.domaindn, lp) - except TypeError, e: - acl_error(e) - - def increment_calculated_keyversion_number(samdb, rootdn, hashDns): """For a given hash associating dn and a number, this function will update the replPropertyMetaData of each dn in the hash, so that the -- cgit